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.
38 lines
1.6 KiB
Templ
38 lines
1.6 KiB
Templ
package templates
|
|
|
|
import (
|
|
"bookhoard/internal/services"
|
|
"net/url"
|
|
)
|
|
|
|
// Facet navigation helpers (faceted-navigation plan, Batch B).
|
|
//
|
|
// "Everything filterable should be clickable": every place a facet value
|
|
// is DISPLAYED links to the bookshelf pre-filtered by that value. The
|
|
// raw (stored) value rides the param — for genre that is the canonical
|
|
// lowercase form, while the shown text is prettified — so matching and
|
|
// display can never disagree. Series and tags keep their dedicated
|
|
// pages (/series/detail, /tags/detail) by design and do NOT use these
|
|
// helpers; author links predate them and stay as-is.
|
|
|
|
// FacetLink renders a facet value as an inline text link (metadata
|
|
// grid cells, the narrators line) to /bookshelf?<facet>_filter=<value>.
|
|
templ FacetLink(facet string, value string, display string) {
|
|
<a href={ "/bookshelf?" + facet + "_filter=" + url.QueryEscape(value) } class="hover:underline" style="color: var(--text-primary);">{ display }</a>
|
|
}
|
|
|
|
// FacetBadge is the badge-styled FacetLink (the detail page's
|
|
// age-rating and story-arc badges).
|
|
templ FacetBadge(facet string, value string, display string) {
|
|
<a href={ "/bookshelf?" + facet + "_filter=" + url.QueryEscape(value) } class="badge hover:opacity-80 transition-opacity" style="border: 1px solid var(--border); color: var(--text-secondary); text-decoration: none;">{ display }</a>
|
|
}
|
|
|
|
// languageDisplay renders a stored language value for display:
|
|
// "en" → "English"; unknown/legacy values pass through verbatim.
|
|
func languageDisplay(value string) string {
|
|
if name := services.LanguageDisplayName(value); name != "" {
|
|
return name
|
|
}
|
|
return value
|
|
}
|