spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { PageHeader } from '@/components/ui/section';4import { EmptyState } from '@/components/ui/empty-state';5import { Badge } from '@/components/ui/badge';6import { Pagination } from '@/components/ui/pagination';7import { listDrugs } from '@/lib/queries/drugs';8import { fmtInt, humanize } from '@/lib/format';9import { str, int, withParams, type SP } from '@/lib/search-params';1011export const metadata: Metadata = { title: 'Drugs', description: 'Oncology drugs with jurisdiction-aware approvals, curated evidence and trials.' };12// Filtered list: rendered per request (searchParams); 600 s is the CDN/ISR hint shared by all list pages.13export const revalidate = 600;14const PAGE_SIZE = 50;1516export default async function DrugsPage({ searchParams }: { searchParams: Promise<SP> }) {17 const sp = await searchParams;18 const q = str(sp, 'q');19 const kind = str(sp, 'kind');20 const page = int(sp, 'page', 1, 1, 100_000);21 const { rows, total, kinds } = await listDrugs({ q, kind, page, pageSize: PAGE_SIZE });22 const href = (o: Record<string, string | number | null | undefined>) => `/drugs${withParams({ q, kind }, o)}`;23 return (24 <div>25 <PageHeader kicker="Drugs" title="Drugs" lede="One entity per molecule (INN preferred); brand names and development codes are aliases. Approvals are always shown with jurisdiction and indication." />26 <form method="get" action="/drugs" className="flex flex-wrap items-end gap-2 border-y border-rule py-3 text-[13.5px]">27 <label className="flex flex-col gap-1">28 <span className="ci-kicker">Name or alias</span>29 <input name="q" defaultValue={q} placeholder="e.g. osimertinib, Keytruda, AZD9291" className="border border-rule-strong bg-white px-2 py-1.5 outline-none focus:border-accent" />30 </label>31 <label className="flex flex-col gap-1">32 <span className="ci-kicker">Kind</span>33 <select name="kind" defaultValue={kind} className="border border-rule-strong bg-white px-2 py-1.5">34 <option value="">Any</option>35 {kinds.map((k) => (36 <option key={k.kind} value={k.kind}>37 {humanize(k.kind)} ({k.n})38 </option>39 ))}40 </select>41 </label>42 <button type="submit" className="border border-ink bg-ink px-3 py-1.5 text-paper hover:bg-ink-2">43 Apply44 </button>45 </form>46 <p className="mt-3 text-[13px] text-ink-2">47 <span className="ci-num font-medium text-ink">{fmtInt(total)}</span> drugs48 </p>49 {rows.length === 0 ? (50 <div className="mt-3">51 <EmptyState title={total === 0 && !q && !kind ? 'Drugs not yet available' : 'No drug matches'} knows={[{ label: 'Cancers explorer', href: '/cancers' }, { label: 'Sources', href: '/sources' }]}>52 {total === 0 && !q && !kind ? 'Drug entities are created by the CIViC therapy, NCIt and regulatory connectors; none has run on this environment yet.' : 'Try another name, brand or development code.'}53 </EmptyState>54 </div>55 ) : (56 <>57 <div className="ci-table-wrap mt-3">58 <table className="ci-table">59 <thead>60 <tr>61 <th>Drug</th>62 <th>Kind</th>63 <th>Mechanism</th>64 <th className="num">Approvals</th>65 <th className="num">Evidence items</th>66 <th className="num">Trials</th>67 <th>Identifiers</th>68 </tr>69 </thead>70 <tbody>71 {rows.map((d) => (72 <tr key={d.id}>73 <td className="min-w-[180px]">74 <Link className="ci-link font-medium" href={`/drug/${d.slug}`}>75 {d.name}76 </Link>77 </td>78 <td>{d.kind ? <Badge>{humanize(d.kind)}</Badge> : '—'}</td>79 <td className="max-w-[320px] text-[12.5px] text-ink-2">{d.mechanism ?? '—'}</td>80 <td className="num">{fmtInt(d.approval_count ?? 0)}</td>81 <td className="num">{fmtInt(d.evidence_count ?? 0)}</td>82 <td className="num">{fmtInt(d.trial_count ?? 0)}</td>83 <td className="ci-mono text-[11px] text-ink-3">{[d.ncit_code, d.chembl_id, d.drugbank_id].filter(Boolean).join(' · ') || '—'}</td>84 </tr>85 ))}86 </tbody>87 </table>88 </div>89 <Pagination page={page} pageSize={PAGE_SIZE} total={total} hrefFor={(p) => href({ page: p === 1 ? '' : p })} />90 </>91 )}92 </div>93 );94}95