{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "wallet-dashboard",
  "title": "Wallet dashboard",
  "description": "A wallet dashboard in Tiles: balance, a fanned stack of three payment cards, cash flow bars, a spending donut and subscriptions.",
  "registryDependencies": [
    "utils",
    "@manniche/chart-kit",
    "@manniche/data-tile",
    "@manniche/bar-chart",
    "@manniche/card-stack",
    "@manniche/donut"
  ],
  "files": [
    {
      "path": "registry/manniche/wallet-dashboard/wallet-dashboard.tsx",
      "content": "// A wallet screen in Tiles: balance, a fanned stack of payment cards, cash flow, a spending donut and subscriptions.\n// Compose-only: every chart is a Manniche primitive; the screen owns layout and copy.\nimport { useState } from 'react'\nimport { cn } from '@/lib/utils'\nimport { BigNumber, DeltaPill, Pills } from '@/registry/manniche/chart-kit/chart-kit'\nimport { formatValue, type ValueFormat } from '@/registry/manniche/chart-kit/chart-utils'\nimport { DataTile } from '@/registry/manniche/data-tile/data-tile'\nimport { BarChart, type BarPoint } from '@/registry/manniche/bar-chart/bar-chart'\nimport { CardStack, type StackCard } from '@/registry/manniche/card-stack/card-stack'\nimport { Donut, type DonutDatum } from '@/registry/manniche/donut/donut'\n\nexport type WalletSubscription = { id: string; name: string; amount: number; renews: string }\nexport type WalletPeriod = { id: string; label: string; long: string; flow: BarPoint[]; spending: DonutDatum[] }\n\nexport type WalletDashboardProps = {\n  data: {\n    balance: number\n    change: number\n    cards: StackCard[]\n    periods: WalletPeriod[]\n    subscriptions: WalletSubscription[]\n  }\n  format?: ValueFormat\n  title?: string\n  note?: string\n  labels?: {\n    balance?: string\n    cards?: string\n    cashFlow?: string\n    spending?: string\n    subscriptions?: string\n    period?: string\n    perMonth?: string\n    renews?: (date: string) => string\n    monthlyTotal?: string\n  }\n  /** Reserved for date-aware content; the screen itself is static. */\n  now?: Date\n  className?: string\n}\n\nconst DEFAULT_FORMAT: ValueFormat = { currency: 'EUR', decimals: 2 }\n\n/** Wallet overview: balance, card stack, cash flow, spending by category and recurring payments. */\nexport function WalletDashboard({ data, format = DEFAULT_FORMAT, title = 'Wallet', note = 'Example data.', labels, className }: WalletDashboardProps) {\n  const l = {\n    balance: 'Total balance',\n    cards: 'Cards',\n    cashFlow: 'Cash flow',\n    spending: 'Spending',\n    subscriptions: 'Subscriptions',\n    period: 'Period',\n    perMonth: 'a month',\n    renews: (d: string) => `Renews ${d}`,\n    monthlyTotal: 'Monthly total',\n    ...labels,\n  }\n  const [periodId, setPeriodId] = useState(data.periods[0]?.id ?? '')\n  const period = data.periods.find((p) => p.id === periodId) ?? data.periods[0]\n  const monthly = data.subscriptions.reduce((s, x) => s + x.amount, 0)\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        <div className=\"@3xl:col-span-6 @6xl:col-span-12\">\n          <h1 className=\"text-[22px] leading-tight font-semibold tracking-tight\">{title}</h1>\n        </div>\n        <DataTile title={l.balance} className=\"@3xl:col-span-3 @6xl:col-span-4\" footer={<span>{note}</span>}>\n          <BigNumber value={data.balance} format={format} size=\"lg\" />\n          <div className=\"mt-3\">\n            <DeltaPill value={data.change} format={{ decimals: 1, suffix: '%', sign: true }} />\n          </div>\n        </DataTile>\n        <DataTile title={l.cards} className=\"@3xl:col-span-3 @6xl:col-span-4\">\n          <CardStack data={data.cards} label={l.cards} format={format} />\n        </DataTile>\n        <DataTile\n          title={l.cashFlow}\n          className=\"@3xl:col-span-6 @6xl:col-span-4\"\n          action={<Pills label={l.period} options={data.periods.map(({ id, label }) => ({ id, label }))} value={periodId} onChange={setPeriodId} />}\n        >\n          {period && <BarChart data={period.flow} label={`${l.cashFlow}, ${period.long}`} format={{ ...format, decimals: 0 }} />}\n        </DataTile>\n        <DataTile title={l.spending} className=\"@3xl:col-span-3 @6xl:col-span-6\">\n          {period && <Donut data={period.spending} label={`${l.spending}, ${period.long}`} format={format} />}\n        </DataTile>\n        <DataTile\n          title={l.subscriptions}\n          className=\"@3xl:col-span-3 @6xl:col-span-6\"\n          footer={\n            <span>\n              {l.monthlyTotal}: <strong className=\"tabular-nums\">{formatValue(monthly, { ...format, decimals: 2 })}</strong>\n            </span>\n          }\n        >\n          {/* The tile's heading names the list; a hidden <li> as its label would be counted as an extra item. */}\n          <ul aria-label={l.subscriptions} className=\"divide-y divide-border\">\n            {data.subscriptions.map((s) => (\n              <li key={s.id} className=\"flex items-center gap-3 py-3 text-sm\">\n                <span aria-hidden className=\"grid size-9 shrink-0 place-items-center rounded-full bg-muted text-xs font-semibold text-muted-foreground\">\n                  {s.name.slice(0, 2).toUpperCase()}\n                </span>\n                <span className=\"min-w-0 flex-1\">\n                  <span className=\"block truncate font-medium\">{s.name}</span>\n                  <span className=\"block text-xs text-muted-foreground\">{l.renews(s.renews)}</span>\n                </span>\n                <span className=\"font-medium tabular-nums\">\n                  {formatValue(s.amount, { ...format, decimals: 2 })}\n                  <span className=\"sr-only\"> {l.perMonth}</span>\n                </span>\n              </li>\n            ))}\n          </ul>\n        </DataTile>\n      </div>\n    </div>\n  )\n}\n\nexport default WalletDashboard\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}