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
This commit is contained in:
1 parent
2ee5ba5c4c
commit
04250e3e59
2 files changed
+90
-16
No files matched your search
@@ -316,6 +316,7 @@ export async function runCatalogAudit(
|
|||||||
classname: item.item_name,
|
classname: item.item_name,
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
itemType: item.type,
|
itemType: item.type,
|
||||||
|
publicName: item.public_name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ import {
|
|||||||
} from "@/lib/services/furni-data";
|
} from "@/lib/services/furni-data";
|
||||||
import {
|
import {
|
||||||
autoPriceFurni,
|
autoPriceFurni,
|
||||||
getOrCreateCategoryPage,
|
CATEGORY_PAGE,
|
||||||
|
classifyFurni,
|
||||||
|
getOrCreateImportedParentPage,
|
||||||
} from "@/lib/services/furni-import";
|
} from "@/lib/services/furni-import";
|
||||||
|
|
||||||
export interface MissingCatalogEntry {
|
export interface MissingCatalogEntry {
|
||||||
classname: string;
|
classname: string;
|
||||||
itemId: number;
|
itemId: number;
|
||||||
itemType: string;
|
itemType: string;
|
||||||
|
publicName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CatalogSqlResult {
|
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
|
* 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
|
* 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.
|
* 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(
|
export async function generateCatalogSql(
|
||||||
entries: MissingCatalogEntry[],
|
entries: MissingCatalogEntry[],
|
||||||
): Promise<CatalogSqlResult> {
|
): Promise<CatalogSqlResult> {
|
||||||
|
// ── 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[] = [
|
const statements: string[] = [
|
||||||
"-- AtomCMS catalog repair",
|
"-- AtomCMS catalog repair",
|
||||||
`-- Generated ${new Date().toISOString()}`,
|
`-- Generated ${new Date().toISOString()}`,
|
||||||
@@ -42,27 +114,28 @@ export async function generateCatalogSql(
|
|||||||
"START TRANSACTION;",
|
"START TRANSACTION;",
|
||||||
];
|
];
|
||||||
|
|
||||||
// The generation pass does not INSERT, so MAX(id) would never advance and
|
// Emit catalog_pages INSERTs for NEW pages only.
|
||||||
// per-entry allocateCatalogItemId would hand out the same id every time.
|
for (const [captionSave, info] of pageMap) {
|
||||||
// Read the current max once and hand out sequential ids from there.
|
if (!existingPageMap.has(captionSave)) {
|
||||||
const [maxIdResult] = (await db.execute(sql`
|
statements.push(
|
||||||
SELECT MAX(id) as maxId FROM catalog_items
|
`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', '', '', '', '', '', '', '', '');`,
|
||||||
`)) as unknown as [Array<{ maxId: number | bigint | null }>, unknown];
|
);
|
||||||
let nextId = Number(maxIdResult[0]?.maxId ?? 0) + 1;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let generated = 0;
|
let generated = 0;
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
let pageId: number;
|
const catKey = classifyFurni(entry.classname, entry.itemType);
|
||||||
try {
|
const captionSave = `imp_${catKey}`;
|
||||||
pageId = await getOrCreateCategoryPage(entry.classname, entry.itemType);
|
const pageInfo = pageMap.get(captionSave);
|
||||||
} catch {
|
if (!pageInfo) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const price = autoPriceFurni(entry.classname);
|
const price = autoPriceFurni(entry.classname);
|
||||||
|
const displayName = entry.publicName || entry.classname;
|
||||||
statements.push(
|
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++;
|
generated++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user