import type { Metadata } from 'next'; import Link from 'next/link'; import { PageHeader } from '@/components/ui/section'; import { EmptyState } from '@/components/ui/empty-state'; import { Badge } from '@/components/ui/badge'; import { searchEntities } from '@/lib/queries/search'; import { str, type SP } from '@/lib/search-params'; import { fmtInt } from '@/lib/format'; export const metadata: Metadata = { title: 'Search', robots: { index: false } }; export const dynamic = 'force-dynamic'; const TYPE_LABEL: Record = { cancer: 'Cancers', gene: 'Genes', variant: 'Variants', drug: 'Drugs', trial: 'Clinical trials', publication: 'Publications', source: 'Sources' }; const TYPE_ORDER = ['cancer', 'gene', 'variant', 'drug', 'trial', 'publication', 'source']; export default async function SearchPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const q = str(sp, 'q').slice(0, 200); const hits = q.length >= 2 ? await searchEntities(q, 50) : []; const groups = new Map(); for (const h of hits) groups.set(h.type, [...(groups.get(h.type) ?? []), h]); const types = [...groups.keys()].sort((a, b) => TYPE_ORDER.indexOf(a) - TYPE_ORDER.indexOf(b)); return (
Results for {q} : 'Search'} lede="Typed results across cancers, genes, variants, drugs, trials, publications and sources. Ordering: exact name, then alias or identifier, then prefix, then fuzzy match." />
{q.length > 0 && q.length < 2 ?

Type at least two characters.

: null} {q.length >= 2 && hits.length === 0 ? (
No cancer, gene, variant, drug, trial or publication matched {q}. Only indexed entities are searchable; some entity types are not yet ingested on this environment.
) : null} {hits.length ? (

{fmtInt(hits.length)} results in {types.length} {types.length === 1 ? 'type' : 'types'}

{types.map((t) => (

{TYPE_LABEL[t] ?? t} {groups.get(t)!.length}

    {groups.get(t)!.map((h) => (
  • {h.title} {h.subtitle ? {h.subtitle} : null} {h.id} {h.match}
  • ))}
))}
) : null}
); }