import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader, Note } from '@/components/ui/section'; import { EmptyState } from '@/components/ui/empty-state'; import { Badge, ClaimBadge } from '@/components/ui/badge'; import { SourceBadge } from '@/components/ui/source-badge'; import { Freshness } from '@/components/ui/freshness'; import { listBiomarkers, biomarkerKindFacets, kindLabel, BIOMARKER_KINDS, BIOMARKER_LINKS_FORMULA } from '@/lib/queries/biomarkers'; import { fmtInt } from '@/lib/format'; import { str, oneOf, withParams, type SP } from '@/lib/search-params'; export const metadata: Metadata = { title: 'Biomarkers', description: 'Canonical oncology biomarkers with verified NCIt concepts, anchor genes, assay conventions, and links derived from curated evidence, approvals and trials.', alternates: { canonical: '/biomarkers' }, }; // Filtered list: rendered per request (searchParams); 600 s is the CDN/ISR hint shared by all list pages. export const revalidate = 600; const KIND_FILTER = ['', ...BIOMARKER_KINDS] as const; export default async function BiomarkersPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const q = str(sp, 'q'); const kind = oneOf(sp, 'kind', KIND_FILTER, ''); const [rows, facets] = await Promise.all([listBiomarkers({ kind: kind || undefined, q: q || undefined }), biomarkerKindFacets()]); const href = (o: Record) => `/biomarkers${withParams({ q, kind }, o)}`; const total = facets.reduce((s, f) => s + f.n, 0); const latest = rows.reduce((m, r) => (m == null || String(r.updated_at) > String(m) ? r.updated_at : m), null); const verification = rows[0]?.measurement.verification; return (
{kind ? : null}
{fmtInt(rows.length)} biomarker{rows.length === 1 ? '' : 's'} identity · counts, rule {BIOMARKER_LINKS_FORMULA} ·{' '} methodology
{rows.length === 0 ? (
{total === 0 ? 'The biomarker catalogue has not been seeded on this environment (pnpm db:seed).' : 'Try a different name, alias, gene symbol or NCIt code, or clear the kind filter.'}
) : ( <>
{rows.map((b) => { const noScope = b.gene_ids.length === 0 && b.variant_ids.length === 0; return ( ); })}
Biomarker Kind Gene(s) NCIt Cancers Drugs Approvals Tissue-agnostic Active trials
{b.name} {b.measurement.aliases?.length ? {b.measurement.aliases.slice(0, 4).join(' · ')} : null} {kindLabel(b.kind)} {b.gene_symbols.length ? ( b.gene_symbols.map((s, i) => ( {i > 0 ? ', ' : ''} {s} )) ) : ( none )} {b.variant_ids.length ? {b.variant_ids.length} variant anchor{b.variant_ids.length === 1 ? '' : 's'} : null} {b.ncit_code ? ( {b.ncit_code} ) : ( '—' )} {noScope ? — : fmtInt(b.cancers_n)} {noScope ? — : fmtInt(b.drugs_n)} {fmtInt(b.approvals_n)} {b.tumor_agnostic_n > 0 ? ( Tumor-agnostic · {b.tumor_agnostic_n} ) : b.measurement.tumorAgnostic ? ( curated · no row yet ) : ( — )} {noScope ? — : fmtInt(b.active_trials_n)}
Counts are computed over ingested sources only (CIViC, ClinicalTrials.gov, openFDA, Health Canada, PubMed) and say nothing about biomarkers or links that are not yet ingested. A trial counted here tests a linked drug in a linked cancer; it does not necessarily select patients on the biomarker.
)}
); }