Standardize ZXDB UI; add SSR search/tables

Unify the look and feel of all /zxdb pages and minimize client pop-in.

- Make all /zxdb pages full-width to match /explorer
- Convert Languages, Genres, Machine Types, and Labels lists to
  Bootstrap tables with table-striped and table-hover inside
  table-responsive wrappers
- Replace raw FK IDs with linked names via SSR repository joins
- Add scoped search boxes on detail pages (labels, genres, languages,
  machine types) with SSR filtering and pagination that preserves q/tab
- Keep explorer results consistent: show Machine/Language names with
  links, no client lookups required

This improves consistency, readability, and first paint stability across
the ZXDB section while keeping navigation fast and discoverable.

Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
2025-12-12 16:58:50 +00:00
parent ddbf72ea52
commit 240936a850
18 changed files with 873 additions and 147 deletions

View File

@@ -12,13 +12,19 @@ export type EntryDetailData = {
genre: { id: number | null; name: string | null };
authors: Label[];
publishers: Label[];
// extra fields for richer details
maxPlayers?: number;
availabletypeId?: string | null;
withoutLoadScreen?: number;
withoutInlay?: number;
issueId?: number | null;
};
export default function EntryDetailClient({ data }: { data: EntryDetailData }) {
if (!data) return <div className="alert alert-warning">Not found</div>;
return (
<div className="container">
<div>
<div className="d-flex align-items-center gap-2 flex-wrap">
<h1 className="mb-0">{data.title}</h1>
{data.genre.name && (
@@ -41,6 +47,101 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData }) {
<hr />
<div className="table-responsive">
<table className="table table-striped table-hover align-middle">
<thead>
<tr>
<th style={{ width: 220 }}>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>{data.id}</td>
</tr>
<tr>
<td>Title</td>
<td>{data.title}</td>
</tr>
<tr>
<td>Machine</td>
<td>
{data.machinetype.id != null ? (
data.machinetype.name ? (
<Link href={`/zxdb/machinetypes/${data.machinetype.id}`}>{data.machinetype.name}</Link>
) : (
<span>#{data.machinetype.id}</span>
)
) : (
<span className="text-secondary">-</span>
)}
</td>
</tr>
<tr>
<td>Language</td>
<td>
{data.language.id ? (
data.language.name ? (
<Link href={`/zxdb/languages/${data.language.id}`}>{data.language.name}</Link>
) : (
<span>{data.language.id}</span>
)
) : (
<span className="text-secondary">-</span>
)}
</td>
</tr>
<tr>
<td>Genre</td>
<td>
{data.genre.id ? (
data.genre.name ? (
<Link href={`/zxdb/genres/${data.genre.id}`}>{data.genre.name}</Link>
) : (
<span>#{data.genre.id}</span>
)
) : (
<span className="text-secondary">-</span>
)}
</td>
</tr>
{typeof data.maxPlayers !== "undefined" && (
<tr>
<td>Max Players</td>
<td>{data.maxPlayers}</td>
</tr>
)}
{typeof data.availabletypeId !== "undefined" && (
<tr>
<td>Available Type</td>
<td>{data.availabletypeId ?? <span className="text-secondary">-</span>}</td>
</tr>
)}
{typeof data.withoutLoadScreen !== "undefined" && (
<tr>
<td>Without Load Screen</td>
<td>{data.withoutLoadScreen ? "Yes" : "No"}</td>
</tr>
)}
{typeof data.withoutInlay !== "undefined" && (
<tr>
<td>Without Inlay</td>
<td>{data.withoutInlay ? "Yes" : "No"}</td>
</tr>
)}
{typeof data.issueId !== "undefined" && (
<tr>
<td>Issue</td>
<td>{data.issueId ? <span>#{data.issueId}</span> : <span className="text-secondary">-</span>}</td>
</tr>
)}
</tbody>
</table>
</div>
<hr />
<div className="row g-4">
<div className="col-lg-6">
<h5>Authors</h5>