A single Go binary with SQLite that serves a public page of the owner's ranked lists (movies, TV, albums, songs, video games, theater) and a login-gated editor. - Lists of 1–65,535 entries (100 recommended), several per category - Search-to-add via TMDB, TheTVDB, IGDB, Deezer/MusicBrainz, Wikipedia; manual entry as a fallback - Theater entries carry a production (Broadway, tour, …), venue and cast - Artwork cached locally; URL override and uploads - Drag, arrow and type-a-rank reordering; paginated for long lists - 12 theme presets plus custom CSS - First-run setup code or env-provided admin; bcrypt, rate-limited login, CSRF-guarded JSON API - Dockerfile and docker-compose.yml Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.6 KiB
HTML
56 lines
2.6 KiB
HTML
{{define "bodyclass"}}page-list{{end}}
|
|
{{define "content"}}
|
|
{{with .Data}}
|
|
<section class="list-hero{{if .Hero}} list-hero--art{{end}}">
|
|
{{if .Hero}}<div class="list-hero-backdrop" aria-hidden="true"><img src="{{.Hero.Image}}" alt=""></div>{{end}}
|
|
<div class="container list-hero-inner">
|
|
<a class="back-link" href="/">{{icon "arrow-left"}}All lists</a>
|
|
<div class="list-hero-row">
|
|
<div>
|
|
<p class="eyebrow">{{icon .Category.Icon}}{{.Category.Label}}</p>
|
|
<h1 class="list-title">{{.List.Title}}</h1>
|
|
{{with .List.Description}}<p class="list-desc">{{.}}</p>{{end}}
|
|
<p class="list-meta">
|
|
{{num .List.ItemCount}} {{plural .List.ItemCount "entry" "entries"}}{{if lt .List.ItemCount .List.MaxItems}} <span class="muted">of {{num .List.MaxItems}}</span>{{end}}
|
|
{{if not .List.Visible}}<span class="badge badge--warn">{{icon "eye-off"}}Hidden</span>{{end}}
|
|
</p>
|
|
</div>
|
|
<div class="list-actions">
|
|
{{if $.LoggedIn}}<a class="btn btn--ghost" href="/admin/lists/{{.List.ID}}">{{icon "pencil"}}Edit</a>{{end}}
|
|
{{if .Items}}
|
|
<div class="segmented" role="group" aria-label="Layout" data-layout-toggle data-default="{{.List.Layout}}" data-key="{{.List.Slug}}">
|
|
<button type="button" data-layout="grid" title="Grid">{{icon "grid"}}<span class="sr-only">Grid</span></button>
|
|
<button type="button" data-layout="rows" title="List">{{icon "rows"}}<span class="sr-only">List</span></button>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="container list-body">
|
|
{{if .Items}}
|
|
{{template "items" (dict "Items" .Items "Category" .Category "Layout" .List.Layout "Title" .List.Title)}}
|
|
{{if .PageNums}}
|
|
<nav class="pager" aria-label="Pages">
|
|
{{if gt .Page 1}}<a class="pager-btn" href="?page={{add .Page -1}}" rel="prev">{{icon "chevron-left"}}<span class="sr-only">Previous</span></a>{{end}}
|
|
{{range .PageNums}}
|
|
{{if eq . 0}}<span class="pager-gap">…</span>
|
|
{{else if eq . $.Data.Page}}<span class="pager-btn is-current" aria-current="page">{{.}}</span>
|
|
{{else}}<a class="pager-btn" href="?page={{.}}">{{.}}</a>{{end}}
|
|
{{end}}
|
|
{{if lt .Page .Pages}}<a class="pager-btn" href="?page={{add .Page 1}}" rel="next">{{icon "chevron-right"}}<span class="sr-only">Next</span></a>{{end}}
|
|
</nav>
|
|
{{end}}
|
|
{{else}}
|
|
<div class="empty">
|
|
<div class="empty-art">{{icon .Category.Icon}}</div>
|
|
<h2>Nothing here yet</h2>
|
|
<p>This list hasn't been filled in.</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
{{end}}
|
|
{{define "scripts"}}<script type="module" src="/static/js/public.js?v={{.AssetVer}}"></script>{{end}}
|