Add ZXDB origins and label types
Show entry origins data and display label type names in label detail view. Signed-off-by: codex@lucy.xalior.com
This commit is contained in:
@@ -22,6 +22,14 @@ export type EntryDetailData = {
|
||||
linkSite?: string | null;
|
||||
comments?: string | null;
|
||||
}[];
|
||||
origins?: {
|
||||
type: { id: string; name: string | null };
|
||||
libraryTitle: string;
|
||||
publication: string | null;
|
||||
containerId: number | null;
|
||||
issueId: number | null;
|
||||
date: { year: number | null; month: number | null; day: number | null };
|
||||
}[];
|
||||
// extra fields for richer details
|
||||
maxPlayers?: number;
|
||||
availabletypeId?: string | null;
|
||||
@@ -340,6 +348,53 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<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="table-responsive">
|
||||
<table className="table table-sm table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Title</th>
|
||||
<th>Publication</th>
|
||||
<th style={{ width: 140 }}>Issue</th>
|
||||
<th style={{ width: 140 }}>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.origins.map((o, idx) => {
|
||||
const dateParts = [o.date.year, o.date.month, o.date.day]
|
||||
.filter((v) => typeof v === "number" && Number.isFinite(v))
|
||||
.map((v, i) => (i === 0 ? String(v) : String(v).padStart(2, "0")));
|
||||
const dateText = dateParts.length ? dateParts.join("/") : "-";
|
||||
return (
|
||||
<tr key={`${o.type.id}-${idx}`}>
|
||||
<td>{o.type.name ?? o.type.id}</td>
|
||||
<td>{o.libraryTitle}</td>
|
||||
<td>{o.publication ?? <span className="text-secondary">-</span>}</td>
|
||||
<td>
|
||||
{o.issueId ? (
|
||||
<Link href={`/zxdb/issues/${o.issueId}`}>#{o.issueId}</Link>
|
||||
) : o.containerId ? (
|
||||
<span>#{o.containerId}</span>
|
||||
) : (
|
||||
<span className="text-secondary">-</span>
|
||||
)}
|
||||
</td>
|
||||
<td>{dateText}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
{/* Aliases (alternative titles) */}
|
||||
<div>
|
||||
<h5>Aliases</h5>
|
||||
|
||||
@@ -9,6 +9,7 @@ type Label = {
|
||||
id: number;
|
||||
name: string;
|
||||
labeltypeId: string | null;
|
||||
labeltypeName: string | null;
|
||||
permissions: {
|
||||
website: { id: number; name: string; link?: string | null };
|
||||
type: { id: string; name: string | null };
|
||||
@@ -49,7 +50,11 @@ export default function LabelDetailClient({ id, initial, initialTab, initialQ }:
|
||||
<div className="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h1 className="mb-0">{initial.label.name}</h1>
|
||||
<div>
|
||||
<span className="badge text-bg-light">{initial.label.labeltypeId ?? "?"}</span>
|
||||
<span className="badge text-bg-light">
|
||||
{initial.label.labeltypeName
|
||||
? `${initial.label.labeltypeName} (${initial.label.labeltypeId ?? "?"})`
|
||||
: (initial.label.labeltypeId ?? "?")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
searchByAliases,
|
||||
searchByOrigins,
|
||||
labels,
|
||||
labeltypes,
|
||||
authors,
|
||||
publishers,
|
||||
languages,
|
||||
@@ -31,6 +32,7 @@ import {
|
||||
licensors,
|
||||
permissions,
|
||||
permissiontypes,
|
||||
origintypes,
|
||||
webrefs,
|
||||
websites,
|
||||
magazines,
|
||||
@@ -297,6 +299,14 @@ export interface EntryDetail {
|
||||
linkSite?: string | null;
|
||||
comments?: string | null;
|
||||
}[];
|
||||
origins?: {
|
||||
type: { id: string; name: string | null };
|
||||
libraryTitle: string;
|
||||
publication: string | null;
|
||||
containerId: number | null;
|
||||
issueId: number | null;
|
||||
date: { year: number | null; month: number | null; day: number | null };
|
||||
}[];
|
||||
// Additional entry fields for richer details
|
||||
maxPlayers?: number;
|
||||
availabletypeId?: string | null;
|
||||
@@ -559,6 +569,17 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||
linkSite: string | null;
|
||||
comments: string | null;
|
||||
}[] = [];
|
||||
let originRows: {
|
||||
libraryTitle: string;
|
||||
origintypeId: string;
|
||||
origintypeName: string | null;
|
||||
containerId: number | string | null;
|
||||
issueId: number | string | null;
|
||||
dateYear: number | string | null;
|
||||
dateMonth: number | string | null;
|
||||
dateDay: number | string | null;
|
||||
publication: string | null;
|
||||
}[] = [];
|
||||
try {
|
||||
aliasRows = await db
|
||||
.select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title })
|
||||
@@ -591,6 +612,24 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||
.where(eq(relatedlicenses.entryId, id));
|
||||
licenseRows = rows as typeof licenseRows;
|
||||
} catch {}
|
||||
try {
|
||||
const rows = await db
|
||||
.select({
|
||||
libraryTitle: searchByOrigins.libraryTitle,
|
||||
origintypeId: searchByOrigins.origintypeId,
|
||||
origintypeName: origintypes.name,
|
||||
containerId: searchByOrigins.containerId,
|
||||
issueId: searchByOrigins.issueId,
|
||||
dateYear: searchByOrigins.dateYear,
|
||||
dateMonth: searchByOrigins.dateMonth,
|
||||
dateDay: searchByOrigins.dateDay,
|
||||
publication: searchByOrigins.publication,
|
||||
})
|
||||
.from(searchByOrigins)
|
||||
.leftJoin(origintypes, eq(origintypes.id, searchByOrigins.origintypeId))
|
||||
.where(eq(searchByOrigins.entryId, id));
|
||||
originRows = rows as typeof originRows;
|
||||
} catch {}
|
||||
|
||||
return {
|
||||
id: base.id,
|
||||
@@ -610,6 +649,18 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||
linkSite: l.linkSite ?? null,
|
||||
comments: l.comments ?? null,
|
||||
})),
|
||||
origins: originRows.map((o) => ({
|
||||
type: { id: o.origintypeId, name: o.origintypeName ?? null },
|
||||
libraryTitle: o.libraryTitle,
|
||||
publication: o.publication ?? null,
|
||||
containerId: o.containerId != null ? Number(o.containerId) : null,
|
||||
issueId: o.issueId != null ? Number(o.issueId) : null,
|
||||
date: {
|
||||
year: o.dateYear != null ? Number(o.dateYear) : null,
|
||||
month: o.dateMonth != null ? Number(o.dateMonth) : null,
|
||||
day: o.dateDay != null ? Number(o.dateDay) : null,
|
||||
},
|
||||
})),
|
||||
maxPlayers: (base.maxPlayers) ?? undefined,
|
||||
availabletypeId: (base.availabletypeId) ?? undefined,
|
||||
withoutLoadScreen: (base.withoutLoadScreen) ?? undefined,
|
||||
@@ -651,6 +702,7 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||
// ----- Labels -----
|
||||
|
||||
export interface LabelDetail extends LabelSummary {
|
||||
labeltypeName: string | null;
|
||||
permissions: {
|
||||
website: { id: number; name: string; link?: string | null };
|
||||
type: { id: string; name: string | null };
|
||||
@@ -709,7 +761,17 @@ export async function searchLabels(params: LabelSearchParams): Promise<PagedResu
|
||||
}
|
||||
|
||||
export async function getLabelById(id: number): Promise<LabelDetail | null> {
|
||||
const rows = await db.select().from(labels).where(eq(labels.id, id)).limit(1);
|
||||
const rows = await db
|
||||
.select({
|
||||
id: labels.id,
|
||||
name: labels.name,
|
||||
labeltypeId: labels.labeltypeId,
|
||||
labeltypeName: labeltypes.name,
|
||||
})
|
||||
.from(labels)
|
||||
.leftJoin(labeltypes, eq(labeltypes.id, labels.labeltypeId))
|
||||
.where(eq(labels.id, id))
|
||||
.limit(1);
|
||||
const base = rows[0];
|
||||
if (!base) return null;
|
||||
|
||||
@@ -770,6 +832,7 @@ export async function getLabelById(id: number): Promise<LabelDetail | null> {
|
||||
id: base.id,
|
||||
name: base.name,
|
||||
labeltypeId: base.labeltypeId,
|
||||
labeltypeName: base.labeltypeName ?? null,
|
||||
permissions: permissionRows.map((p) => ({
|
||||
website: { id: Number(p.websiteId), name: p.websiteName, link: p.websiteLink ?? null },
|
||||
type: { id: p.permissiontypeId, name: p.permissiontypeName ?? null },
|
||||
|
||||
Reference in New Issue
Block a user