feat: editorial review dashboard + elite-grade pilot batch (5 SKUs)

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>
This commit is contained in:
Omair Saleh
2026-06-02 18:20:17 +08:00
parent e470e113ee
commit 056c47581f
3302 changed files with 391779 additions and 854 deletions
@@ -0,0 +1,117 @@
#!/usr/bin/env bun
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
const root = process.cwd()
const productsRoot = join(root, 'data', 'content', 'products')
const workflowRoot = join(root, 'data', 'content', 'workflow')
const outRoot = join(root, 'content_population_exports')
const generatedAt = new Date().toISOString()
function readJson(path: string, fallback: any = null) {
if (!existsSync(path)) return fallback
return JSON.parse(readFileSync(path, 'utf8').replace(/^\uFEFF/, ''))
}
function csv(rows: any[], headers: string[]) {
const esc = (value: any) => {
if (Array.isArray(value)) value = value.join(' | ')
if (value === undefined || value === null) value = ''
value = String(value)
return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value
}
return [headers.join(','), ...rows.map(row => headers.map(header => esc(row[header])).join(','))].join('\n') + '\n'
}
function decideBlocker(product: any) {
const approval = product.approval || {}
const blockers = []
if (approval.content !== 'approved') blockers.push('content_approval')
if (approval.compliance !== 'approved') blockers.push('compliance_approval')
if (approval.images !== 'ready' && approval.images !== 'approved') blockers.push('image_approval')
if (approval.export !== 'ready' && approval.export !== 'approved') blockers.push('export_approval')
return blockers
}
const rows = readdirSync(productsRoot).filter(file => file.endsWith('.json')).sort().map(file => {
const product = readJson(join(productsRoot, file), {})
const sku = product.sku || file.replace(/\.json$/, '')
const workflow = readJson(join(workflowRoot, `${sku}.json`), {})
const blockers = decideBlocker(product)
const imageRefs = [
product.metafields?.pdpResults,
product.metafields?.pdpResultsMobile,
product.metafields?.pdpHowWeCompare
].filter(Boolean)
return {
SKU: sku,
ProductName: product.name || '',
Handle: product.handle || '',
ContentApproval: product.approval?.content || '',
ComplianceApproval: product.approval?.compliance || '',
ImageApproval: product.approval?.images || '',
ExportApproval: product.approval?.export || '',
ClinicalRef: product.metafields?.pdpClinicallyShownTo || '',
ImageRefs: imageRefs,
Blockers: blockers,
ReadyForExportApproval: blockers.length === 0 ? 'yes' : 'no',
WorkflowStage: workflow.stage || '',
LastUpdated: product.updatedAt || workflow.updatedAt || '',
ReviewerAction: blockers.length === 0
? 'Confirm final spot-check and keep approval evidence.'
: `Review and approve/resolve: ${blockers.join(', ')}.`,
ProductFile: `data/content/products/${sku}.json`,
WorkflowFile: `data/content/workflow/${sku}.json`
}
})
const blockerCounts = rows.reduce((acc: Record<string, number>, row: any) => {
for (const blocker of row.Blockers) acc[blocker] = (acc[blocker] || 0) + 1
return acc
}, {})
const manifest = {
generatedAt,
ready: rows.every((row: any) => row.ReadyForExportApproval === 'yes'),
purpose: 'Reviewer-facing approval queue packet for the 190 product records. This does not self-approve content.',
counts: {
products: rows.length,
readyForExportApproval: rows.filter((row: any) => row.ReadyForExportApproval === 'yes').length,
blockedProducts: rows.filter((row: any) => row.ReadyForExportApproval !== 'yes').length,
blockerCounts
},
rows
}
const headers = ['SKU', 'ProductName', 'Handle', 'ContentApproval', 'ComplianceApproval', 'ImageApproval', 'ExportApproval', 'ClinicalRef', 'ImageRefs', 'Blockers', 'ReadyForExportApproval', 'WorkflowStage', 'LastUpdated', 'ReviewerAction', 'ProductFile', 'WorkflowFile']
writeFileSync(join(outRoot, 'product_approval_review_packet.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'product_approval_review_packet.csv'), csv(rows, headers), 'utf8')
const md = [
'# Product approval review packet',
'',
`Generated: ${generatedAt}`,
'',
'This packet turns the product-level approval blocker into a reviewer queue. It does not mark any product approved.',
'',
`Products: ${manifest.counts.products}`,
`Blocked products: ${manifest.counts.blockedProducts}`,
`Ready for export approval: ${manifest.counts.readyForExportApproval}`,
'',
'## Blocker counts',
...Object.entries(blockerCounts).map(([key, count]) => `- ${key}: ${count}`),
'',
'## First 50 reviewer rows',
...rows.slice(0, 50).map((row: any) => `- **${row.SKU}** ${row.ProductName}: ${row.Blockers.join(', ') || 'no blockers'}; clinical ref=${row.ClinicalRef || 'blank'}`),
'',
rows.length > 50 ? '_Full queue is in `product_approval_review_packet.csv`._' : '',
'',
'## Regenerate after review',
'After human approvals are recorded, run `bun run approval:review-packet && bun run export:content && bun run audit:content`.',
''
].join('\n')
writeFileSync(join(outRoot, 'product_approval_review_packet.md'), md, 'utf8')
console.log(`Product approval review packet ready: ${manifest.ready}`)
console.log(`Blocked products: ${manifest.counts.blockedProducts}`)
if (!manifest.ready) process.exitCode = 1