{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "cards",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Invoice summary with aging state.",
  "files": [
    {
      "content": "import * as React from \"react\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport { daysBetween, formatDate } from \"@/lib/ledger/fiscal-period\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { Card } from \"@/components/ledger/card\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Reference } from \"@/components/ledger/reference\";\nimport { Rule } from \"@/components/ledger/rule\";\nimport { StatusPill } from \"@/components/ledger/status-pill\";\n\nexport type AgingBucket = \"current\" | \"1-30\" | \"31-60\" | \"61-90\" | \"90+\";\n\nexport function bucketFor(dueDate: Date, today: Date): AgingBucket {\n  const overdue = daysBetween(dueDate, today);\n  if (overdue <= 0) return \"current\";\n  if (overdue <= 30) return \"1-30\";\n  if (overdue <= 60) return \"31-60\";\n  if (overdue <= 90) return \"61-90\";\n  return \"90+\";\n}\n\nexport interface InvoiceCardProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  customer: string;\n  dueDate: Date;\n  number: string;\n  outstanding: MoneyValue;\n  paid?: MoneyValue;\n  today?: Date;\n  total: MoneyValue;\n}\n\n/** Overdue is `--negative` with the day count, never colour alone. */\nexport const InvoiceCard = React.forwardRef<HTMLDivElement, InvoiceCardProps>(\n  ({ className, number, customer, total, paid, outstanding, dueDate, today = new Date(), ...props }, ref) => {\n    const overdueDays = daysBetween(dueDate, today);\n    const bucket = bucketFor(dueDate, today);\n    const partial = paid != null && Number(paid.amount) > 0;\n\n    return (\n      <Card className={cn(\"flex flex-col gap-3\", className)} ref={ref} {...props}>\n        <div className=\"flex items-start justify-between gap-3\">\n          <div className=\"min-w-0\">\n            <p className=\"truncate text-body font-medium\">{customer}</p>\n            <Reference truncate={false} value={number} />\n          </div>\n          <StatusPill entity=\"Invoice\" status={bucket === \"current\" ? \"pending\" : \"overdue\"} />\n        </div>\n\n        <div className=\"space-y-1\">\n          <Row label=\"Invoice total\" value={total} />\n          {partial ? <Row label=\"Paid\" tone=\"secondary\" value={paid} /> : null}\n        </div>\n\n        <div>\n          <Rule className=\"ml-auto w-[45%]\" variant=\"subtotal\" />\n          <div className=\"flex items-baseline justify-between pt-1\">\n            <span className=\"text-label font-medium\">Outstanding</span>\n            <Money accounting size=\"md\" value={outstanding} />\n          </div>\n        </div>\n\n        <p className={cn(\"text-caption\", bucket === \"current\" ? \"text-text-secondary\" : \"text-negative\")}>\n          {bucket === \"current\"\n            ? `Due ${formatDate(dueDate)}`\n            : `${overdueDays} days overdue · due ${formatDate(dueDate)}`}\n        </p>\n      </Card>\n    );\n  },\n);\nInvoiceCard.displayName = \"InvoiceCard\";\n\nfunction Row({\n  label,\n  value,\n  tone = \"primary\",\n}: {\n  label: string;\n  value: MoneyValue;\n  tone?: \"primary\" | \"secondary\";\n}) {\n  return (\n    <div className=\"flex items-baseline justify-between gap-4\">\n      <span className=\"text-body text-text-secondary\">{label}</span>\n      <Money accounting tone={tone} value={value} />\n    </div>\n  );\n}\n",
      "path": "components/ledger/invoice-card.tsx",
      "target": "components/ledger/invoice-card.tsx",
      "type": "registry:component"
    }
  ],
  "name": "invoice-card",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/card",
    "@lopifi/cn",
    "@lopifi/fiscal-period",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/reference",
    "@lopifi/rule",
    "@lopifi/status-pill"
  ],
  "title": "InvoiceCard",
  "type": "registry:component"
}
