Revamp entry detail layout

Restructure entry detail into a two-column layout with
summary/people cards and section cards on the right.

Signed-off-by: codex@lucy.xalior.com
This commit is contained in:
2026-01-10 22:32:55 +00:00
parent e94492eab6
commit 0b0dced512

View File

@@ -165,27 +165,24 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
{data.isXrated ? <span className="badge text-bg-danger">18+</span> : null} {data.isXrated ? <span className="badge text-bg-danger">18+</span> : null}
</div> </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">Entry Summary</h5>
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-striped table-hover align-middle"> <table className="table table-sm table-striped align-middle mb-0">
<thead>
<tr>
<th style={{ width: 220 }}>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody> <tbody>
<tr> <tr>
<td>ID</td> <th style={{ width: 180 }}>ID</th>
<td>{data.id}</td> <td>{data.id}</td>
</tr> </tr>
<tr> <tr>
<td>Title</td> <th>Title</th>
<td>{data.title}</td> <td>{data.title}</td>
</tr> </tr>
<tr> <tr>
<td>Machine</td> <th>Machine</th>
<td> <td>
{data.machinetype.id != null ? ( {data.machinetype.id != null ? (
data.machinetype.name ? ( data.machinetype.name ? (
@@ -199,7 +196,7 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Language</td> <th>Language</th>
<td> <td>
{data.language.id ? ( {data.language.id ? (
data.language.name ? ( data.language.name ? (
@@ -213,7 +210,7 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Genre</td> <th>Genre</th>
<td> <td>
{data.genre.id ? ( {data.genre.id ? (
data.genre.name ? ( data.genre.name ? (
@@ -228,43 +225,86 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</tr> </tr>
{typeof data.maxPlayers !== "undefined" && ( {typeof data.maxPlayers !== "undefined" && (
<tr> <tr>
<td>Max Players</td> <th>Max Players</th>
<td>{data.maxPlayers}</td> <td>{data.maxPlayers}</td>
</tr> </tr>
)} )}
{typeof data.availabletypeId !== "undefined" && ( {typeof data.availabletypeId !== "undefined" && (
<tr> <tr>
<td>Available Type</td> <th>Available Type</th>
<td>{data.availabletypeId ?? <span className="text-secondary">-</span>}</td> <td>{data.availabletypeId ?? <span className="text-secondary">-</span>}</td>
</tr> </tr>
)} )}
{typeof data.withoutLoadScreen !== "undefined" && ( {typeof data.withoutLoadScreen !== "undefined" && (
<tr> <tr>
<td>Without Load Screen</td> <th>Without Load Screen</th>
<td>{data.withoutLoadScreen ? "Yes" : "No"}</td> <td>{data.withoutLoadScreen ? "Yes" : "No"}</td>
</tr> </tr>
)} )}
{typeof data.withoutInlay !== "undefined" && ( {typeof data.withoutInlay !== "undefined" && (
<tr> <tr>
<td>Without Inlay</td> <th>Without Inlay</th>
<td>{data.withoutInlay ? "Yes" : "No"}</td> <td>{data.withoutInlay ? "Yes" : "No"}</td>
</tr> </tr>
)} )}
{typeof data.issueId !== "undefined" && ( {typeof data.issueId !== "undefined" && (
<tr> <tr>
<td>Issue</td> <th>Issue</th>
<td>{data.issueId ? <span>#{data.issueId}</span> : <span className="text-secondary">-</span>}</td> <td>{data.issueId ? <span>#{data.issueId}</span> : <span className="text-secondary">-</span>}</td>
</tr> </tr>
)} )}
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<h5 className="card-title">People</h5>
<div className="row g-3">
<div className="col-12">
<div className="text-secondary small mb-1">Authors</div>
{data.authors.length === 0 && <div className="text-secondary">Unknown</div>}
{data.authors.length > 0 && (
<ul className="list-unstyled mb-0">
{data.authors.map((a) => (
<li key={a.id}>
<Link href={`/zxdb/labels/${a.id}`}>{a.name}</Link>
</li>
))}
</ul>
)}
</div>
<div className="col-12">
<div className="text-secondary small mb-1">Publishers</div>
{data.publishers.length === 0 && <div className="text-secondary">Unknown</div>}
{data.publishers.length > 0 && (
<ul className="list-unstyled mb-0">
{data.publishers.map((p) => (
<li key={p.id}>
<Link href={`/zxdb/labels/${p.id}`}>{p.name}</Link>
</li>
))}
</ul>
)}
</div>
</div>
</div>
</div>
{/* Downloads (flat, by entry_id). Render only this flat section; do not render grouped downloads here. */} <div className="card shadow-sm">
<div> <div className="card-body d-flex flex-wrap gap-2">
<h5>Downloads</h5> <Link className="btn btn-sm btn-outline-secondary" href={`/zxdb/entries/${data.id}`}>Permalink</Link>
<Link className="btn btn-sm btn-outline-primary" href="/zxdb">Back to Explorer</Link>
</div>
</div>
</div>
<div className="col-lg-8">
<div className="card shadow-sm mb-3">
<div className="card-body">
<h5 className="card-title">Downloads</h5>
{(!data.downloadsFlat || data.downloadsFlat.length === 0) && <div className="text-secondary">No downloads</div>} {(!data.downloadsFlat || data.downloadsFlat.length === 0) && <div className="text-secondary">No downloads</div>}
{data.downloadsFlat && data.downloadsFlat.length > 0 && ( {data.downloadsFlat && data.downloadsFlat.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -326,42 +366,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
<hr />
<div className="row g-4">
<div className="col-lg-6">
<h5>Authors</h5>
{data.authors.length === 0 && <div className="text-secondary">Unknown</div>}
{data.authors.length > 0 && (
<ul className="list-unstyled mb-0">
{data.authors.map((a) => (
<li key={a.id}>
<Link href={`/zxdb/labels/${a.id}`}>{a.name}</Link>
</li>
))}
</ul>
)}
</div>
<div className="col-lg-6">
<h5>Publishers</h5>
{data.publishers.length === 0 && <div className="text-secondary">Unknown</div>}
{data.publishers.length > 0 && (
<ul className="list-unstyled mb-0">
{data.publishers.map((p) => (
<li key={p.id}>
<Link href={`/zxdb/labels/${p.id}`}>{p.name}</Link>
</li>
))}
</ul>
)}
</div>
</div> </div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Releases</h5>
<h5>Releases</h5>
{(!data.releases || data.releases.length === 0) && <div className="text-secondary">No releases recorded</div>} {(!data.releases || data.releases.length === 0) && <div className="text-secondary">No releases recorded</div>}
{data.releases && data.releases.length > 0 && ( {data.releases && data.releases.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -388,11 +397,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Origins</h5>
<h5>Origins</h5>
{(!data.origins || data.origins.length === 0) && <div className="text-secondary">No origins recorded</div>} {(!data.origins || data.origins.length === 0) && <div className="text-secondary">No origins recorded</div>}
{data.origins && data.origins.length > 0 && ( {data.origins && data.origins.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -442,11 +451,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Relations</h5>
<h5>Relations</h5>
{(!data.relations || data.relations.length === 0) && <div className="text-secondary">No relations recorded</div>} {(!data.relations || data.relations.length === 0) && <div className="text-secondary">No relations recorded</div>}
{data.relations && data.relations.length > 0 && ( {data.relations && data.relations.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -475,11 +484,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Tags / Members</h5>
<h5>Tags / Members</h5>
{(!data.tags || data.tags.length === 0) && <div className="text-secondary">No tags recorded</div>} {(!data.tags || data.tags.length === 0) && <div className="text-secondary">No tags recorded</div>}
{data.tags && data.tags.length > 0 && ( {data.tags && data.tags.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -516,11 +525,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Ports</h5>
<h5>Ports</h5>
{(!data.ports || data.ports.length === 0) && <div className="text-secondary">No ports recorded</div>} {(!data.ports || data.ports.length === 0) && <div className="text-secondary">No ports recorded</div>}
{data.ports && data.ports.length > 0 && ( {data.ports && data.ports.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -553,11 +562,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Remakes</h5>
<h5>Remakes</h5>
{(!data.remakes || data.remakes.length === 0) && <div className="text-secondary">No remakes recorded</div>} {(!data.remakes || data.remakes.length === 0) && <div className="text-secondary">No remakes recorded</div>}
{data.remakes && data.remakes.length > 0 && ( {data.remakes && data.remakes.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -592,11 +601,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Scores</h5>
<h5>Scores</h5>
{(!data.scores || data.scores.length === 0) && <div className="text-secondary">No scores recorded</div>} {(!data.scores || data.scores.length === 0) && <div className="text-secondary">No scores recorded</div>}
{data.scores && data.scores.length > 0 && ( {data.scores && data.scores.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -621,11 +630,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Notes</h5>
<h5>Notes</h5>
{(!data.notes || data.notes.length === 0) && <div className="text-secondary">No notes recorded</div>} {(!data.notes || data.notes.length === 0) && <div className="text-secondary">No notes recorded</div>}
{data.notes && data.notes.length > 0 && ( {data.notes && data.notes.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -648,12 +657,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
{/* Aliases (alternative titles) */} <h5 className="card-title">Aliases</h5>
<div>
<h5>Aliases</h5>
{(!data.aliases || data.aliases.length === 0) && <div className="text-secondary">No aliases</div>} {(!data.aliases || data.aliases.length === 0) && <div className="text-secondary">No aliases</div>}
{data.aliases && data.aliases.length > 0 && ( {data.aliases && data.aliases.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -680,11 +688,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
<div> <h5 className="card-title">Licenses</h5>
<h5>Licenses</h5>
{(!data.licenses || data.licenses.length === 0) && <div className="text-secondary">No licenses linked</div>} {(!data.licenses || data.licenses.length === 0) && <div className="text-secondary">No licenses linked</div>}
{data.licenses && data.licenses.length > 0 && ( {data.licenses && data.licenses.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -721,12 +729,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm mb-3">
<div className="card-body">
{/* Web links (external references) */} <h5 className="card-title">Web links</h5>
<div>
<h5>Web links</h5>
{(!data.webrefs || data.webrefs.length === 0) && <div className="text-secondary">No web links</div>} {(!data.webrefs || data.webrefs.length === 0) && <div className="text-secondary">No web links</div>}
{data.webrefs && data.webrefs.length > 0 && ( {data.webrefs && data.webrefs.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -759,11 +766,11 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> <div className="card shadow-sm">
<div className="card-body">
<div> <h5 className="card-title">Files</h5>
<h5>Files</h5>
{(!data.files || data.files.length === 0) && <div className="text-secondary">No files linked</div>} {(!data.files || data.files.length === 0) && <div className="text-secondary">No files linked</div>}
{data.files && data.files.length > 0 && ( {data.files && data.files.length > 0 && (
<div className="table-responsive"> <div className="table-responsive">
@@ -801,14 +808,8 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
</div> </div>
)} )}
</div> </div>
</div>
<hr /> </div>
{/* Removed grouped releases/downloads section to avoid duplicate downloads UI. */}
<div className="d-flex align-items-center gap-2">
<Link className="btn btn-sm btn-outline-secondary" href={`/zxdb/entries/${data.id}`}>Permalink</Link>
<Link className="btn btn-sm btn-outline-primary" href="/zxdb">Back to Explorer</Link>
</div> </div>
</div> </div>
); );