Implement magazine reviews, label details, and year filtering
- Aggregate magazine references from all releases on the Entry detail page. - Display country names and external links (Wikipedia/Website) on the Label detail page. - Add a year filter to the ZXDB Explorer to search entries by release year. Signed-off: junie@lucy.xalior.com
This commit is contained in:
@@ -13,6 +13,7 @@ const querySchema = z.object({
|
|||||||
.length(2, "languageId must be a 2-char code")
|
.length(2, "languageId must be a 2-char code")
|
||||||
.optional(),
|
.optional(),
|
||||||
machinetypeId: z.string().optional(),
|
machinetypeId: z.string().optional(),
|
||||||
|
year: z.coerce.number().int().optional(),
|
||||||
sort: z.enum(["title", "id_desc"]).optional(),
|
sort: z.enum(["title", "id_desc"]).optional(),
|
||||||
scope: z.enum(["title", "title_aliases", "title_aliases_origins"]).optional(),
|
scope: z.enum(["title", "title_aliases", "title_aliases_origins"]).optional(),
|
||||||
facets: z.coerce.boolean().optional(),
|
facets: z.coerce.boolean().optional(),
|
||||||
@@ -36,6 +37,7 @@ export async function GET(req: NextRequest) {
|
|||||||
genreId: searchParams.get("genreId") ?? undefined,
|
genreId: searchParams.get("genreId") ?? undefined,
|
||||||
languageId: searchParams.get("languageId") ?? undefined,
|
languageId: searchParams.get("languageId") ?? undefined,
|
||||||
machinetypeId: searchParams.get("machinetypeId") ?? undefined,
|
machinetypeId: searchParams.get("machinetypeId") ?? undefined,
|
||||||
|
year: searchParams.get("year") ?? undefined,
|
||||||
sort: searchParams.get("sort") ?? undefined,
|
sort: searchParams.get("sort") ?? undefined,
|
||||||
scope: searchParams.get("scope") ?? undefined,
|
scope: searchParams.get("scope") ?? undefined,
|
||||||
facets: searchParams.get("facets") ?? undefined,
|
facets: searchParams.get("facets") ?? undefined,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default function ZxdbExplorer({
|
|||||||
const [genreId, setGenreId] = useState<number | "">("");
|
const [genreId, setGenreId] = useState<number | "">("");
|
||||||
const [languageId, setLanguageId] = useState<string | "">("");
|
const [languageId, setLanguageId] = useState<string | "">("");
|
||||||
const [machinetypeId, setMachinetypeId] = useState<number | "">("");
|
const [machinetypeId, setMachinetypeId] = useState<number | "">("");
|
||||||
|
const [year, setYear] = useState<string>("");
|
||||||
const [sort, setSort] = useState<"title" | "id_desc">("id_desc");
|
const [sort, setSort] = useState<"title" | "id_desc">("id_desc");
|
||||||
|
|
||||||
const pageSize = 20;
|
const pageSize = 20;
|
||||||
@@ -56,6 +57,7 @@ export default function ZxdbExplorer({
|
|||||||
if (genreId !== "") params.set("genreId", String(genreId));
|
if (genreId !== "") params.set("genreId", String(genreId));
|
||||||
if (languageId !== "") params.set("languageId", String(languageId));
|
if (languageId !== "") params.set("languageId", String(languageId));
|
||||||
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
||||||
|
if (year !== "") params.set("year", year);
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
const res = await fetch(`/api/zxdb/search?${params.toString()}`);
|
const res = await fetch(`/api/zxdb/search?${params.toString()}`);
|
||||||
if (!res.ok) throw new Error(`Failed: ${res.status}`);
|
if (!res.ok) throw new Error(`Failed: ${res.status}`);
|
||||||
@@ -89,13 +91,14 @@ export default function ZxdbExplorer({
|
|||||||
genreId === "" &&
|
genreId === "" &&
|
||||||
languageId === "" &&
|
languageId === "" &&
|
||||||
machinetypeId === "" &&
|
machinetypeId === "" &&
|
||||||
|
year === "" &&
|
||||||
sort === "id_desc"
|
sort === "id_desc"
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchData(q, page);
|
fetchData(q, page);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [page, genreId, languageId, machinetypeId, sort]);
|
}, [page, genreId, languageId, machinetypeId, year, sort]);
|
||||||
|
|
||||||
// Load filter lists on mount only if not provided by server
|
// Load filter lists on mount only if not provided by server
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -161,6 +164,16 @@ export default function ZxdbExplorer({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-auto">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="form-control"
|
||||||
|
style={{ width: 100 }}
|
||||||
|
placeholder="Year"
|
||||||
|
value={year}
|
||||||
|
onChange={(e) => setYear(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="col-auto">
|
<div className="col-auto">
|
||||||
<select className="form-select" value={sort} onChange={(e) => setSort(e.target.value as "title" | "id_desc")}>
|
<select className="form-select" value={sort} onChange={(e) => setSort(e.target.value as "title" | "id_desc")}>
|
||||||
<option value="title">Sort: Title</option>
|
<option value="title">Sort: Title</option>
|
||||||
|
|||||||
@@ -130,6 +130,26 @@ export type EntryDetailData = {
|
|||||||
// Additional relationships
|
// Additional relationships
|
||||||
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
||||||
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
||||||
|
magazineRefs?: {
|
||||||
|
id: number;
|
||||||
|
issueId: number;
|
||||||
|
magazineId: number | null;
|
||||||
|
magazineName: string | null;
|
||||||
|
referencetypeId: number;
|
||||||
|
referencetypeName: string | null;
|
||||||
|
page: number;
|
||||||
|
isOriginal: number;
|
||||||
|
scoreGroup: string;
|
||||||
|
issue: {
|
||||||
|
dateYear: number | null;
|
||||||
|
dateMonth: number | null;
|
||||||
|
dateDay: number | null;
|
||||||
|
volume: number | null;
|
||||||
|
number: number | null;
|
||||||
|
special: string | null;
|
||||||
|
supplement: string | null;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EntryDetailClient({ data }: { data: EntryDetailData | null }) {
|
export default function EntryDetailClient({ data }: { data: EntryDetailData | null }) {
|
||||||
@@ -293,7 +313,52 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="card shadow-sm">
|
<div className="card shadow-sm mb-3">
|
||||||
|
<div className="card-body">
|
||||||
|
<h5 className="card-title">Magazine References</h5>
|
||||||
|
{(!data.magazineRefs || data.magazineRefs.length === 0) && <div className="text-secondary">No magazine references recorded</div>}
|
||||||
|
{data.magazineRefs && data.magazineRefs.length > 0 && (
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-sm table-striped align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Magazine</th>
|
||||||
|
<th style={{ width: 140 }}>Issue</th>
|
||||||
|
<th style={{ width: 140 }}>Type</th>
|
||||||
|
<th style={{ width: 120 }}>Page</th>
|
||||||
|
<th>Score</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.magazineRefs.map((m) => (
|
||||||
|
<tr key={m.id}>
|
||||||
|
<td>
|
||||||
|
{m.magazineId ? (
|
||||||
|
<Link href={`/zxdb/magazines/${m.magazineId}`}>{m.magazineName}</Link>
|
||||||
|
) : (
|
||||||
|
<span>{m.magazineName}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Link href={`/zxdb/issues/${m.issueId}`}>
|
||||||
|
{m.issue.dateYear ? `${m.issue.dateYear} ` : ""}
|
||||||
|
{m.issue.number ? `#${m.issue.number}` : ""}
|
||||||
|
{m.issue.special ? ` (${m.issue.special})` : ""}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
<td>{m.referencetypeName}</td>
|
||||||
|
<td>{m.page > 0 ? m.page : "-"}</td>
|
||||||
|
<td>{m.scoreGroup || "-"}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card shadow-sm mb-3">
|
||||||
<div className="card-body d-flex flex-wrap gap-2">
|
<div className="card-body d-flex flex-wrap gap-2">
|
||||||
<Link className="btn btn-sm btn-outline-secondary" href={`/zxdb/entries/${data.id}`}>Permalink</Link>
|
<Link className="btn btn-sm btn-outline-secondary" href={`/zxdb/entries/${data.id}`}>Permalink</Link>
|
||||||
<Link className="btn btn-sm btn-outline-primary" href="/zxdb">Back to Explorer</Link>
|
<Link className="btn btn-sm btn-outline-primary" href="/zxdb">Back to Explorer</Link>
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ type Label = {
|
|||||||
name: string;
|
name: string;
|
||||||
labeltypeId: string | null;
|
labeltypeId: string | null;
|
||||||
labeltypeName: string | null;
|
labeltypeName: string | null;
|
||||||
|
countryId: string | null;
|
||||||
|
countryName: string | null;
|
||||||
|
country2Id: string | null;
|
||||||
|
country2Name: string | null;
|
||||||
|
linkWikipedia: string | null;
|
||||||
|
linkSite: string | null;
|
||||||
permissions: {
|
permissions: {
|
||||||
website: { id: number; name: string; link?: string | null };
|
website: { id: number; name: string; link?: string | null };
|
||||||
type: { id: string; name: string | null };
|
type: { id: string; name: string | null };
|
||||||
@@ -57,6 +63,24 @@ export default function LabelDetailClient({ id, initial, initialTab, initialQ }:
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{(initial.label.countryId || initial.label.linkWikipedia || initial.label.linkSite) && (
|
||||||
|
<div className="mt-2 d-flex gap-3 flex-wrap align-items-center">
|
||||||
|
{initial.label.countryId && (
|
||||||
|
<span className="text-secondary small">
|
||||||
|
Country: <strong>{initial.label.countryName || initial.label.countryId}</strong>
|
||||||
|
{initial.label.country2Id && (
|
||||||
|
<> / <strong>{initial.label.country2Name || initial.label.country2Id}</strong></>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{initial.label.linkWikipedia && (
|
||||||
|
<a href={initial.label.linkWikipedia} target="_blank" rel="noreferrer" className="btn btn-sm btn-outline-secondary py-0">Wikipedia</a>
|
||||||
|
)}
|
||||||
|
{initial.label.linkSite && (
|
||||||
|
<a href={initial.label.linkSite} target="_blank" rel="noreferrer" className="btn btn-sm btn-outline-secondary py-0">Website</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="row g-4 mt-1">
|
<div className="row g-4 mt-1">
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import {
|
|||||||
magrefs,
|
magrefs,
|
||||||
searchByMagrefs,
|
searchByMagrefs,
|
||||||
referencetypes,
|
referencetypes,
|
||||||
|
countries,
|
||||||
} from "@/server/schema/zxdb";
|
} from "@/server/schema/zxdb";
|
||||||
|
|
||||||
export type EntrySearchScope = "title" | "title_aliases" | "title_aliases_origins";
|
export type EntrySearchScope = "title" | "title_aliases" | "title_aliases_origins";
|
||||||
@@ -64,6 +65,8 @@ export interface SearchParams {
|
|||||||
genreId?: number;
|
genreId?: number;
|
||||||
languageId?: string;
|
languageId?: string;
|
||||||
machinetypeId?: number | number[];
|
machinetypeId?: number | number[];
|
||||||
|
// Year filter
|
||||||
|
year?: number;
|
||||||
// Sorting
|
// Sorting
|
||||||
sort?: "title" | "id_desc";
|
sort?: "title" | "id_desc";
|
||||||
// Search scope (defaults to titles only)
|
// Search scope (defaults to titles only)
|
||||||
@@ -206,6 +209,9 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
||||||
whereClauses.push(sql`${entries.machinetypeId} in (${sql.join(ids, sql`, `)})`);
|
whereClauses.push(sql`${entries.machinetypeId} in (${sql.join(ids, sql`, `)})`);
|
||||||
}
|
}
|
||||||
|
if (typeof params.year === "number") {
|
||||||
|
whereClauses.push(sql`${entries.id} in (select entry_id from releases where release_year = ${params.year})`);
|
||||||
|
}
|
||||||
|
|
||||||
const whereExpr = whereClauses.length ? and(...whereClauses) : undefined;
|
const whereExpr = whereClauses.length ? and(...whereClauses) : undefined;
|
||||||
|
|
||||||
@@ -250,9 +256,30 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
if (scope !== "title") {
|
if (scope !== "title") {
|
||||||
try {
|
try {
|
||||||
const union = buildEntrySearchUnion(pattern, scope);
|
const union = buildEntrySearchUnion(pattern, scope);
|
||||||
|
const whereClauses: Array<ReturnType<typeof sql>> = [
|
||||||
|
sql`${entries.id} in (select entry_id from (${union}) as matches)`
|
||||||
|
];
|
||||||
|
if (typeof params.genreId === "number") {
|
||||||
|
whereClauses.push(eq(entries.genretypeId, params.genreId));
|
||||||
|
}
|
||||||
|
if (typeof params.languageId === "string") {
|
||||||
|
whereClauses.push(eq(entries.languageId, params.languageId));
|
||||||
|
}
|
||||||
|
if (typeof params.machinetypeId === "number") {
|
||||||
|
whereClauses.push(eq(entries.machinetypeId, params.machinetypeId));
|
||||||
|
} else if (Array.isArray(params.machinetypeId) && params.machinetypeId.length > 0) {
|
||||||
|
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
||||||
|
whereClauses.push(sql`${entries.machinetypeId} in (${sql.join(ids, sql`, `)})`);
|
||||||
|
}
|
||||||
|
if (typeof params.year === "number") {
|
||||||
|
whereClauses.push(sql`${entries.id} in (select entry_id from releases where release_year = ${params.year})`);
|
||||||
|
}
|
||||||
|
const whereExpr = and(...whereClauses);
|
||||||
|
|
||||||
const countRows = await db.execute(sql`
|
const countRows = await db.execute(sql`
|
||||||
select count(distinct entry_id) as total
|
select count(distinct id) as total
|
||||||
from (${union}) as matches
|
from entries
|
||||||
|
where ${whereExpr}
|
||||||
`);
|
`);
|
||||||
type CountRow = { total: number | string };
|
type CountRow = { total: number | string };
|
||||||
const total = Number((countRows as unknown as CountRow[])[0]?.total ?? 0);
|
const total = Number((countRows as unknown as CountRow[])[0]?.total ?? 0);
|
||||||
@@ -273,7 +300,7 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
.leftJoin(genretypes, eq(genretypes.id, entries.genretypeId))
|
.leftJoin(genretypes, eq(genretypes.id, entries.genretypeId))
|
||||||
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
|
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
|
||||||
.leftJoin(languages, eq(languages.id, entries.languageId))
|
.leftJoin(languages, eq(languages.id, entries.languageId))
|
||||||
.where(sql`${entries.id} in (select entry_id from (${union}) as matches)`)
|
.where(whereExpr)
|
||||||
.groupBy(entries.id)
|
.groupBy(entries.id)
|
||||||
.orderBy(
|
.orderBy(
|
||||||
...(preferMachineOrder ? [preferMachineOrder] : []),
|
...(preferMachineOrder ? [preferMachineOrder] : []),
|
||||||
@@ -289,10 +316,31 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Count matches via helper table
|
// Count matches via helper table
|
||||||
|
const whereClauses: Array<ReturnType<typeof sql>> = [
|
||||||
|
sql`lower(${searchByTitles.entryTitle}) like ${pattern}`
|
||||||
|
];
|
||||||
|
if (typeof params.genreId === "number") {
|
||||||
|
whereClauses.push(eq(entries.genretypeId, params.genreId));
|
||||||
|
}
|
||||||
|
if (typeof params.languageId === "string") {
|
||||||
|
whereClauses.push(eq(entries.languageId, params.languageId));
|
||||||
|
}
|
||||||
|
if (typeof params.machinetypeId === "number") {
|
||||||
|
whereClauses.push(eq(entries.machinetypeId, params.machinetypeId));
|
||||||
|
} else if (Array.isArray(params.machinetypeId) && params.machinetypeId.length > 0) {
|
||||||
|
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
||||||
|
whereClauses.push(sql`${entries.machinetypeId} in (${sql.join(ids, sql`, `)})`);
|
||||||
|
}
|
||||||
|
if (typeof params.year === "number") {
|
||||||
|
whereClauses.push(sql`${entries.id} in (select entry_id from releases where release_year = ${params.year})`);
|
||||||
|
}
|
||||||
|
const whereExpr = and(...whereClauses);
|
||||||
|
|
||||||
const countRows = await db
|
const countRows = await db
|
||||||
.select({ total: sql<number>`count(distinct ${searchByTitles.entryId})` })
|
.select({ total: sql<number>`count(distinct ${searchByTitles.entryId})` })
|
||||||
.from(searchByTitles)
|
.from(searchByTitles)
|
||||||
.where(sql`lower(${searchByTitles.entryTitle}) like ${pattern}`);
|
.innerJoin(entries, eq(entries.id, searchByTitles.entryId))
|
||||||
|
.where(whereExpr);
|
||||||
|
|
||||||
const total = Number(countRows[0]?.total ?? 0);
|
const total = Number(countRows[0]?.total ?? 0);
|
||||||
|
|
||||||
@@ -314,7 +362,7 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
.leftJoin(genretypes, eq(genretypes.id, entries.genretypeId))
|
.leftJoin(genretypes, eq(genretypes.id, entries.genretypeId))
|
||||||
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
|
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
|
||||||
.leftJoin(languages, eq(languages.id, entries.languageId))
|
.leftJoin(languages, eq(languages.id, entries.languageId))
|
||||||
.where(sql`lower(${searchByTitles.entryTitle}) like ${pattern}`)
|
.where(whereExpr)
|
||||||
.groupBy(entries.id)
|
.groupBy(entries.id)
|
||||||
.orderBy(
|
.orderBy(
|
||||||
...(preferMachineOrder ? [preferMachineOrder] : []),
|
...(preferMachineOrder ? [preferMachineOrder] : []),
|
||||||
@@ -458,6 +506,26 @@ export interface EntryDetail {
|
|||||||
// Additional relationships surfaced on the entry detail page
|
// Additional relationships surfaced on the entry detail page
|
||||||
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
||||||
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
||||||
|
magazineRefs?: {
|
||||||
|
id: number;
|
||||||
|
issueId: number;
|
||||||
|
magazineId: number | null;
|
||||||
|
magazineName: string | null;
|
||||||
|
referencetypeId: number;
|
||||||
|
referencetypeName: string | null;
|
||||||
|
page: number;
|
||||||
|
isOriginal: number;
|
||||||
|
scoreGroup: string;
|
||||||
|
issue: {
|
||||||
|
dateYear: number | null;
|
||||||
|
dateMonth: number | null;
|
||||||
|
dateDay: number | null;
|
||||||
|
volume: number | null;
|
||||||
|
number: number | null;
|
||||||
|
special: string | null;
|
||||||
|
supplement: string | null;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||||
@@ -731,6 +799,24 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
notetypeName: string | null;
|
notetypeName: string | null;
|
||||||
text: string;
|
text: string;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
let magazineRefRows: {
|
||||||
|
id: number;
|
||||||
|
issueId: number;
|
||||||
|
magazineId: number | null;
|
||||||
|
magazineName: string | null;
|
||||||
|
referencetypeId: number;
|
||||||
|
referencetypeName: string | null;
|
||||||
|
page: number;
|
||||||
|
isOriginal: number;
|
||||||
|
scoreGroup: string;
|
||||||
|
issueDateYear: number | null;
|
||||||
|
issueDateMonth: number | null;
|
||||||
|
issueDateDay: number | null;
|
||||||
|
issueVolume: number | null;
|
||||||
|
issueNumber: number | null;
|
||||||
|
issueSpecial: string | null;
|
||||||
|
issueSupplement: string | null;
|
||||||
|
}[] = [];
|
||||||
try {
|
try {
|
||||||
aliasRows = await db
|
aliasRows = await db
|
||||||
.select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title })
|
.select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title })
|
||||||
@@ -907,6 +993,43 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
noteRows = rows as typeof noteRows;
|
noteRows = rows as typeof noteRows;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rows = await db
|
||||||
|
.select({
|
||||||
|
id: magrefs.id,
|
||||||
|
issueId: magrefs.issueId,
|
||||||
|
magazineId: magazines.id,
|
||||||
|
magazineName: magazines.name,
|
||||||
|
referencetypeId: magrefs.referencetypeId,
|
||||||
|
referencetypeName: referencetypes.name,
|
||||||
|
page: magrefs.page,
|
||||||
|
isOriginal: magrefs.isOriginal,
|
||||||
|
scoreGroup: magrefs.scoreGroup,
|
||||||
|
issueDateYear: issues.dateYear,
|
||||||
|
issueDateMonth: issues.dateMonth,
|
||||||
|
issueDateDay: issues.dateDay,
|
||||||
|
issueVolume: issues.volume,
|
||||||
|
issueNumber: issues.number,
|
||||||
|
issueSpecial: issues.special,
|
||||||
|
issueSupplement: issues.supplement,
|
||||||
|
})
|
||||||
|
.from(searchByMagrefs)
|
||||||
|
.innerJoin(magrefs, eq(magrefs.id, searchByMagrefs.magrefId))
|
||||||
|
.leftJoin(issues, eq(issues.id, magrefs.issueId))
|
||||||
|
.leftJoin(magazines, eq(magazines.id, issues.magazineId))
|
||||||
|
.leftJoin(referencetypes, eq(referencetypes.id, magrefs.referencetypeId))
|
||||||
|
.where(eq(searchByMagrefs.entryId, id))
|
||||||
|
.orderBy(
|
||||||
|
asc(magazines.name),
|
||||||
|
asc(issues.dateYear),
|
||||||
|
asc(issues.dateMonth),
|
||||||
|
asc(issues.id),
|
||||||
|
asc(magrefs.page),
|
||||||
|
asc(magrefs.id)
|
||||||
|
);
|
||||||
|
magazineRefRows = rows as typeof magazineRefRows;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: base.id,
|
id: base.id,
|
||||||
title: base.title,
|
title: base.title,
|
||||||
@@ -1028,6 +1151,26 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
})),
|
})),
|
||||||
aliases: aliasRows.map((a) => ({ releaseSeq: Number(a.releaseSeq), languageId: a.languageId, title: a.title })),
|
aliases: aliasRows.map((a) => ({ releaseSeq: Number(a.releaseSeq), languageId: a.languageId, title: a.title })),
|
||||||
webrefs: webrefRows.map((w) => ({ link: w.link, languageId: w.languageId, website: { id: Number(w.websiteId), name: w.websiteName, link: w.websiteLink } })),
|
webrefs: webrefRows.map((w) => ({ link: w.link, languageId: w.languageId, website: { id: Number(w.websiteId), name: w.websiteName, link: w.websiteLink } })),
|
||||||
|
magazineRefs: magazineRefRows.map((m) => ({
|
||||||
|
id: m.id,
|
||||||
|
issueId: Number(m.issueId),
|
||||||
|
magazineId: m.magazineId != null ? Number(m.magazineId) : null,
|
||||||
|
magazineName: m.magazineName ?? null,
|
||||||
|
referencetypeId: Number(m.referencetypeId),
|
||||||
|
referencetypeName: m.referencetypeName ?? null,
|
||||||
|
page: Number(m.page),
|
||||||
|
isOriginal: Number(m.isOriginal),
|
||||||
|
scoreGroup: m.scoreGroup ?? "",
|
||||||
|
issue: {
|
||||||
|
dateYear: m.issueDateYear != null ? Number(m.issueDateYear) : null,
|
||||||
|
dateMonth: m.issueDateMonth != null ? Number(m.issueDateMonth) : null,
|
||||||
|
dateDay: m.issueDateDay != null ? Number(m.issueDateDay) : null,
|
||||||
|
volume: m.issueVolume != null ? Number(m.issueVolume) : null,
|
||||||
|
number: m.issueNumber != null ? Number(m.issueNumber) : null,
|
||||||
|
special: m.issueSpecial ?? null,
|
||||||
|
supplement: m.issueSupplement ?? null,
|
||||||
|
},
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1035,6 +1178,12 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
|
|
||||||
export interface LabelDetail extends LabelSummary {
|
export interface LabelDetail extends LabelSummary {
|
||||||
labeltypeName: string | null;
|
labeltypeName: string | null;
|
||||||
|
countryId: string | null;
|
||||||
|
countryName: string | null;
|
||||||
|
country2Id: string | null;
|
||||||
|
country2Name: string | null;
|
||||||
|
linkWikipedia: string | null;
|
||||||
|
linkSite: string | null;
|
||||||
permissions: {
|
permissions: {
|
||||||
website: { id: number; name: string; link?: string | null };
|
website: { id: number; name: string; link?: string | null };
|
||||||
type: { id: string; name: string | null };
|
type: { id: string; name: string | null };
|
||||||
@@ -1099,9 +1248,17 @@ export async function getLabelById(id: number): Promise<LabelDetail | null> {
|
|||||||
name: labels.name,
|
name: labels.name,
|
||||||
labeltypeId: labels.labeltypeId,
|
labeltypeId: labels.labeltypeId,
|
||||||
labeltypeName: labeltypes.name,
|
labeltypeName: labeltypes.name,
|
||||||
|
countryId: labels.countryId,
|
||||||
|
countryName: sql<string>`c1.text`,
|
||||||
|
country2Id: labels.country2Id,
|
||||||
|
country2Name: sql<string>`c2.text`,
|
||||||
|
linkWikipedia: labels.linkWikipedia,
|
||||||
|
linkSite: labels.linkSite,
|
||||||
})
|
})
|
||||||
.from(labels)
|
.from(labels)
|
||||||
.leftJoin(labeltypes, eq(labeltypes.id, labels.labeltypeId))
|
.leftJoin(labeltypes, eq(labeltypes.id, labels.labeltypeId))
|
||||||
|
.leftJoin(sql`${countries} c1`, eq(sql`c1.id`, labels.countryId))
|
||||||
|
.leftJoin(sql`${countries} c2`, eq(sql`c2.id`, labels.country2Id))
|
||||||
.where(eq(labels.id, id))
|
.where(eq(labels.id, id))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
const base = rows[0];
|
const base = rows[0];
|
||||||
@@ -1165,6 +1322,12 @@ export async function getLabelById(id: number): Promise<LabelDetail | null> {
|
|||||||
name: base.name,
|
name: base.name,
|
||||||
labeltypeId: base.labeltypeId,
|
labeltypeId: base.labeltypeId,
|
||||||
labeltypeName: base.labeltypeName ?? null,
|
labeltypeName: base.labeltypeName ?? null,
|
||||||
|
countryId: base.countryId ?? null,
|
||||||
|
countryName: base.countryName ?? null,
|
||||||
|
country2Id: base.country2Id ?? null,
|
||||||
|
country2Name: base.country2Name ?? null,
|
||||||
|
linkWikipedia: base.linkWikipedia ?? null,
|
||||||
|
linkSite: base.linkSite ?? null,
|
||||||
permissions: permissionRows.map((p) => ({
|
permissions: permissionRows.map((p) => ({
|
||||||
website: { id: Number(p.websiteId), name: p.websiteName, link: p.websiteLink ?? null },
|
website: { id: Number(p.websiteId), name: p.websiteName, link: p.websiteLink ?? null },
|
||||||
type: { id: p.permissiontypeId, name: p.permissiontypeName ?? null },
|
type: { id: p.permissiontypeId, name: p.permissiontypeName ?? null },
|
||||||
|
|||||||
Reference in New Issue
Block a user