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:
@@ -30,7 +30,7 @@ export default function ZxdbExplorer({
|
||||
initialMachines?: { id: number; name: string }[];
|
||||
}) {
|
||||
const [q, setQ] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
const [page, setPage] = useState(initial?.page ?? 1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<Paged<Item> | null>(initial ?? null);
|
||||
const [genres, setGenres] = useState<{ id: number; name: string }[]>(initialGenres ?? []);
|
||||
@@ -39,7 +39,7 @@ export default function ZxdbExplorer({
|
||||
const [genreId, setGenreId] = useState<number | "">("");
|
||||
const [languageId, setLanguageId] = useState<string | "">("");
|
||||
const [machinetypeId, setMachinetypeId] = useState<number | "">("");
|
||||
const [sort, setSort] = useState<"title" | "id_desc">("title");
|
||||
const [sort, setSort] = useState<"title" | "id_desc">("id_desc");
|
||||
|
||||
const pageSize = 20;
|
||||
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
||||
@@ -69,8 +69,27 @@ export default function ZxdbExplorer({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Avoid immediate client fetch on first paint if server provided initial data
|
||||
if (initial && page === 1 && q === "" && genreId === "" && languageId === "" && machinetypeId === "" && sort === "title") {
|
||||
// When navigating via Next.js Links that change ?page=, SSR provides new `initial`.
|
||||
// Sync local state from new SSR payload so the list and counter update immediately
|
||||
// without an extra client fetch.
|
||||
if (initial) {
|
||||
setData(initial);
|
||||
setPage(initial.page);
|
||||
}
|
||||
}, [initial]);
|
||||
|
||||
useEffect(() => {
|
||||
// Avoid immediate client fetch on first paint if server provided initial data for this exact state
|
||||
const initialPage = initial?.page ?? 1;
|
||||
if (
|
||||
initial &&
|
||||
page === initialPage &&
|
||||
q === "" &&
|
||||
genreId === "" &&
|
||||
languageId === "" &&
|
||||
machinetypeId === "" &&
|
||||
sort === "id_desc"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fetchData(q, page);
|
||||
@@ -185,23 +204,25 @@ export default function ZxdbExplorer({
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
||||
disabled={loading || page <= 1}
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
<span>
|
||||
Page {data?.page ?? page} / {totalPages}
|
||||
Page {data?.page ?? 1} / {totalPages}
|
||||
</span>
|
||||
<button
|
||||
className="btn btn-outline-secondary"
|
||||
onClick={() => setPage((p) => p + 1)}
|
||||
disabled={loading || (data ? data.page >= totalPages : false)}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div className="ms-auto d-flex gap-2">
|
||||
<Link
|
||||
className={`btn btn-outline-secondary ${!data || (data.page <= 1) ? "disabled" : ""}`}
|
||||
aria-disabled={!data || data.page <= 1}
|
||||
href={`/zxdb?page=${Math.max(1, (data?.page ?? 1) - 1)}`}
|
||||
>
|
||||
Prev
|
||||
</Link>
|
||||
<Link
|
||||
className={`btn btn-outline-secondary ${!data || (data.page >= totalPages) ? "disabled" : ""}`}
|
||||
aria-disabled={!data || data.page >= totalPages}
|
||||
href={`/zxdb?page=${Math.min(totalPages, (data?.page ?? 1) + 1)}`}
|
||||
>
|
||||
Next
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
Reference in New Issue
Block a user