Files
bookhoard/web/src/series.ts
T
John O'Keefe 96c1228805 feat(series): multi-field search, sort-direction toggle, apply placement
Toolbar feedback round: the series search box now matches whatever the
main search matches for a group — series NAME, any book's AUTHOR, and
any book's TITLE inside the series (WHERE EXISTS per group-row, so
counts stay true); the separate author input is gone from the toolbar
(the author= param stays live for the JSON API and deep links). The
sort control becomes a base-key select (Date added/Name/Author/Books/
Progress/Last read) plus a chevron direction toggle that flips and
resubmits; sort/dir compose through ResolveSeriesSort (full registry
keys keep working for back-compat, unknown base keys fall to recent).
The search/apply button moved next to the search box as requested, and
default URLs stay clean (sort/dir only emitted once the user chooses).
2026-10-27 19:05:23 -04:00

40 lines
1.5 KiB
TypeScript

import { Alpine } from "./alpine";
import { initLibrarySwitcher } from "./library-switcher";
// Rendering ownership (AGENTS.md): the series grid, its paginator and
// its sort/filter toolbar are rendered by templates/series.templ. This
// page intentionally has NO TypeScript renderer — the old one here
// duplicated the templ markup via innerHTML and re-fetched at hardcoded
// page 1, stomping SSR pagination whenever the header's library restore
// fired. A library switch is a plain SSR navigation so the URL stays
// the single source of truth for library_id, page AND sort/filter state
// (the switch carries the current q/author/status/sort params along).
function navigateToSeries(libraryId: string): void {
const params = new URLSearchParams(window.location.search);
if (libraryId) {
params.set("library_id", libraryId);
} else {
params.delete("library_id");
}
params.set("page", "1");
window.location.href = `/series?${params.toString()}`;
}
Alpine.data("seriesPage", () => ({
initSeriesPage() {
initLibrarySwitcher({
onSwitch: navigateToSeries,
});
},
// flipSortDir inverts the direction hidden input and resubmits the
// plain-GET filter form (the URL stays the single source of truth).
flipSortDir() {
const input = this.$refs.sortDir as HTMLInputElement | undefined;
if (!input) return;
input.value = input.value === "asc" ? "desc" : "asc";
const form = input.closest("form") as HTMLFormElement | null;
if (form) form.requestSubmit();
},
}));