Fix ZXDB pagination counters and navigation
Implement URL-driven pagination and correct total counts across ZXDB: - Root /zxdb: SSR reads ?page; client syncs to SSR; Prev/Next as Links. - Sub-index pages (genres, languages, machinetypes): parse ?page on server; use SSR props in clients; Prev/Next via Links. - Labels browse (/zxdb/labels): dynamic SSR, reads ?q & ?page; typed count(*); client syncs to SSR; Prev/Next preserve q. - Label detail (/zxdb/labels/[id]): tab-aware Prev/Next Links; counters from server. - Repo: replace raw counts with typed Drizzle count(*) for reliable totals. Signed-off-by: Junie <Junie@lucy.xalior.com>
This commit is contained in:
@@ -9,22 +9,21 @@ type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||
|
||||
type Payload = { label: Label | null; authored: Paged<Item>; published: Paged<Item> };
|
||||
|
||||
export default function LabelDetailClient({ id, initial }: { id: number; initial: Payload }) {
|
||||
const [data] = useState<Payload>(initial);
|
||||
const [tab, setTab] = useState<"authored" | "published">("authored");
|
||||
const [page] = useState(1);
|
||||
export default function LabelDetailClient({ id, initial, initialTab }: { id: number; initial: Payload; initialTab?: "authored" | "published" }) {
|
||||
// Keep only interactive UI state (tab). Data should come directly from SSR props so it updates on navigation.
|
||||
const [tab, setTab] = useState<"authored" | "published">(initialTab ?? "authored");
|
||||
|
||||
if (!data || !data.label) return <div className="alert alert-warning">Not found</div>;
|
||||
if (!initial || !initial.label) return <div className="alert alert-warning">Not found</div>;
|
||||
|
||||
const current = useMemo(() => (tab === "authored" ? data.authored : data.published), [data, tab]);
|
||||
const current = useMemo(() => (tab === "authored" ? initial.authored : initial.published), [initial, tab]);
|
||||
const totalPages = useMemo(() => Math.max(1, Math.ceil(current.total / current.pageSize)), [current]);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h1 className="mb-0">{data.label.name}</h1>
|
||||
<h1 className="mb-0">{initial.label.name}</h1>
|
||||
<div>
|
||||
<span className="badge text-bg-light">{data.label.labeltypeId ?? "?"}</span>
|
||||
<span className="badge text-bg-light">{initial.label.labeltypeId ?? "?"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,7 +65,23 @@ export default function LabelDetailClient({ id, initial }: { id: number; initial
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<span>Page {page} / {totalPages}</span>
|
||||
<span>Page {current.page} / {totalPages}</span>
|
||||
<div className="ms-auto d-flex gap-2">
|
||||
<Link
|
||||
className={`btn btn-sm btn-outline-secondary ${current.page <= 1 ? "disabled" : ""}`}
|
||||
aria-disabled={current.page <= 1}
|
||||
href={`/zxdb/labels/${id}?tab=${tab}&page=${Math.max(1, current.page - 1)}`}
|
||||
>
|
||||
Prev
|
||||
</Link>
|
||||
<Link
|
||||
className={`btn btn-sm btn-outline-secondary ${current.page >= totalPages ? "disabled" : ""}`}
|
||||
aria-disabled={current.page >= totalPages}
|
||||
href={`/zxdb/labels/${id}?tab=${tab}&page=${Math.min(totalPages, current.page + 1)}`}
|
||||
>
|
||||
Next
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user