{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "business-finance",
  "title": "Business finance",
  "description": "A business finance dashboard in Tiles: income against payments, a growth ring against target, a stock sparkline, activity and a verification checklist.",
  "registryDependencies": [
    "utils",
    "@manniche/chart-kit",
    "@manniche/data-tile",
    "@manniche/area-chart",
    "@manniche/donut",
    "@manniche/sparkline",
    "@manniche/transaction-list"
  ],
  "files": [
    {
      "path": "registry/manniche/business-finance/business-finance.tsx",
      "content": "// A business finance screen in Tiles: income against payments, a growth ring, a stock sparkline, activity and a\n// verification checklist.\nimport { cn } from '@/lib/utils'\nimport { BigNumber, DeltaPill } from '@/registry/manniche/chart-kit/chart-kit'\nimport type { ValueFormat } from '@/registry/manniche/chart-kit/chart-utils'\nimport { DataTile, TileLegend } from '@/registry/manniche/data-tile/data-tile'\nimport { AreaChart, type AreaPoint } from '@/registry/manniche/area-chart/area-chart'\nimport { Donut } from '@/registry/manniche/donut/donut'\nimport { Sparkline } from '@/registry/manniche/sparkline/sparkline'\nimport { TransactionList, type Transaction } from '@/registry/manniche/transaction-list/transaction-list'\n\nexport type VerificationStep = { id: string; label: string; done: boolean; hint?: string }\n\nexport type BusinessFinanceProps = {\n  /** The cash flow, income, growth, stock, activity and verification steps to show. */\n  data: {\n    /** Income per period, with `previous` holding payments for the same period. */\n    cashflow: AreaPoint[]\n    income: number\n    incomeChange: number\n    /** Growth toward the target, 0 to 1. */\n    growth: number\n    growthTarget: string\n    stock: { label: string; value: number }[]\n    stockName: string\n    activity: Transaction[]\n    verification: VerificationStep[]\n  }\n  /** How amounts are formatted. Default euro with no decimals. */\n  format?: ValueFormat\n  /** The page heading (h1). Default \"Business finance\". */\n  title?: string\n  /** Footer text on the cash flow tile. Default \"Example data.\" */\n  note?: string\n  /** \"YYYY-MM-DD\" of today, so the activity list reads \"Today\" and \"Yesterday\". Default the day of `now`. */\n  today?: string\n  /** The current time. Used for `today` when that is not given. */\n  now?: Date\n  /** Visible text and screen reader text, with English defaults. */\n  labels?: { income?: string; payments?: string; growth?: string; /** Under the share in the growth ring. Default \"of target\". */ ofTarget?: string; stock?: string; activity?: string; verification?: string; stepsDone?: (done: number, total: number) => string; done?: string; todo?: string }\n  /** Classes for the outer container. */\n  className?: string\n}\n\nconst DEFAULT_FORMAT: ValueFormat = { currency: 'EUR', decimals: 0 }\n\n/** Business finance: cash flow, growth ring, stock sparkline, activity and a verification checklist. */\n// \"YYYY-MM-DD\" of `now` in local time, for TransactionList's \"Today\" and \"Yesterday\".\nconst dayOf = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n\nexport function BusinessFinance({ data, format = DEFAULT_FORMAT, title = 'Business finance', note = 'Example data.', today, now, labels, className }: BusinessFinanceProps) {\n  const day = today ?? (now ? dayOf(now) : undefined)\n  const l = {\n    income: 'Income',\n    payments: 'Payments',\n    growth: 'Growth',\n    ofTarget: 'of target',\n    stock: 'Stock',\n    activity: 'Activity',\n    verification: 'Verification',\n    stepsDone: (d: number, t: number) => `${d} of ${t} steps done`,\n    done: 'Done',\n    todo: 'To do',\n    ...labels,\n  }\n  const doneCount = data.verification.filter((s) => s.done).length\n  const pct = Math.round(data.growth * 100)\n  return (\n    <div className={cn('@container w-full font-sans text-foreground', className)}>\n      <div className=\"grid gap-4 @3xl:grid-cols-6 @6xl:grid-cols-12\">\n        <h1 className=\"text-[22px] leading-tight font-semibold tracking-tight @3xl:col-span-6 @6xl:col-span-12\">{title}</h1>\n        <DataTile headingLevel={2}\n          title={`${l.income} / ${l.payments}`}\n          className=\"@3xl:col-span-6 @6xl:col-span-8\"\n          footer={<span>{note}</span>}\n          action={<TileLegend items={[{ label: l.income, color: 'var(--chart-1)' }, { label: l.payments, color: 'var(--chart-3)', shape: 'dashed' }]} />}\n        >\n          <div className=\"flex flex-wrap items-center gap-3\">\n            <BigNumber value={data.income} format={format} size=\"lg\" />\n            <DeltaPill value={data.incomeChange} format={{ decimals: 1, suffix: '%', sign: true }} />\n          </div>\n          <AreaChart className=\"mt-4\" data={data.cashflow} label={`${l.income} and ${l.payments}`} format={format} compare labels={{ previous: l.payments }} />\n        </DataTile>\n        <DataTile headingLevel={2} title={l.growth} inverted className=\"@3xl:col-span-3 @6xl:col-span-4\">\n          {/* One segment against a whole of 100, held highlighted so the centre reads the share of the target rather\n              than \"Total 100%\". */}\n          <Donut\n            data={[{ label: l.growth, value: pct }]}\n            total={100}\n            activeIndex={0}\n            label={`${l.growth}, ${pct} percent of ${data.growthTarget}`}\n            format={{ suffix: '%' }}\n            labels={{ ofTotal: l.ofTarget }}\n          />\n        </DataTile>\n        <DataTile headingLevel={2} title={l.stock} className=\"@3xl:col-span-3 @6xl:col-span-4\">\n          <p className=\"text-sm font-medium text-muted-foreground\">{data.stockName}</p>\n          <Sparkline className=\"mt-3\" data={data.stock} label={`${l.stock}, ${data.stockName}`} height={72} />\n        </DataTile>\n        <DataTile headingLevel={2} title={l.verification} className=\"@3xl:col-span-3 @6xl:col-span-4\" footer={<span>{l.stepsDone(doneCount, data.verification.length)}</span>}>\n          <ol className=\"space-y-3\">\n            {data.verification.map((s) => (\n              <li key={s.id} className=\"flex items-start gap-3 text-sm\">\n                <span\n                  aria-hidden\n                  className={cn('mt-0.5 grid size-5 shrink-0 place-items-center rounded-full border text-[11px]', s.done ? 'border-transparent bg-foreground text-background' : 'border-border text-transparent')}\n                >\n                  ✓\n                </span>\n                <span>\n                  <span className=\"font-medium\">{s.label}</span>\n                  <span className=\"sr-only\"> ({s.done ? l.done : l.todo})</span>\n                  {s.hint && <span className=\"block text-xs text-muted-foreground\">{s.hint}</span>}\n                </span>\n              </li>\n            ))}\n          </ol>\n        </DataTile>\n        <DataTile headingLevel={2} title={l.activity} className=\"@3xl:col-span-6 @6xl:col-span-4\">\n          <TransactionList headingLevel={3} data={data.activity} label={l.activity} today={day} format={{ ...format, decimals: 2 }} limit={4} />\n        </DataTile>\n      </div>\n    </div>\n  )\n}\n\nexport default BusinessFinance\n",
      "type": "registry:component"
    }
  ],
  "meta": {
    "tier": "free",
    "added": "2026-10-06",
    "usedIn": [],
    "props": [
      {
        "name": "data",
        "type": "{ cashflow: AreaPoint[]; income: number; incomeChange: number; growth: number; growthTarget: string; stock: { label: string; value: number }[]; stockName: string; activity: Transaction[]; …",
        "default": null,
        "required": true,
        "description": "The cash flow, income, growth, stock, activity and verification steps to show."
      },
      {
        "name": "format",
        "type": "ValueFormat",
        "default": "DEFAULT_FORMAT",
        "required": false,
        "description": "How amounts are formatted. Default euro with no decimals."
      },
      {
        "name": "title",
        "type": "string",
        "default": "'Business finance'",
        "required": false,
        "description": "The page heading (h1). Default \"Business finance\"."
      },
      {
        "name": "note",
        "type": "string",
        "default": "'Example data.'",
        "required": false,
        "description": "Footer text on the cash flow tile. Default \"Example data.\""
      },
      {
        "name": "today",
        "type": "string",
        "default": null,
        "required": false,
        "description": "\"YYYY-MM-DD\" of today, so the activity list reads \"Today\" and \"Yesterday\". Default the day of `now`."
      },
      {
        "name": "now",
        "type": "Date",
        "default": null,
        "required": false,
        "description": "The current time. Used for `today` when that is not given."
      },
      {
        "name": "labels",
        "type": "{ income?: string; payments?: string; growth?: string; ofTarget?: string; stock?: string; activity?: string; verification?: string; stepsDone?: (done: number, total: number) => string; done?: …",
        "default": null,
        "required": false,
        "description": "Visible text and screen reader text, with English defaults."
      },
      {
        "name": "className",
        "type": "string",
        "default": null,
        "required": false,
        "description": "Classes for the outer container."
      }
    ],
    "a11y": [
      {
        "topic": "screen-reader",
        "text": "An h1 and tiles named by their h2. The legend marks income with a square and payments with a dashed line, as in the chart; the marks are aria-hidden and the names are text. The growth donut is labelled \"Growth, {pct} percent of {target}\". The verification list writes each state \"(Done)\" or \"(To do)\" and the check mark is aria-hidden; the footer reads \"{d} of {t} steps done\". See BigNumber and DeltaPill from chart-kit, area-chart, donut, sparkline and transaction-list (limit 4)."
      }
    ]
  },
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}