SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
3.9 KB · 89 lines tsx
Raw Blame History
1import Link from 'next/link';2import { Section } from '@/components/ui/section';3import { EmptyState } from '@/components/ui/empty-state';4import { Freshness } from '@/components/ui/freshness';5import { Badge, ClaimBadge, StatusBadge } from '@/components/ui/badge';6import { SourceBadge } from '@/components/ui/source-badge';7import { recentApprovals } from '@/lib/queries/approvals';8import { fmtDate, toDate } from '@/lib/format';910/**11 * Home module "New oncology approvals": the last dated approval records across every ingested12 * authority (one record = one authority's decision for one application/DIN — never a bare13 * "approved"). Regulatory claim label; source badge per row because authorities differ.14 */15export async function ApprovalsModule({ limit = 8 }: { limit?: number }) {16  const rows = await recentApprovals(limit);17  return (18    <Section19      id="approvals"20      kicker="Regulatory"21      title="New oncology approvals"22      description="Latest dated approval records by authority and jurisdiction. A record is one decision for one application or DIN; Health Canada records do not state indications."23      actions={24        <Link href="/approvals" className="ci-link">25          Approvals feed →26        </Link>27      }28    >29      {rows.length === 0 ? (30        <EmptyState knows={[{ label: 'Drugs', href: '/drugs' }, { label: 'Sources', href: '/sources' }]}>No regulatory approval record has been ingested yet.</EmptyState>31      ) : (32        <>33          <div className="ci-table-wrap">34            <table className="ci-table">35              <thead>36                <tr>37                  <th>Date</th>38                  <th>Drug</th>39                  <th>Authority</th>40                  <th>Cancer</th>41                  <th>Status</th>42                  <th>Source</th>43                </tr>44              </thead>45              <tbody>46                {rows.map((a) => (47                  <tr key={a.id}>48                    <td className="whitespace-nowrap">{fmtDate(a.approval_date)}</td>49                    <td>50                      <Link className="ci-link font-medium" href={`/drug/${a.drug_slug}#approvals`}>51                        {a.drug_name}52                      </Link>53                      {a.approval_type ? <span className="ml-1.5 ci-mono text-[11px] text-ink-3">{a.approval_type}</span> : null}54                    </td>55                    <td className="whitespace-nowrap">56                      <span className="text-ink-2">{a.authority}</span> <Badge mono tone="outline">{a.jurisdiction}</Badge>57                    </td>58                    <td className="text-[12.5px]">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">not stated in this record</span>66                      ) : null}67                    </td>68                    <td>69                      <StatusBadge status={a.status} />70                    </td>71                    <td>72                      <SourceBadge compact p={{ sourceSlug: a.source_slug, sourceName: a.source_name }} />73                    </td>74                  </tr>75                ))}76              </tbody>77            </table>78          </div>79          <div className="mt-2 flex flex-wrap items-center gap-1.5 text-[12px] text-ink-3">80            <ClaimBadge kind="regulatory" />81            <span>Jurisdiction-specific and time-bound; not a treatment recommendation.</span>82          </div>83          <Freshness dataUpdatedAt={rows.map((r) => toDate(r.updated_at)).filter((d): d is Date => !!d).sort((a, b) => b.getTime() - a.getTime())[0] ?? null} sourceUpdatedAt={rows[0]?.approval_date ?? null} />84        </>85      )}86    </Section>87  );88}89