"use client"; import Link from "next/link"; import EntryLink from "../../components/EntryLink"; import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; type Label = { id: number; name: string; labeltypeId: string | null; labeltypeName: string | null; permissions: { website: { id: number; name: string; link?: string | null }; type: { id: string; name: string | null }; text: string | null; }[]; licenses: { id: number; name: string; type: { id: string; name: string | null }; linkWikipedia?: string | null; linkSite?: string | null; comments?: string | null; }[]; }; 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 }; type Payload = { label: Label | null; authored: Paged; published: Paged }; export default function LabelDetailClient({ id, initial, initialTab, initialQ }: { id: number; initial: Payload; initialTab?: "authored" | "published"; initialQ?: string }) { // 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"); const [q, setQ] = useState(initialQ ?? ""); const router = useRouter(); // Names are now delivered by SSR payload to minimize pop-in. // Hooks must be called unconditionally const current = useMemo | null>( () => (tab === "authored" ? initial?.authored : initial?.published) ?? null, [initial, tab] ); const totalPages = useMemo(() => (current ? Math.max(1, Math.ceil(current.total / current.pageSize)) : 1), [current]); if (!initial || !initial.label) return
Not found
; return (

{initial.label.name}

{initial.label.labeltypeName ? `${initial.label.labeltypeName} (${initial.label.labeltypeId ?? "?"})` : (initial.label.labeltypeId ?? "?")}
Permissions
{initial.label.permissions.length === 0 &&
No permissions recorded
} {initial.label.permissions.length > 0 && (
{initial.label.permissions.map((p, idx) => ( ))}
Website Type Notes
{p.website.link ? ( {p.website.name} ) : ( {p.website.name} )} {p.type.name ?? p.type.id} {p.text ?? ""}
)}
Licenses
{initial.label.licenses.length === 0 &&
No licenses linked
} {initial.label.licenses.length > 0 && (
{initial.label.licenses.map((l) => ( ))}
Name Type Links
{l.name} {l.type.name ?? l.type.id}
{l.linkWikipedia && ( Wikipedia )} {l.linkSite && ( Site )} {!l.linkWikipedia && !l.linkSite && -}
)}
{ e.preventDefault(); const p = new URLSearchParams(); p.set("tab", tab); if (q) p.set("q", q); p.set("page", "1"); router.push(`/zxdb/labels/${id}?${p.toString()}`); }}>
setQ(e.target.value)} />
{current && current.items.length === 0 &&
No entries.
} {current && current.items.length > 0 && (
{current.items.map((it) => ( ))}
ID Title Machine Language
{it.machinetypeId != null ? ( it.machinetypeName ? ( {it.machinetypeName} ) : ( {it.machinetypeId} ) ) : ( - )} {it.languageId ? ( it.languageName ? ( {it.languageName} ) : ( {it.languageId} ) ) : ( - )}
)}
Page {current ? current.page : 1} / {totalPages}
{ const p = new URLSearchParams(); p.set("tab", tab); if (q) p.set("q", q); p.set("page", String(Math.max(1, (current ? current.page : 1) - 1))); return p.toString(); })()}`} > Prev = totalPages ? "disabled" : ""}`} aria-disabled={current ? current.page >= totalPages : true} href={`/zxdb/labels/${id}?${(() => { const p = new URLSearchParams(); p.set("tab", tab); if (q) p.set("q", q); p.set("page", String(Math.min(totalPages, (current ? current.page : 1) + 1))); return p.toString(); })()}`} > Next
); }