redesign: schedule page layout with scrollable calendar grid

This commit is contained in:
2026-06-04 10:36:29 +02:00
committed by Milas Holsting
parent 95b1e2b93e
commit 41be636c4d

View File

@@ -1,12 +1,10 @@
{{define "title"}}Schedule{{end}} {{define "title"}}Schedule{{end}}
{{define "content"}} {{define "content"}}
<div class="flex flex-col gap-12 pb-24"> <div class="flex flex-col gap-12 pb-24">
<section class="flex items-end justify-between gap-6 px-1"> <section class="px-1">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<h1 class="text-4xl font-semibold tracking-tight text-foreground">Schedule</h1> <h1 class="text-4xl font-normal tracking-[-0.02em] text-foreground">Schedule</h1>
<p class="text-lg text-foreground-muted">Shows from your watchlist that are currently airing.</p>
</div> </div>
<a href="/watchlist" class="shrink-0 text-sm font-medium text-foreground-muted transition-colors hover:text-foreground">Manage watchlist</a>
</section> </section>
<div hx-get="/api/schedule?year={{.ScheduleYear}}&week={{.ScheduleWeek}}" hx-trigger="load" hx-swap="outerHTML"> <div hx-get="/api/schedule?year={{.ScheduleYear}}&week={{.ScheduleWeek}}" hx-trigger="load" hx-swap="outerHTML">
@@ -36,63 +34,52 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<div class="flex flex-wrap items-center justify-between gap-6 bg-background-surface px-6 py-5 ring-1 ring-[var(--border)]"> <div class="flex flex-col gap-4">
<div class="flex flex-wrap items-center gap-x-10 gap-y-4 text-sm text-foreground-muted"> <div class="flex flex-wrap items-center gap-3">
<div class="flex items-center gap-4"> <div class="inline-flex overflow-hidden bg-background-button">
<span class="text-xs font-semibold uppercase tracking-wider text-foreground-muted">Time</span> <button type="button" data-schedule-setting="timeFormat" data-schedule-value="24" class="h-10 px-4 text-sm font-normal text-foreground transition-colors hover:bg-background-button-hover">24H</button>
<div class="inline-flex overflow-hidden ring-1 ring-[var(--border)]"> <button type="button" data-schedule-setting="timeFormat" data-schedule-value="12" class="h-10 px-4 text-sm font-normal text-foreground transition-colors hover:bg-background-button-hover">12H</button>
<button type="button" data-schedule-setting="timeFormat" data-schedule-value="24" class="h-9 px-4 text-xs font-semibold bg-background-button hover:bg-background-button-hover text-foreground transition-colors">24H</button>
<button type="button" data-schedule-setting="timeFormat" data-schedule-value="12" class="h-9 px-4 text-xs font-semibold bg-background-button hover:bg-background-button-hover text-foreground transition-colors">12H</button>
</div>
</div> </div>
<div class="flex items-center gap-4"> <div class="inline-flex overflow-hidden bg-background-button">
<span class="text-xs font-semibold uppercase tracking-wider text-foreground-muted">Images</span> <button type="button" data-schedule-setting="showImages" data-schedule-value="true" class="h-10 px-4 text-sm font-normal text-foreground transition-colors hover:bg-background-button-hover">Posters</button>
<div class="inline-flex overflow-hidden ring-1 ring-[var(--border)]"> <button type="button" data-schedule-setting="showImages" data-schedule-value="false" class="h-10 px-4 text-sm font-normal text-foreground transition-colors hover:bg-background-button-hover">Text only</button>
<button type="button" data-schedule-setting="showImages" data-schedule-value="true" class="h-9 px-4 text-xs font-semibold bg-background-button hover:bg-background-button-hover text-foreground transition-colors">Show</button>
<button type="button" data-schedule-setting="showImages" data-schedule-value="false" class="h-9 px-4 text-xs font-semibold bg-background-button hover:bg-background-button-hover text-foreground transition-colors">Hide</button>
</div>
</div> </div>
<div class="flex items-center gap-4"> <select data-schedule-setting="sortBy" class="h-10 bg-background-button px-4 text-sm font-normal text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">
<span class="text-xs font-semibold uppercase tracking-wider text-foreground-muted">Sort</span> <option value="time">Sort by: Time</option>
<select data-schedule-setting="sortBy" class="h-9 bg-background-button px-4 text-xs font-semibold text-foreground ring-1 ring-[var(--border)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent"> <option value="alpha">Sort by: A-Z</option>
<option value="time">Time</option> <option value="score">Sort by: Score</option>
<option value="alpha">Alphabetical</option> </select>
<option value="score">Score</option>
</select>
</div>
<div class="flex items-center gap-4"> <select data-schedule-setting="weekStart" class="h-10 bg-background-button px-4 text-sm font-normal text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">
<span class="text-xs font-semibold uppercase tracking-wider text-foreground-muted">Week starts</span> <option value="monday">Week starts: Monday</option>
<select data-schedule-setting="weekStart" class="h-9 bg-background-button px-4 text-xs font-semibold text-foreground ring-1 ring-[var(--border)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent"> <option value="sunday">Week starts: Sunday</option>
<option value="monday">Monday</option> <option value="saturday">Week starts: Saturday</option>
<option value="sunday">Sunday</option> </select>
<option value="saturday">Saturday</option>
</select>
</div>
</div> </div>
<div class="flex items-center gap-3"> <button type="button" data-schedule-week-nav="-1" class="inline-flex h-10 items-center justify-center bg-background-button px-4 text-sm font-normal text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">Previous</button>
<button type="button" data-schedule-week-nav="-1" class="inline-flex h-9 items-center justify-center bg-background-button px-4 text-xs font-semibold text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground ring-1 ring-[var(--border)]">← Previous</button> <button type="button" data-schedule-week-nav="1" class="inline-flex h-10 items-center justify-center bg-background-button px-4 text-sm font-normal text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">Next</button>
<button type="button" data-schedule-week-nav="1" class="inline-flex h-9 items-center justify-center bg-background-button px-4 text-xs font-semibold text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground ring-1 ring-[var(--border)]">Next →</button>
</div> </div>
</div> </div>
<div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" data-schedule-board> <div class="overflow-x-auto pb-3">
<div class="grid min-w-[1400px] grid-cols-7 gap-px border border-[var(--border)] bg-[var(--border)]" data-schedule-board>
{{range (list "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")}} {{range (list "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday")}}
{{$day := .}} {{$day := .}}
<section class="flex min-w-0 flex-col bg-background-surface ring-1 ring-[var(--border)]" data-schedule-day="{{$day}}"> <section class="flex min-w-0 flex-col bg-background-surface" data-schedule-day="{{$day}}">
<header class="flex items-baseline justify-between gap-3 border-b border-[var(--border-light)] px-7 py-6"> <header class="flex min-h-28 items-end justify-between gap-3 border-b border-[var(--border-light)] px-5 py-5">
<div class="min-w-0"> <div class="min-w-0">
<h2 class="text-lg font-semibold tracking-tight text-foreground" data-schedule-day-label>{{$day}}</h2> <div class="text-xs font-normal uppercase tracking-wide text-foreground-muted" data-schedule-date-label>—</div>
<div class="mt-1 text-sm text-foreground-muted" data-schedule-date-label>—</div> <h2 class="mt-1 text-xl font-normal tracking-[-0.02em] text-foreground" data-schedule-day-label>{{$day}}</h2>
</div> </div>
<span class="shrink-0 text-sm tabular-nums font-medium text-foreground-muted" data-schedule-count>0</span> <span class="shrink-0 text-sm font-normal tabular-nums text-foreground-muted" data-schedule-count>0</span>
</header> </header>
<div class="flex flex-col pb-8 pt-4" data-schedule-day-items></div> <div class="flex min-h-[34rem] flex-col" data-schedule-day-items></div>
</section> </section>
{{end}} {{end}}
</div>
</div> </div>
<div class="hidden" data-schedule-items-source> <div class="hidden" data-schedule-items-source>
@@ -113,15 +100,15 @@
data-broadcast-time="{{$anime.Broadcast.Time}}" data-broadcast-time="{{$anime.Broadcast.Time}}"
data-broadcast-timezone="{{$anime.Broadcast.Timezone}}" data-broadcast-timezone="{{$anime.Broadcast.Timezone}}"
> >
<div class="flex gap-6 border-b border-[var(--border-light)] px-7 py-6 transition-colors last:border-b-0 hover:bg-[color:var(--color-surface-hover)]"> <div class="flex min-h-[31rem] flex-col gap-4 border-b border-[var(--border-light)] px-5 py-5 transition-colors last:border-b-0 hover:bg-[color:var(--color-surface-hover)]">
<a href="/anime/{{$anime.MalID}}" class="shrink-0 overflow-hidden bg-background-surface" data-schedule-poster> <a href="/anime/{{$anime.MalID}}" class="overflow-hidden bg-background-button" data-schedule-poster>
<img src="{{$imageUrl}}" alt="{{$anime.DisplayTitle}}" class="h-28 w-20 object-cover" loading="lazy" /> <img src="{{$imageUrl}}" alt="{{$anime.DisplayTitle}}" class="h-44 w-full object-cover" loading="lazy" />
</a> </a>
<div class="flex min-w-0 flex-1 flex-col justify-center gap-3"> <div class="flex min-w-0 flex-1 flex-col gap-3">
<div class="flex items-start justify-between gap-4"> <div class="flex items-start justify-between gap-4">
<a href="/anime/{{$anime.MalID}}" class="min-w-0"> <a href="/anime/{{$anime.MalID}}" class="min-w-0">
<h3 class="line-clamp-2 text-lg font-semibold leading-tight tracking-tight text-foreground">{{$anime.DisplayTitle}}</h3> <h3 class="line-clamp-3 min-h-[4.5rem] text-xl font-normal leading-tight tracking-[-0.02em] text-foreground">{{$anime.DisplayTitle}}</h3>
</a> </a>
<button <button
type="button" type="button"
@@ -136,15 +123,16 @@
</button> </button>
</div> </div>
<div class="flex items-center gap-5 text-base text-foreground-muted"> <div class="flex flex-wrap items-center gap-2 text-sm text-foreground-muted">
<span class="tabular-nums font-medium" data-schedule-time>Unknown</span> <span class="bg-background-button px-2.5 py-1 text-sm font-normal tabular-nums text-foreground" data-schedule-time>Unknown</span>
{{if $anime.Score}} {{if $anime.Score}}
<span class="flex items-center gap-1.5 tabular-nums font-medium" title="Score"> <span class="flex items-center gap-1.5 bg-background-button px-2.5 py-1 text-sm tabular-nums font-normal text-foreground-muted" title="Score">
<svg class="h-4 w-4 fill-accent" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg> <svg class="h-4 w-4 fill-accent" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
<span>{{$anime.Score}}</span> <span>{{$anime.Score}}</span>
</span> </span>
{{end}} {{end}}
</div> </div>
<span class="mt-auto pt-2 text-sm font-normal text-foreground-muted">View details</span>
</div> </div>
</div> </div>
</article> </article>
@@ -168,42 +156,45 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<div class="flex items-center justify-end gap-2"> <div class="flex flex-col gap-4">
<a href="/schedule?year={{.PrevYear}}&week={{.PrevWeek}}" class="inline-flex h-8 items-center justify-center bg-background-button px-3 text-xs font-medium text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent ring-1 ring-[var(--border)]">← Previous</a> <div class="flex items-center gap-3">
<a href="/schedule?year={{.NextYear}}&week={{.NextWeek}}" class="inline-flex h-8 items-center justify-center bg-background-button px-3 text-xs font-medium text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent ring-1 ring-[var(--border)]">Next →</a> <a href="/schedule?year={{.PrevYear}}&week={{.PrevWeek}}" class="inline-flex h-10 items-center justify-center bg-background-button px-4 text-sm font-normal text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">Previous</a>
<a href="/schedule?year={{.NextYear}}&week={{.NextWeek}}" class="inline-flex h-10 items-center justify-center bg-background-button px-4 text-sm font-normal text-foreground-muted transition-colors hover:bg-background-button-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">Next</a>
</div>
</div> </div>
<div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> <div class="overflow-x-auto pb-3">
<div class="grid min-w-[1400px] grid-cols-7 gap-px border border-[var(--border)] bg-[var(--border)]">
{{range .ScheduleDays}} {{range .ScheduleDays}}
<section class="flex min-w-0 flex-col bg-background-surface ring-1 ring-[var(--border)]"> <section class="flex min-w-0 flex-col bg-background-surface">
<header class="flex items-center justify-between gap-3 border-b border-[var(--border-light)] bg-background px-7 py-6"> <header class="flex min-h-28 items-end justify-between gap-3 border-b border-[var(--border-light)] bg-background px-5 py-5">
<div class="min-w-0"> <div class="min-w-0">
<div class="text-xs font-semibold uppercase tracking-wider text-foreground-muted">{{.DateLabel}}</div> <div class="text-xs font-normal uppercase tracking-wide text-foreground-muted">{{.DateLabel}}</div>
<h2 class="mt-1 text-lg font-semibold leading-tight tracking-tight text-foreground">{{.WeekdayLabel}}</h2> <h2 class="mt-1 text-xl font-normal tracking-[-0.02em] text-foreground">{{.WeekdayLabel}}</h2>
</div> </div>
</header> </header>
<div class="flex flex-col pb-8 pt-4"> <div class="flex min-h-[34rem] flex-col">
{{if eq (len .Entries) 0}} {{if eq (len .Entries) 0}}
<div class="flex min-h-32 items-center justify-center px-7 py-10 text-center text-sm text-foreground-muted">No releases scheduled</div> <div class="flex min-h-32 flex-1 items-center justify-center px-6 py-10 text-center text-sm text-foreground-muted">No releases scheduled</div>
{{else}} {{else}}
{{range .Entries}} {{range .Entries}}
<a href="/browse?q={{urlquery .Title}}" class="group flex gap-6 border-b border-[var(--border-light)] px-7 py-6 transition-colors last:border-b-0 hover:bg-[color:var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent"> <a href="/browse?q={{urlquery .Title}}" class="group flex min-h-[31rem] flex-col gap-4 border-b border-[var(--border-light)] px-5 py-5 transition-colors last:border-b-0 hover:bg-[color:var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent">
<div class="relative aspect-2/3 h-28 shrink-0 overflow-hidden bg-background-surface"> <div class="relative aspect-[4/5] w-full overflow-hidden bg-background-button">
{{if .ImageURL}} {{if .ImageURL}}
<img src="{{.ImageURL}}" alt="{{.Title}}" class="h-full w-full object-cover" loading="lazy" /> <img src="{{.ImageURL}}" alt="{{.Title}}" class="h-full w-full object-cover" loading="lazy" />
{{end}} {{end}}
<div class="absolute inset-0 bg-linear-to-t from-black/40 via-transparent to-transparent opacity-70"></div> <div class="absolute inset-0 bg-linear-to-t from-black/40 via-transparent to-transparent opacity-70"></div>
</div> </div>
<div class="flex min-w-0 flex-1 flex-col justify-center"> <div class="flex min-w-0 flex-1 flex-col gap-3">
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-2">
<span class="bg-background-button px-2.5 py-1 text-xs font-semibold tabular-nums text-foreground">{{.LocalTime}}</span> <span class="bg-background-button px-2.5 py-1 text-sm font-normal tabular-nums text-foreground">{{.LocalTime}}</span>
<span class="bg-background-button px-2.5 py-1 text-xs font-semibold text-foreground-muted">{{.EpisodeText}}</span> <span class="bg-background-button px-2.5 py-1 text-sm font-normal text-foreground-muted">{{.EpisodeText}}</span>
<span class="ml-auto bg-accent px-2 py-0.5 text-[10px] font-bold text-black/95">{{.AirType}}</span> <span class="bg-background-button px-2.5 py-1 text-sm font-normal text-accent">{{.AirType}}</span>
</div> </div>
<h3 class="mt-3 line-clamp-2 text-lg font-semibold leading-tight tracking-tight text-foreground">{{.Title}}</h3> <h3 class="line-clamp-3 min-h-[4.5rem] text-xl font-normal leading-tight tracking-[-0.02em] text-foreground">{{.Title}}</h3>
<span class="mt-auto pt-4 text-sm font-medium text-foreground-muted transition-colors group-hover:text-foreground">View details</span> <span class="mt-auto pt-2 text-sm font-normal text-foreground-muted transition-colors group-hover:text-foreground">View details</span>
</div> </div>
</a> </a>
{{end}} {{end}}
@@ -211,28 +202,42 @@
</div> </div>
</section> </section>
{{end}} {{end}}
</div>
</div> </div>
{{end}} {{end}}
</section> </section>
{{end}} {{end}}
{{define "schedule_skeleton"}} {{define "schedule_skeleton"}}
<div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> <div class="flex flex-wrap items-center justify-between gap-3">
<div class="flex flex-wrap gap-3">
{{range (seq 4)}}
<div class="skeleton h-10 w-32"></div>
{{end}}
</div>
<div class="flex gap-3">
<div class="skeleton h-10 w-24"></div>
<div class="skeleton h-10 w-24"></div>
</div>
</div>
<div class="overflow-x-auto pb-3">
<div class="grid min-w-[1400px] grid-cols-7 gap-px border border-[var(--border)] bg-[var(--border)]">
{{range (seq 7)}} {{range (seq 7)}}
<div class="flex flex-col bg-background-surface ring-1 ring-[var(--border)]"> <div class="flex flex-col bg-background-surface">
<div class="flex items-baseline justify-between border-b border-[var(--border-light)] px-7 py-6"> <div class="flex min-h-28 items-end justify-between border-b border-[var(--border-light)] px-5 py-5">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="skeleton h-6 w-32"></div> <div class="skeleton skeleton-subtle h-3 w-20"></div>
<div class="skeleton skeleton-subtle h-4 w-20"></div> <div class="skeleton h-8 w-28"></div>
</div> </div>
<div class="skeleton skeleton-subtle h-4 w-10"></div> <div class="skeleton skeleton-subtle h-8 w-10"></div>
</div> </div>
<div class="flex flex-col pb-8 pt-4"> <div class="flex min-h-[34rem] flex-col">
{{range (seq 3)}} {{range (seq 3)}}
<div class="flex gap-6 px-7 py-6 border-b border-[var(--border-light)] last:border-b-0"> <div class="flex flex-col gap-4 border-b border-[var(--border-light)] px-5 py-5 last:border-b-0">
<div class="skeleton h-28 w-20 shrink-0"></div> <div class="skeleton h-44 w-full"></div>
<div class="flex flex-1 flex-col justify-center gap-4"> <div class="flex flex-1 flex-col gap-3">
<div class="skeleton h-5 w-full"></div> <div class="skeleton skeleton-subtle h-7 w-20"></div>
<div class="skeleton h-6 w-full"></div>
<div class="skeleton skeleton-subtle h-4 w-2/3"></div> <div class="skeleton skeleton-subtle h-4 w-2/3"></div>
</div> </div>
</div> </div>
@@ -240,5 +245,6 @@
</div> </div>
</div> </div>
{{end}} {{end}}
</div>
</div> </div>
{{end}} {{end}}