diff --git a/next.config.ts b/next.config.ts
index db0a372..7a50060 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -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;
diff --git a/src/app/zxdb/releases/ReleasesExplorer.tsx b/src/app/zxdb/releases/ReleasesExplorer.tsx
index 80f6e5b..6d15037 100644
--- a/src/app/zxdb/releases/ReleasesExplorer.tsx
+++ b/src/app/zxdb/releases/ReleasesExplorer.tsx
@@ -307,7 +307,7 @@ export default function ReleasesExplorer({
| Entry ID |
Title |
- Release # |
+ Release # |
Year |
@@ -320,7 +320,11 @@ export default function ReleasesExplorer({
|
- #{it.releaseSeq} |
+
+
+ #{it.releaseSeq}
+
+ |
{it.year ?? -} |
))}
diff --git a/src/server/repo/zxdb.ts b/src/server/repo/zxdb.ts
index 0cc722a..0b43a14 100644
--- a/src/server/repo/zxdb.ts
+++ b/src/server/repo/zxdb.ts
@@ -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`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`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`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;
+ 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 {
+ 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);