Unify ZXDB list layouts
Apply sidebar filter layout to label/genre/language/machine lists and restructure release detail into a two-column view. Signed-off-by: codex@lucy.xalior.com
This commit is contained in:
@@ -39,17 +39,31 @@ export default function GenresSearch({ initial, initialQ }: { initial?: Paged<Ge
|
||||
]}
|
||||
/>
|
||||
|
||||
<h1>Genres</h1>
|
||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<h1 className="mb-1">Genres</h1>
|
||||
<div className="text-secondary">{data?.total.toLocaleString() ?? "0"} results</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row g-3">
|
||||
<div className="col-lg-3">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-body">
|
||||
<form className="d-flex flex-column gap-2" onSubmit={submit}>
|
||||
<div>
|
||||
<label className="form-label small text-secondary">Search</label>
|
||||
<input className="form-control" placeholder="Search genres…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<div className="d-grid">
|
||||
<button className="btn btn-primary">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="col-lg-9">
|
||||
{data && data.items.length === 0 && <div className="alert alert-warning">No genres found.</div>}
|
||||
{data && data.items.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -74,6 +88,7 @@ export default function GenresSearch({ initial, initialQ }: { initial?: Paged<Ge
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<span>Page {data?.page ?? 1} / {totalPages}</span>
|
||||
|
||||
@@ -41,17 +41,31 @@ export default function LabelsSearch({ initial, initialQ }: { initial?: Paged<La
|
||||
]}
|
||||
/>
|
||||
|
||||
<h1>Labels</h1>
|
||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<h1 className="mb-1">Labels</h1>
|
||||
<div className="text-secondary">{data?.total.toLocaleString() ?? "0"} results</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row g-3">
|
||||
<div className="col-lg-3">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-body">
|
||||
<form className="d-flex flex-column gap-2" onSubmit={submit}>
|
||||
<div>
|
||||
<label className="form-label small text-secondary">Search</label>
|
||||
<input className="form-control" placeholder="Search labels…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<div className="d-grid">
|
||||
<button className="btn btn-primary">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="col-lg-9">
|
||||
{data && data.items.length === 0 && <div className="alert alert-warning">No labels found.</div>}
|
||||
{data && data.items.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -80,6 +94,7 @@ export default function LabelsSearch({ initial, initialQ }: { initial?: Paged<La
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<span>Page {data?.page ?? 1} / {totalPages}</span>
|
||||
|
||||
@@ -39,17 +39,31 @@ export default function LanguagesSearch({ initial, initialQ }: { initial?: Paged
|
||||
]}
|
||||
/>
|
||||
|
||||
<h1>Languages</h1>
|
||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<h1 className="mb-1">Languages</h1>
|
||||
<div className="text-secondary">{data?.total.toLocaleString() ?? "0"} results</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row g-3">
|
||||
<div className="col-lg-3">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-body">
|
||||
<form className="d-flex flex-column gap-2" onSubmit={submit}>
|
||||
<div>
|
||||
<label className="form-label small text-secondary">Search</label>
|
||||
<input className="form-control" placeholder="Search languages…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<div className="d-grid">
|
||||
<button className="btn btn-primary">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="col-lg-9">
|
||||
{data && data.items.length === 0 && <div className="alert alert-warning">No languages found.</div>}
|
||||
{data && data.items.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -74,6 +88,7 @@ export default function LanguagesSearch({ initial, initialQ }: { initial?: Paged
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<span>Page {data?.page ?? 1} / {totalPages}</span>
|
||||
|
||||
@@ -41,17 +41,31 @@ export default function MachineTypesSearch({ initial, initialQ }: { initial?: Pa
|
||||
]}
|
||||
/>
|
||||
|
||||
<h1>Machine Types</h1>
|
||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<h1 className="mb-1">Machine Types</h1>
|
||||
<div className="text-secondary">{data?.total.toLocaleString() ?? "0"} results</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row g-3">
|
||||
<div className="col-lg-3">
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-body">
|
||||
<form className="d-flex flex-column gap-2" onSubmit={submit}>
|
||||
<div>
|
||||
<label className="form-label small text-secondary">Search</label>
|
||||
<input className="form-control" placeholder="Search machine types…" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<div className="d-grid">
|
||||
<button className="btn btn-primary">Search</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3">
|
||||
<div className="col-lg-9">
|
||||
{data && data.items.length === 0 && <div className="alert alert-warning">No machine types found.</div>}
|
||||
{data && data.items.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -76,6 +90,7 @@ export default function MachineTypesSearch({ initial, initialQ }: { initial?: Pa
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<span>Page {data?.page ?? 1} / {totalPages}</span>
|
||||
|
||||
@@ -191,29 +191,26 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div className="row g-3 mt-2">
|
||||
<div className="col-lg-4">
|
||||
<div className="card shadow-sm mb-3">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Release Summary</h5>
|
||||
<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>
|
||||
<table className="table table-sm table-striped align-middle mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Entry</td>
|
||||
<th style={{ width: 160 }}>Entry</th>
|
||||
<td>
|
||||
<Link href={`/zxdb/entries/${data.entry.id}`}>#{data.entry.id}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Release Sequence</td>
|
||||
<th>Release Sequence</th>
|
||||
<td>#{data.release.releaseSeq}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Release Date</td>
|
||||
<th>Release Date</th>
|
||||
<td>
|
||||
{data.release.year != null ? (
|
||||
<span>
|
||||
@@ -227,7 +224,7 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Currency</td>
|
||||
<th>Currency</th>
|
||||
<td>
|
||||
{data.release.currency.id ? (
|
||||
<span>{data.release.currency.id} {data.release.currency.name ? `(${data.release.currency.name})` : ""}</span>
|
||||
@@ -237,41 +234,42 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Price</td>
|
||||
<th>Price</th>
|
||||
<td>{formatCurrency(data.release.prices.release, data.release.currency)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Budget Price</td>
|
||||
<th>Budget Price</th>
|
||||
<td>{formatCurrency(data.release.prices.budget, data.release.currency)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microdrive Price</td>
|
||||
<th>Microdrive Price</th>
|
||||
<td>{formatCurrency(data.release.prices.microdrive, data.release.currency)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Disk Price</td>
|
||||
<th>Disk Price</th>
|
||||
<td>{formatCurrency(data.release.prices.disk, data.release.currency)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cartridge Price</td>
|
||||
<th>Cartridge Price</th>
|
||||
<td>{formatCurrency(data.release.prices.cartridge, data.release.currency)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Book ISBN</td>
|
||||
<th>Book ISBN</th>
|
||||
<td>{data.release.book.isbn ?? <span className="text-secondary">-</span>}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Book Pages</td>
|
||||
<th>Book Pages</th>
|
||||
<td>{data.release.book.pages ?? <span className="text-secondary">-</span>}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h5>Other Releases</h5>
|
||||
<div className="card shadow-sm">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Other Releases</h5>
|
||||
{otherReleases.length === 0 && <div className="text-secondary">No other releases</div>}
|
||||
{otherReleases.length > 0 && (
|
||||
<div className="d-flex flex-wrap gap-2">
|
||||
@@ -287,11 +285,13 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h5>Places (Magazines)</h5>
|
||||
<div className="col-lg-8">
|
||||
<div className="card shadow-sm mb-3">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Places (Magazines)</h5>
|
||||
{magazineGroups.length === 0 && <div className="text-secondary">No magazine references</div>}
|
||||
{magazineGroups.length > 0 && (
|
||||
<div className="d-flex flex-column gap-3">
|
||||
@@ -349,11 +349,11 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h5>Downloads</h5>
|
||||
<div className="card shadow-sm mb-3">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Downloads</h5>
|
||||
{data.downloads.length === 0 && <div className="text-secondary">No downloads</div>}
|
||||
{data.downloads.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -412,11 +412,11 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h5>Scraps / Media</h5>
|
||||
<div className="card shadow-sm mb-3">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Scraps / Media</h5>
|
||||
{data.scraps.length === 0 && <div className="text-secondary">No scraps</div>}
|
||||
{data.scraps.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -477,11 +477,11 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<h5>Issue Files</h5>
|
||||
<div className="card shadow-sm mb-3">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Issue Files</h5>
|
||||
{data.files.length === 0 && <div className="text-secondary">No files linked</div>}
|
||||
{data.files.length > 0 && (
|
||||
<div className="table-responsive">
|
||||
@@ -519,8 +519,9 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<Link className="btn btn-sm btn-outline-secondary" href={`/zxdb/releases/${data.entry.id}/${data.release.releaseSeq}`}>Permalink</Link>
|
||||
|
||||
Reference in New Issue
Block a user