ZXDB: Releases browser filters, schema lists, and fixes
- UI: Add /zxdb hub cards for Entries and Releases; implement Releases browser
with URL‑synced filters (q, year, sort, DL language/machine, file/scheme/source/case, demo)
and a paginated table (Entry ID, Title, Release #, Year).
- API: Add GET /api/zxdb/releases/search (Zod‑validated, Node runtime) supporting
title, year, sort, and downloads‑based filters; return paged JSON.
- Repo: Rewrite searchReleases to Drizzle QB; correct ORDER BY on releases.release_year;
implement EXISTS on downloads using explicit "from downloads as d"; return JSON‑safe rows.
- Schema: Align Drizzle models with ZXDB for releases/downloads; add lookups
availabletypes, currencies, roletypes, and roles relation.
- API (lookups): Add GET /api/zxdb/{availabletypes,currencies,roletypes} for dropdowns.
- Stability: JSON‑clone SSR payloads before passing to Client Components to avoid
RowDataPacket serialization errors.
Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { mysqlTable, int, varchar, tinyint, char, smallint } from "drizzle-orm/mysql-core";
|
||||
import { mysqlTable, int, varchar, tinyint, char, smallint, decimal } from "drizzle-orm/mysql-core";
|
||||
|
||||
// Minimal subset needed for browsing/searching
|
||||
export const entries = mysqlTable("entries", {
|
||||
@@ -80,6 +80,21 @@ export const genretypes = mysqlTable("genretypes", {
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
// Additional lookups
|
||||
export const availabletypes = mysqlTable("availabletypes", {
|
||||
id: char("id", { length: 1 }).notNull().primaryKey(),
|
||||
// DB column `text`
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
export const currencies = mysqlTable("currencies", {
|
||||
id: char("id", { length: 3 }).notNull().primaryKey(),
|
||||
name: varchar("name", { length: 50 }).notNull(),
|
||||
symbol: varchar("symbol", { length: 20 }),
|
||||
// Stored as tinyint(1) 0/1
|
||||
prefix: tinyint("prefix").notNull(),
|
||||
});
|
||||
|
||||
// ----- Files and Filetypes (for downloads/assets) -----
|
||||
export const filetypes = mysqlTable("filetypes", {
|
||||
id: tinyint("id").notNull().primaryKey(),
|
||||
@@ -100,14 +115,6 @@ export const files = mysqlTable("files", {
|
||||
comments: varchar("comments", { length: 250 }),
|
||||
});
|
||||
|
||||
// ----- Releases / Downloads (linked assets per release) -----
|
||||
// Lookups used by releases/downloads
|
||||
export const releasetypes = mysqlTable("releasetypes", {
|
||||
id: char("id", { length: 1 }).notNull().primaryKey(),
|
||||
// column name in DB is `text`
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
export const schemetypes = mysqlTable("schemetypes", {
|
||||
id: char("id", { length: 2 }).notNull().primaryKey(),
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
@@ -123,6 +130,11 @@ export const casetypes = mysqlTable("casetypes", {
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
export const roletypes = mysqlTable("roletypes", {
|
||||
id: char("id", { length: 1 }).notNull().primaryKey(),
|
||||
name: varchar("text", { length: 50 }).notNull(),
|
||||
});
|
||||
|
||||
export const hosts = mysqlTable("hosts", {
|
||||
id: tinyint("id").notNull().primaryKey(),
|
||||
title: varchar("title", { length: 150 }).notNull(),
|
||||
@@ -135,13 +147,17 @@ export const hosts = mysqlTable("hosts", {
|
||||
export const releases = mysqlTable("releases", {
|
||||
entryId: int("entry_id").notNull(),
|
||||
releaseSeq: smallint("release_seq").notNull(),
|
||||
releasetypeId: char("releasetype_id", { length: 1 }),
|
||||
languageId: char("language_id", { length: 2 }),
|
||||
machinetypeId: tinyint("machinetype_id"),
|
||||
labelId: int("label_id"), // developer
|
||||
publisherId: int("publisher_label_id"),
|
||||
releaseYear: smallint("release_year"),
|
||||
comments: varchar("comments", { length: 250 }),
|
||||
releaseMonth: smallint("release_month"),
|
||||
releaseDay: smallint("release_day"),
|
||||
currencyId: char("currency_id", { length: 3 }),
|
||||
releasePrice: decimal("release_price", { precision: 9, scale: 2 }),
|
||||
budgetPrice: decimal("budget_price", { precision: 9, scale: 2 }),
|
||||
microdrivePrice: decimal("microdrive_price", { precision: 9, scale: 2 }),
|
||||
diskPrice: decimal("disk_price", { precision: 9, scale: 2 }),
|
||||
cartridgePrice: decimal("cartridge_price", { precision: 9, scale: 2 }),
|
||||
bookIsbn: varchar("book_isbn", { length: 50 }),
|
||||
bookPages: smallint("book_pages"),
|
||||
});
|
||||
|
||||
// Downloads are linked to a release via (entry_id, release_seq)
|
||||
@@ -167,3 +183,10 @@ export const downloads = mysqlTable("downloads", {
|
||||
releaseYear: smallint("release_year"),
|
||||
comments: varchar("comments", { length: 250 }),
|
||||
});
|
||||
|
||||
// Roles relation (composite PK in DB)
|
||||
export const roles = mysqlTable("roles", {
|
||||
entryId: int("entry_id").notNull(),
|
||||
labelId: int("label_id").notNull(),
|
||||
roletypeId: char("roletype_id", { length: 1 }).notNull(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user