docs: add ZXDB guide; refresh README & AGENTS

Expand and update documentation to reflect the current app (Registers + ZXDB Explorer), with clear setup and usage instructions.

Changes
- README: add project overview including ZXDB Explorer; routes tour; ZXDB setup (DB import, helper search tables, readonly role); environment configuration; selected API endpoints; implementation notes (Next 15 async params, Node runtime for mysql2, SSR/ISR usage); links to AGENTS.md and docs/ZXDB.md.
- docs/ZXDB.md (new): deep-dive guide covering database preparation, helper tables, environment, Explorer UI, API reference under /api/zxdb, performance approach (helper tables, parallel queries, ISR), troubleshooting, and roadmap.
- AGENTS.md: refresh Project Overview/Structure with ZXDB routes and server/client boundaries; document Next.js 15 dynamic params async pattern for pages and API routes; note Drizzle+mysql2, Node runtime, and lookup `text`→`name` mapping; keep commit workflow guidance.
- example.env: add reference to docs/ZXDB.md and clarify mysql:// format and setup pointers.

Notes
- Documentation focuses on the current state of the codebase (what the code does), not a log of agent actions.
- Helper SQL at ZXDB/scripts/ZXDB_help_search.sql is required for performant searches.

Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
2025-12-12 16:17:35 +00:00
parent 3ef3a16bc0
commit ddbf72ea52
10 changed files with 409 additions and 56 deletions

View File

@@ -311,6 +311,103 @@ export async function listMachinetypes() {
return db.select().from(machinetypes).orderBy(machinetypes.name);
}
// Search with pagination for lookups
export interface SimpleSearchParams {
q?: string;
page?: number;
pageSize?: number;
}
export async function searchLanguages(params: SimpleSearchParams) {
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;
if (!q) {
const [items, countRows] = await Promise.all([
db.select().from(languages).orderBy(languages.name).limit(pageSize).offset(offset),
db.select({ total: sql<number>`count(*)` }).from(languages) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
const pattern = `%${q}%`;
const [items, countRows] = await Promise.all([
db
.select()
.from(languages)
.where(like(languages.name as any, pattern))
.orderBy(languages.name)
.limit(pageSize)
.offset(offset),
db.select({ total: sql<number>`count(*)` }).from(languages).where(like(languages.name as any, pattern)) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
export async function searchGenres(params: SimpleSearchParams) {
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;
if (!q) {
const [items, countRows] = await Promise.all([
db.select().from(genretypes).orderBy(genretypes.name).limit(pageSize).offset(offset),
db.select({ total: sql<number>`count(*)` }).from(genretypes) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
const pattern = `%${q}%`;
const [items, countRows] = await Promise.all([
db
.select()
.from(genretypes)
.where(like(genretypes.name as any, pattern))
.orderBy(genretypes.name)
.limit(pageSize)
.offset(offset),
db.select({ total: sql<number>`count(*)` }).from(genretypes).where(like(genretypes.name as any, pattern)) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
export async function searchMachinetypes(params: SimpleSearchParams) {
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;
if (!q) {
const [items, countRows] = await Promise.all([
db.select().from(machinetypes).orderBy(machinetypes.name).limit(pageSize).offset(offset),
db.select({ total: sql<number>`count(*)` }).from(machinetypes) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
const pattern = `%${q}%`;
const [items, countRows] = await Promise.all([
db
.select()
.from(machinetypes)
.where(like(machinetypes.name as any, pattern))
.orderBy(machinetypes.name)
.limit(pageSize)
.offset(offset),
db.select({ total: sql<number>`count(*)` }).from(machinetypes).where(like(machinetypes.name as any, pattern)) as unknown as Promise<{ total: number }[]>,
]);
const total = Number(countRows?.[0]?.total ?? 0);
return { items: items as any, page, pageSize, total };
}
export async function entriesByGenre(genreId: number, page: number, pageSize: number): Promise<PagedResult<SearchResultItem>> {
const offset = (page - 1) * pageSize;
const countRows = (await db