{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "close-controls-consolidation",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Field-level diffs on every financial object.",
  "files": [
    {
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport { formatTimestamp } from \"@/lib/ledger/fiscal-period\";\nimport { UserCircleIcon } from \"@/components/ledger/icons\";\n\nexport type AuditRecord = {\n  id: string;\n  at: Date;\n  actor: string;\n  role: string;\n  /** Delegated and support access renders explicitly, never silently. */\n  onBehalfOf?: string;\n  action: string;\n  /** Field-level diffs: \"Amount changed from $1,240.00 to $1,420.00\". */\n  diffs?: { field: string; from: string; to: string }[];\n};\n\nexport interface AuditTrailProps {\n  className?: string;\n  records: AuditRecord[];\n  /** Server UTC, with the zone displayed. */\n  timeZone?: string;\n}\n\n/**\n * A timeline on **every** financial object, not a hidden admin log.\n * Append-only. Filters narrow the view and say so.\n */\nexport function AuditTrail({ records, timeZone = \"UTC\", className }: AuditTrailProps) {\n  const [filter, setFilter] = React.useState(\"\");\n  const filtered = filter\n    ? records.filter(\n        (r) =>\n          r.actor.toLowerCase().includes(filter.toLowerCase()) ||\n          r.action.toLowerCase().includes(filter.toLowerCase()),\n      )\n    : records;\n\n  return (\n    <div className={cn(\"rounded-xl border border-border bg-surface p-6\", className)}>\n      <div className=\"flex flex-wrap items-baseline justify-between gap-3\">\n        <h3 className=\"text-title-sm font-medium\">Audit trail</h3>\n        <input\n          aria-label=\"Filter audit trail\"\n          className=\"h-8 w-56 rounded-sm border border-border bg-surface px-2 text-label outline-none focus-visible:border-accent\"\n          onChange={(e) => setFilter(e.target.value)}\n          placeholder=\"Filter by actor or action\"\n          value={filter}\n        />\n      </div>\n\n      {filter ? (\n        <p aria-live=\"polite\" className=\"mt-1 text-caption text-text-secondary\">\n          Showing <span className=\"numeric\">{filtered.length}</span> of{\" \"}\n          <span className=\"numeric\">{records.length}</span> events.\n        </p>\n      ) : (\n        <p className=\"mt-1 numeric text-caption text-text-tertiary\">{records.length} events · append-only</p>\n      )}\n\n      <ol className=\"mt-4 space-y-4\">\n        {filtered.map((r) => (\n          <li className=\"border-l-2 border-border pl-4\" key={r.id}>\n            <p className=\"text-body\">{r.action}</p>\n            {r.diffs?.length ? (\n              <ul className=\"mt-1 space-y-0.5\">\n                {r.diffs.map((d) => (\n                  <li className=\"text-caption text-text-secondary\" key={d.field}>\n                    <span className=\"font-medium text-text-primary\">{d.field}</span> changed from{\" \"}\n                    <span className=\"numeric\">{d.from}</span> to <span className=\"numeric\">{d.to}</span>\n                  </li>\n                ))}\n              </ul>\n            ) : null}\n            <p className=\"mt-1 flex flex-wrap items-center gap-1 text-caption text-text-tertiary\">\n              <span className=\"text-text-secondary\">\n                {r.actor} ({r.role})\n              </span>\n              {r.onBehalfOf ? (\n                <span className=\"inline-flex items-center gap-1 rounded-xs bg-warning-subtle px-1.5 py-0.5 text-warning\">\n                  <UserCircleIcon aria-hidden=\"true\" className=\"size-3\" />\n                  on behalf of {r.onBehalfOf}\n                </span>\n              ) : null}\n              <span aria-hidden=\"true\">·</span>\n              <span className=\"numeric\">{formatTimestamp(r.at, timeZone)}</span>\n            </p>\n          </li>\n        ))}\n      </ol>\n    </div>\n  );\n}\n",
      "path": "components/ledger/audit-trail.tsx",
      "target": "components/ledger/audit-trail.tsx",
      "type": "registry:component"
    }
  ],
  "name": "audit-trail",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/cn",
    "@lopifi/fiscal-period",
    "@lopifi/icons"
  ],
  "title": "AuditTrail",
  "type": "registry:component"
}
