diff --git a/src/app/zxdb/entries/[id]/EntryDetail.tsx b/src/app/zxdb/entries/[id]/EntryDetail.tsx index 609c3b1..baf9daa 100644 --- a/src/app/zxdb/entries/[id]/EntryDetail.tsx +++ b/src/app/zxdb/entries/[id]/EntryDetail.tsx @@ -22,6 +22,20 @@ export type EntryDetailData = { linkSite?: string | null; comments?: string | null; }[]; + relations?: { + direction: "from" | "to"; + type: { id: string; name: string | null }; + entry: { id: number; title: string | null }; + }[]; + tags?: { + id: number; + name: string; + type: { id: string; name: string | null }; + category: { id: number | null; name: string | null }; + memberSeq: number | null; + link: string | null; + comments: string | null; + }[]; origins?: { type: { id: string; name: string | null }; libraryTitle: string; @@ -403,6 +417,80 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
+
+
Relations
+ {(!data.relations || data.relations.length === 0) &&
No relations recorded
} + {data.relations && data.relations.length > 0 && ( +
+ + + + + + + + + + {data.relations.map((r, idx) => ( + + + + + + ))} + +
DirectionTypeEntry
{r.direction === "from" ? "From" : "To"}{r.type.name ?? r.type.id} + + {r.entry.title ?? `Entry #${r.entry.id}`} + +
+
+ )} +
+ +
+ +
+
Tags / Members
+ {(!data.tags || data.tags.length === 0) &&
No tags recorded
} + {data.tags && data.tags.length > 0 && ( +
+ + + + + + + + + + + + {data.tags.map((t) => ( + + + + + + + + ))} + +
TagTypeCategoryMember SeqLinks
{t.name}{t.type.name ?? t.type.id}{t.category.name ?? (t.category.id != null ? `#${t.category.id}` : "-")}{t.memberSeq ?? -} +
+ {t.link && ( + Link + )} + {t.comments && {t.comments}} + {!t.link && !t.comments && -} +
+
+
+ )} +
+ +
+ {/* Aliases (alternative titles) */}
Aliases
diff --git a/src/server/repo/zxdb.ts b/src/server/repo/zxdb.ts index 2e452a7..371cae4 100644 --- a/src/server/repo/zxdb.ts +++ b/src/server/repo/zxdb.ts @@ -14,6 +14,7 @@ import { languages, machinetypes, genretypes, + categories, files, filetypes, releases, @@ -33,6 +34,11 @@ import { permissions, permissiontypes, origintypes, + relations, + relationtypes, + tags, + tagtypes, + members, webrefs, websites, magazines, @@ -299,6 +305,20 @@ export interface EntryDetail { linkSite?: string | null; comments?: string | null; }[]; + relations?: { + direction: "from" | "to"; + type: { id: string; name: string | null }; + entry: { id: number; title: string | null }; + }[]; + tags?: { + id: number; + name: string; + type: { id: string; name: string | null }; + category: { id: number | null; name: string | null }; + memberSeq: number | null; + link: string | null; + comments: string | null; + }[]; origins?: { type: { id: string; name: string | null }; libraryTitle: string; @@ -583,6 +603,30 @@ export async function getEntryById(id: number): Promise { dateDay: number | string | null; publication: string | null; }[] = []; + let relationOutRows: { + relationtypeId: string; + relationtypeName: string | null; + relatedId: number | string; + relatedTitle: string | null; + }[] = []; + let relationInRows: { + relationtypeId: string; + relationtypeName: string | null; + relationtypeReciprocal: string | null; + relatedId: number | string; + relatedTitle: string | null; + }[] = []; + let tagRows: { + id: number | string; + name: string; + tagtypeId: string; + tagtypeName: string | null; + categoryId: number | string | null; + categoryName: string | null; + memberSeq: number | string | null; + link: string | null; + comments: string | null; + }[] = []; try { aliasRows = await db .select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title }) @@ -644,6 +688,59 @@ export async function getEntryById(id: number): Promise { ); originRows = rows as typeof originRows; } catch {} + try { + const rows = await db + .select({ + relationtypeId: relations.relationtypeId, + relationtypeName: relationtypes.name, + relatedId: relations.originalId, + relatedTitle: entries.title, + }) + .from(relations) + .innerJoin(relationtypes, eq(relationtypes.id, relations.relationtypeId)) + .leftJoin(entries, eq(entries.id, relations.originalId)) + .where(eq(relations.entryId, id)); + relationOutRows = rows as typeof relationOutRows; + } catch {} + try { + const rows = await db + .select({ + relationtypeId: relations.relationtypeId, + relationtypeName: relationtypes.name, + relationtypeReciprocal: relationtypes.reciprocal, + relatedId: relations.entryId, + relatedTitle: entries.title, + }) + .from(relations) + .innerJoin(relationtypes, eq(relationtypes.id, relations.relationtypeId)) + .leftJoin(entries, eq(entries.id, relations.entryId)) + .where(eq(relations.originalId, id)); + relationInRows = rows as typeof relationInRows; + } catch {} + try { + const rows = await db + .select({ + id: tags.id, + name: tags.name, + tagtypeId: tags.tagtypeId, + tagtypeName: tagtypes.name, + categoryId: members.categoryId, + categoryName: categories.name, + memberSeq: members.memberSeq, + link: tags.link, + comments: tags.comments, + }) + .from(members) + .innerJoin(tags, eq(tags.id, members.tagId)) + .leftJoin(tagtypes, eq(tagtypes.id, tags.tagtypeId)) + .leftJoin(categories, eq(categories.id, members.categoryId)) + .where(eq(members.entryId, id)) + .orderBy( + asc(tags.name), + asc(members.memberSeq) + ); + tagRows = rows as typeof tagRows; + } catch {} return { id: base.id, @@ -663,6 +760,27 @@ export async function getEntryById(id: number): Promise { linkSite: l.linkSite ?? null, comments: l.comments ?? null, })), + relations: [ + ...relationOutRows.map((r) => ({ + direction: "from" as const, + type: { id: r.relationtypeId, name: r.relationtypeName ?? null }, + entry: { id: Number(r.relatedId), title: r.relatedTitle ?? null }, + })), + ...relationInRows.map((r) => ({ + direction: "to" as const, + type: { id: r.relationtypeId, name: r.relationtypeReciprocal ?? r.relationtypeName ?? null }, + entry: { id: Number(r.relatedId), title: r.relatedTitle ?? null }, + })), + ], + tags: tagRows.map((t) => ({ + id: Number(t.id), + name: t.name, + type: { id: t.tagtypeId, name: t.tagtypeName ?? null }, + category: { id: t.categoryId != null ? Number(t.categoryId) : null, name: t.categoryName ?? null }, + memberSeq: t.memberSeq != null ? Number(t.memberSeq) : null, + link: t.link ?? null, + comments: t.comments ?? null, + })), origins: originRows.map((o) => ({ type: { id: o.origintypeId, name: o.origintypeName ?? null }, libraryTitle: o.libraryTitle,