diff --git a/src/app/zxdb/entries/[id]/EntryDetail.tsx b/src/app/zxdb/entries/[id]/EntryDetail.tsx
index baf9daa..56bcceb 100644
--- a/src/app/zxdb/entries/[id]/EntryDetail.tsx
+++ b/src/app/zxdb/entries/[id]/EntryDetail.tsx
@@ -36,6 +36,34 @@ export type EntryDetailData = {
link: string | null;
comments: string | null;
}[];
+ ports?: {
+ id: number;
+ title: string | null;
+ platform: { id: number; name: string | null };
+ isOfficial: boolean;
+ linkSystem: string | null;
+ }[];
+ remakes?: {
+ id: number;
+ title: string;
+ fileLink: string;
+ fileDate: string | null;
+ fileSize: number | null;
+ authors: string | null;
+ platforms: string | null;
+ remakeYears: string | null;
+ remakeStatus: string | null;
+ }[];
+ scores?: {
+ website: { id: number; name: string | null };
+ score: number;
+ votes: number;
+ }[];
+ notes?: {
+ id: number;
+ type: { id: string; name: string | null };
+ text: string;
+ }[];
origins?: {
type: { id: string; name: string | null };
libraryTitle: string;
@@ -491,6 +519,138 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
+
+
Ports
+ {(!data.ports || data.ports.length === 0) &&
No ports recorded
}
+ {data.ports && data.ports.length > 0 && (
+
+
+
+
+ | Title |
+ Platform |
+ Official |
+ Link |
+
+
+
+ {data.ports.map((p) => (
+
+ | {p.title ?? -} |
+ {p.platform.name ?? `#${p.platform.id}`} |
+ {p.isOfficial ? "Yes" : "No"} |
+
+ {p.linkSystem ? (
+ Link
+ ) : (
+ -
+ )}
+ |
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
Remakes
+ {(!data.remakes || data.remakes.length === 0) &&
No remakes recorded
}
+ {data.remakes && data.remakes.length > 0 && (
+
+
+
+
+ | Title |
+ Platforms |
+ Years |
+ File |
+ Notes |
+
+
+
+ {data.remakes.map((r) => (
+
+ | {r.title} |
+ {r.platforms ?? -} |
+ {r.remakeYears ?? -} |
+
+ {r.fileLink ? (
+ File
+ ) : (
+ -
+ )}
+ |
+ {r.remakeStatus ?? r.authors ?? -} |
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
Scores
+ {(!data.scores || data.scores.length === 0) &&
No scores recorded
}
+ {data.scores && data.scores.length > 0 && (
+
+
+
+
+ | Website |
+ Score |
+ Votes |
+
+
+
+ {data.scores.map((s, idx) => (
+
+ | {s.website.name ?? `#${s.website.id}`} |
+ {s.score} |
+ {s.votes} |
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
Notes
+ {(!data.notes || data.notes.length === 0) &&
No notes recorded
}
+ {data.notes && data.notes.length > 0 && (
+
+
+
+
+ | Type |
+ Text |
+
+
+
+ {data.notes.map((n) => (
+
+ | {n.type.name ?? n.type.id} |
+ {n.text} |
+
+ ))}
+
+
+
+ )}
+
+
+
+
{/* Aliases (alternative titles) */}
Aliases
diff --git a/src/server/repo/zxdb.ts b/src/server/repo/zxdb.ts
index 371cae4..7ec2782 100644
--- a/src/server/repo/zxdb.ts
+++ b/src/server/repo/zxdb.ts
@@ -39,6 +39,12 @@ import {
tags,
tagtypes,
members,
+ ports,
+ platforms,
+ remakes,
+ scores,
+ notes,
+ notetypes,
webrefs,
websites,
magazines,
@@ -319,6 +325,34 @@ export interface EntryDetail {
link: string | null;
comments: string | null;
}[];
+ ports?: {
+ id: number;
+ title: string | null;
+ platform: { id: number; name: string | null };
+ isOfficial: boolean;
+ linkSystem: string | null;
+ }[];
+ remakes?: {
+ id: number;
+ title: string;
+ fileLink: string;
+ fileDate: string | null;
+ fileSize: number | null;
+ authors: string | null;
+ platforms: string | null;
+ remakeYears: string | null;
+ remakeStatus: string | null;
+ }[];
+ scores?: {
+ website: { id: number; name: string | null };
+ score: number;
+ votes: number;
+ }[];
+ notes?: {
+ id: number;
+ type: { id: string; name: string | null };
+ text: string;
+ }[];
origins?: {
type: { id: string; name: string | null };
libraryTitle: string;
@@ -627,6 +661,37 @@ export async function getEntryById(id: number): Promise {
link: string | null;
comments: string | null;
}[] = [];
+ let portRows: {
+ id: number | string;
+ title: string | null;
+ platformId: number | string;
+ platformName: string | null;
+ isOfficial: number | boolean;
+ linkSystem: string | null;
+ }[] = [];
+ let remakeRows: {
+ id: number | string;
+ title: string;
+ fileLink: string;
+ fileDate: string | null;
+ fileSize: number | string | null;
+ authors: string | null;
+ platforms: string | null;
+ remakeYears: string | null;
+ remakeStatus: string | null;
+ }[] = [];
+ let scoreRows: {
+ websiteId: number | string;
+ websiteName: string | null;
+ score: number | string;
+ votes: number | string;
+ }[] = [];
+ let noteRows: {
+ id: number | string;
+ notetypeId: string;
+ notetypeName: string | null;
+ text: string;
+ }[] = [];
try {
aliasRows = await db
.select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title })
@@ -741,6 +806,67 @@ export async function getEntryById(id: number): Promise {
);
tagRows = rows as typeof tagRows;
} catch {}
+ try {
+ const rows = await db
+ .select({
+ id: ports.id,
+ title: ports.title,
+ platformId: ports.platformId,
+ platformName: platforms.name,
+ isOfficial: ports.isOfficial,
+ linkSystem: ports.linkSystem,
+ })
+ .from(ports)
+ .leftJoin(platforms, eq(platforms.id, ports.platformId))
+ .where(eq(ports.entryId, id))
+ .orderBy(asc(ports.title));
+ portRows = rows as typeof portRows;
+ } catch {}
+ try {
+ const rows = await db
+ .select({
+ id: remakes.id,
+ title: remakes.title,
+ fileLink: remakes.fileLink,
+ fileDate: remakes.fileDate,
+ fileSize: remakes.fileSize,
+ authors: remakes.authors,
+ platforms: remakes.platforms,
+ remakeYears: remakes.remakeYears,
+ remakeStatus: remakes.remakeStatus,
+ })
+ .from(remakes)
+ .where(eq(remakes.entryId, id))
+ .orderBy(asc(remakes.title));
+ remakeRows = rows as typeof remakeRows;
+ } catch {}
+ try {
+ const rows = await db
+ .select({
+ websiteId: websites.id,
+ websiteName: websites.name,
+ score: scores.score,
+ votes: scores.votes,
+ })
+ .from(scores)
+ .innerJoin(websites, eq(websites.id, scores.websiteId))
+ .where(eq(scores.entryId, id));
+ scoreRows = rows as typeof scoreRows;
+ } catch {}
+ try {
+ const rows = await db
+ .select({
+ id: notes.id,
+ notetypeId: notes.notetypeId,
+ notetypeName: notetypes.name,
+ text: notes.text,
+ })
+ .from(notes)
+ .leftJoin(notetypes, eq(notetypes.id, notes.notetypeId))
+ .where(eq(notes.entryId, id))
+ .orderBy(asc(notes.id));
+ noteRows = rows as typeof noteRows;
+ } catch {}
return {
id: base.id,
@@ -781,6 +907,34 @@ export async function getEntryById(id: number): Promise {
link: t.link ?? null,
comments: t.comments ?? null,
})),
+ ports: portRows.map((p) => ({
+ id: Number(p.id),
+ title: p.title ?? null,
+ platform: { id: Number(p.platformId), name: p.platformName ?? null },
+ isOfficial: !!p.isOfficial,
+ linkSystem: p.linkSystem ?? null,
+ })),
+ remakes: remakeRows.map((r) => ({
+ id: Number(r.id),
+ title: r.title,
+ fileLink: r.fileLink,
+ fileDate: r.fileDate ?? null,
+ fileSize: r.fileSize != null ? Number(r.fileSize) : null,
+ authors: r.authors ?? null,
+ platforms: r.platforms ?? null,
+ remakeYears: r.remakeYears ?? null,
+ remakeStatus: r.remakeStatus ?? null,
+ })),
+ scores: scoreRows.map((s) => ({
+ website: { id: Number(s.websiteId), name: s.websiteName ?? null },
+ score: Number(s.score),
+ votes: Number(s.votes),
+ })),
+ notes: noteRows.map((n) => ({
+ id: Number(n.id),
+ type: { id: n.notetypeId, name: n.notetypeName ?? null },
+ text: n.text,
+ })),
origins: originRows.map((o) => ({
type: { id: o.origintypeId, name: o.origintypeName ?? null },
libraryTitle: o.libraryTitle,