spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import Link from 'next/link';2import { Badge, ClaimBadge, StatusBadge } from '@/components/ui/badge';3import { SourceBadge, TableProvenance } from '@/components/ui/source-badge';4import type { ApprovalRow } from '@/lib/queries/drugs';5import type { ProvRow } from '@/lib/queries/provenance';6import { toInfo } from '@/lib/queries/provenance';7import { fmtDate, fmtInt } from '@/lib/format';8import { Pager } from '@/components/ui/pager';9import { pageInfo } from '@/lib/pagination';1011/**12 * Jurisdiction-aware regulatory status (§13). Never a bare "approved". Approvals may come from13 * different authorities' datasets, so rows keep a compact badge with a dataset · version title;14 * the caption carries one full popover (first row's provenance) and the claim label.15 */16export function ApprovalsTable({ rows: allRows, prov, showDrug = true, showCancer = true, page = 1, pageSize = 50, hrefFor }: { rows: ApprovalRow[]; prov: Map<number, ProvRow>; showDrug?: boolean; showCancer?: boolean; page?: number; pageSize?: number; hrefFor?: (page: number) => string }) {17 // Optional server-side pagination (URL state); without hrefFor the first page only is shown with a count.18 const info = pageInfo(page, pageSize, allRows.length);19 const rows = allRows.slice(info.offset, info.offset + pageSize);20 const first = rows[0];21 const caption = first ? (toInfo(prov.get(first.provenance_id)) ?? { sourceSlug: first.source_slug, sourceName: first.source_name }) : null;22 const sources = new Set(rows.map((r) => r.source_slug));23 return (24 <div>25 {caption ? (26 <TableProvenance p={caption} claim={<ClaimBadge kind="regulatory" />}>27 {fmtInt(rows.length)} approval record{rows.length === 1 ? '' : 's'}28 {sources.size > 1 ? ` from ${sources.size} sources (hover a row badge for its dataset)` : ''} · authority, jurisdiction and indication text as published.29 </TableProvenance>30 ) : null}31 <div className="ci-table-wrap">32 <table className="ci-table">33 <thead>34 <tr>35 {showDrug ? <th scope="col">Drug</th> : null}36 <th scope="col">Jurisdiction · authority</th>37 {showCancer ? <th scope="col">Cancer</th> : null}38 <th scope="col">Indication</th>39 <th scope="col">Status</th>40 <th scope="col">Approval date</th>41 <th scope="col">Source</th>42 </tr>43 </thead>44 <tbody>45 {rows.map((a) => (46 <tr key={a.id}>47 {showDrug ? (48 <td>49 <Link className="ci-link" href={`/drug/${a.drug_slug}`}>50 {a.drug_name}51 </Link>52 </td>53 ) : null}54 <td className="whitespace-nowrap">55 <span className="ci-mono font-medium">{a.jurisdiction}</span> <span className="text-ink-3">{a.authority}</span>56 </td>57 {showCancer ? (58 <td>59 {a.tumor_agnostic ? <Badge tone="accent">Tumor-agnostic</Badge> : null}60 {a.cancer_slug ? (61 <Link className="ci-link" href={`/cancer/${a.cancer_slug}`}>62 {a.cancer_name}63 </Link>64 ) : !a.tumor_agnostic ? (65 <span className="text-ink-3">—</span>66 ) : null}67 </td>68 ) : null}69 <td className="min-w-[280px] max-w-[520px] text-[12.5px]">70 {a.indication}71 <span className="mt-0.5 flex flex-wrap gap-1">72 {a.line_of_therapy ? <Badge tone="outline">{a.line_of_therapy}</Badge> : null}73 {a.disease_stage ? <Badge tone="outline">{a.disease_stage}</Badge> : null}74 {a.accelerated ? <Badge tone="warn">accelerated</Badge> : null}75 {a.conditional ? <Badge tone="warn">conditional</Badge> : null}76 {a.biomarker_ids.length ? <Badge tone="outline">biomarker-restricted</Badge> : null}77 </span>78 </td>79 <td>80 <StatusBadge status={a.status} />81 {a.source_status && a.source_status.toLowerCase() !== a.status ? (82 <span className="block text-[11px] text-ink-3" title="Status as published by the source for this one product (DIN); not the status of the molecule.">83 source: {a.source_status}84 </span>85 ) : null}86 {a.withdrawal_date ? <span className="block text-[11px] text-danger">since {fmtDate(a.withdrawal_date)}</span> : null}87 </td>88 <td className="whitespace-nowrap">{fmtDate(a.approval_date)}</td>89 <td>90 <SourceBadge compact p={toInfo(prov.get(a.provenance_id)) ?? { sourceSlug: a.source_slug, sourceName: a.source_name }} />91 </td>92 </tr>93 ))}94 </tbody>95 </table>96 </div>97 {hrefFor ? <Pager total={allRows.length} pageSize={pageSize} page={info.page} hrefFor={hrefFor} label="Approval pages" noun="approval records" /> : allRows.length > rows.length ? <p className="mt-1 text-[12px] text-ink-3">Showing {rows.length} of {fmtInt(allRows.length)} approval records.</p> : null}98 </div>99 );100}101