From 54d577ea082a9c3bf8f78c7338d0ff5b590fc060 Mon Sep 17 00:00:00 2001 From: mkelvers Date: Mon, 20 Apr 2026 16:05:54 +0200 Subject: [PATCH] refactor(templates): simplify anime details using extracted components --- web/templates/anime.templ | 239 ++++++-------------------------------- 1 file changed, 38 insertions(+), 201 deletions(-) diff --git a/web/templates/anime.templ b/web/templates/anime.templ index 93a35a6..1f36926 100644 --- a/web/templates/anime.templ +++ b/web/templates/anime.templ @@ -1,15 +1,21 @@ package templates -import "mal/internal/jikan" -import "mal/internal/shared/ui" -import "fmt" -import "strings" +import ( + "fmt" + "strings" + + "mal/integrations/jikan" + animecomponents "mal/web/components/anime" + "mal/web/components" + watchlistcomponents "mal/web/components/watchlist" + "mal/web/shared" +) templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) { @Layout("mal - " + anime.DisplayTitle(), true) {
-
+
if anime.ImageURL() != "" { { @@ -36,15 +42,15 @@ templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) { { anime.ShortDuration() } }
-
-
- @WatchlistDropdown(anime.MalID, anime.Title, anime.TitleEnglish, anime.TitleJapanese, anime.ImageURL(), currentStatus, anime.Airing) - Watch +
+
+ @watchlistcomponents.WatchlistDropdown(anime.MalID, anime.Title, anime.TitleEnglish, anime.TitleJapanese, anime.ImageURL(), currentStatus, anime.Airing) + Watch +
-
if anime.Synopsis != "" {

{ anime.Synopsis }

@@ -54,18 +60,18 @@ templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) {
-
-

Related

-
- @ui.LoadingIndicator("Loading relations") -
-
-
-

Recommendations

-
- @ui.LoadingIndicator("Loading recommendations") -
-
+
+

Related

+
+ @components.LoadingIndicator("Loading relations") +
+
+
+

Recommendations

+
+ @components.LoadingIndicator("Loading recommendations") +
+
} } - -func watchTargetEpisode(currentStatus string, nextEpisode int) int { - if currentStatus == "watching" && nextEpisode > 0 { - return nextEpisode - } - - return 1 -} - -templ AnimePending(id int) { - @Layout("mal - anime pending", true) { -
-
-
-

Anime data is being fetched

-

We could not load this anime right now. A background worker is retrying data fetch for anime #{ fmt.Sprintf("%d", id) }.

-

Refresh this page in a few seconds.

-
-
-
- - } -} - -func joinNames(entities []jikan.NamedEntity) string { - names := make([]string, len(entities)) - for i, e := range entities { - names[i] = e.Name - } - return strings.Join(names, ", ") -} - -func joinStreamingNames(anime jikan.Anime) string { - names := make([]string, len(anime.Streaming)) - for i, s := range anime.Streaming { - names[i] = s.Name - } - return strings.Join(names, ", ") -} - -templ WatchlistDropdown(animeID int, animeTitle string, animeTitleEnglish string, animeTitleJapanese string, animeImage string, currentStatus string, airing bool) { -
- - -
-} - -templ dropdownStatusOption(animeID int, animeTitle string, animeTitleEnglish string, animeTitleJapanese string, animeImage string, status string, currentStatus string, airing bool) { - -} - -func formatStatus(status string) string { - switch status { - case "watching": - return "Watching" - case "completed": - return "Completed" - case "on_hold": - return "On hold" - case "dropped": - return "Dropped" - case "plan_to_watch": - return "Plan to watch" - default: - return status - } -} - -templ AnimeRelationsList(relations []jikan.RelationEntry) { - if len(relations) > 1 { -
- for _, rel := range relations { - @ui.AnimeCard(ui.AnimeCardProps{ - ID: rel.Anime.MalID, - Title: rel.Anime.DisplayTitle(), - ImageURL: rel.Anime.ImageURL(), - Class: relationCardClass(rel), - ImgClass: "relation-thumb", - TitleClass: "relation-title", - CurrentNode: rel.IsCurrent, - }) { - if rel.IsCurrent { -
- } - if rel.Relation != "" && rel.Relation != "Current" { -
{ rel.Relation }
- } - } - } -
- } else { -

No related anime found.

- } -} - -func relationCardClass(rel jikan.RelationEntry) string { - return "relation-card min-w-0 flex flex-col bg-transparent text-inherit no-underline" -} - -templ AnimeRecommendations(recs []jikan.Anime) { - if len(recs) > 0 { -
- for _, anime := range recs { - @ui.AnimeCard(ui.AnimeCardProps{ - ID: anime.MalID, - Title: anime.DisplayTitle(), - ImageURL: anime.ImageURL(), - Class: "relation-card", - ImgClass: "relation-thumb", - TitleClass: "relation-title", - }) - } -
- } else { -

No recommendations available.

- } -} - -func hasExtraSidebarDetails(anime jikan.Anime) bool { - return anime.TitleJapanese != "" || len(anime.TitleSynonyms) > 0 || len(anime.Studios) > 0 || len(anime.Producers) > 0 || anime.Source != "" || len(anime.Demographics) > 0 || len(anime.Themes) > 0 || anime.Broadcast.String != "" || len(anime.Streaming) > 0 -} - -templ studioLinks(studios []jikan.NamedEntity) { - for i, studio := range studios { - { studio.Name } - if i < len(studios)-1 { - , - } - } -}