Adding the first stubs of the magazine browser

This commit is contained in:
2025-12-18 13:10:58 +00:00
parent 279abac91a
commit a1a04a89cf
11 changed files with 632 additions and 228 deletions

View File

@@ -23,6 +23,10 @@ import {
aliases,
webrefs,
websites,
magazines,
issues,
magrefs,
referencetypes,
} from "@/server/schema/zxdb";
export interface SearchParams {
@@ -66,6 +70,58 @@ export interface EntryFacets {
machinetypes: FacetItem<number>[];
}
export interface MagazineListItem {
id: number;
title: string;
languageId: string;
issueCount: number;
}
export interface MagazineDetail {
id: number;
title: string;
languageId: string;
linkSite?: string | null;
linkMask?: string | null;
archiveMask?: string | null;
issues: Array<{
id: number;
dateYear: number | null;
dateMonth: number | null;
number: number | null;
volume: number | null;
special: string | null;
supplement: string | null;
linkMask?: string | null;
archiveMask?: string | null;
}>;
}
export interface IssueDetail {
id: number;
magazine: { id: number; title: string };
dateYear: number | null;
dateMonth: number | null;
number: number | null;
volume: number | null;
special: string | null;
supplement: string | null;
linkMask?: string | null;
archiveMask?: string | null;
refs: Array<{
id: number;
page: number;
typeId: number;
typeName: string;
entryId: number | null;
entryTitle: string | null;
labelId: number | null;
labelName: string | null;
isOriginal: number;
scoreGroup: string;
}>
}
export async function searchEntries(params: SearchParams): Promise<PagedResult<SearchResultItem>> {
const q = (params.q ?? "").trim();
const pageSize = Math.max(1, Math.min(params.pageSize ?? 20, 100));
@@ -1180,3 +1236,149 @@ export async function listCurrencies() {
export async function listRoletypes() {
return db.select().from(roletypes).orderBy(roletypes.name);
}
export async function listMagazines(params: { q?: string; page?: number; pageSize?: number }): Promise<PagedResult<MagazineListItem>> {
const q = (params.q ?? "").trim();
const pageSize = Math.max(1, Math.min(params.pageSize ?? 20, 100));
const page = Math.max(1, params.page ?? 1);
const offset = (page - 1) * pageSize;
const whereExpr = q ? like(magazines.name, `%${q}%`) : sql`true`;
const [items, totalRows] = await Promise.all([
db
.select({
id: magazines.id,
// Expose as `title` to UI while DB column is `name`
title: magazines.name,
languageId: magazines.languageId,
issueCount: sql<number>`count(${issues.id})`,
})
.from(magazines)
.leftJoin(issues, eq(issues.magazineId, magazines.id))
.where(whereExpr)
.groupBy(magazines.id)
.orderBy(asc(magazines.name))
.limit(pageSize)
.offset(offset),
db
.select({ cnt: sql<number>`count(*)` })
.from(magazines)
.where(whereExpr),
]);
return {
items,
page,
pageSize,
total: totalRows[0]?.cnt ?? 0,
};
}
export async function getMagazine(id: number): Promise<MagazineDetail | null> {
const rows = await db
.select({
id: magazines.id,
// Alias DB `name` as `title` for UI shape
title: magazines.name,
languageId: magazines.languageId,
linkSite: magazines.linkSite,
linkMask: magazines.linkMask,
archiveMask: magazines.archiveMask,
})
.from(magazines)
.where(eq(magazines.id, id));
if (rows.length === 0) return null;
const mag = rows[0];
const iss = await db
.select({
id: issues.id,
dateYear: issues.dateYear,
dateMonth: issues.dateMonth,
number: issues.number,
volume: issues.volume,
special: issues.special,
supplement: issues.supplement,
linkMask: issues.linkMask,
archiveMask: issues.archiveMask,
})
.from(issues)
.where(eq(issues.magazineId, id))
.orderBy(
asc(issues.dateYear),
asc(issues.dateMonth),
asc(issues.volume),
asc(issues.number),
asc(issues.id),
);
return { ...mag, issues: iss };
}
export async function getIssue(id: number): Promise<IssueDetail | null> {
const rows = await db
.select({
id: issues.id,
magazineId: issues.magazineId,
magazineTitle: magazines.name,
dateYear: issues.dateYear,
dateMonth: issues.dateMonth,
number: issues.number,
volume: issues.volume,
special: issues.special,
supplement: issues.supplement,
linkMask: issues.linkMask,
archiveMask: issues.archiveMask,
})
.from(issues)
.leftJoin(magazines, eq(magazines.id, issues.magazineId))
.where(eq(issues.id, id));
const base = rows[0];
if (!base) return null;
const refs = await db
.select({
id: magrefs.id,
page: magrefs.page,
typeId: magrefs.referencetypeId,
typeName: referencetypes.name,
entryId: magrefs.entryId,
entryTitle: entries.title,
labelId: magrefs.labelId,
labelName: labels.name,
isOriginal: magrefs.isOriginal,
scoreGroup: magrefs.scoreGroup,
})
.from(magrefs)
.leftJoin(referencetypes, eq(referencetypes.id, magrefs.referencetypeId))
.leftJoin(entries, eq(entries.id, magrefs.entryId))
.leftJoin(labels, eq(labels.id, magrefs.labelId))
.where(eq(magrefs.issueId, id))
.orderBy(asc(magrefs.page), asc(magrefs.id));
return {
id: base.id,
magazine: { id: Number(base.magazineId), title: base.magazineTitle ?? "" },
dateYear: base.dateYear,
dateMonth: base.dateMonth,
number: base.number,
volume: base.volume,
special: base.special,
supplement: base.supplement,
linkMask: base.linkMask,
archiveMask: base.archiveMask,
refs: refs.map((r) => ({
id: r.id,
page: r.page,
typeId: Number(r.typeId),
typeName: r.typeName ?? "",
entryId: r.entryId ?? null,
entryTitle: r.entryTitle ?? null,
labelId: r.labelId ?? null,
labelName: r.labelName ?? null,
isOriginal: Number(r.isOriginal),
scoreGroup: r.scoreGroup,
})),
};
}