import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader, Section, Note } from '@/components/ui/section'; import { EmptyState } from '@/components/ui/empty-state'; import { Freshness } from '@/components/ui/freshness'; import { Badge, ClaimBadge, StatusBadge } from '@/components/ui/badge'; import { SourceBadge } from '@/components/ui/source-badge'; import { recentApprovals, newPhase3Recruiting, trialPulseByPhase, newTrialsByCancer, rankingMoves, recentPublications, recentIngests, recentEvents } from '@/lib/queries/pulse'; import { fmtDate, fmtInt, fmtValue, humanize, phaseLabel, scopeLabel, truncate } from '@/lib/format'; export const metadata: Metadata = { title: 'Pulse — what changed in cancer', description: 'New oncology approvals, newly recruiting Phase III trials, registration momentum, ranking moves, recent publications and dataset updates — every item is a dated source record.' }; export const revalidate = 900; const APPROVAL_DAYS = 90; const TRIAL_DAYS = 30; const PUB_DAYS = 60; function Delta({ current, previous }: { current: number; previous: number }) { if (previous === 0) return —; const pct = ((current - previous) / previous) * 100; const cls = pct > 0 ? 'text-ok' : pct < 0 ? 'text-danger' : 'text-ink-3'; return ( {pct > 0 ? '+' : ''} {pct.toFixed(0)} % ); } export default async function PulsePage() { const [approvals, phase3, pulse, byCancer, moves, pubs, ingests, events] = await Promise.all([recentApprovals(APPROVAL_DAYS, 40), newPhase3Recruiting(TRIAL_DAYS, 25), trialPulseByPhase(TRIAL_DAYS), newTrialsByCancer(TRIAL_DAYS, 10), rankingMoves(3, 20), recentPublications(PUB_DAYS, 15), recentIngests(14, 60), recentEvents(30, 30)]); const all = pulse.rows.find((r) => r.phase === 'ALL'); const byAuthority = new Map(); for (const a of approvals) byAuthority.set(`${a.authority} · ${a.jurisdiction}`, (byAuthority.get(`${a.authority} · ${a.jurisdiction}`) ?? 0) + 1); const lastIngest = ingests.find((r) => r.status === 'succeeded' || r.status === 'partial'); return (

Windows: approvals {APPROVAL_DAYS} days · trials {TRIAL_DAYS} days · publications {PUB_DAYS} days · datasets 14 days. Data update log → Year in cancer →

All approvals →}> {approvals.length ? ( <>

{[...byAuthority.entries()].map(([k, n]) => ( {k}: {fmtInt(n)} ))}

    {approvals.map((a) => (
  • {fmtDate(a.approval_date)} {a.drug_name} {' '} {a.authority} · {a.jurisdiction} {' '} {' '} {a.cancer_slug ? ( {a.cancer_name} ) : a.tumor_agnostic ? ( tumor-agnostic ) : ( cancer not stated in this record )} {truncate(a.indication, 180)} {a.approval_type ? {a.approval_type} : null}
  • ))}
) : ( Approval feeds are refreshed weekly (openFDA, Health Canada DPD); records appear here as soon as they carry a date within the window. )}
All recruiting Phase III →}> {phase3.length ? (
{phase3.map((t) => ( ))}
First posted NCT Title Cancer Sponsor Enrollment (n)
{fmtDate(t.first_posted_date)} {t.nct_id} {t.brief_title} {t.acronym ? ({t.acronym}) : null} {t.cancer_slug ? {t.cancer_name} : unmapped} {t.lead_sponsor ?? '—'} {t.lead_sponsor_class === 'INDUSTRY' ? industry : null} {t.enrollment_count == null ? '—' : fmtInt(t.enrollment_count)}
) : ( The ClinicalTrials.gov connector runs daily; newly posted studies appear after the next run. )}
{moves.length ? (
{moves.map((m) => ( ))}
Metric Scope Cancer Rank Previous Value Snapshot
{m.metric_name} {scopeLabel(m.scope_key)} {m.canonical_name} {m.rank} {m.previous_rank} m.rank ? 'text-ok' : 'text-danger'}>{m.previous_rank > m.rank ? '▲' : '▼'} {fmtValue(m.value, m.unit)} {fmtDate(m.generated_at)}
) : ( Ranks are compared with the previous snapshot of the same metric and scope; the first snapshot has no previous rank. )}
{pubs.length ? (
    {pubs.map((p) => (
  • {fmtDate(p.pub_date)} {p.title} {p.journal ? {p.journal} : null} {p.publication_types.filter((t) => /trial|review|meta/i.test(t)).slice(0, 2).map((t) => ( {t} ))} {p.pmid ? PMID {p.pmid} : null}
  • ))}
) : ( Literature is linked through entity queries (PubMed) and connector references; recent records appear after the nightly PubMed run. )}
Guideline changes, safety communications and trial-result events are not yet connected (no guideline or pharmacovigilance connector is live); this page will list them as event types once a licensed source is ingested. Nothing is inferred from press releases.
); }