feat(facets+ui): clickable facet values everywhere, genre prettify, active-filter chips
Frontend half of the faceted-navigation Phase 2. 'Everything filterable should be clickable': every displayed facet value links to the bookshelf pre-filtered by its raw stored value; display forms are prettified at render, never in the DB. - templates/facet_links.templ: FacetLink (inline links) + FacetBadge (badge links) to /bookshelf?<facet>_filter=<value>; the raw value rides the param while the shown text is prettified, so matching and display can never disagree. Series/tags keep their dedicated pages by design; the card media-type icon stays unlinked (a derived kind, not a column value — deep-linking it would send wrong values). - book_detail.templ: publisher, language, edition, genre, imprint cells + narrators line + age-rating/story-arc badges are now links. - prettifyGenre (templates/utils.go + unit tests): title-cases only entirely-lowercase values; small words stay down except first position; & preserved. TS port for chip labels documents the twin. - bookshelf.templ + bookshelf.ts: active-filter chips bar — Alpine renders chips from client-local form state (sanctioned island); one bubbled htmx:afterRequest hook keeps chips + shareable URL in sync; removeFilterChip clears the facet and re-applies. URL deep links now accept all 18 facet params (hidden inputs where the drawer has no field). Pins changelog: Batch B/C entry in docs/contract_pins.md. Rendering ownership guard (make lint-rendering) green.
This commit is contained in:
1 parent
7e586df48a
commit
bbedbfb13f
10 files changed
+990
-545
No files matched your search
@@ -180,3 +180,25 @@ cover_image_path, has_conflict, format_group, badge`.
|
||||
input leaves `genre` empty). Existing rows are unaffected until a
|
||||
rescan/re-extract.
|
||||
|
||||
## 2026-10-19 — Faceted navigation Batch B/C: chips, FacetLink, facets endpoint
|
||||
|
||||
- `GET /api/media-items/facets?facet=X&library_id=...` is a NEW pinned
|
||||
surface (registry-driven value+count listing for the filter panel):
|
||||
200 → `{facet, values[]: {value, display?, count}}` (display present
|
||||
only for language codes → names); 400 for unknown facets and for
|
||||
bool/range facets (they render as toggles, not lists); 403 for an
|
||||
explicit library_id outside the requester's visible set.
|
||||
- `GET /api/media-items/search` (JSON) and its HTMX grid partial now
|
||||
share ONE param parser (`handlers.ParseMediaSearchParams`). Behavior
|
||||
notes: the HTMX branch previously dropped `publisher_filter` and
|
||||
`narrator_filter` silently — it now honors every facet param; and an
|
||||
explicit hidden-library `library_id` is now a 403 on BOTH branches
|
||||
(was: silent empty set on JSON). Response shapes are untouched; the
|
||||
search row pin (47 keys) is unchanged.
|
||||
- `language_filter` now normalizes comma elements through the ingest
|
||||
language map, so display names ("English") match rows stored as ISO
|
||||
codes ("en"). The param name is unchanged; single-code values behave
|
||||
identically.
|
||||
- Web: the bookshelf gains an active-filter chips bar (client-local
|
||||
form state rendered by Alpine; templ owns the markup) and
|
||||
`applyURLFilters` accepts the new facet params.
|
||||
Reference in new issue
Block a user