{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-invoice-details",
  "title": "Billing Invoice Details",
  "description": "Detailed invoice view in a sheet with line items and payment information.",
  "registryDependencies": [
    "badge",
    "button",
    "separator",
    "sheet"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-invoice-details.tsx",
      "content": "\"use client\";\n\nimport { Download, Loader2, Printer } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n} from \"@/registry/new-york/ui/sheet\";\n\nexport interface InvoiceLineItem {\n  description: string;\n  quantity: number;\n  unitPrice: number;\n  subtotal: number;\n}\n\nexport interface InvoiceDetails {\n  id: string;\n  invoiceNumber: string;\n  date: Date;\n  dueDate?: Date;\n  amount: number;\n  currency?: string;\n  status: \"paid\" | \"pending\" | \"failed\" | \"refunded\" | \"void\";\n  description?: string;\n  lineItems: InvoiceLineItem[];\n  subtotal: number;\n  tax?: {\n    amount: number;\n    rate?: number;\n    label?: string;\n  };\n  discount?: {\n    amount: number;\n    code?: string;\n    label?: string;\n  };\n  total: number;\n  paymentMethod?: {\n    type: string;\n    last4?: string;\n    brand?: string;\n  };\n  billingAddress?: {\n    name: string;\n    line1: string;\n    line2?: string;\n    city: string;\n    state: string;\n    zip: string;\n    country: string;\n  };\n  downloadUrl?: string;\n}\n\nexport interface BillingInvoiceDetailsProps {\n  invoice: InvoiceDetails | null;\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n  onDownload?: (invoiceId: string) => void;\n  onPrint?: (invoiceId: string) => void;\n  className?: string;\n  currency?: string;\n}\n\nfunction formatDate(date: Date): string {\n  const year = date.getFullYear();\n  const month = date.toLocaleString(\"en-US\", { month: \"short\" });\n  const day = date.getDate();\n  return `${month} ${day}, ${year}`;\n}\n\nfunction formatPrice(amount: number, currency = \"USD\"): string {\n  return new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency,\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  }).format(amount);\n}\n\nfunction getStatusConfig(status: InvoiceDetails[\"status\"]) {\n  switch (status) {\n    case \"paid\":\n      return {\n        label: \"Paid\",\n        variant: \"default\" as const,\n        className: \"bg-green-500/10 text-green-600 border-green-500/20\",\n      };\n    case \"pending\":\n      return {\n        label: \"Pending\",\n        variant: \"secondary\" as const,\n        className: \"bg-yellow-500/10 text-yellow-600 border-yellow-500/20\",\n      };\n    case \"failed\":\n      return {\n        label: \"Failed\",\n        variant: \"destructive\" as const,\n        className: \"bg-destructive/10 text-destructive border-destructive/20\",\n      };\n    case \"refunded\":\n      return {\n        label: \"Refunded\",\n        variant: \"secondary\" as const,\n        className: \"bg-blue-500/10 text-blue-600 border-blue-500/20\",\n      };\n    case \"void\":\n      return {\n        label: \"Void\",\n        variant: \"secondary\" as const,\n        className: \"\",\n      };\n    default:\n      return {\n        label: \"Unknown\",\n        variant: \"secondary\" as const,\n        className: \"\",\n      };\n  }\n}\n\nexport default function BillingInvoiceDetails({\n  invoice,\n  open,\n  onOpenChange,\n  onDownload,\n  onPrint,\n  className,\n  currency = \"USD\",\n}: BillingInvoiceDetailsProps) {\n  const [isDownloading, setIsDownloading] = useState(false);\n  const [isPrinting, setIsPrinting] = useState(false);\n\n  if (!invoice) return null;\n\n  const statusConfig = getStatusConfig(invoice.status);\n  const invoiceCurrency = invoice.currency || currency;\n\n  const handleDownload = async () => {\n    setIsDownloading(true);\n    try {\n      await onDownload?.(invoice.id);\n    } finally {\n      setIsDownloading(false);\n    }\n  };\n\n  const handlePrint = async () => {\n    setIsPrinting(true);\n    try {\n      await onPrint?.(invoice.id);\n      window.print();\n    } finally {\n      setIsPrinting(false);\n    }\n  };\n\n  return (\n    <Sheet onOpenChange={onOpenChange} open={open}>\n      <SheetContent\n        className={cn(\n          \"flex flex-col gap-0 overflow-y-auto sm:max-w-2xl\",\n          className\n        )}\n      >\n        <SheetHeader className=\"shrink-0\">\n          <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n              <SheetTitle className=\"wrap-break-word\">\n                Invoice {invoice.invoiceNumber}\n              </SheetTitle>\n              <SheetDescription className=\"wrap-break-word\">\n                {invoice.description ||\n                  `Invoice dated ${formatDate(invoice.date)}`}\n              </SheetDescription>\n            </div>\n            <Badge\n              className={cn(\"shrink-0 text-xs\", statusConfig.className)}\n              variant={statusConfig.variant}\n            >\n              {statusConfig.label}\n            </Badge>\n          </div>\n        </SheetHeader>\n\n        <div className=\"flex flex-1 flex-col gap-6 overflow-y-auto p-4\">\n          <div className=\"flex flex-col gap-6\">\n            <div className=\"flex flex-col gap-4 sm:flex-row sm:justify-between\">\n              <div className=\"flex flex-col gap-2\">\n                <h3 className=\"font-medium text-sm\">Invoice Details</h3>\n                <div className=\"flex flex-col gap-1 text-muted-foreground text-sm\">\n                  <div className=\"flex flex-wrap items-center gap-2\">\n                    <span>Date:</span>\n                    <span className=\"text-foreground\">\n                      {formatDate(invoice.date)}\n                    </span>\n                  </div>\n                  {invoice.dueDate && (\n                    <div className=\"flex flex-wrap items-center gap-2\">\n                      <span>Due Date:</span>\n                      <span className=\"text-foreground\">\n                        {formatDate(invoice.dueDate)}\n                      </span>\n                    </div>\n                  )}\n                  <div className=\"flex flex-wrap items-center gap-2\">\n                    <span>Status:</span>\n                    <Badge\n                      className={cn(\"text-xs\", statusConfig.className)}\n                      variant={statusConfig.variant}\n                    >\n                      {statusConfig.label}\n                    </Badge>\n                  </div>\n                </div>\n              </div>\n\n              {invoice.billingAddress && (\n                <div className=\"flex flex-col gap-2\">\n                  <h3 className=\"font-medium text-sm\">Billing Address</h3>\n                  <div className=\"flex flex-col gap-1 text-muted-foreground text-sm\">\n                    <div className=\"wrap-break-word text-foreground\">\n                      {invoice.billingAddress.name}\n                    </div>\n                    <div className=\"wrap-break-word\">\n                      {invoice.billingAddress.line1}\n                    </div>\n                    {invoice.billingAddress.line2 && (\n                      <div className=\"wrap-break-word\">\n                        {invoice.billingAddress.line2}\n                      </div>\n                    )}\n                    <div className=\"wrap-break-word\">\n                      {invoice.billingAddress.city},{\" \"}\n                      {invoice.billingAddress.state}{\" \"}\n                      {invoice.billingAddress.zip}\n                    </div>\n                    <div className=\"wrap-break-word\">\n                      {invoice.billingAddress.country}\n                    </div>\n                  </div>\n                </div>\n              )}\n            </div>\n\n            <Separator />\n\n            <div className=\"flex flex-col gap-4\">\n              <h3 className=\"font-medium text-sm\">Line Items</h3>\n              <div className=\"flex flex-col gap-0 overflow-hidden rounded-lg border\">\n                <div className=\"hidden grid-cols-[2fr_1fr_1fr_1fr] gap-4 border-b bg-muted/50 p-3 font-medium text-muted-foreground text-xs sm:grid\">\n                  <div>Description</div>\n                  <div className=\"text-right\">Quantity</div>\n                  <div className=\"text-right\">Unit Price</div>\n                  <div className=\"text-right\">Subtotal</div>\n                </div>\n                {invoice.lineItems.map((item, idx) => (\n                  <div\n                    className=\"flex flex-col gap-2 border-b p-3 last:border-b-0 sm:grid sm:grid-cols-[2fr_1fr_1fr_1fr] sm:gap-4 sm:gap-y-0\"\n                    key={idx}\n                  >\n                    <div className=\"wrap-break-word font-medium text-sm\">\n                      {item.description}\n                    </div>\n                    <div className=\"flex items-center justify-between text-muted-foreground text-sm sm:justify-end\">\n                      <span className=\"sm:hidden\">Quantity:</span>\n                      <span>{item.quantity}</span>\n                    </div>\n                    <div className=\"flex items-center justify-between text-muted-foreground text-sm sm:justify-end\">\n                      <span className=\"sm:hidden\">Unit Price:</span>\n                      <span>\n                        {formatPrice(item.unitPrice, invoiceCurrency)}\n                      </span>\n                    </div>\n                    <div className=\"flex items-center justify-between font-medium text-sm sm:justify-end\">\n                      <span className=\"sm:hidden\">Subtotal:</span>\n                      <span>{formatPrice(item.subtotal, invoiceCurrency)}</span>\n                    </div>\n                  </div>\n                ))}\n              </div>\n            </div>\n\n            <Separator />\n\n            <div className=\"flex flex-col gap-2\">\n              <div className=\"flex items-center justify-between text-sm\">\n                <span className=\"text-muted-foreground\">Subtotal</span>\n                <span>{formatPrice(invoice.subtotal, invoiceCurrency)}</span>\n              </div>\n              {invoice.discount && (\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">\n                    Discount\n                    {invoice.discount.label && ` (${invoice.discount.label})`}\n                    {invoice.discount.code && ` - ${invoice.discount.code}`}\n                  </span>\n                  <span className=\"text-green-600\">\n                    -{formatPrice(invoice.discount.amount, invoiceCurrency)}\n                  </span>\n                </div>\n              )}\n              {invoice.tax && (\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">\n                    Tax{invoice.tax.label && ` (${invoice.tax.label})`}\n                    {invoice.tax.rate !== undefined &&\n                      ` ${(invoice.tax.rate * 100).toFixed(1)}%`}\n                  </span>\n                  <span>\n                    {formatPrice(invoice.tax.amount, invoiceCurrency)}\n                  </span>\n                </div>\n              )}\n              <Separator />\n              <div className=\"flex items-center justify-between font-semibold text-base\">\n                <span>Total</span>\n                <span>{formatPrice(invoice.total, invoiceCurrency)}</span>\n              </div>\n            </div>\n\n            {invoice.paymentMethod && (\n              <>\n                <Separator />\n                <div className=\"flex flex-col gap-2\">\n                  <h3 className=\"font-medium text-sm\">Payment Method</h3>\n                  <div className=\"flex flex-wrap items-center gap-2 text-muted-foreground text-sm\">\n                    <span className=\"capitalize\">\n                      {invoice.paymentMethod.type}\n                    </span>\n                    {invoice.paymentMethod.brand && (\n                      <>\n                        <span aria-hidden=\"true\">•</span>\n                        <span className=\"capitalize\">\n                          {invoice.paymentMethod.brand}\n                        </span>\n                      </>\n                    )}\n                    {invoice.paymentMethod.last4 && (\n                      <>\n                        <span aria-hidden=\"true\">•</span>\n                        <span>•••• {invoice.paymentMethod.last4}</span>\n                      </>\n                    )}\n                  </div>\n                </div>\n              </>\n            )}\n          </div>\n        </div>\n\n        <div className=\"flex shrink-0 flex-col gap-2 border-t p-4 sm:flex-row sm:justify-end\">\n          {onPrint && (\n            <Button\n              aria-label=\"Print invoice\"\n              className=\"w-full sm:w-auto\"\n              onClick={handlePrint}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              {isPrinting ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Printing…\n                </>\n              ) : (\n                <>\n                  <Printer className=\"size-4\" />\n                  Print\n                </>\n              )}\n            </Button>\n          )}\n          {onDownload && (\n            <Button\n              aria-label={`Download invoice ${invoice.invoiceNumber}`}\n              className=\"w-full sm:w-auto\"\n              onClick={handleDownload}\n              type=\"button\"\n            >\n              {isDownloading ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Downloading…\n                </>\n              ) : (\n                <>\n                  <Download className=\"size-4\" />\n                  Download PDF\n                </>\n              )}\n            </Button>\n          )}\n        </div>\n      </SheetContent>\n    </Sheet>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}