056c47581f
Ships the second dashboard surface — a Pattern Library + Preview Theatre — that presents the 4-section PDP pilot batch back to Umar, compliance, and the board in an editorial format. Adds the full data layer that drives it: 5 source-backed per-SKU drafts at QA 100/100, 15 competitor PDP semantic extracts, PubMed evidence packs, EFSA claims library extension, JV brand voice guide, hand-curated product FAQs, and the Matrixify-ready CSV exports for Lewis. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
2.7 KiB
TypeScript
57 lines
2.7 KiB
TypeScript
#!/usr/bin/env bun
|
|
import { readdirSync } from 'fs'
|
|
import { join } from 'path'
|
|
import { ROOT, readJson, writeJson } from './lib/intel-utils'
|
|
|
|
const contentRoot = join(ROOT, 'data', 'content')
|
|
const productsRoot = join(contentRoot, 'products')
|
|
const workflowRoot = join(contentRoot, 'workflow')
|
|
const outRoot = join(ROOT, 'content_population_exports')
|
|
const fields = [
|
|
['pdpResults', 'results'],
|
|
['pdpResultsMobile', 'results-mobile'],
|
|
['pdpHowWeCompare', 'how-we-compare']
|
|
]
|
|
function filename(sku: string, suffix: string) { return `${sku.toLowerCase()}-${suffix}.png` }
|
|
const rows: any[] = []
|
|
const files = readdirSync(productsRoot).filter(file => file.endsWith('.json'))
|
|
let assigned = 0
|
|
for (const file of files) {
|
|
const path = join(productsRoot, file)
|
|
const product = readJson(path, null)
|
|
if (!product) continue
|
|
product.metafields = product.metafields || {}
|
|
for (const [field, suffix] of fields) {
|
|
const before = product.metafields[field] || ''
|
|
const after = before || filename(product.sku, suffix)
|
|
product.metafields[field] = after
|
|
if (!before) assigned += 1
|
|
rows.push({ sku: product.sku, field, filename: after, status: before ? 'preserved_existing' : 'assigned_pending_asset_upload' })
|
|
}
|
|
product.approval = { ...(product.approval || {}), images: product.approval?.images || 'needed' }
|
|
product.updatedAt = new Date().toISOString()
|
|
writeJson(path, product)
|
|
|
|
const workflowPath = join(workflowRoot, `${product.sku}.json`)
|
|
const workflow = readJson(workflowPath, null)
|
|
if (workflow?.fields) {
|
|
for (const [field] of fields) {
|
|
if (workflow.fields[field] && workflow.fields[field].status === 'blocked_asset') {
|
|
workflow.fields[field].status = 'pending_asset_upload'
|
|
}
|
|
}
|
|
workflow.events = Array.isArray(workflow.events) ? workflow.events : []
|
|
if (assigned) {
|
|
workflow.events.unshift({ at: new Date().toISOString(), type: 'asset_filenames_assigned', actor: 'script', summary: 'Deterministic PDP image filenames assigned; upload/production still required.' })
|
|
}
|
|
workflow.updatedAt = new Date().toISOString()
|
|
writeJson(workflowPath, workflow)
|
|
}
|
|
}
|
|
const headers = ['sku', 'field', 'filename', 'status']
|
|
const esc = (value: any) => /[",\n\r]/.test(String(value ?? '')) ? `"${String(value ?? '').replace(/"/g, '""')}"` : String(value ?? '')
|
|
await Bun.write(join(outRoot, 'planned_image_filenames.csv'), [headers.join(','), ...rows.map(row => headers.map(h => esc(row[h])).join(','))].join('\n') + '\n')
|
|
writeJson(join(outRoot, 'planned_image_filenames.json'), { generatedAt: new Date().toISOString(), assigned, totalRows: rows.length, rows })
|
|
console.log(`Planned image filename rows: ${rows.length}`)
|
|
console.log(`New filenames assigned: ${assigned}`)
|