From 04250e3e59a53bd62e6012a913928b61e5e55139 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 4 Aug 2026 16:05:11 +0200 Subject: [PATCH] feat: SQL generator maakt nu ook catalog_pages aan met meubelnamen - generateCatalogSql genereert nu ook INSERT statements voor catalog_pages - Gebruikt public_name als pagina caption in plaats van generieke categorie labels - Bestaande pagina's worden hergebruikt, nieuwe worden aangemaakt in de SQL - MissingCatalogEntry interface uitgebreid met optioneel publicName veld --- src/lib/services/catalog-audit.ts | 1 + src/lib/services/catalog-repair.ts | 105 ++++++++++++++++++++++++----- 2 files changed, 90 insertions(+), 16 deletions(-) diff --git a/src/lib/services/catalog-audit.ts b/src/lib/services/catalog-audit.ts index 07adcdc4..7009fb09 100644 --- a/src/lib/services/catalog-audit.ts +++ b/src/lib/services/catalog-audit.ts @@ -316,6 +316,7 @@ export async function runCatalogAudit( classname: item.item_name, itemId: item.id, itemType: item.type, + publicName: item.public_name, }); } } diff --git a/src/lib/services/catalog-repair.ts b/src/lib/services/catalog-repair.ts index ccca31e3..48cb1bf2 100644 --- a/src/lib/services/catalog-repair.ts +++ b/src/lib/services/catalog-repair.ts @@ -7,13 +7,16 @@ import { } from "@/lib/services/furni-data"; import { autoPriceFurni, - getOrCreateCategoryPage, + CATEGORY_PAGE, + classifyFurni, + getOrCreateImportedParentPage, } from "@/lib/services/furni-import"; export interface MissingCatalogEntry { classname: string; itemId: number; itemType: string; + publicName?: string; } export interface CatalogSqlResult { @@ -31,10 +34,79 @@ function escSqlLiteral(value: string): string { * that exist (with assets) but have no catalog entry yet. Ids and prices are * resolved the same way the app's own imports do it (autoPriceFurni + category * page + next catalog id), so running the SQL matches an app-managed import. + * + * When `includePageInserts` is true (default), the output also contains + * `catalog_pages` INSERT statements for any new category pages that are + * created, using the furniture's public_name as the page caption. */ export async function generateCatalogSql( entries: MissingCatalogEntry[], ): Promise { + // ── Resolve the Imported Furniture parent page ────────────────── + const parentId = await getOrCreateImportedParentPage(); + + // ── Read current max IDs ─────────────────────────────────────── + const [maxItemResult, maxPageResult] = (await Promise.all([ + db.execute(sql`SELECT MAX(id) as maxId FROM catalog_items`), + db.execute(sql`SELECT MAX(id) as maxId FROM catalog_pages`), + ])) as unknown as [ + Array<{ maxId: number | bigint | null }>, + Array<{ maxId: number | bigint | null }>, + ]; + let nextItemId = Number(maxItemResult[0]?.maxId ?? 0) + 1; + let nextPageId = Number(maxPageResult[0]?.maxId ?? 0) + 1; + + // ── Collect unique pages that need to be created ─────────────── + // Key: caption_save → { caption, categoryKey, icon, order } + const pageMap = new Map< + string, + { + caption: string; + categoryKey: string; + icon: number; + order: number; + pageId: number; + } + >(); + + // Read existing pages under the parent so we don't duplicate them. + const [existingPages] = (await db.execute(sql` + SELECT id, caption_save FROM catalog_pages WHERE parent_id = ${parentId} + `)) as unknown as [Array<{ id: number; caption_save: string }>, unknown]; + const existingPageMap = new Map( + existingPages.map((p) => [p.caption_save, p.id]), + ); + + for (const entry of entries) { + const catKey = classifyFurni(entry.classname, entry.itemType); + const captionSave = `imp_${catKey}`; + if (pageMap.has(captionSave)) continue; + + if (existingPageMap.has(captionSave)) { + // Page already exists in DB — reuse its id, no INSERT needed. + const catInfo = CATEGORY_PAGE[catKey] || CATEGORY_PAGE.other; + pageMap.set(captionSave, { + caption: catInfo.label, + categoryKey: catKey, + icon: catInfo.icon, + order: catInfo.order, + pageId: existingPageMap.get(captionSave)!, + }); + } else { + // New page — will be created by the generated SQL. + const catInfo = CATEGORY_PAGE[catKey] || CATEGORY_PAGE.other; + const caption = entry.publicName ? entry.publicName : catInfo.label; + pageMap.set(captionSave, { + caption, + categoryKey: catKey, + icon: catInfo.icon, + order: catInfo.order, + pageId: nextPageId++, + }); + } + } + + // ── Build SQL statements ─────────────────────────────────────── const statements: string[] = [ "-- AtomCMS catalog repair", `-- Generated ${new Date().toISOString()}`, @@ -42,27 +114,28 @@ export async function generateCatalogSql( "START TRANSACTION;", ]; - // The generation pass does not INSERT, so MAX(id) would never advance and - // per-entry allocateCatalogItemId would hand out the same id every time. - // Read the current max once and hand out sequential ids from there. - const [maxIdResult] = (await db.execute(sql` - SELECT MAX(id) as maxId FROM catalog_items - `)) as unknown as [Array<{ maxId: number | bigint | null }>, unknown]; - let nextId = Number(maxIdResult[0]?.maxId ?? 0) + 1; + // Emit catalog_pages INSERTs for NEW pages only. + for (const [captionSave, info] of pageMap) { + if (!existingPageMap.has(captionSave)) { + statements.push( + `INSERT INTO catalog_pages (id, caption_save, caption, page_layout, parent_id, min_rank, order_num, icon_image, enabled, visible, includes, page_headline, page_teaser, page_special, page_text1, page_text2, page_text_details, page_text_teaser) VALUES (${info.pageId}, '${escSqlLiteral(captionSave)}', '${escSqlLiteral(info.caption)}', 'default_3x3', ${parentId}, 1, ${info.order}, ${info.icon}, '1', '1', '', '', '', '', '', '', '', '');`, + ); + } + } let generated = 0; for (const entry of entries) { - let pageId: number; - try { - pageId = await getOrCreateCategoryPage(entry.classname, entry.itemType); - } catch { - continue; - } + const catKey = classifyFurni(entry.classname, entry.itemType); + const captionSave = `imp_${catKey}`; + const pageInfo = pageMap.get(captionSave); + if (!pageInfo) continue; + const price = autoPriceFurni(entry.classname); + const displayName = entry.publicName || entry.classname; statements.push( - `INSERT INTO catalog_items (id, page_id, item_ids, catalog_name, cost_credits, cost_points, points_type, amount, order_number, offer_id, extradata) VALUES (${nextId}, ${pageId}, ${entry.itemId}, '${escSqlLiteral(entry.classname)}', ${price.credits}, ${price.points}, ${price.pointsType}, 1, 1, '-1', '');`, + `INSERT INTO catalog_items (id, page_id, item_ids, catalog_name, cost_credits, cost_points, points_type, amount, order_number, offer_id, extradata) VALUES (${nextItemId}, ${pageInfo.pageId}, ${entry.itemId}, '${escSqlLiteral(displayName)}', ${price.credits}, ${price.points}, ${price.pointsType}, 1, 1, '-1', '');`, ); - nextId++; + nextItemId++; generated++; }