{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-payment-method",
  "title": "Billing Payment Method",
  "description": "Manage payment methods with add, edit, delete, and set default options.",
  "registryDependencies": [
    "alert-dialog",
    "badge",
    "button",
    "card",
    "dropdown-menu"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-payment-method.tsx",
      "content": "\"use client\";\n\nimport { CreditCard, Loader2, MoreVertical, Plus, Trash2 } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n} from \"@/registry/new-york/ui/alert-dialog\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/registry/new-york/ui/dropdown-menu\";\n\nexport interface PaymentMethod {\n  id: string;\n  type: \"card\" | \"bank_account\" | \"paypal\" | \"other\";\n  last4?: string;\n  brand?: \"visa\" | \"mastercard\" | \"amex\" | \"discover\" | \"other\";\n  expiryMonth?: number;\n  expiryYear?: number;\n  holderName?: string;\n  isDefault?: boolean;\n  billingAddress?: {\n    line1?: string;\n    city?: string;\n    state?: string;\n    zip?: string;\n    country?: string;\n  };\n}\n\nexport interface BillingPaymentMethodProps {\n  paymentMethods: PaymentMethod[];\n  onAdd?: () => void;\n  onEdit?: (methodId: string) => void;\n  onDelete?: (methodId: string) => void;\n  onSetDefault?: (methodId: string) => void;\n  className?: string;\n  allowMultiple?: boolean;\n  isLoading?: boolean;\n}\n\nfunction getCardBrandIcon(brand?: PaymentMethod[\"brand\"]) {\n  switch (brand) {\n    case \"visa\":\n      return \"💳\";\n    case \"mastercard\":\n      return \"💳\";\n    case \"amex\":\n      return \"💳\";\n    case \"discover\":\n      return \"💳\";\n    default:\n      return \"💳\";\n  }\n}\n\nfunction formatExpiry(month?: number, year?: number): string {\n  if (!(month && year)) return \"\";\n  const monthStr = month.toString().padStart(2, \"0\");\n  const yearStr = year.toString().slice(-2);\n  return `${monthStr}/${yearStr}`;\n}\n\nexport default function BillingPaymentMethod({\n  paymentMethods,\n  onAdd,\n  onEdit,\n  onDelete,\n  onSetDefault,\n  className,\n  allowMultiple = true,\n  isLoading = false,\n}: BillingPaymentMethodProps) {\n  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n  const [deletingId, setDeletingId] = useState<string | null>(null);\n\n  const handleDelete = async (methodId: string) => {\n    setDeletingId(methodId);\n    setDeleteDialogOpen(true);\n  };\n\n  const confirmDelete = async () => {\n    if (deletingId) {\n      await onDelete?.(deletingId);\n      setDeletingId(null);\n    }\n    setDeleteDialogOpen(false);\n  };\n\n  if (paymentMethods.length === 0) {\n    return (\n      <Card className={cn(\"w-full shadow-xs\", className)}>\n        <CardHeader>\n          <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div className=\"flex flex-col gap-1\">\n              <CardTitle>Payment methods</CardTitle>\n              <CardDescription>Manage your payment methods</CardDescription>\n            </div>\n            {onAdd && (\n              <Button\n                className=\"w-full sm:w-auto\"\n                onClick={onAdd}\n                type=\"button\"\n              >\n                <Plus className=\"size-4\" />\n                Add payment method\n              </Button>\n            )}\n          </div>\n        </CardHeader>\n        <CardContent>\n          <div className=\"flex flex-col items-center justify-center gap-4 py-12 text-center\">\n            <div className=\"flex size-12 items-center justify-center rounded-full bg-muted\">\n              <CreditCard className=\"size-6 text-muted-foreground\" />\n            </div>\n            <p className=\"text-muted-foreground text-sm\">\n              No payment methods added yet\n            </p>\n            {onAdd && (\n              <Button onClick={onAdd} type=\"button\" variant=\"outline\">\n                <Plus className=\"size-4\" />\n                Add payment method\n              </Button>\n            )}\n          </div>\n        </CardContent>\n      </Card>\n    );\n  }\n\n  return (\n    <>\n      <Card className={cn(\"w-full shadow-xs\", className)}>\n        <CardHeader>\n          <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div className=\"flex flex-col gap-1\">\n              <CardTitle>Payment methods</CardTitle>\n              <CardDescription>Manage your payment methods</CardDescription>\n            </div>\n            {onAdd && allowMultiple && (\n              <Button\n                className=\"w-full sm:w-auto\"\n                onClick={onAdd}\n                type=\"button\"\n              >\n                <Plus className=\"size-4\" />\n                Add payment method\n              </Button>\n            )}\n          </div>\n        </CardHeader>\n        <CardContent>\n          <div className=\"flex flex-col gap-3\">\n            {paymentMethods.map((method) => (\n              <div\n                className=\"flex flex-col gap-3 rounded-lg border bg-card p-4 sm:flex-row sm:items-center sm:justify-between\"\n                key={method.id}\n              >\n                <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n                  <div className=\"flex size-10 shrink-0 items-center justify-center rounded-lg border bg-background text-xl\">\n                    {method.type === \"card\"\n                      ? getCardBrandIcon(method.brand)\n                      : method.type === \"paypal\"\n                        ? \"P\"\n                        : \"🏦\"}\n                  </div>\n                  <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n                    <div className=\"flex flex-wrap items-center gap-2\">\n                      <span className=\"font-medium text-sm\">\n                        {method.type === \"card\"\n                          ? `${method.brand ? method.brand.charAt(0).toUpperCase() + method.brand.slice(1) : \"Card\"} •••• ${method.last4 || \"0000\"}`\n                          : method.type === \"paypal\"\n                            ? \"PayPal\"\n                            : method.type === \"bank_account\"\n                              ? `Bank Account •••• ${method.last4 || \"0000\"}`\n                              : \"Other\"}\n                      </span>\n                    </div>\n                    <div className=\"flex flex-wrap items-center gap-2 text-muted-foreground text-xs\">\n                      {method.holderName && <span>{method.holderName}</span>}\n                      {method.expiryMonth && method.expiryYear && (\n                        <>\n                          {method.holderName && (\n                            <span aria-hidden=\"true\">•</span>\n                          )}\n                          <span>\n                            Expires{\" \"}\n                            {formatExpiry(\n                              method.expiryMonth,\n                              method.expiryYear\n                            )}\n                          </span>\n                        </>\n                      )}\n                    </div>\n                  </div>\n                </div>\n                <div className=\"flex shrink-0 items-center gap-2\">\n                  {method.isDefault && (\n                    <Badge className=\"text-xs\">Default</Badge>\n                  )}\n                  {!method.isDefault && onSetDefault && (\n                    <Button\n                      onClick={() => onSetDefault(method.id)}\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      Set default\n                    </Button>\n                  )}\n                  <DropdownMenu>\n                    <DropdownMenuTrigger asChild>\n                      <Button\n                        aria-label=\"Payment method options\"\n                        size=\"icon\"\n                        type=\"button\"\n                        variant=\"ghost\"\n                      >\n                        <MoreVertical className=\"size-4\" />\n                      </Button>\n                    </DropdownMenuTrigger>\n                    <DropdownMenuContent align=\"end\">\n                      {onEdit && (\n                        <DropdownMenuItem onClick={() => onEdit(method.id)}>\n                          Edit\n                        </DropdownMenuItem>\n                      )}\n                      {!method.isDefault && onSetDefault && (\n                        <DropdownMenuItem\n                          onClick={() => onSetDefault(method.id)}\n                        >\n                          Set as default\n                        </DropdownMenuItem>\n                      )}\n                      {onDelete && (\n                        <>\n                          <DropdownMenuSeparator />\n                          <DropdownMenuItem\n                            className=\"text-destructive\"\n                            onClick={() => handleDelete(method.id)}\n                          >\n                            <Trash2 className=\"size-4\" />\n                            Delete\n                          </DropdownMenuItem>\n                        </>\n                      )}\n                    </DropdownMenuContent>\n                  </DropdownMenu>\n                </div>\n              </div>\n            ))}\n          </div>\n        </CardContent>\n      </Card>\n\n      <AlertDialog onOpenChange={setDeleteDialogOpen} open={deleteDialogOpen}>\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogTitle>Delete payment method?</AlertDialogTitle>\n            <AlertDialogDescription>\n              This action cannot be undone. This payment method will be\n              permanently removed from your account.\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <AlertDialogFooter>\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <AlertDialogAction\n              className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n              onClick={confirmDelete}\n            >\n              {deletingId && isLoading ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Deleting…\n                </>\n              ) : (\n                \"Delete\"\n              )}\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n    </>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}