{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "statements",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Statement line → the GL entries behind it.",
  "files": [
    {
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/ledger/cn\";\nimport { formatDateShort } from \"@/lib/ledger/fiscal-period\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { ArrowTopRightOnSquareIcon, CheckIcon, ExclamationTriangleIcon } from \"@/components/ledger/icons\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Reference } from \"@/components/ledger/reference\";\nimport { Rule } from \"@/components/ledger/rule\";\nimport { Sheet } from \"@/components/ledger/sheet\";\n\nexport type GlEntry = {\n  id: string;\n  date: Date;\n  journal: string;\n  account: string;\n  memo: string;\n  amount: MoneyValue;\n  sourceDocument?: string;\n};\n\nexport interface DrilldownSheetProps {\n  entries: GlEntry[];\n  /** Passed in — the component doesn't sum. It compares. */\n  entriesTotal: MoneyValue;\n  lineLabel: string;\n  /** The statement line's value, as reported. */\n  lineValue: MoneyValue;\n  onOpenChange: (open: boolean) => void;\n  open: boolean;\n  /** The resolved period the figure was read from. Shown under the line\n      label rather than concatenated into it — one heading, not a slug. */\n  periodLabel?: string;\n}\n\n/**\n * Statement line → the GL entries behind it. The entries sum to the line and\n * the sum is displayed — that verification is the whole point of drilling\n * down.\n *\n * Two columns, not four: the memo is what identifies an entry to a human, so\n * it leads, and the date, journal, account and source document sit under it\n * as one quiet provenance line. Everything else competing for the same\n * horizontal space is what made this hard to read.\n */\nexport function DrilldownSheet({\n  open,\n  onOpenChange,\n  lineLabel,\n  periodLabel,\n  lineValue,\n  entries,\n  entriesTotal,\n}: DrilldownSheetProps) {\n  const ties = Number(entriesTotal.amount) === Number(lineValue.amount);\n  const difference = Number(entriesTotal.amount) - Number(lineValue.amount);\n\n  return (\n    <Sheet\n      description={\n        periodLabel\n          ? `${periodLabel} · ${entries.length} general ledger entries`\n          : `${entries.length} general ledger entries`\n      }\n      onOpenChange={onOpenChange}\n      open={open}\n      side=\"right\"\n      title={lineLabel}\n    >\n      <table className=\"w-full\">\n        <caption className=\"sr-only\">Entries behind {lineLabel}</caption>\n        <thead>\n          <tr className=\"border-b border-border-subtle\">\n            <th className=\"py-2 text-left text-label text-text-secondary\" scope=\"col\">\n              Entry\n            </th>\n            <th className=\"py-2 text-right text-label text-text-secondary\" scope=\"col\">\n              Amount\n            </th>\n          </tr>\n        </thead>\n        <tbody>\n          {entries.map((e) => (\n            <tr className=\"border-b border-border-subtle align-top\" key={e.id}>\n              <th className=\"py-2.5 pr-4 text-left font-normal\" scope=\"row\">\n                <span className=\"block text-body\">{e.memo}</span>\n                {/* Provenance, one line, tertiary: the reader scans memos\n                    and amounts, and only reaches for this when an entry looks\n                    wrong. */}\n                {/* Each separator travels with the item it precedes, so a\n                    wrap at 480px never strands a \"·\" at the end of a line. */}\n                <span className=\"mt-0.5 flex flex-wrap items-center gap-x-1.5 text-caption text-text-tertiary\">\n                  <span className=\"numeric\">{formatDateShort(e.date)}</span>\n                  <span className=\"flex items-center gap-x-1.5 whitespace-nowrap\">\n                    <span aria-hidden=\"true\">·</span>\n                    {/* Not copyable here. A copy control per row is a button\n                        every four lines, and hiding it until hover either\n                        reserves the gap anyway or reflows the line under the\n                        pointer. The entry opens from its source document. */}\n                    <Reference copyable={false} truncate={false} value={e.journal} />\n                  </span>\n                  <span className=\"whitespace-nowrap\">\n                    <span aria-hidden=\"true\" className=\"mr-1.5\">\n                      ·\n                    </span>\n                    {e.account}\n                  </span>\n                  {e.sourceDocument ? (\n                    <span className=\"whitespace-nowrap\">\n                      <span aria-hidden=\"true\" className=\"mr-1.5\">\n                        ·\n                      </span>\n                      <a\n                        className=\"inline-flex items-center gap-1 align-middle text-accent hover:underline\"\n                        href=\"#source\"\n                      >\n                        <ArrowTopRightOnSquareIcon aria-hidden=\"true\" className=\"size-3\" />\n                        {e.sourceDocument}\n                      </a>\n                    </span>\n                  ) : null}\n                </span>\n              </th>\n              <td className=\"py-2.5 text-right\">\n                <Money accounting value={e.amount} />\n              </td>\n            </tr>\n          ))}\n        </tbody>\n        <tfoot>\n          <tr>\n            <td />\n            <td>\n              <Rule variant=\"subtotal\" />\n            </td>\n          </tr>\n          <tr>\n            <th className=\"py-2 text-left text-label font-medium\" scope=\"row\">\n              Entries total\n            </th>\n            <td className=\"py-2 text-right\">\n              <Money accounting size=\"md\" value={entriesTotal} />\n            </td>\n          </tr>\n          <tr>\n            <th className=\"py-2 text-left text-label text-text-secondary\" scope=\"row\">\n              Statement line\n            </th>\n            <td className=\"py-2 text-right\">\n              <Money accounting tone=\"secondary\" value={lineValue} />\n            </td>\n          </tr>\n        </tfoot>\n      </table>\n\n      {/* When it ties this is a receipt, not an announcement — one quiet\n          line. When it does not, it is the most important thing on screen. */}\n      <p\n        className={cn(\n          \"mt-4 flex items-center gap-2 text-body\",\n          ties ? \"text-text-secondary\" : \"rounded-sm bg-negative-subtle px-3 py-2 text-negative\",\n        )}\n        role={ties ? undefined : \"alert\"}\n      >\n        {ties ? (\n          <CheckIcon aria-hidden=\"true\" className=\"size-4 shrink-0 text-positive\" />\n        ) : (\n          <ExclamationTriangleIcon aria-hidden=\"true\" className=\"size-4 shrink-0\" />\n        )}\n        {ties ? (\n          \"The entries sum to the statement line.\"\n        ) : (\n          <>\n            The entries do not sum to the statement line — out by{\" \"}\n            <Money tone=\"inherit\" value={{ amount: difference, currency: lineValue.currency }} />.\n          </>\n        )}\n      </p>\n    </Sheet>\n  );\n}\n",
      "path": "components/ledger/drilldown-sheet.tsx",
      "target": "components/ledger/drilldown-sheet.tsx",
      "type": "registry:component"
    }
  ],
  "name": "drilldown-sheet",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/cn",
    "@lopifi/fiscal-period",
    "@lopifi/icons",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/reference",
    "@lopifi/rule",
    "@lopifi/sheet"
  ],
  "title": "DrilldownSheet",
  "type": "registry:component"
}
