{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "allergy-list",
  "title": "Allergy list",
  "description": "Recorded allergies and intolerances with reaction, severity and verification, telling not recorded from no known allergies.",
  "dependencies": [
    "cn@^0.5.0"
  ],
  "registryDependencies": [
    "table"
  ],
  "files": [
    {
      "path": "registry/base-nova/allergy-list/allergy-list.tsx",
      "content": "import * as React from \"react\"\nimport { cn } from \"cn\"\n\nimport {\n  Table,\n  TableBody,\n  TableCaption,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\"\nimport { IconPlaceholder } from \"@/icons/icon-placeholder\"\n\nexport type AllergySeverity = \"mild\" | \"moderate\" | \"severe\"\n\nexport type Allergy = {\n  id: string\n  /** What the patient reacts to, such as \"Penicillin\". */\n  substance: string\n  /** An allergy is immune-mediated; an intolerance, such as nausea, is not. */\n  type?: \"allergy\" | \"intolerance\"\n  /** What happened, such as \"Anaphylaxis\" or \"Rash\". */\n  reactions?: readonly string[]\n  /** How bad the worst reaction was. */\n  severity?: AllergySeverity\n  /** Whether it has been confirmed, by testing or a clear history. */\n  verification?: \"confirmed\" | \"unconfirmed\"\n  /** An inactive or resolved allergy shows muted, with its status. */\n  status?: \"active\" | \"inactive\" | \"resolved\"\n  /** An ISO 8601 date. */\n  recordedDate?: string\n}\n\n/**\n * The allergies, `\"none-known\"` once \"no known allergies\" has been recorded,\n * or nothing (or an empty list) when they have not been asked about.\n */\nexport type AllergyRecord = readonly Allergy[] | \"none-known\"\n\nexport type AllergyListProps = React.ComponentProps<\"div\"> & {\n  /** The allergies, `\"none-known\"`, or nothing when not recorded. */\n  allergies?: AllergyRecord\n  /** Names the table for assistive technology and shows below it. */\n  caption?: React.ReactNode\n  /** Defaults to a UTC date such as \"Oct 1, 2026\". */\n  formatDate?: (date: string) => string\n}\n\nexport const allergySeverityLabels: Record<AllergySeverity, string> = {\n  mild: \"Mild\",\n  moderate: \"Moderate\",\n  severe: \"Severe\",\n}\n\n/**\n * The active allergies for a summary such as the patient banner's: each\n * substance with its first reaction in brackets, `\"none-known\"` when every\n * recorded allergy has resolved, and nothing when none were recorded.\n */\nexport function allergySummary(\n  allergies?: AllergyRecord\n): readonly string[] | \"none-known\" | undefined {\n  if (allergies === \"none-known\") return allergies\n  if (!allergies?.length) return undefined\n  const active = allergies.filter(\n    ({ status = \"active\" }) => status === \"active\"\n  )\n  if (!active.length) return \"none-known\"\n  return active.map(({ substance, reactions }) =>\n    reactions?.length ? `${substance} (${reactions[0]})` : substance\n  )\n}\n\nconst dateFormat = new Intl.DateTimeFormat(\"en-US\", {\n  dateStyle: \"medium\",\n  timeZone: \"UTC\",\n})\n\n// A fixed locale and time zone render the same on the server and the client.\nfunction defaultFormatDate(date: string) {\n  const time = Date.parse(date)\n  return Number.isNaN(time) ? date : dateFormat.format(time)\n}\n\nfunction Severity({ severity }: { severity?: AllergySeverity }) {\n  if (!severity)\n    return (\n      <>\n        <span aria-hidden className=\"text-muted-foreground\">\n          —\n        </span>\n        <span className=\"sr-only\">Not recorded</span>\n      </>\n    )\n  return (\n    <span\n      data-slot=\"allergy-severity\"\n      data-severity={severity}\n      className={cn(\n        \"inline-flex items-center gap-1\",\n        severity === \"severe\" && \"font-semibold\",\n        severity === \"mild\" && \"text-muted-foreground\"\n      )}\n    >\n      {severity === \"severe\" && (\n        <IconPlaceholder\n          lucide=\"TriangleAlertIcon\"\n          tabler=\"IconAlertTriangle\"\n          hugeicons=\"Alert02Icon\"\n          phosphor=\"WarningIcon\"\n          remixicon=\"RiErrorWarningLine\"\n          aria-hidden=\"true\"\n          className=\"size-4 shrink-0\"\n        />\n      )}\n      {allergySeverityLabels[severity]}\n    </span>\n  )\n}\n\nfunction EmptyRow({\n  allergies,\n  columns,\n}: {\n  allergies?: AllergyRecord\n  columns: number\n}) {\n  const noneKnown = allergies === \"none-known\"\n  return (\n    <TableRow>\n      <TableCell\n        colSpan={columns}\n        data-slot=\"allergy-list-empty\"\n        data-state={noneKnown ? \"none-known\" : \"not-recorded\"}\n        className={cn(\n          \"h-16 text-center\",\n          !noneKnown && \"text-muted-foreground\"\n        )}\n      >\n        <span className=\"inline-flex items-center gap-1.5\">\n          {noneKnown ? (\n            <IconPlaceholder\n              lucide=\"CheckIcon\"\n              tabler=\"IconCheck\"\n              hugeicons=\"Tick02Icon\"\n              phosphor=\"CheckIcon\"\n              remixicon=\"RiCheckLine\"\n              aria-hidden=\"true\"\n              className=\"size-4 shrink-0 text-muted-foreground\"\n            />\n          ) : (\n            <IconPlaceholder\n              lucide=\"CircleHelpIcon\"\n              tabler=\"IconHelpCircle\"\n              hugeicons=\"HelpCircleIcon\"\n              phosphor=\"QuestionIcon\"\n              remixicon=\"RiQuestionLine\"\n              aria-hidden=\"true\"\n              className=\"size-4 shrink-0\"\n            />\n          )}\n          {noneKnown ? \"No known allergies\" : \"Allergies not recorded\"}\n        </span>\n      </TableCell>\n    </TableRow>\n  )\n}\n\nexport function AllergyList({\n  allergies,\n  caption,\n  formatDate = defaultFormatDate,\n  className,\n  ...props\n}: AllergyListProps) {\n  const rows = Array.isArray(allergies) ? (allergies as readonly Allergy[]) : []\n  return (\n    <div\n      data-slot=\"allergy-list\"\n      className={cn(\"@container w-full min-w-0\", className)}\n      {...props}\n    >\n      <Table>\n        {caption && <TableCaption>{caption}</TableCaption>}\n        <TableHeader>\n          <TableRow>\n            <TableHead scope=\"col\">Substance</TableHead>\n            <TableHead scope=\"col\">Reaction</TableHead>\n            <TableHead scope=\"col\">Severity</TableHead>\n            <TableHead scope=\"col\" className=\"hidden @lg:table-cell\">\n              Recorded\n            </TableHead>\n          </TableRow>\n        </TableHeader>\n        <TableBody>\n          {rows.length === 0 && <EmptyRow allergies={allergies} columns={4} />}\n          {rows.map((allergy) => {\n            const status = allergy.status ?? \"active\"\n            const details = [\n              allergy.type === \"intolerance\" ? \"Intolerance\" : null,\n              allergy.verification === \"unconfirmed\" ? \"Unconfirmed\" : null,\n              status === \"active\"\n                ? null\n                : status === \"resolved\"\n                  ? \"Resolved\"\n                  : \"Inactive\",\n            ].filter(Boolean)\n            return (\n              <TableRow\n                key={allergy.id}\n                data-status={status}\n                className={cn(status !== \"active\" && \"text-muted-foreground\")}\n              >\n                <TableHead\n                  scope=\"row\"\n                  className=\"h-auto py-2 font-normal whitespace-normal\"\n                >\n                  <span\n                    className={cn(\n                      \"font-medium\",\n                      status !== \"active\" && \"line-through\"\n                    )}\n                  >\n                    {allergy.substance}\n                  </span>\n                  {details.length > 0 && (\n                    <span className=\"block text-xs text-muted-foreground\">\n                      {details.join(\" · \")}\n                    </span>\n                  )}\n                  {allergy.recordedDate && (\n                    <span className=\"block text-xs text-muted-foreground @lg:hidden\">\n                      Recorded {formatDate(allergy.recordedDate)}\n                    </span>\n                  )}\n                </TableHead>\n                <TableCell className=\"whitespace-normal\">\n                  {allergy.reactions?.length ? (\n                    allergy.reactions.join(\", \")\n                  ) : (\n                    <>\n                      <span aria-hidden className=\"text-muted-foreground\">\n                        —\n                      </span>\n                      <span className=\"sr-only\">Not recorded</span>\n                    </>\n                  )}\n                </TableCell>\n                <TableCell>\n                  <Severity severity={allergy.severity} />\n                </TableCell>\n                <TableCell className=\"hidden text-muted-foreground tabular-nums @lg:table-cell\">\n                  {allergy.recordedDate ? formatDate(allergy.recordedDate) : \"\"}\n                </TableCell>\n              </TableRow>\n            )\n          })}\n        </TableBody>\n      </Table>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Allergy list docs: https://organui.com/docs/components/allergy-list",
  "categories": [
    "clinical"
  ],
  "type": "registry:ui"
}