{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-subscription-card",
  "title": "Billing Subscription Card",
  "description": "Display current subscription with usage, billing date, and management options.",
  "registryDependencies": [
    "badge",
    "button",
    "card",
    "progress",
    "separator",
    "switch"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-subscription-card.tsx",
      "content": "\"use client\";\n\nimport { AlertTriangle, Calendar, TrendingUp } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\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 { Progress } from \"@/registry/new-york/ui/progress\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\nimport { Switch } from \"@/registry/new-york/ui/switch\";\n\nexport interface SubscriptionUsage {\n  label: string;\n  used: number;\n  limit: number | null;\n  unit?: string;\n  warningThreshold?: number;\n}\n\nexport interface BillingSubscriptionCardProps {\n  plan: {\n    id: string;\n    name: string;\n    price: number;\n    currency?: string;\n    billingPeriod: \"monthly\" | \"annual\";\n  };\n  usage?: SubscriptionUsage[];\n  nextBillingDate?: Date;\n  autoRenew?: boolean;\n  status?: \"active\" | \"canceled\" | \"expired\" | \"past_due\";\n  onUpgrade?: () => void;\n  onDowngrade?: () => void;\n  onCancel?: () => void;\n  onManage?: () => void;\n  className?: string;\n  showUsageDetails?: boolean;\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: 0,\n    maximumFractionDigits: 0,\n  }).format(amount);\n}\n\nfunction getStatusConfig(status: string) {\n  switch (status) {\n    case \"active\":\n      return {\n        label: \"Active\",\n        variant: \"default\" as const,\n        className: \"bg-green-500/10 text-green-600 border-green-500/20\",\n      };\n    case \"canceled\":\n      return {\n        label: \"Canceled\",\n        variant: \"secondary\" as const,\n        className: \"bg-yellow-500/10 text-yellow-600 border-yellow-500/20\",\n      };\n    case \"expired\":\n      return {\n        label: \"Expired\",\n        variant: \"destructive\" as const,\n        className: \"bg-destructive/10 text-destructive border-destructive/20\",\n      };\n    case \"past_due\":\n      return {\n        label: \"Past Due\",\n        variant: \"destructive\" as const,\n        className: \"bg-destructive/10 text-destructive border-destructive/20\",\n      };\n    default:\n      return {\n        label: \"Unknown\",\n        variant: \"secondary\" as const,\n        className: \"\",\n      };\n  }\n}\n\nexport default function BillingSubscriptionCard({\n  plan,\n  usage = [],\n  nextBillingDate,\n  autoRenew = true,\n  status = \"active\",\n  onUpgrade,\n  onDowngrade,\n  onCancel,\n  onManage,\n  className,\n  showUsageDetails = true,\n}: BillingSubscriptionCardProps) {\n  const statusConfig = getStatusConfig(status);\n  const currency = plan.currency || \"USD\";\n\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 min-w-0 flex-1 flex-col gap-2\">\n            <div className=\"flex flex-wrap items-center gap-2\">\n              <CardTitle className=\"wrap-break-word\">{plan.name}</CardTitle>\n              <Badge\n                className={cn(\"text-xs\", statusConfig.className)}\n                variant={statusConfig.variant}\n              >\n                {statusConfig.label}\n              </Badge>\n            </div>\n            <CardDescription className=\"wrap-break-word\">\n              {formatPrice(plan.price, currency)}/\n              {plan.billingPeriod === \"monthly\" ? \"month\" : \"year\"}\n            </CardDescription>\n          </div>\n          {onManage && (\n            <Button\n              className=\"w-full sm:w-auto\"\n              onClick={onManage}\n              type=\"button\"\n              variant=\"outline\"\n            >\n              Manage\n            </Button>\n          )}\n        </div>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex flex-col gap-6\">\n          {showUsageDetails && usage.length > 0 && (\n            <div className=\"flex flex-col gap-4\">\n              <h3 className=\"font-medium text-sm\">Usage</h3>\n              <div className=\"flex flex-col gap-4\">\n                {usage.map((item, idx) => {\n                  const percentage =\n                    item.limit !== null ? (item.used / item.limit) * 100 : 0;\n                  const isWarning =\n                    item.warningThreshold !== undefined &&\n                    percentage >= item.warningThreshold;\n                  const isOverLimit =\n                    item.limit !== null && item.used > item.limit;\n\n                  return (\n                    <div className=\"flex flex-col gap-2\" key={idx}>\n                      <div className=\"flex items-center justify-between\">\n                        <span className=\"text-sm\">{item.label}</span>\n                        <span className=\"text-muted-foreground text-sm\">\n                          {item.used.toLocaleString()}\n                          {item.unit && ` ${item.unit}`}\n                          {item.limit !== null && (\n                            <> / {item.limit.toLocaleString()}</>\n                          )}\n                          {item.limit === null && \" (Unlimited)\"}\n                        </span>\n                      </div>\n                      {item.limit !== null && (\n                        <Progress\n                          aria-label={`${item.label} usage: ${percentage.toFixed(0)}%`}\n                          className=\"h-2\"\n                          value={Math.min(percentage, 100)}\n                        />\n                      )}\n                      {isWarning && !isOverLimit && (\n                        <div className=\"flex items-center gap-2 text-xs text-yellow-600\">\n                          <AlertTriangle className=\"size-3.5\" />\n                          <span>\n                            You&apos;ve used {percentage.toFixed(0)}% of your\n                            limit\n                          </span>\n                        </div>\n                      )}\n                      {isOverLimit && (\n                        <div className=\"flex items-center gap-2 text-destructive text-xs\">\n                          <AlertTriangle className=\"size-3.5\" />\n                          <span>You&apos;ve exceeded your limit</span>\n                        </div>\n                      )}\n                    </div>\n                  );\n                })}\n              </div>\n            </div>\n          )}\n\n          {nextBillingDate && (\n            <>\n              {showUsageDetails && usage.length > 0 && <Separator />}\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"flex items-center gap-2\">\n                  <Calendar className=\"size-4 text-muted-foreground\" />\n                  <span className=\"text-sm\">Next billing date</span>\n                </div>\n                <p className=\"text-muted-foreground text-sm\">\n                  {formatDate(nextBillingDate)}\n                </p>\n              </div>\n            </>\n          )}\n\n          <Separator />\n\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex items-center justify-between\">\n              <div className=\"flex flex-col gap-1\">\n                <span className=\"text-sm\">Auto-renewal</span>\n                <span className=\"text-muted-foreground text-xs\">\n                  {autoRenew\n                    ? \"Your subscription will renew automatically\"\n                    : \"Your subscription will not renew\"}\n                </span>\n              </div>\n              <Switch checked={autoRenew} disabled />\n            </div>\n\n            <div className=\"flex flex-col gap-2 sm:flex-row\">\n              {onUpgrade && (\n                <Button\n                  className=\"w-full sm:w-auto\"\n                  onClick={onUpgrade}\n                  type=\"button\"\n                  variant=\"default\"\n                >\n                  <TrendingUp className=\"size-4\" />\n                  Upgrade\n                </Button>\n              )}\n              {onDowngrade && (\n                <Button\n                  className=\"w-full sm:w-auto\"\n                  onClick={onDowngrade}\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  Downgrade\n                </Button>\n              )}\n              {onCancel && (\n                <Button\n                  className=\"w-full sm:w-auto\"\n                  onClick={onCancel}\n                  type=\"button\"\n                  variant=\"destructive\"\n                >\n                  Cancel subscription\n                </Button>\n              )}\n            </div>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}