{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-usage-center",
  "title": "Billing Usage Center",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/billing-usage-center/BillingUsageCenter.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { CreditCard, Download, FileText, TriangleAlert } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Progress } from '@/components/ui/progress'\nimport { SectionCard } from '@/components/ui/section-card'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'\n\nexport interface PlanInfo {\n  name: string\n  price: string\n  /** Billing cadence shown after the price, e.g. 'month' or 'year'. */\n  cadence: string\n  renewalDate: string\n  seatsUsed: number\n  seatsTotal: number\n}\n\nexport interface UsageMetric {\n  id: string\n  label: string\n  usedText: string\n  includedText: string\n  /** Percent of the included allowance. Above 100 means overage. */\n  percent: number\n  /** Shown when percent exceeds 100; falls back to a generic overage note. */\n  overageNote?: string\n}\n\nexport type InvoiceStatus = 'paid' | 'open' | 'past-due'\n\nexport interface Invoice {\n  id: string\n  number: string\n  period: string\n  amount: string\n  status: InvoiceStatus\n}\n\nexport interface BillingUsageCenterProps {\n  plan?: PlanInfo\n  usage?: UsageMetric[]\n  invoices?: Invoice[]\n  className?: string\n}\n\nconst defaultPlan: PlanInfo = {\n  name: 'Pro',\n  price: '$49',\n  cadence: 'month',\n  renewalDate: 'September 12, 2026',\n  seatsUsed: 18,\n  seatsTotal: 20,\n}\n\nconst defaultUsage: UsageMetric[] = [\n  { id: 'storage', label: 'Storage', usedText: '8.2 GB', includedText: '10 GB', percent: 82 },\n  { id: 'bandwidth', label: 'Bandwidth', usedText: '412 GB', includedText: '1 TB', percent: 41 },\n  { id: 'api-calls', label: 'API calls', usedText: '640K', includedText: '1M', percent: 64 },\n]\n\nconst defaultInvoices: Invoice[] = [\n  { id: 'inv-003', number: 'INV-2026-003', period: 'Jul 1 – Jul 31, 2026', amount: '$49.00', status: 'paid' },\n  { id: 'inv-002', number: 'INV-2026-002', period: 'Jun 1 – Jun 30, 2026', amount: '$49.00', status: 'paid' },\n  { id: 'inv-001', number: 'INV-2026-001', period: 'May 1 – May 31, 2026', amount: '$49.00', status: 'paid' },\n]\n\nconst statusMeta: Record<InvoiceStatus, { label: string; variant: 'success' | 'default' | 'destructive' }> = {\n  paid: { label: 'Paid', variant: 'success' },\n  open: { label: 'Open', variant: 'default' },\n  'past-due': { label: 'Past due', variant: 'destructive' },\n}\n\n// success below 80%, warning as usage approaches the limit, destructive at/over it.\nfunction usageBarClass(percent: number): string {\n  if (percent >= 100) return '[&_[data-slot=progress-indicator]]:bg-destructive'\n  if (percent >= 80) return '[&_[data-slot=progress-indicator]]:bg-warning'\n  return '[&_[data-slot=progress-indicator]]:bg-success'\n}\n\nexport function BillingUsageCenter({\n  plan = defaultPlan,\n  usage = defaultUsage,\n  invoices = defaultInvoices,\n  className,\n}: BillingUsageCenterProps) {\n  const seatsPercent = plan.seatsTotal > 0 ? (plan.seatsUsed / plan.seatsTotal) * 100 : 0\n\n  const unpaidInvoices = invoices.filter((invoice) => invoice.status !== 'paid')\n  const pastDueCount = invoices.filter((invoice) => invoice.status === 'past-due').length\n\n  const unpaidNote = (() => {\n    const count = unpaidInvoices.length\n    const plural = count === 1 ? '' : 's'\n    const pastPart = pastDueCount > 0 ? `, ${pastDueCount} of them past due` : ''\n    return `You have ${count} unpaid invoice${plural}${pastPart}. Update your payment method to avoid service interruption.`\n  })()\n\n  return (\n    <div data-slot=\"billing-usage-center\" className={cn('mx-auto w-full max-w-3xl space-y-4', className)}>\n      <SectionCard title=\"Current plan\" description={`Renews on ${plan.renewalDate}`}>\n        <div className=\"flex items-start justify-between gap-4\">\n          <div>\n            <p className=\"text-2xl font-bold tracking-tight\">{plan.name}</p>\n            <p className=\"text-muted-foreground mt-1 text-sm\">\n              {plan.price} <span className=\"text-xs\">/ {plan.cadence}</span>\n            </p>\n          </div>\n        </div>\n\n        <div className=\"mt-6 space-y-1.5\">\n          <div className=\"flex flex-wrap items-baseline justify-between gap-4 text-sm\">\n            <span className=\"font-medium\">Seats used</span>\n            <span className=\"text-muted-foreground text-xs tabular-nums\">\n              {plan.seatsUsed} of {plan.seatsTotal}\n            </span>\n          </div>\n          <Progress value={seatsPercent} />\n          <p className=\"text-muted-foreground text-xs\">\n            {Math.max(plan.seatsTotal - plan.seatsUsed, 0)} seats remaining on this plan.\n          </p>\n        </div>\n\n        <div className=\"mt-6 flex flex-wrap justify-end gap-2\">\n          <Button variant=\"outline\" size=\"sm\">\n            <CreditCard aria-hidden=\"true\" />\n            Manage payment\n          </Button>\n          <Button size=\"sm\">Change plan</Button>\n        </div>\n      </SectionCard>\n\n      <SectionCard title=\"Usage this month\" description=\"Metered against your plan allowances.\">\n        <div className=\"space-y-5\">\n          {usage.map((metric) => (\n            <div key={metric.id} className=\"space-y-1.5\">\n              <div className=\"flex flex-wrap items-baseline justify-between gap-4 text-sm\">\n                <span className=\"font-medium\">{metric.label}</span>\n                <span className=\"text-muted-foreground text-xs tabular-nums\">\n                  {metric.usedText} of {metric.includedText}\n                </span>\n              </div>\n              <Progress value={Math.min(metric.percent, 100)} className={usageBarClass(metric.percent)} />\n              {metric.percent > 100 && (\n                <p className=\"text-destructive text-xs\">\n                  {metric.overageNote ??\n                    `Over the ${metric.includedText} included in your plan — overage charges apply.`}\n                </p>\n              )}\n            </div>\n          ))}\n        </div>\n      </SectionCard>\n\n      <SectionCard\n        title=\"Invoice history\"\n        description={`${invoices.length} invoice${invoices.length === 1 ? '' : 's'}`}\n      >\n        {invoices.length > 0 ? (\n          <div className=\"space-y-4\">\n            {unpaidInvoices.length > 0 && (\n              <Alert className=\"border-warning/40 bg-warning/5\">\n                <TriangleAlert className=\"text-warning size-4\" aria-hidden=\"true\" />\n                <AlertTitle>Past-due balance</AlertTitle>\n                <AlertDescription>{unpaidNote}</AlertDescription>\n              </Alert>\n            )}\n\n            <div className=\"overflow-x-auto\">\n              <Table>\n                <TableHeader>\n                  <TableRow>\n                    <TableHead>Invoice</TableHead>\n                    <TableHead>Period</TableHead>\n                    <TableHead className=\"text-right\">Amount</TableHead>\n                    <TableHead>Status</TableHead>\n                    <TableHead className=\"w-12\">\n                      <span className=\"sr-only\">Download</span>\n                    </TableHead>\n                  </TableRow>\n                </TableHeader>\n                <TableBody>\n                  {invoices.map((invoice) => (\n                    <TableRow key={invoice.id}>\n                      <TableCell className=\"font-medium\">{invoice.number}</TableCell>\n                      <TableCell className=\"text-muted-foreground\">{invoice.period}</TableCell>\n                      <TableCell className=\"text-right tabular-nums\">{invoice.amount}</TableCell>\n                      <TableCell>\n                        <Badge variant={statusMeta[invoice.status].variant}>{statusMeta[invoice.status].label}</Badge>\n                      </TableCell>\n                      <TableCell className=\"text-right\">\n                        <Button variant=\"ghost\" size=\"icon-sm\" aria-label={`Download invoice ${invoice.number}`}>\n                          <Download aria-hidden=\"true\" />\n                        </Button>\n                      </TableCell>\n                    </TableRow>\n                  ))}\n                </TableBody>\n              </Table>\n            </div>\n          </div>\n        ) : (\n          <div className=\"flex flex-col items-center justify-center px-6 py-10 text-center\">\n            <div className=\"bg-muted mb-3 rounded-full p-3\">\n              <FileText className=\"text-muted-foreground size-5\" />\n            </div>\n            <p className=\"text-sm font-medium\">No invoices yet</p>\n            <p className=\"text-muted-foreground mt-0.5 text-xs\">Invoices appear here after your first billing cycle.</p>\n          </div>\n        )}\n      </SectionCard>\n    </div>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/BillingUsageCenter.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/alert.json",
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/progress.json",
    "https://uipkge.dev/r/react/section-card.json",
    "https://uipkge.dev/r/react/table.json"
  ],
  "description": "Stacked billing center: a current-plan card with seat meter, monthly usage rows whose bars shift success → warning → destructive as they approach the limit (with overage notes), and an invoice history table with status pills, an unpaid-balance alert, and an empty state. Plan, usage, and invoices are all overridable.",
  "categories": [
    "finance",
    "dashboard"
  ]
}