{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "spend-control",
  "title": "Spend control",
  "description": "Company spend dashboard in Tiles: an icon rail, an inverted upsell tile, virtual cards with limits, spending bars and a bubble chart of merchants.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "@manniche/chart-kit",
    "@manniche/data-tile",
    "@manniche/bar-chart",
    "@manniche/bubble-chart"
  ],
  "files": [
    {
      "path": "registry/manniche/spend-control/spend-control.tsx",
      "content": "// A spend-control screen: icon rail, an upsell panel, virtual cards, the spending bars and a bubble chart of merchants.\n// Built from the Tiles primitives. All figures are example data passed in as props.\nimport { type LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { BarChart, type BarPoint } from '@/registry/manniche/bar-chart/bar-chart'\nimport { BubbleChart, type BubbleDatum } from '@/registry/manniche/bubble-chart/bubble-chart'\nimport { BigNumber, DeltaPill } from '@/registry/manniche/chart-kit/chart-kit'\nimport { DataTile, TileFact } from '@/registry/manniche/data-tile/data-tile'\n\nexport type SpendCard = { id: string; name: string; last4: string; spent: number; limit: number; frozen?: boolean }\nexport type SpendRailItem = { id: string; label: string; icon: LucideIcon; current?: boolean }\n\nexport type SpendControlData = {\n  rail: SpendRailItem[]\n  cards: SpendCard[]\n  spending: BarPoint[]\n  spendingTotal: number\n  spendingChange: number\n  merchants: BubbleDatum[]\n  upsell: { title: string; text: string; action: string }\n}\n\nexport type SpendControlLabels = {\n  title?: string\n  cards?: string\n  spending?: string\n  merchants?: string\n  limit?: string\n  frozen?: string\n  note?: string\n  navLabel?: string\n}\n\nexport type SpendControlProps = {\n  data: SpendControlData\n  labels?: SpendControlLabels\n  now?: Date\n  currency?: string\n  onUpsell?: () => void\n  className?: string\n}\n\nconst L: Required<SpendControlLabels> = {\n  title: 'Spend control',\n  cards: 'Virtual cards',\n  spending: 'Spending, last 7 days',\n  merchants: 'Merchants by visits and average spend',\n  limit: 'of',\n  frozen: 'Frozen',\n  note: 'Example data.',\n  navLabel: 'Sections',\n}\n\nexport function SpendControl({ data, labels, now, currency = 'EUR', onUpsell, className }: SpendControlProps) {\n  const t = { ...L, ...labels }\n  const money = { currency, decimals: 0 }\n  const date = now?.toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'long' })\n  return (\n    <div className={cn('@container w-full rounded-2xl bg-background p-3 text-foreground', className)}>\n      {/* Container queries match an ancestor, so the row/column switch sits one level in. */}\n      <div className=\"flex flex-col gap-3 @sm:flex-row\">\n        <nav aria-label={t.navLabel} className=\"flex shrink-0 items-center gap-1 self-start overflow-x-auto rounded-xl border border-border bg-card p-1.5 @sm:flex-col @sm:self-stretch\">\n          {data.rail.map((item) => (\n            <a\n              key={item.id}\n              href={`#${item.id}`}\n              aria-label={item.label}\n              title={item.label}\n              aria-current={item.current ? 'page' : undefined}\n              className={cn(\n                'grid size-10 place-items-center rounded-lg text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',\n                item.current && 'bg-foreground text-background hover:bg-foreground hover:text-background',\n              )}\n            >\n              <item.icon className=\"size-4\" aria-hidden />\n            </a>\n          ))}\n        </nav>\n        <div className=\"grid min-w-0 flex-1 grid-cols-1 gap-3 @2xl:grid-cols-6\">\n          <header className=\"@2xl:col-span-6\">\n            {date && <p className=\"text-xs text-muted-foreground\">{date}</p>}\n            <h2 className=\"text-lg font-semibold tracking-tight\">{t.title}</h2>\n          </header>\n          <div className=\"@2xl:col-span-4\">\n            <DataTile className=\"h-full\" title={t.spending} footer={t.note}>\n              <TileFact aside={<DeltaPill value={data.spendingChange} goodWhen=\"down\" />}>\n                <BigNumber value={data.spendingTotal} format={money} size=\"xl\" />\n              </TileFact>\n              <BarChart data={data.spending} label={t.spending} format={money} />\n            </DataTile>\n          </div>\n          <div className=\"@2xl:col-span-2\">\n            <DataTile className=\"h-full\" title={data.upsell.title} inverted>\n              <p className=\"text-sm opacity-80\">{data.upsell.text}</p>\n              <button\n                type=\"button\"\n                onClick={onUpsell}\n                className=\"mt-4 rounded-full bg-foreground px-4 py-2 text-sm font-medium text-background transition-transform active:scale-[0.97] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n              >\n                {data.upsell.action}\n              </button>\n            </DataTile>\n          </div>\n          <div className=\"@2xl:col-span-2\">\n            <DataTile className=\"h-full\" title={t.cards}>\n              <ul className=\"flex flex-col gap-3\">\n                {data.cards.map((c) => {\n                  const pct = c.limit > 0 ? Math.min(100, Math.round((c.spent / c.limit) * 100)) : 0\n                  return (\n                    <li key={c.id} className={cn('flex flex-col gap-1.5', c.frozen && 'opacity-60')}>\n                      <div className=\"flex items-baseline justify-between gap-2 text-sm\">\n                        <span className=\"truncate font-medium\">{c.name}</span>\n                        <span className=\"tabular-nums text-xs text-muted-foreground\">\n                          {c.frozen ? t.frozen : `•••• ${c.last4}`}\n                        </span>\n                      </div>\n                      <div\n                        role=\"progressbar\"\n                        aria-label={c.name}\n                        aria-valuenow={pct}\n                        aria-valuemin={0}\n                        aria-valuemax={100}\n                        className=\"h-1.5 overflow-hidden rounded-full bg-muted\"\n                      >\n                        <div className=\"h-full rounded-full bg-foreground\" style={{ width: `${pct}%` }} />\n                      </div>\n                      <p className=\"text-xs tabular-nums text-muted-foreground\">\n                        {c.spent.toLocaleString('en-GB', { style: 'currency', currency, maximumFractionDigits: 0 })} {t.limit}{' '}\n                        {c.limit.toLocaleString('en-GB', { style: 'currency', currency, maximumFractionDigits: 0 })}\n                      </p>\n                    </li>\n                  )\n                })}\n              </ul>\n            </DataTile>\n          </div>\n          <div className=\"@2xl:col-span-4\">\n            <DataTile className=\"h-full\" title={t.merchants}>\n              <BubbleChart data={data.merchants} label={t.merchants} format={money} yFormat={money} />\n            </DataTile>\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}