lyrics

lyrics
git clone git://brookjeynes.dev/bjeynes/lyrics.git
Log | Files | Refs

commit 5a0dab710ca30af1094bd3d437ca83553b4dee8a
parent 0ca68e5b20e1d1b08ee1780a86bf94f3001186a6
Author: brookjeynes <me@brookjeynes.dev>
Date:   Tue, 12 May 2026 18:12:27 +1000

feat: create album page

Signed-off-by: brookjeynes <me@brookjeynes.dev>

Diffstat:
Ainternal/server/album.go | 47+++++++++++++++++++++++++++++++++++++++++++++++
Minternal/server/router.go | 3+++
Ainternal/server/ui/views/album/album.go | 7+++++++
Ainternal/server/ui/views/album/album.templ | 49+++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 106 insertions(+), 0 deletions(-)

diff --git a/internal/server/album.go b/internal/server/album.go @@ -0,0 +1,47 @@ +package server + +import ( + "fmt" + "lyrics/internal/db" + "lyrics/internal/domain" + "lyrics/internal/server/htmx" + albumview "lyrics/internal/server/ui/views/album" + "net/http" + "strconv" +) + +func (ss *ServerState) Album(w http.ResponseWriter, r *http.Request) { + logger := ss.logger.With("handler", "Album") + + idStr := r.PathValue("id") + id, err := strconv.Atoi(idStr) + if err != nil { + logger.Error("failed to convert album id to integer", "id", idStr, "err", err) + htmx.HxError(w, http.StatusBadRequest, err.Error()) + return + } + + slug := r.PathValue("slug") + logger = logger.With("id", id, "slug", slug) + + album, err := db.GetAlbum(ss.db, domain.AlbumID(id)) + if err != nil { + logger.Error("failed to get album", "err", err) + htmx.HxError(w, http.StatusInternalServerError, fmt.Sprintf("failed to query for album '%s'", slug)) + return + } + + albumTitle := album.Title + if album.LatinName != nil { + albumTitle = *album.LatinName + } + + if domain.Slug(albumTitle) != slug { + htmx.HxRedirect(w, http.StatusMovedPermanently, fmt.Sprintf("/album/%d/%s", id, domain.Slug(albumTitle))) + return + } + + albumview.Album(albumview.AlbumParams{ + Album: album, + }).Render(r.Context(), w) +} diff --git a/internal/server/router.go b/internal/server/router.go @@ -23,6 +23,9 @@ func (ss *ServerState) Router() http.Handler { }) router.Route("/album", func(router chi.Router) { + router.Route("/{id}", func(router chi.Router) { + router.Get("/{slug}", ss.Album) + }) router.Get("/new", ss.NewAlbum) router.Post("/new", ss.NewAlbum) router.Get("/search", ss.SearchAlbum) diff --git a/internal/server/ui/views/album/album.go b/internal/server/ui/views/album/album.go @@ -0,0 +1,7 @@ +package album + +import "lyrics/internal/domain" + +type AlbumParams struct { + Album domain.Album +} diff --git a/internal/server/ui/views/album/album.templ b/internal/server/ui/views/album/album.templ @@ -0,0 +1,49 @@ +package album + +import ( + "fmt" + "lyrics/internal/server/ui/layouts/base" + "time" +) + +templ Album(params AlbumParams) { + @base.Base(base.BaseParams{Title: params.Album.Title}) { + <article> + <header> + <hgroup> + <h1> + { params.Album.Title } + if params.Album.LatinName != nil { + <span>({ *params.Album.LatinName })</span> + } + </h1> + </hgroup> + <ul> + for _, artist := range params.Album.Artists { + <li> + <a href={ fmt.Sprintf("/artist/%d/%s", artist.Artist.ID, artist.Artist.Name) }> + { artist.Artist.Name } + if artist.Artist.LatinName != nil { + <span>({ *artist.Artist.LatinName })</span> + } + </a> + </li> + } + </ul> + <time datetime={ params.Album.ReleaseDate.Format(time.DateOnly) }>{ params.Album.ReleaseDate.Format("Jan 2, 2006") }</time> + </header> + <ol> + for _, song := range params.Album.Songs { + <li> + <a href={ fmt.Sprintf("/song/%d/%s", song.ID, song.Title) }> + { song.Title } + if song.LatinTitle != nil { + <span>({ *song.LatinTitle })</span> + } + </a> + </li> + } + </ol> + </article> + } +}