Adding more releases, and sorting SASS

This commit is contained in:
2026-01-10 16:55:12 +00:00
parent 686e057bb4
commit 208a06c351
3 changed files with 363 additions and 8 deletions

View File

@@ -1,6 +1,18 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
sassOptions: {
// Silence noisy deprecation warnings coming from dependencies like Bootstrap.
quietDeps: true,
// Dart Sass deprecations still trigger via @import; explicitly mute them.
silenceDeprecations: [
"import",
"global-builtin",
"if-function",
"legacy-js-api",
"color-functions",
],
},
};
export default nextConfig;

View File

@@ -307,7 +307,7 @@ export default function ReleasesExplorer({
<tr>
<th style={{width: 80}}>Entry ID</th>
<th>Title</th>
<th style={{width: 110}}>Release #</th>
<th style={{width: 140}}>Release #</th>
<th style={{width: 100}}>Year</th>
</tr>
</thead>
@@ -320,7 +320,11 @@ export default function ReleasesExplorer({
<td>
<EntryLink id={it.entryId} title={it.entryTitle} />
</td>
<td>#{it.releaseSeq}</td>
<td>
<Link href={`/zxdb/releases/${it.entryId}/${it.releaseSeq}`}>
#{it.releaseSeq}
</Link>
</td>
<td>{it.year ?? <span className="text-secondary">-</span>}</td>
</tr>
))}

View File

@@ -14,6 +14,7 @@ import {
filetypes,
releases,
downloads,
scraps,
schemetypes,
sourcetypes,
casetypes,
@@ -26,6 +27,7 @@ import {
magazines,
issues,
magrefs,
searchByMagrefs,
referencetypes,
} from "@/server/schema/zxdb";
@@ -877,7 +879,7 @@ export async function entriesByGenre(
const countRows = await db
.select({ total: sql<number>`count(distinct ${entries.id})` })
.from(entries)
.where(and(eq(entries.genretypeId, genreId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
.where(and(eq(entries.genretypeId, genreId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
const total = Number(countRows[0]?.total ?? 0);
const items = await db
.select({
@@ -892,7 +894,7 @@ export async function entriesByGenre(
.from(entries)
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
.leftJoin(languages, eq(languages.id, entries.languageId))
.where(and(eq(entries.genretypeId, genreId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.where(and(eq(entries.genretypeId, genreId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.groupBy(entries.id)
.orderBy(entries.title)
.limit(pageSize)
@@ -938,7 +940,7 @@ export async function entriesByLanguage(
const countRows = await db
.select({ total: sql<number>`count(distinct ${entries.id})` })
.from(entries)
.where(and(eq(entries.languageId, langId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
.where(and(eq(entries.languageId, langId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
const total = Number(countRows[0]?.total ?? 0);
const items = await db
.select({
@@ -953,7 +955,7 @@ export async function entriesByLanguage(
.from(entries)
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
.leftJoin(languages, eq(languages.id, entries.languageId))
.where(and(eq(entries.languageId, langId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.where(and(eq(entries.languageId, langId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.groupBy(entries.id)
.orderBy(entries.title)
.limit(pageSize)
@@ -999,7 +1001,7 @@ export async function entriesByMachinetype(
const countRows = await db
.select({ total: sql<number>`count(distinct ${entries.id})` })
.from(entries)
.where(and(eq(entries.machinetypeId, mtId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
.where(and(eq(entries.machinetypeId, mtId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`));
const total = Number(countRows[0]?.total ?? 0);
const items = await db
.select({
@@ -1014,7 +1016,7 @@ export async function entriesByMachinetype(
.from(entries)
.leftJoin(machinetypes, eq(machinetypes.id, entries.machinetypeId))
.leftJoin(languages, eq(languages.id, entries.languageId))
.where(and(eq(entries.machinetypeId, mtId), sql`id in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.where(and(eq(entries.machinetypeId, mtId), sql`${entries.id} in (select entry_id from ${searchByTitles} where ${searchByTitles.entryTitle} like ${pattern})`))
.groupBy(entries.id)
.orderBy(entries.title)
.limit(pageSize)
@@ -1206,6 +1208,343 @@ export async function searchReleases(params: ReleaseSearchParams): Promise<Paged
return { items, page, pageSize, total };
}
export interface ReleaseDetail {
entry: {
id: number;
title: string;
issueId: number | null;
};
release: {
entryId: number;
releaseSeq: number;
year: number | null;
month: number | null;
day: number | null;
currency: { id: string | null; name: string | null; symbol: string | null; prefix: number | null };
prices: {
release: number | null;
budget: number | null;
microdrive: number | null;
disk: number | null;
cartridge: number | null;
};
book: { isbn: string | null; pages: number | null };
};
downloads: Array<{
id: number;
link: string;
size: number | null;
md5: string | null;
comments: string | null;
isDemo: boolean;
type: { id: number; name: string };
language: { id: string | null; name: string | null };
machinetype: { id: number | null; name: string | null };
scheme: { id: string | null; name: string | null };
source: { id: string | null; name: string | null };
case: { id: string | null; name: string | null };
year: number | null;
}>;
scraps: Array<{
id: number;
link: string | null;
size: number | null;
comments: string | null;
rationale: string;
isDemo: boolean;
type: { id: number; name: string };
language: { id: string | null; name: string | null };
machinetype: { id: number | null; name: string | null };
scheme: { id: string | null; name: string | null };
source: { id: string | null; name: string | null };
case: { id: string | null; name: string | null };
year: number | null;
}>;
files: Array<{
id: number;
link: string;
size: number | null;
md5: string | null;
comments: string | null;
type: { id: number; name: string };
}>;
magazineRefs: Array<{
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 getReleaseDetail(entryId: number, releaseSeq: number): Promise<ReleaseDetail | null> {
const rows = await db
.select({
entryId: releases.entryId,
releaseSeq: releases.releaseSeq,
year: releases.releaseYear,
month: releases.releaseMonth,
day: releases.releaseDay,
currencyId: releases.currencyId,
currencyName: currencies.name,
currencySymbol: currencies.symbol,
currencyPrefix: currencies.prefix,
releasePrice: releases.releasePrice,
budgetPrice: releases.budgetPrice,
microdrivePrice: releases.microdrivePrice,
diskPrice: releases.diskPrice,
cartridgePrice: releases.cartridgePrice,
bookIsbn: releases.bookIsbn,
bookPages: releases.bookPages,
entryTitle: entries.title,
issueId: entries.issueId,
})
.from(releases)
.leftJoin(entries, eq(entries.id, releases.entryId))
.leftJoin(currencies, eq(currencies.id, releases.currencyId))
.where(and(eq(releases.entryId, entryId), eq(releases.releaseSeq, releaseSeq)))
.limit(1);
const base = rows[0];
if (!base) return null;
type DownloadRow = {
id: number | string;
link: string;
size: number | string | null;
md5: string | null;
comments: string | null;
isDemo: number | boolean | null;
filetypeId: number | string;
filetypeName: string;
dlLangId: string | null;
dlLangName: string | null;
dlMachineId: number | string | null;
dlMachineName: string | null;
schemeId: string | null;
schemeName: string | null;
sourceId: string | null;
sourceName: string | null;
caseId: string | null;
caseName: string | null;
year: number | string | null;
};
type ScrapRow = DownloadRow & { rationale: string };
const downloadRows = await db
.select({
id: downloads.id,
link: downloads.fileLink,
size: downloads.fileSize,
md5: downloads.fileMd5,
comments: downloads.comments,
isDemo: downloads.isDemo,
filetypeId: filetypes.id,
filetypeName: filetypes.name,
dlLangId: downloads.languageId,
dlLangName: languages.name,
dlMachineId: downloads.machinetypeId,
dlMachineName: machinetypes.name,
schemeId: schemetypes.id,
schemeName: schemetypes.name,
sourceId: sourcetypes.id,
sourceName: sourcetypes.name,
caseId: casetypes.id,
caseName: casetypes.name,
year: downloads.releaseYear,
})
.from(downloads)
.innerJoin(filetypes, eq(filetypes.id, downloads.filetypeId))
.leftJoin(languages, eq(languages.id, downloads.languageId))
.leftJoin(machinetypes, eq(machinetypes.id, downloads.machinetypeId))
.leftJoin(schemetypes, eq(schemetypes.id, downloads.schemetypeId))
.leftJoin(sourcetypes, eq(sourcetypes.id, downloads.sourcetypeId))
.leftJoin(casetypes, eq(casetypes.id, downloads.casetypeId))
.where(and(eq(downloads.entryId, entryId), eq(downloads.releaseSeq, releaseSeq)));
const scrapRows = await db
.select({
id: scraps.id,
link: scraps.fileLink,
size: scraps.fileSize,
comments: scraps.comments,
rationale: scraps.rationale,
isDemo: scraps.isDemo,
filetypeId: filetypes.id,
filetypeName: filetypes.name,
dlLangId: scraps.languageId,
dlLangName: languages.name,
dlMachineId: scraps.machinetypeId,
dlMachineName: machinetypes.name,
schemeId: schemetypes.id,
schemeName: schemetypes.name,
sourceId: sourcetypes.id,
sourceName: sourcetypes.name,
caseId: casetypes.id,
caseName: casetypes.name,
year: scraps.releaseYear,
})
.from(scraps)
.innerJoin(filetypes, eq(filetypes.id, scraps.filetypeId))
.leftJoin(languages, eq(languages.id, scraps.languageId))
.leftJoin(machinetypes, eq(machinetypes.id, scraps.machinetypeId))
.leftJoin(schemetypes, eq(schemetypes.id, scraps.schemetypeId))
.leftJoin(sourcetypes, eq(sourcetypes.id, scraps.sourcetypeId))
.leftJoin(casetypes, eq(casetypes.id, scraps.casetypeId))
.where(and(eq(scraps.entryId, entryId), eq(scraps.releaseSeq, releaseSeq)));
const fileRows = base.issueId != null ? await db
.select({
id: files.id,
link: files.fileLink,
size: files.fileSize,
md5: files.fileMd5,
comments: files.comments,
typeId: filetypes.id,
typeName: filetypes.name,
})
.from(files)
.innerJoin(filetypes, eq(filetypes.id, files.filetypeId))
.where(eq(files.issueId, base.issueId)) : [];
const magazineRefs = 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, entryId))
.orderBy(
asc(magazines.name),
asc(issues.dateYear),
asc(issues.dateMonth),
asc(issues.id),
asc(magrefs.page),
asc(magrefs.id),
);
return {
entry: {
id: Number(base.entryId),
title: base.entryTitle ?? "",
issueId: base.issueId ?? null,
},
release: {
entryId: Number(base.entryId),
releaseSeq: Number(base.releaseSeq),
year: base.year != null ? Number(base.year) : null,
month: base.month != null ? Number(base.month) : null,
day: base.day != null ? Number(base.day) : null,
currency: {
id: base.currencyId ?? null,
name: base.currencyName ?? null,
symbol: base.currencySymbol ?? null,
prefix: base.currencyPrefix != null ? Number(base.currencyPrefix) : null,
},
prices: {
release: base.releasePrice != null ? Number(base.releasePrice) : null,
budget: base.budgetPrice != null ? Number(base.budgetPrice) : null,
microdrive: base.microdrivePrice != null ? Number(base.microdrivePrice) : null,
disk: base.diskPrice != null ? Number(base.diskPrice) : null,
cartridge: base.cartridgePrice != null ? Number(base.cartridgePrice) : null,
},
book: {
isbn: base.bookIsbn ?? null,
pages: base.bookPages != null ? Number(base.bookPages) : null,
},
},
downloads: (downloadRows as DownloadRow[]).map((d) => ({
id: Number(d.id),
link: d.link,
size: d.size != null ? Number(d.size) : null,
md5: d.md5 ?? null,
comments: d.comments ?? null,
isDemo: !!d.isDemo,
type: { id: Number(d.filetypeId), name: d.filetypeName },
language: { id: d.dlLangId ?? null, name: d.dlLangName ?? null },
machinetype: { id: d.dlMachineId != null ? Number(d.dlMachineId) : null, name: d.dlMachineName ?? null },
scheme: { id: d.schemeId ?? null, name: d.schemeName ?? null },
source: { id: d.sourceId ?? null, name: d.sourceName ?? null },
case: { id: d.caseId ?? null, name: d.caseName ?? null },
year: d.year != null ? Number(d.year) : null,
})),
scraps: (scrapRows as ScrapRow[]).map((s) => ({
id: Number(s.id),
link: s.link ?? null,
size: s.size != null ? Number(s.size) : null,
comments: s.comments ?? null,
rationale: s.rationale ?? "",
isDemo: !!s.isDemo,
type: { id: Number(s.filetypeId), name: s.filetypeName },
language: { id: s.dlLangId ?? null, name: s.dlLangName ?? null },
machinetype: { id: s.dlMachineId != null ? Number(s.dlMachineId) : null, name: s.dlMachineName ?? null },
scheme: { id: s.schemeId ?? null, name: s.schemeName ?? null },
source: { id: s.sourceId ?? null, name: s.sourceName ?? null },
case: { id: s.caseId ?? null, name: s.caseName ?? null },
year: s.year != null ? Number(s.year) : null,
})),
files: fileRows.map((f) => ({
id: f.id,
link: f.link,
size: f.size ?? null,
md5: f.md5 ?? null,
comments: f.comments ?? null,
type: { id: f.typeId, name: f.typeName },
})),
magazineRefs: magazineRefs.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,
},
})),
};
}
// ----- Download/lookups simple lists -----
export async function listFiletypes() {
return db.select().from(filetypes).orderBy(filetypes.name);