{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "accounting-core-gl",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "One shell, seven presets. Prepaids through debt.",
  "files": [
    {
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useScrollEdges } from \"@/hooks/ledger/use-scroll-edges\";\nimport type { ScheduleType } from \"@/lib/ledger/accounting.types\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport { formatDate } from \"@/lib/ledger/fiscal-period\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Rule } from \"@/components/ledger/rule\";\n\nexport type ScheduleRow = {\n  id: string;\n  description: string;\n  startDate: Date;\n  endDate: Date;\n  original: MoneyValue;\n  opening: MoneyValue;\n  periodMovement: MoneyValue;\n  closing: MoneyValue;\n  /** Fully-released items are shown, then dropped — silently disappearing\n      rows make a schedule impossible to follow. */\n  fullyReleased?: boolean;\n  extra?: Record<string, React.ReactNode>;\n};\n\n/** Columns are type-specific; the shell, totals and tie-out are shared. */\nconst PRESETS: Record<ScheduleType, { title: string; movementLabel: string; extraColumns: string[] }> = {\n  accrual: { extraColumns: [], movementLabel: \"Released this period\", title: \"Accrued liabilities\" },\n  debt: { extraColumns: [\"Rate\", \"Maturity\"], movementLabel: \"Principal movement\", title: \"Debt\" },\n  \"deferred-revenue\": {\n    extraColumns: [\"Obligation\"],\n    movementLabel: \"Recognized this period\",\n    title: \"Deferred revenue\",\n  },\n  \"fixed-asset\": {\n    extraColumns: [\"In service\", \"Life\", \"Method\"],\n    movementLabel: \"Depreciation this period\",\n    title: \"Fixed assets\",\n  },\n  intangible: {\n    extraColumns: [\"Life\", \"Method\"],\n    movementLabel: \"Amortization this period\",\n    title: \"Intangible assets\",\n  },\n  lease: {\n    extraColumns: [\"Classification\", \"Discount rate\"],\n    movementLabel: \"Liability movement\",\n    title: \"Leases\",\n  },\n  prepaid: { extraColumns: [], movementLabel: \"Amortized this period\", title: \"Prepaid expenses\" },\n};\n\nexport interface ScheduleTableProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  /** Caps the scroller so the header and the item column can freeze. */\n  maxHeight?: number;\n  onDrilldown?: (row: ScheduleRow) => void;\n  periodLabel: string;\n  rows: ScheduleRow[];\n  totals: { opening: MoneyValue; movement: MoneyValue; closing: MoneyValue };\n  type: ScheduleType;\n}\n\n/**\n * One component, seven presets. Every row drills to the entries behind it,\n * and they sum to the row.\n */\nexport const ScheduleTable = React.forwardRef<HTMLDivElement, ScheduleTableProps>(\n  ({ className, type, periodLabel, rows, totals, onDrilldown, maxHeight = 520, ...props }, ref) => {\n    const scroller = useScrollEdges<HTMLDivElement>();\n    const preset = PRESETS[type];\n    const [showReleased, setShowReleased] = React.useState(true);\n    const visible = showReleased ? rows : rows.filter((r) => !r.fullyReleased);\n    const releasedCount = rows.filter((r) => r.fullyReleased).length;\n\n    return (\n      <div className={cn(\"space-y-3\", className)} ref={ref} {...props}>\n        <div className=\"rounded-xl border border-border bg-surface\">\n          <div className=\"px-6 pb-4 pt-6\">\n            <p className=\"text-title-sm font-medium\">{preset.title}</p>\n            <p className=\"text-caption text-text-secondary\">{periodLabel}</p>\n          </div>\n          <div className=\"ledger-frozen scrollbar-thin\" ref={scroller} style={{ maxHeight }}>\n            <table className=\"w-full min-w-[900px]\">\n              <caption className=\"sr-only\">\n                {preset.title}. {periodLabel}\n              </caption>\n              <thead>\n                <tr className=\"border-b border-border-subtle\">\n                  <th\n                    className=\"py-3 pl-6 pr-3 text-left text-label font-medium text-text-secondary\"\n                    scope=\"col\"\n                  >\n                    Item\n                  </th>\n                  <th className=\"px-3 py-3 text-left text-label font-medium text-text-secondary\" scope=\"col\">\n                    Term\n                  </th>\n                  {preset.extraColumns.map((c) => (\n                    <th\n                      className=\"px-3 py-3 text-left text-label font-medium text-text-secondary\"\n                      key={c}\n                      scope=\"col\"\n                    >\n                      {c}\n                    </th>\n                  ))}\n                  <th className=\"px-3 py-3 text-right text-label font-medium text-text-secondary\" scope=\"col\">\n                    Opening\n                  </th>\n                  <th className=\"px-3 py-3 text-right text-label font-medium text-text-secondary\" scope=\"col\">\n                    {preset.movementLabel}\n                  </th>\n                  <th\n                    className=\"py-3 pl-3 pr-6 text-right text-label font-medium text-text-secondary\"\n                    scope=\"col\"\n                  >\n                    Closing\n                  </th>\n                </tr>\n              </thead>\n              <tbody>\n                {visible.map((r) => (\n                  <tr\n                    className={cn(\n                      \"border-b border-border-subtle transition-colors duration-1 ease-standard hover:bg-surface-hover\",\n                      r.fullyReleased && \"text-text-secondary\",\n                    )}\n                    key={r.id}\n                  >\n                    <th className=\"py-3 pl-6 pr-3 text-left font-normal\" scope=\"row\">\n                      <button\n                        className=\"text-left text-body hover:underline\"\n                        onClick={() => onDrilldown?.(r)}\n                        type=\"button\"\n                      >\n                        {r.description}\n                      </button>\n                      {r.fullyReleased ? (\n                        <span className=\"ml-2 text-caption text-text-tertiary\">Fully released</span>\n                      ) : null}\n                    </th>\n                    <td className=\"numeric px-3 py-3 text-caption text-text-secondary\">\n                      {formatDate(r.startDate)} – {formatDate(r.endDate)}\n                    </td>\n                    {preset.extraColumns.map((c) => (\n                      <td className=\"px-3 py-3 text-caption text-text-secondary\" key={c}>\n                        {r.extra?.[c] ?? \"—\"}\n                      </td>\n                    ))}\n                    <td className=\"px-3 py-3 text-right\">\n                      <Money accounting value={r.opening} />\n                    </td>\n                    <td className=\"px-3 py-3 text-right\">\n                      <Money accounting value={r.periodMovement} />\n                    </td>\n                    <td className=\"py-3 pl-3 pr-6 text-right\">\n                      <Money accounting value={r.closing} />\n                    </td>\n                  </tr>\n                ))}\n              </tbody>\n              <tfoot>\n                <tr>\n                  <td colSpan={2 + preset.extraColumns.length} />\n                  <td className=\"px-3\">\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                  <td className=\"px-3\">\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                  <td className=\"pr-6\">\n                    <Rule variant=\"subtotal\" />\n                  </td>\n                </tr>\n                <tr>\n                  <th className=\"py-3 pl-6 pr-3 text-left text-label font-medium\" scope=\"row\">\n                    Total\n                  </th>\n                  <td colSpan={1 + preset.extraColumns.length} />\n                  <td className=\"px-3 py-3 text-right\">\n                    <Money accounting size=\"md\" value={totals.opening} />\n                  </td>\n                  <td className=\"px-3 py-3 text-right\">\n                    <Money accounting size=\"md\" value={totals.movement} />\n                  </td>\n                  <td className=\"py-3 pl-3 pr-6 text-right\">\n                    <Money accounting size=\"md\" value={totals.closing} />\n                  </td>\n                </tr>\n                <tr>\n                  <td colSpan={4 + preset.extraColumns.length} />\n                  <td className=\"pr-6\">\n                    <Rule variant=\"total\" />\n                  </td>\n                </tr>\n              </tfoot>\n            </table>\n          </div>\n        </div>\n\n        {releasedCount > 0 ? (\n          <button\n            aria-pressed={showReleased}\n            className=\"text-caption font-medium text-accent hover:underline\"\n            onClick={() => setShowReleased((v) => !v)}\n            type=\"button\"\n          >\n            {showReleased ? \"Hide\" : \"Show\"} {releasedCount} fully released item\n            {releasedCount === 1 ? \"\" : \"s\"}\n          </button>\n        ) : null}\n      </div>\n    );\n  },\n);\nScheduleTable.displayName = \"ScheduleTable\";\n\nexport { PRESETS as SCHEDULE_PRESETS };\n",
      "path": "components/ledger/schedule-table.tsx",
      "target": "components/ledger/schedule-table.tsx",
      "type": "registry:component"
    }
  ],
  "name": "schedule-table",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/accounting.types",
    "@lopifi/cn",
    "@lopifi/fiscal-period",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/rule",
    "@lopifi/use-scroll-edges"
  ],
  "title": "ScheduleTable",
  "type": "registry:component"
}
