import { Section, Note } from '@/components/ui/section'; import { EmptyState } from '@/components/ui/empty-state'; import { Freshness } from '@/components/ui/freshness'; import { Badge, ClaimBadge } from '@/components/ui/badge'; import { SourceBadge, TableProvenance } from '@/components/ui/source-badge'; import { LineChart, type Series } from '@/components/charts/line-chart'; import { epidemiologyFor, EPI_METRIC_LABEL } from '@/lib/queries/epidemiology'; import { loadProvenance, toInfo } from '@/lib/queries/provenance'; import { fmtValue, humanize, unitLabel } from '@/lib/format'; import type { CancerBundle } from '../load'; const ROWS_PER_TABLE = 12; export async function StatisticsTab({ b }: { b: CancerBundle }) { const obs = await epidemiologyFor(b.cancer.id); if (obs.length === 0) { return (
No epidemiology observation is attached to this entity. Population statistics come from registries (SEER, CDC WONDER) and the IARC Global Cancer Observatory; IARC data stays under license review and SEER awaits credentials, so no burden figure is displayed. Statistics are attached per geography, year and sex — never invented or extrapolated.
); } const prov = await loadProvenance(obs.map((o) => o.provenance_id)); // Group: geography → metric → (sex, age) → series by year type Key = string; const groups = new Map(); for (const o of obs) { const k = `${o.geography_id}|${o.metric}`; if (!groups.has(k)) groups.set(k, { geography: o.geography_name, metric: o.metric, unit: o.unit, rows: [] }); groups.get(k)!.rows.push(o); } const latest = obs.reduce((m, o) => (m == null || String(o.updated_at) > String(m) ? o.updated_at : m), null); return (
Population statistics describe groups defined by geography, period and sex. They do not predict any individual outcome. Values labelled "estimated" or "projected" are model outputs from the source, not registry counts. {[...groups.values()].map((g) => { const seriesMap = new Map(); for (const r of g.rows) { const name = `${humanize(r.sex)}${r.age_group !== 'all' ? ` · ${r.age_group}` : ''}${r.estimate_type !== 'observed' ? ` (${r.estimate_type})` : ''} · ${r.source_slug}`; if (!seriesMap.has(name)) seriesMap.set(name, { name, points: [], dashed: r.estimate_type !== 'observed' }); seriesMap.get(name)!.points.push({ x: r.year, y: r.value, lo: r.lower_ci, hi: r.upper_ci }); } const series = [...seriesMap.values()]; const multiYear = series.some((s) => s.points.length > 1); const sources = new Set(g.rows.map((r) => r.source_slug)); const firstProv = toInfo(prov.get(g.rows[0]!.provenance_id)) ?? { sourceSlug: g.rows[0]!.source_slug, sourceName: g.rows[0]!.source_name }; const shown = [...g.rows].sort((a, c) => c.year - a.year || a.sex.localeCompare(c.sex)).slice(0, ROWS_PER_TABLE); return (
{multiYear ? : null} }> {g.rows.length} observation{g.rows.length === 1 ? '' : 's'} {sources.size > 1 ? ` from ${sources.size} sources` : ''} · observations differ by dataset and year: hover a row badge for its dataset and version.
{shown.map((r) => ( ))}
Year Sex Age group Value ({unitLabel(g.unit)}) 95% CI Type Site definition Source
{r.year_end && r.year_end !== r.year ? `${r.year}–${r.year_end}` : r.year} {humanize(r.sex)} {r.age_group === 'all' ? 'All ages' : r.age_group} {fmtValue(r.value, r.unit)} {r.lower_ci != null && r.upper_ci != null ? `${fmtValue(r.lower_ci, r.unit)}–${fmtValue(r.upper_ci, r.unit)}` : '—'} {r.estimate_type} {r.site_definition ?? '—'}
{g.rows.length > ROWS_PER_TABLE ? (

Showing the {ROWS_PER_TABLE} most recent rows of {g.rows.length}; every year is plotted above and available via{' '} the API .

) : null} ((m, r) => (m == null || String(r.updated_at) > String(m) ? r.updated_at : m), null)} sourceVersion={prov.get(g.rows[0]!.provenance_id)?.dataset_version ?? null} />
); })}
); }