"use client"; import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; type Item = { id: number; title: string; isXrated: number; machinetypeId: number | null; machinetypeName?: string | null; languageId: string | null; languageName?: string | null; }; type Paged = { items: T[]; page: number; pageSize: number; total: number; }; export default function ZxdbExplorer({ initial, initialGenres, initialLanguages, initialMachines, }: { initial?: Paged; initialGenres?: { id: number; name: string }[]; initialLanguages?: { id: string; name: string }[]; initialMachines?: { id: number; name: string }[]; }) { const [q, setQ] = useState(""); const [page, setPage] = useState(initial?.page ?? 1); const [loading, setLoading] = useState(false); const [data, setData] = useState | null>(initial ?? null); const [genres, setGenres] = useState<{ id: number; name: string }[]>(initialGenres ?? []); const [languages, setLanguages] = useState<{ id: string; name: string }[]>(initialLanguages ?? []); const [machines, setMachines] = useState<{ id: number; name: string }[]>(initialMachines ?? []); const [genreId, setGenreId] = useState(""); const [languageId, setLanguageId] = useState(""); const [machinetypeId, setMachinetypeId] = useState(""); const [year, setYear] = useState(""); 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]); async function fetchData(query: string, p: number) { setLoading(true); try { const params = new URLSearchParams(); if (query) params.set("q", query); params.set("page", String(p)); params.set("pageSize", String(pageSize)); if (genreId !== "") params.set("genreId", String(genreId)); if (languageId !== "") params.set("languageId", String(languageId)); if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId)); if (year !== "") params.set("year", year); if (sort) params.set("sort", sort); const res = await fetch(`/api/zxdb/search?${params.toString()}`); if (!res.ok) throw new Error(`Failed: ${res.status}`); const json: Paged = await res.json(); setData(json); } catch (e) { console.error(e); setData({ items: [], page: 1, pageSize, total: 0 }); } finally { setLoading(false); } } useEffect(() => { // 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 === "" && year === "" && sort === "id_desc" ) { return; } fetchData(q, page); // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, genreId, languageId, machinetypeId, year, sort]); // Load filter lists on mount only if not provided by server useEffect(() => { if (initialGenres && initialLanguages && initialMachines) return; async function loadLists() { try { const [g, l, m] = await Promise.all([ fetch("/api/zxdb/genres", { cache: "force-cache" }).then((r) => r.json()), fetch("/api/zxdb/languages", { cache: "force-cache" }).then((r) => r.json()), fetch("/api/zxdb/machinetypes", { cache: "force-cache" }).then((r) => r.json()), ]); setGenres(g.items ?? []); setLanguages(l.items ?? []); setMachines(m.items ?? []); } catch {} } loadLists(); }, [initialGenres, initialLanguages, initialMachines]); function onSubmit(e: React.FormEvent) { e.preventDefault(); setPage(1); fetchData(q, 1); } return (

ZXDB Explorer

setQ(e.target.value)} />
setYear(e.target.value)} />
{loading && (
Loading...
)}
{data && data.items.length === 0 && !loading && (
No results.
)} {data && data.items.length > 0 && (
{data.items.map((it) => ( ))}
ID Title Machine Language
{it.id} {it.title} {it.machinetypeId != null ? ( it.machinetypeName ? ( {it.machinetypeName} ) : ( {it.machinetypeId} ) ) : ( - )} {it.languageId ? ( it.languageName ? ( {it.languageName} ) : ( {it.languageId} ) ) : ( - )}
)}
Page {data?.page ?? 1} / {totalPages}
Prev = totalPages) ? "disabled" : ""}`} aria-disabled={!data || data.page >= totalPages} href={`/zxdb?page=${Math.min(totalPages, (data?.page ?? 1) + 1)}`} > Next

Browse Labels Browse Genres Browse Languages Browse Machines
); }