fix: break import cycle by moving Layout to shared package and fix import paths

This commit is contained in:
2026-04-20 16:59:55 +02:00
parent b57d689468
commit 8088ab7105
13 changed files with 60 additions and 48 deletions

View File

@@ -2,11 +2,11 @@ package anime
import ( import (
"fmt" "fmt"
"mal/web/templates" "mal/web/shared/layout"
) )
templ Pending(id int) { templ Pending(id int) {
@templates.Layout("mal - anime pending", true) { @layout.Layout("mal - anime pending", true) {
<div class="grid items-start gap-5 xl:grid-cols-[minmax(0,1fr)_300px]"> <div class="grid items-start gap-5 xl:grid-cols-[minmax(0,1fr)_300px]">
<div class="grid min-w-0 gap-8"> <div class="grid min-w-0 gap-8">
<section> <section>

View File

@@ -2,7 +2,7 @@ package ui
import ( import (
"fmt" "fmt"
"mal/internal/jikan" "mal/integrations/jikan"
) )
templ InfiniteAnimeList(animes []jikan.Anime, hasNext bool, nextURL string, containerID string) { templ InfiniteAnimeList(animes []jikan.Anime, hasNext bool, nextURL string, containerID string) {

View File

@@ -1,4 +1,4 @@
package templates package layout
import "mal/web/components/icons" import "mal/web/components/icons"

View File

@@ -9,10 +9,11 @@ import (
"mal/web/components" "mal/web/components"
watchlistcomponents "mal/web/components/watchlist" watchlistcomponents "mal/web/components/watchlist"
"mal/web/shared" "mal/web/shared"
"mal/web/shared/layout"
) )
templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) { templ AnimeDetails(anime jikan.Anime, currentStatus string, nextEpisode int) {
@Layout("mal - " + anime.DisplayTitle(), true) { @layout.Layout("mal - " + anime.DisplayTitle(), true) {
<div class="grid items-start gap-5 xl:grid-cols-[minmax(0,1fr)_300px]"> <div class="grid items-start gap-5 xl:grid-cols-[minmax(0,1fr)_300px]">
<div class="grid min-w-0 gap-8"> <div class="grid min-w-0 gap-8">
<div class="grid gap-5 lg:grid-cols-[220px_minmax(0,1fr)]"> <div class="grid gap-5 lg:grid-cols-[220px_minmax(0,1fr)]">

View File

@@ -1,7 +1,9 @@
package templates package templates
import "mal/web/shared/layout"
templ Login(formError string, username string) { templ Login(formError string, username string) {
@Layout("Login", false) { @layout.Layout("Login", false) {
<div class="w-full max-w-xl"> <div class="w-full max-w-xl">
<div class="mx-auto w-full bg-(--panel) p-6"> <div class="mx-auto w-full bg-(--panel) p-6">
<h2 class="m-0 text-2xl">Sign in</h2> <h2 class="m-0 text-2xl">Sign in</h2>

View File

@@ -1,11 +1,12 @@
package templates package templates
import "mal/internal/jikan" import "mal/integrations/jikan"
import "mal/internal/shared/ui" import "mal/web/components"
import "fmt" import "fmt"
import "mal/web/shared/layout"
templ Catalog() { templ Catalog() {
@Layout("mal - catalog", true) { @layout.Layout("mal - catalog", true) {
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:gap-4 lg:grid-cols-4 xl:grid-cols-5" id="catalog-content"> <div class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:gap-4 lg:grid-cols-4 xl:grid-cols-5" id="catalog-content">
<div class="col-span-full" hx-get="/api/catalog?page=1" hx-trigger="load" hx-swap="outerHTML"> <div class="col-span-full" hx-get="/api/catalog?page=1" hx-trigger="load" hx-swap="outerHTML">
@ui.LoadingIndicator("Loading catalog") @ui.LoadingIndicator("Loading catalog")

View File

@@ -2,12 +2,13 @@ package templates
import ( import (
"fmt" "fmt"
"mal/internal/database" "mal/internal/db"
"mal/internal/shared/ui" "mal/web/components"
"mal/web/shared/layout"
) )
templ ContinueWatching(entries []database.GetContinueWatchingEntriesRow) { templ ContinueWatching(entries []db.GetContinueWatchingEntriesRow) {
@Layout("mal - continue watching", true) { @layout.Layout("mal - continue watching", true) {
<div class="grid gap-4"> <div class="grid gap-4">
<h1>Continue watching</h1> <h1>Continue watching</h1>
<p class="m-0 text-sm text-(--text-muted)">Pick up where you left off.</p> <p class="m-0 text-sm text-(--text-muted)">Pick up where you left off.</p>
@@ -53,7 +54,7 @@ templ ContinueWatching(entries []database.GetContinueWatchingEntriesRow) {
} }
} }
func continueWatchingURL(entry database.GetContinueWatchingEntriesRow) string { func continueWatchingURL(entry db.GetContinueWatchingEntriesRow) string {
episode := 1 episode := 1
if entry.CurrentEpisode.Valid && entry.CurrentEpisode.Int64 > 0 { if entry.CurrentEpisode.Valid && entry.CurrentEpisode.Int64 > 0 {
episode = int(entry.CurrentEpisode.Int64) episode = int(entry.CurrentEpisode.Int64)
@@ -62,6 +63,6 @@ func continueWatchingURL(entry database.GetContinueWatchingEntriesRow) string {
return fmt.Sprintf("/watch/%d/%d", entry.AnimeID, episode) return fmt.Sprintf("/watch/%d/%d", entry.AnimeID, episode)
} }
func displayContinueWatchingTitle(entry database.GetContinueWatchingEntriesRow) string { func displayContinueWatchingTitle(entry db.GetContinueWatchingEntriesRow) string {
return database.DisplayTitle(entry.TitleEnglish, entry.TitleJapanese, entry.TitleOriginal) return db.DisplayTitle(entry.TitleEnglish, entry.TitleJapanese, entry.TitleOriginal)
} }

View File

@@ -1,11 +1,12 @@
package templates package templates
import "mal/internal/jikan" import "mal/integrations/jikan"
import "mal/internal/shared/ui" import "mal/web/components"
import "fmt" import "fmt"
import "mal/web/shared/layout"
templ Discover() { templ Discover() {
@Layout("mal - discover", true) { @layout.Layout("mal - discover", true) {
<div class="grid gap-4"> <div class="grid gap-4">
<div class="grid gap-4"> <div class="grid gap-4">
<h1>Discover</h1> <h1>Discover</h1>

View File

@@ -2,13 +2,14 @@ package templates
import ( import (
"fmt" "fmt"
"mal/internal/jikan" "mal/integrations/jikan"
"mal/internal/shared/ui" "mal/web/components"
"mal/web/shared/layout"
"net/url" "net/url"
) )
templ Search(q string) { templ Search(q string) {
@Layout("mal - search", true) { @layout.Layout("mal - search", true) {
if q != "" { if q != "" {
<div id="loading" class="hidden htmx-request:inline-flex"> <div id="loading" class="hidden htmx-request:inline-flex">
@ui.LoadingIndicator("Searching...") @ui.LoadingIndicator("Searching...")

View File

@@ -1,7 +1,9 @@
package templates package templates
import "mal/web/shared/layout"
templ NotFoundPage() { templ NotFoundPage() {
@Layout("mal - not found", false) { @layout.Layout("mal - not found", false) {
<section class="w-full max-w-3xl min-h-dvh mx-auto grid content-center justify-items-center gap-3 px-7 py-8 text-center"> <section class="w-full max-w-3xl min-h-dvh mx-auto grid content-center justify-items-center gap-3 px-7 py-8 text-center">
<p class="m-0 text-6xl leading-none tracking-wider text-(--text-muted) sm:text-7xl md:text-8xl lg:text-9xl">404</p> <p class="m-0 text-6xl leading-none tracking-wider text-(--text-muted) sm:text-7xl md:text-8xl lg:text-9xl">404</p>
<h1 class="m-0 text-3xl sm:text-4xl md:text-5xl">Page not found</h1> <h1 class="m-0 text-3xl sm:text-4xl md:text-5xl">Page not found</h1>

View File

@@ -6,10 +6,11 @@ import (
"mal/integrations/jikan" "mal/integrations/jikan"
"mal/web/components" "mal/web/components"
"mal/web/shared" "mal/web/shared"
"mal/web/shared/layout"
) )
templ StudioDetails(producer jikan.ProducerResponse, animes []jikan.Anime, hasNext bool, nextPage int) { templ StudioDetails(producer jikan.ProducerResponse, animes []jikan.Anime, hasNext bool, nextPage int) {
@Layout("mal - "+shared.GetProducerName(producer), true) { @layout.Layout("mal - "+shared.GetProducerName(producer), true) {
<div class="grid gap-5"> <div class="grid gap-5">
<div class="grid gap-4 bg-(--panel) p-4"> <div class="grid gap-4 bg-(--panel) p-4">
<div class="flex items-start gap-4"> <div class="flex items-start gap-4">

View File

@@ -9,10 +9,11 @@ import (
"mal/web/components/watch" "mal/web/components/watch"
"mal/web/components/watchlist" "mal/web/components/watchlist"
"mal/web/shared" "mal/web/shared"
"mal/web/shared/layout"
) )
templ WatchPage(anime jikan.Anime, data shared.WatchPageData) { templ WatchPage(anime jikan.Anime, data shared.WatchPageData) {
@Layout(fmt.Sprintf("%s - episode %s", anime.DisplayTitle(), data.CurrentEpisode), true) { @layout.Layout(fmt.Sprintf("%s - episode %s", anime.DisplayTitle(), data.CurrentEpisode), true) {
<div class="w-full overflow-x-clip"> <div class="w-full overflow-x-clip">
<div class="mx-auto grid w-full gap-4 lg:gap-5 lg:grid-cols-[220px_minmax(0,1fr)_250px] xl:grid-cols-[240px_minmax(0,1fr)_280px]"> <div class="mx-auto grid w-full gap-4 lg:gap-5 lg:grid-cols-[220px_minmax(0,1fr)_250px] xl:grid-cols-[240px_minmax(0,1fr)_280px]">
<!-- Left sidebar: Episodes --> <!-- Left sidebar: Episodes -->

View File

@@ -7,16 +7,17 @@ import (
"mal/web/components" "mal/web/components"
"mal/web/components/watchlist" "mal/web/components/watchlist"
"mal/web/shared" "mal/web/shared"
"mal/web/shared/layout"
) )
templ Watchlist( templ Watchlist(
entries []db.GetUserWatchListRow, entries []db.GetUserWatchListRow,
layout string, viewLayout string,
currentStatus string, currentStatus string,
sortBy string, sortBy string,
sortOrder string, sortOrder string,
) { ) {
@Layout("mal - watchlist", true) { @layout.Layout("mal - watchlist", true) {
<div <div
class="mb-4 flex items-end justify-between gap-4 max-lg:flex-col max-lg:items-start" class="mb-4 flex items-end justify-between gap-4 max-lg:flex-col max-lg:items-start"
> >
@@ -60,14 +61,14 @@ templ Watchlist(
class="flex flex-wrap gap-2 max-md:flex-nowrap max-md:overflow-x-auto max-md:pb-1" class="flex flex-wrap gap-2 max-md:flex-nowrap max-md:overflow-x-auto max-md:pb-1"
> >
<a <a
href={ templ.URL(shared.WatchlistURL("grid", currentStatus, sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "all", sortBy, sortOrder)) }
class={ shared.TabClass(layout == "grid") } class={ shared.TabClass(viewLayout == "grid") }
> >
Grid Grid
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL("table", currentStatus, sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "table", sortBy, sortOrder)) }
class={ shared.TabClass(layout == "table") } class={ shared.TabClass(viewLayout == "table") }
> >
Table Table
</a> </a>
@@ -78,48 +79,48 @@ templ Watchlist(
class="mb-3 flex flex-wrap gap-2 max-md:flex-nowrap max-md:overflow-x-auto max-md:pb-1" class="mb-3 flex flex-wrap gap-2 max-md:flex-nowrap max-md:overflow-x-auto max-md:pb-1"
> >
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "all", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "all", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "all") } class={ shared.TabClass(currentStatus == "all") }
> >
All All
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "watching", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "watching", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "watching") } class={ shared.TabClass(currentStatus == "watching") }
> >
Watching Watching
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "on_hold", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "on_hold", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "on_hold") } class={ shared.TabClass(currentStatus == "on_hold") }
> >
On hold On hold
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "plan_to_watch", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "plan_to_watch", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "plan_to_watch") } class={ shared.TabClass(currentStatus == "plan_to_watch") }
> >
Plan to watch Plan to watch
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "dropped", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "dropped", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "dropped") } class={ shared.TabClass(currentStatus == "dropped") }
> >
Dropped Dropped
</a> </a>
<a <a
href={ templ.URL(shared.WatchlistURL(layout, "completed", sortBy, sortOrder)) } href={ templ.URL(shared.WatchlistURL(viewLayout, "completed", sortBy, sortOrder)) }
class={ shared.TabClass(currentStatus == "completed") } class={ shared.TabClass(currentStatus == "completed") }
> >
Completed Completed
</a> </a>
</div> </div>
@components.SortFilter(components.SortFilterOptions{ @components.SortFilter(components.SortFilterOptions{
Sort: sortBy, Sort: sortBy,
Order: sortOrder, Order: sortOrder,
View: layout, View: viewLayout,
Status: currentStatus, Status: currentStatus,
}) })
if len(entries) == 0 { if len(entries) == 0 {
@components.EmptyState("Nothing here yet") { @components.EmptyState("Nothing here yet") {
if currentStatus == "all" { if currentStatus == "all" {
@@ -129,7 +130,7 @@ templ Watchlist(
} }
} }
} else { } else {
if layout == "grid" { if viewLayout == "grid" {
<div <div
class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:gap-4 lg:grid-cols-4 xl:grid-cols-5" class="grid grid-cols-2 gap-3 sm:grid-cols-3 md:gap-4 lg:grid-cols-4 xl:grid-cols-5"
> >