diff --git a/internal/handlers/watchlist.go b/internal/handlers/watchlist.go
new file mode 100644
index 0000000..34dd021
--- /dev/null
+++ b/internal/handlers/watchlist.go
@@ -0,0 +1,160 @@
+package handlers
+
+import (
+ "fmt"
+ "net/http"
+ "strconv"
+
+ "github.com/google/uuid"
+
+ "malago/internal/database"
+ "malago/internal/middleware"
+ "malago/internal/templates"
+)
+
+type WatchlistHandler struct {
+ db database.Querier
+}
+
+func NewWatchlistHandler(db database.Querier) *WatchlistHandler {
+ return &WatchlistHandler{db: db}
+}
+
+func (h *WatchlistHandler) HandleUpdateWatchlist(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ user, ok := r.Context().Value(middleware.UserContextKey).(*database.User)
+ if !ok || user == nil {
+ w.Header().Set("HX-Redirect", "/login")
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
+
+ if err := r.ParseForm(); err != nil {
+ http.Error(w, "invalid form", http.StatusBadRequest)
+ return
+ }
+
+ animeIDStr := r.FormValue("anime_id")
+ animeTitle := r.FormValue("anime_title")
+ animeImage := r.FormValue("anime_image")
+ status := r.FormValue("status")
+
+ animeID, err := strconv.ParseInt(animeIDStr, 10, 64)
+ if err != nil {
+ http.Error(w, "invalid anime ID", http.StatusBadRequest)
+ return
+ }
+
+ // Ensure the anime exists in our local DB first (foreign key constraint)
+ _, err = h.db.UpsertAnime(r.Context(), database.UpsertAnimeParams{
+ ID: animeID,
+ Title: animeTitle,
+ ImageUrl: animeImage,
+ })
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to save anime reference: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ // Now insert/update the watchlist entry
+ entryID := uuid.New().String()
+ _, err = h.db.UpsertWatchListEntry(r.Context(), database.UpsertWatchListEntryParams{
+ ID: entryID,
+ UserID: user.ID,
+ AnimeID: animeID,
+ Status: status,
+ })
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to update watchlist: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ // For HTMX, we can just return a success toast or update a portion of the UI
+ displayStatus := status
+ switch status {
+ case "on_hold":
+ displayStatus = "on hold"
+ case "plan_to_watch":
+ displayStatus = "plan to watch"
+ }
+
+ w.Header().Set("HX-Trigger", fmt.Sprintf(`{"toast": "added to %s"}`, displayStatus))
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func (h *WatchlistHandler) HandleDeleteWatchlist(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodDelete {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ user, ok := r.Context().Value(middleware.UserContextKey).(*database.User)
+ if !ok || user == nil {
+ w.Header().Set("HX-Redirect", "/login")
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
+
+ animeIDStr := r.URL.Path[len("/api/watchlist/"):]
+ animeID, err := strconv.ParseInt(animeIDStr, 10, 64)
+ if err != nil {
+ http.Error(w, "invalid anime ID", http.StatusBadRequest)
+ return
+ }
+
+ err = h.db.DeleteWatchListEntry(r.Context(), database.DeleteWatchListEntryParams{
+ UserID: user.ID,
+ AnimeID: animeID,
+ })
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to delete from watchlist: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("HX-Trigger", `{"toast": "removed from watchlist"}`)
+ w.WriteHeader(http.StatusOK)
+}
+
+func (h *WatchlistHandler) HandleGetWatchlist(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ layout := r.URL.Query().Get("view")
+ if layout != "grid" && layout != "table" {
+ layout = "table"
+ }
+
+ statusFilter := r.URL.Query().Get("status")
+
+ user, ok := r.Context().Value(middleware.UserContextKey).(*database.User)
+ if !ok || user == nil {
+ http.Redirect(w, r, "/login", http.StatusFound)
+ return
+ }
+
+ entries, err := h.db.GetUserWatchList(r.Context(), user.ID)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("failed to fetch watchlist: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ var filteredEntries []database.GetUserWatchListRow
+ if statusFilter != "" && statusFilter != "all" {
+ for _, entry := range entries {
+ if entry.Status == statusFilter {
+ filteredEntries = append(filteredEntries, entry)
+ }
+ }
+ } else {
+ statusFilter = "all"
+ filteredEntries = entries
+ }
+
+ templates.Watchlist(filteredEntries, layout, statusFilter).Render(r.Context(), w)
+}
diff --git a/internal/templates/anime.templ b/internal/templates/anime.templ
new file mode 100644
index 0000000..d60a004
--- /dev/null
+++ b/internal/templates/anime.templ
@@ -0,0 +1,141 @@
+package templates
+
+import "malago/internal/jikan"
+import "fmt"
+
+templ AnimeDetails(anime jikan.Anime) {
+ @Layout("malago - " + anime.DisplayTitle()) {
+
+
+
+
+
+
+
+ Synopsis
+ if anime.Synopsis != "" {
+ { anime.Synopsis }
+ } else {
+ No synopsis available.
+ }
+
+
+
> loading relations...
+
+
+
+
+ }
+}
+
+templ AnimeRelationsList(relations []jikan.RelationEntry) {
+
+
Related Anime (Chronological)
+ if len(relations) <= 1 {
+
no prequel or sequel relations found.
+ } else {
+
+ for _, rel := range relations {
+ -
+ if rel.IsCurrent {
+ > { rel.Anime.DisplayTitle() } (Current)
+ } else {
+ { rel.Anime.DisplayTitle() }
+ }
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/internal/templates/anime_templ.go b/internal/templates/anime_templ.go
new file mode 100644
index 0000000..ed788c7
--- /dev/null
+++ b/internal/templates/anime_templ.go
@@ -0,0 +1,490 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.3.1001
+package templates
+
+//lint:file-ignore SA4006 This context is only used if a nested component is present.
+
+import "github.com/a-h/templ"
+import templruntime "github.com/a-h/templ/runtime"
+
+import "malago/internal/jikan"
+import "fmt"
+
+func AnimeDetails(anime jikan.Anime) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var1 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var1 == nil {
+ templ_7745c5c3_Var1 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "Synopsis
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if anime.Synopsis != "" {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var19 string
+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(anime.Synopsis)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/anime.templ`, Line: 108, Col: 52}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "No synopsis available.
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, " ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+ templ_7745c5c3_Err = Layout("malago - "+anime.DisplayTitle()).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+func AnimeRelationsList(relations []jikan.RelationEntry) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var21 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var21 == nil {
+ templ_7745c5c3_Var21 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "Related Anime (Chronological)
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if len(relations) <= 1 {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
no prequel or sequel relations found.
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, rel := range relations {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "- ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if rel.IsCurrent {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "> ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var22 string
+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(rel.Anime.DisplayTitle())
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/anime.templ`, Line: 132, Col: 71}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, " (Current)")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var24 string
+ templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(rel.Anime.DisplayTitle())
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/anime.templ`, Line: 134, Col: 98}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/internal/templates/watchlist.templ b/internal/templates/watchlist.templ
new file mode 100644
index 0000000..9effe45
--- /dev/null
+++ b/internal/templates/watchlist.templ
@@ -0,0 +1,100 @@
+package templates
+
+import (
+ "fmt"
+ "malago/internal/database"
+)
+
+templ Watchlist(entries []database.GetUserWatchListRow, layout string, currentStatus string) {
+ @Layout("My Watchlist") {
+
+
+
+
+ if len(entries) == 0 {
+
+ } else {
+ if layout == "grid" {
+
+ for _, entry := range entries {
+
+ }
+
+ } else {
+
+
+
+ | Image |
+ Title |
+ Status |
+ |
+
+
+
+ for _, entry := range entries {
+
+
+
+
+
+ |
+
+
+ { entry.Title }
+
+ |
+ { entry.Status } |
+
+
+ |
+
+ }
+
+
+ }
+ }
+ }
+}
+
+func viewLinkStyle(active bool) string {
+ if active {
+ return "color: var(--text); font-weight: bold; text-decoration: none;"
+ }
+ return "color: var(--text-muted); text-decoration: none;"
+}
+
+func tabLinkStyle(active bool) string {
+ if active {
+ return "color: var(--text); font-weight: bold; text-decoration: none; border-bottom: 2px solid var(--text); padding-bottom: 7px;"
+ }
+ return "color: var(--text-muted); text-decoration: none; padding-bottom: 7px;"
+}
diff --git a/internal/templates/watchlist_templ.go b/internal/templates/watchlist_templ.go
new file mode 100644
index 0000000..6b220bd
--- /dev/null
+++ b/internal/templates/watchlist_templ.go
@@ -0,0 +1,559 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.3.1001
+package templates
+
+//lint:file-ignore SA4006 This context is only used if a nested component is present.
+
+import "github.com/a-h/templ"
+import templruntime "github.com/a-h/templ/runtime"
+
+import (
+ "fmt"
+ "malago/internal/database"
+)
+
+func Watchlist(entries []database.GetUserWatchListRow, layout string, currentStatus string) templ.Component {
+ return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
+ return templ_7745c5c3_CtxErr
+ }
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var1 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var1 == nil {
+ templ_7745c5c3_Var1 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
+ if !templ_7745c5c3_IsBuffer {
+ defer func() {
+ templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err == nil {
+ templ_7745c5c3_Err = templ_7745c5c3_BufErr
+ }
+ }()
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if len(entries) == 0 {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ if layout == "grid" {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, entry := range entries {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if entry.ImageUrl != "" {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "no image
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var23 string
+ templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Title)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/watchlist.templ`, Line: 45, Col: 21}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var24 string
+ templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Status)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/watchlist.templ`, Line: 46, Col: 95}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "| Image | Title | Status | |
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, entry := range entries {
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ") | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var32 string
+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Title)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/watchlist.templ`, Line: 72, Col: 31}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, " | ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var33 string
+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(entry.Status)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/templates/watchlist.templ`, Line: 75, Col: 26}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, " | |
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ }
+ return nil
+ })
+ templ_7745c5c3_Err = Layout("My Watchlist").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return nil
+ })
+}
+
+func viewLinkStyle(active bool) string {
+ if active {
+ return "color: var(--text); font-weight: bold; text-decoration: none;"
+ }
+ return "color: var(--text-muted); text-decoration: none;"
+}
+
+func tabLinkStyle(active bool) string {
+ if active {
+ return "color: var(--text); font-weight: bold; text-decoration: none; border-bottom: 2px solid var(--text); padding-bottom: 7px;"
+ }
+ return "color: var(--text-muted); text-decoration: none; padding-bottom: 7px;"
+}
+
+var _ = templruntime.GeneratedTemplate