feat(editorial): curated scientific studies with real PMID citations
Replaces the JSON-path source list with structured CitationLink objects (label / href / meta / type) so every citation in the dashboard resolves to a real URL — PubMed PMIDs, EFSA opinions, EU regulation register. Adds a hand-curated scientificStudyDraft per pilot SKU with study framing first and EFSA verbatim wording quoted separately, matching the compliance pattern Wild Nutrition, Ritual, Heights, and Nothing Fishy use. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,20 @@ const ENGINEERING_JARGON = [
|
||||
]
|
||||
|
||||
type PilotFaq = { question: string; answer: string; evidenceTier?: string; complianceNotes?: string }
|
||||
type PubMedCitation = { title?: string; firstAuthor?: string; journal?: string; year?: string; pmid?: string; url?: string }
|
||||
type EfsaCitation = { title?: string; register?: string; url?: string }
|
||||
type CuratedStatTile = { value: string; label: string; citation?: 'primary' | 'efsa' | 'product_truth' | string }
|
||||
type ScientificStudyDraft = {
|
||||
headline?: string
|
||||
studySummary?: string
|
||||
efsaClaimVerbatim?: string
|
||||
efsaClaimContext?: string
|
||||
statTiles?: CuratedStatTile[]
|
||||
primaryCitation?: PubMedCitation
|
||||
supportingCitations?: PubMedCitation[]
|
||||
efsaCitation?: EfsaCitation
|
||||
complianceNotes?: string
|
||||
}
|
||||
type Pilot = {
|
||||
sku: string
|
||||
name: string
|
||||
@@ -65,6 +79,15 @@ type Pilot = {
|
||||
timelineCadence: 'weeks' | 'months' | string
|
||||
phases: string[]
|
||||
productSpecificFaqs?: PilotFaq[]
|
||||
scientificStudyDraft?: ScientificStudyDraft
|
||||
}
|
||||
// Structured citation surface — what the dashboard renders. Every claim,
|
||||
// stat tile, and supporting line resolves to one of these. Never a file path.
|
||||
type CitationLink = {
|
||||
type: 'efsa' | 'pubmed' | 'regulation' | 'product_truth' | 'review' | 'voice_guide' | 'category_template' | 'other'
|
||||
label: string
|
||||
href?: string
|
||||
meta?: string
|
||||
}
|
||||
type Pilots = { skus: Pilot[] }
|
||||
|
||||
@@ -236,6 +259,106 @@ function generateScientificStudy(skuInfo: Pilot, claimsEv: ClaimsEvidence | null
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// TIER 0 — hand-curated scientificStudyDraft from pilot-skus.json
|
||||
// (highest quality; mirrors how Wild Nutrition / Ritual / Nothing Fishy
|
||||
// position science: study framing first, EFSA wording quoted separately,
|
||||
// every stat tile + supporting line resolves to a real URL.)
|
||||
// ====================================================================
|
||||
const draft = skuInfo.scientificStudyDraft
|
||||
if (draft && draft.headline && draft.studySummary && draft.efsaClaimVerbatim) {
|
||||
const handle = `${slug(skuInfo.sku)}-scientific-study`.slice(0, 60)
|
||||
const headline = draft.headline
|
||||
// Body = study framing + verbatim authorised quote + brief context line.
|
||||
// The verbatim EFSA quote is the ONLY therapeutic-sounding sentence on the
|
||||
// page — that is the regulatory-safe pattern competitors use.
|
||||
const body_copy = `${draft.studySummary.trim()}\n\nAuthorised health claim: "${draft.efsaClaimVerbatim.trim()}"\n\n${(draft.efsaClaimContext || '').trim()}`.trim()
|
||||
|
||||
// Stat tiles from the curated list. Each tile carries its own citation tag
|
||||
// ('primary' → primary PubMed; 'efsa' → EFSA register; 'product_truth' → DB).
|
||||
const resolveTileCitation = (tag?: string): string | undefined => {
|
||||
if (tag === 'primary' && draft.primaryCitation?.url) return draft.primaryCitation.url
|
||||
if (tag === 'efsa' && draft.efsaCitation?.url) return draft.efsaCitation.url
|
||||
if (tag === 'product_truth') return `data/sources/jv-product-truth/${skuInfo.sku}.json`
|
||||
return undefined
|
||||
}
|
||||
const stats = (draft.statTiles || []).map(t => ({
|
||||
value: t.value,
|
||||
label: t.label,
|
||||
source: resolveTileCitation(t.citation)
|
||||
}))
|
||||
|
||||
// Structured citation surface — what the dashboard renders. Real URLs only.
|
||||
const sourceLinks: CitationLink[] = []
|
||||
if (draft.primaryCitation) {
|
||||
const c = draft.primaryCitation
|
||||
sourceLinks.push({
|
||||
type: 'pubmed',
|
||||
label: c.title ? `${c.firstAuthor ? c.firstAuthor + ' — ' : ''}${c.title}` : (c.journal || 'Primary citation'),
|
||||
href: c.url,
|
||||
meta: [c.journal, c.year, c.pmid ? `PMID ${c.pmid}` : ''].filter(Boolean).join(' · ')
|
||||
})
|
||||
}
|
||||
for (const c of draft.supportingCitations || []) {
|
||||
sourceLinks.push({
|
||||
type: 'pubmed',
|
||||
label: c.title || c.journal || 'Supporting citation',
|
||||
href: c.url,
|
||||
meta: [c.journal, c.year, c.pmid ? `PMID ${c.pmid}` : ''].filter(Boolean).join(' · ')
|
||||
})
|
||||
}
|
||||
if (draft.efsaCitation) {
|
||||
sourceLinks.push({
|
||||
type: draft.efsaCitation.register?.toLowerCase().includes('regulation') || draft.efsaCitation.title?.toLowerCase().includes('regulation') ? 'regulation' : 'efsa',
|
||||
label: draft.efsaCitation.title || 'EU / UK nutrition and health claims register',
|
||||
href: draft.efsaCitation.url,
|
||||
meta: draft.efsaCitation.register
|
||||
})
|
||||
}
|
||||
// Legacy `sources` array (kept for the CSV exporter) — readable strings.
|
||||
const sources = sourceLinks
|
||||
.map(s => s.href ? `${s.label} — ${s.href}` : s.label)
|
||||
.filter(Boolean)
|
||||
|
||||
qa.push(...qaText('scientificStudy.headline', headline))
|
||||
qa.push(...qaText('scientificStudy.body_copy', body_copy))
|
||||
|
||||
return {
|
||||
handle,
|
||||
name_internal: `${skuInfo.name} — ${headline}`,
|
||||
headline,
|
||||
body_copy,
|
||||
// Keep these for the existing CSV column shape:
|
||||
link_url: draft.primaryCitation?.url || draft.efsaCitation?.url || '',
|
||||
link_label: draft.primaryCitation?.journal
|
||||
? `${draft.primaryCitation.journal} (${draft.primaryCitation.year || ''})`.trim()
|
||||
: (draft.efsaCitation?.title || 'Source'),
|
||||
stats,
|
||||
image: `${slug(skuInfo.sku)}-scientific-study.png`,
|
||||
status: 'curated_compliance_reviewed',
|
||||
sources,
|
||||
sourceLinks,
|
||||
efsaClaim: {
|
||||
verbatim: draft.efsaClaimVerbatim,
|
||||
context: draft.efsaClaimContext || ''
|
||||
},
|
||||
pubmedReferences: [
|
||||
...(draft.primaryCitation ? [{
|
||||
pmid: draft.primaryCitation.pmid,
|
||||
year: draft.primaryCitation.year,
|
||||
journal: draft.primaryCitation.journal,
|
||||
title: draft.primaryCitation.title,
|
||||
url: draft.primaryCitation.url
|
||||
}] : []),
|
||||
...(draft.supportingCitations || []).map(c => ({
|
||||
pmid: c.pmid, year: c.year, journal: c.journal, title: c.title, url: c.url
|
||||
}))
|
||||
],
|
||||
complianceNotes: draft.complianceNotes,
|
||||
qa: scoreArtefact(qa)
|
||||
}
|
||||
}
|
||||
|
||||
let handle: string, headline: string, body_copy: string, link_url: string, link_label: string, status: string, sources: string[]
|
||||
|
||||
if (efsaClaim) {
|
||||
|
||||
Reference in New Issue
Block a user