Files
canon/web/templates/pages/admin_list.html
T
cdnutterandClaude Opus 5.5 88373176f2 Allow empty places in rankings; rebuild the list editor
Entries now sit at places 1…max with gaps allowed (#28, nothing at #29,
#30). Moving onto an empty place just puts the entry there; onto a taken
place, the entries in between shift one step, stopping at the first
empty place, so lists without gaps reorder exactly as before. Inserts
push down only to the next gap; deleting can close up or leave the place
empty. New endpoint POST /api/lists/{id}/places inserts or removes empty
places or closes every gap; move and delete reply with every entry's
place. The podium shows an empty step for an empty #2 or #3, and a list
can't shrink below its last used place.

The editor loads every entry (no paging) and applies edits instantly
with the same rules (places.js mirrors places.go), queueing saves in the
background and reconciling with the server's reply. Type a place and
press Enter (↑/↓ move between boxes, Alt+↑/↓ moves the entry), drag, or
use the row menu. Empty runs show as rows with Add here and Remove, and
a toolbar shows the gap count, Close gaps and a Go to # box. Clicking an
entry opens it for editing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:24:00 -07:00

51 lines
2.8 KiB
HTML

{{define "bodyclass"}}page-admin{{end}}
{{define "content"}}
{{$c := category .Data.List.Category}}
<div class="container admin">
<a class="back-link" href="/admin">{{icon "arrow-left"}}All lists</a>
<header class="admin-head">
<div class="admin-head-title">
<span class="shelf-icon">{{icon $c.Icon}}</span>
<div>
<h1 data-bind="title">{{.Data.List.Title}}</h1>
<p class="muted"><span data-bind="count">{{.Data.List.ItemCount}}</span> of <span data-bind="max">{{.Data.List.MaxItems}}</span> · {{$c.Label}}<span data-bind="hidden">{{if not .Data.List.Visible}} · Hidden{{end}}</span></p>
</div>
</div>
<div class="admin-head-actions">
<a class="btn btn--ghost" data-bind="public" href="/l/{{.Data.List.Slug}}">{{icon "external"}}View</a>
<button class="btn btn--ghost" type="button" data-action="import" title="Add entries from a CSV or text file">{{icon "upload"}}Import</button>
{{template "export" .Data.List.Slug}}
{{if ne $c.Key "custom"}}<button class="btn btn--ghost" type="button" data-action="refresh-all" title="Pull the latest details and artwork from each entry's source">{{icon "refresh"}}Refresh all</button>{{end}}
<button class="btn" type="button" data-action="edit-list">{{icon "settings"}}List settings</button>
</div>
</header>
<div class="meter" aria-hidden="true"><span data-bind="meter" style="width: {{pct .Data.List.ItemCount .Data.List.MaxItems}}%"></span></div>
<div class="editor">
<section class="panel editor-search" aria-labelledby="add-h">
<div class="panel-head">
<h2 id="add-h">{{if eq $c.Key "custom"}}Add favorites{{else}}Add {{$c.Label}}{{end}}</h2>
<span class="muted small" data-bind="provider"></span>
</div>
<form class="search-bar" data-search role="search">
{{icon "search"}}
<input type="search" name="q" placeholder="{{if eq $c.Key "custom"}}Search Wikipedia (optional)…{{else}}Search for a {{$c.Singular}}…{{end}}" autocomplete="off" aria-label="Search">
<span class="spinner" hidden></span>
</form>
<div class="search-results" data-results aria-live="polite"></div>
<button type="button" class="btn btn--ghost btn--block" data-action="manual">{{icon "pencil"}}Add one manually</button>
</section>
<section class="panel editor-items" aria-labelledby="items-h">
<div class="panel-head">
<h2 id="items-h">Ranking</h2>
<span class="muted small">Drag, or type a place and press Enter. Places can be left empty.</span>
</div>
<ol class="rank-list" data-rank-list></ol>
</section>
</div>
</div>
<script type="application/json" id="boot">{{.Data.Boot}}</script>
{{end}}
{{define "scripts"}}<script type="module" src="/static/js/admin-list.js?v={{.AssetVer}}"></script>{{end}}