From 3e13da55526010f92cbcf879149d9d7ac6b00003 Mon Sep 17 00:00:00 2001 From: "D. Rimron-Soutter" Date: Sat, 10 Jan 2026 17:46:57 +0000 Subject: [PATCH] Improve ZXDB releases list Link release titles to release detail, add magref count badges, and show other releases on release detail. Signed-off-by: codex@lucy.xalior.com --- src/app/zxdb/releases/ReleasesExplorer.tsx | 15 ++++++- .../[entryId]/[releaseSeq]/ReleaseDetail.tsx | 25 ++++++++++++ src/server/repo/zxdb.ts | 39 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/app/zxdb/releases/ReleasesExplorer.tsx b/src/app/zxdb/releases/ReleasesExplorer.tsx index a9e0084..1102e17 100644 --- a/src/app/zxdb/releases/ReleasesExplorer.tsx +++ b/src/app/zxdb/releases/ReleasesExplorer.tsx @@ -11,6 +11,7 @@ type Item = { releaseSeq: number; entryTitle: string; year: number | null; + magrefCount: number; }; type Paged = { @@ -337,6 +338,7 @@ export default function ReleasesExplorer({ Entry ID Title Release # + Places Year @@ -347,13 +349,24 @@ export default function ReleasesExplorer({ - +
+ + {it.entryTitle || `Entry #${it.entryId}`} + +
#{it.releaseSeq} + + {it.magrefCount > 0 ? ( + {it.magrefCount} + ) : ( + - + )} + {it.year ?? -} ))} diff --git a/src/app/zxdb/releases/[entryId]/[releaseSeq]/ReleaseDetail.tsx b/src/app/zxdb/releases/[entryId]/[releaseSeq]/ReleaseDetail.tsx index d2dedbb..0c18153 100644 --- a/src/app/zxdb/releases/[entryId]/[releaseSeq]/ReleaseDetail.tsx +++ b/src/app/zxdb/releases/[entryId]/[releaseSeq]/ReleaseDetail.tsx @@ -9,6 +9,10 @@ type ReleaseDetailData = { title: string; issueId: number | null; }; + entryReleases: Array<{ + releaseSeq: number; + year: number | null; + }>; release: { entryId: number; releaseSeq: number; @@ -167,6 +171,7 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData if (!data) return
Not found
; const magazineGroups = groupMagazineRefs(data.magazineRefs); + const otherReleases = data.entryReleases.filter((r) => r.releaseSeq !== data.release.releaseSeq); return (
@@ -265,6 +270,26 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
+
+
Other Releases
+ {otherReleases.length === 0 &&
No other releases
} + {otherReleases.length > 0 && ( +
+ {otherReleases.map((r) => ( + + #{r.releaseSeq}{r.year != null ? ` ยท ${r.year}` : ""} + + ))} +
+ )} +
+ +
+
Places (Magazines)
{magazineGroups.length === 0 &&
No magazine references
} diff --git a/src/server/repo/zxdb.ts b/src/server/repo/zxdb.ts index e655914..1bb1541 100644 --- a/src/server/repo/zxdb.ts +++ b/src/server/repo/zxdb.ts @@ -1103,6 +1103,7 @@ export interface ReleaseListItem { releaseSeq: number; entryTitle: string; year: number | null; + magrefCount: number; } export async function searchReleases(params: ReleaseSearchParams): Promise> { @@ -1192,12 +1193,33 @@ export async function searchReleases(params: ReleaseSearchParams): Promise Number(r.entryId)).filter((id) => Number.isFinite(id)))); + const magrefCounts = new Map(); + if (entryIds.length > 0) { + try { + const values = entryIds.map((id) => sql`${id}`); + const rows = await db.execute(sql` + select ${searchByMagrefs.entryId} as entryId, count(*) as count + from ${searchByMagrefs} + where ${searchByMagrefs.entryId} in (${sql.join(values, sql`, `)}) + group by ${searchByMagrefs.entryId} + `); + type CountRow = { entryId: number | string; count: number | string }; + for (const row of rows as unknown as CountRow[]) { + magrefCounts.set(Number(row.entryId), Number(row.count)); + } + } catch { + // Helper table might be missing; default to 0 counts. + } + } + // Ensure plain primitives const items: ReleaseListItem[] = rowsQB.map((r) => ({ entryId: Number(r.entryId), releaseSeq: Number(r.releaseSeq), entryTitle: r.entryTitle ?? "", year: r.year != null ? Number(r.year) : null, + magrefCount: magrefCounts.get(Number(r.entryId)) ?? 0, })); return { items, page, pageSize, total }; @@ -1209,6 +1231,10 @@ export interface ReleaseDetail { title: string; issueId: number | null; }; + entryReleases: Array<{ + releaseSeq: number; + year: number | null; + }>; release: { entryId: number; releaseSeq: number; @@ -1316,6 +1342,15 @@ export async function getReleaseDetail(entryId: number, releaseSeq: number): Pro const base = rows[0]; if (!base) return null; + const entryReleaseRows = await db + .select({ + releaseSeq: releases.releaseSeq, + year: releases.releaseYear, + }) + .from(releases) + .where(eq(releases.entryId, entryId)) + .orderBy(asc(releases.releaseSeq)); + type DownloadRow = { id: number | string; link: string; @@ -1455,6 +1490,10 @@ export async function getReleaseDetail(entryId: number, releaseSeq: number): Pro title: base.entryTitle ?? "", issueId: base.issueId ?? null, }, + entryReleases: entryReleaseRows.map((r) => ({ + releaseSeq: Number(r.releaseSeq), + year: r.year != null ? Number(r.year) : null, + })), release: { entryId: Number(base.entryId), releaseSeq: Number(base.releaseSeq),