{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-subscription-settings",
  "title": "Billing Subscription Settings",
  "description": "Manage subscription settings including pause, cancel, and billing period changes.",
  "registryDependencies": [
    "alert-dialog",
    "badge",
    "button",
    "card",
    "select",
    "separator",
    "switch"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-subscription-settings.tsx",
      "content": "\"use client\";\n\nimport { Calendar, Loader2, Pause, Play, RefreshCw, X } 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  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/registry/new-york/ui/select\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\nimport { Switch } from \"@/registry/new-york/ui/switch\";\n\nexport interface SubscriptionSettings {\n  id: string;\n  planName: string;\n  status: \"active\" | \"paused\" | \"canceled\" | \"expired\";\n  billingPeriod: \"monthly\" | \"annual\";\n  currentBillingDate: Date;\n  nextBillingDate?: Date;\n  pausedUntil?: Date;\n  canceledAt?: Date;\n  cancelAtPeriodEnd?: boolean;\n  autoRenew: boolean;\n  prorationPreview?: {\n    newAmount: number;\n    creditAmount: number;\n    nextBillingDate: Date;\n  };\n}\n\nexport interface BillingSubscriptionSettingsProps {\n  subscription: SubscriptionSettings;\n  onPause?: (resumeDate?: Date) => Promise<void>;\n  onResume?: () => Promise<void>;\n  onChangeBillingPeriod?: (period: \"monthly\" | \"annual\") => Promise<void>;\n  onUpdateBillingDate?: (date: Date) => Promise<void>;\n  onCancel?: (feedback?: string) => Promise<void>;\n  onReactivate?: () => Promise<void>;\n  onToggleAutoRenew?: (enabled: boolean) => Promise<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\nexport default function BillingSubscriptionSettings({\n  subscription,\n  onPause,\n  onResume,\n  onChangeBillingPeriod,\n  onUpdateBillingDate,\n  onCancel,\n  onReactivate,\n  onToggleAutoRenew,\n  className,\n  currency = \"USD\",\n}: BillingSubscriptionSettingsProps) {\n  const [isLoading, setIsLoading] = useState(false);\n  const [showCancelDialog, setShowCancelDialog] = useState(false);\n  const [cancelFeedback, setCancelFeedback] = useState(\"\");\n  const [showPauseDialog, setShowPauseDialog] = useState(false);\n  const [pauseDuration, setPauseDuration] = useState<\n    \"1\" | \"3\" | \"6\" | \"custom\"\n  >(\"1\");\n\n  const isActive = subscription.status === \"active\";\n  const isPaused = subscription.status === \"paused\";\n  const isCanceled = subscription.status === \"canceled\";\n\n  const handlePause = async () => {\n    if (!onPause) return;\n\n    setIsLoading(true);\n    try {\n      let resumeDate: Date | undefined;\n      if (pauseDuration === \"1\") {\n        resumeDate = new Date();\n        resumeDate.setMonth(resumeDate.getMonth() + 1);\n      } else if (pauseDuration === \"3\") {\n        resumeDate = new Date();\n        resumeDate.setMonth(resumeDate.getMonth() + 3);\n      } else if (pauseDuration === \"6\") {\n        resumeDate = new Date();\n        resumeDate.setMonth(resumeDate.getMonth() + 6);\n      }\n\n      await onPause(resumeDate);\n      setShowPauseDialog(false);\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleResume = async () => {\n    if (!onResume) return;\n\n    setIsLoading(true);\n    try {\n      await onResume();\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleChangeBillingPeriod = async (period: \"monthly\" | \"annual\") => {\n    if (!onChangeBillingPeriod) return;\n\n    setIsLoading(true);\n    try {\n      await onChangeBillingPeriod(period);\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleCancel = async () => {\n    if (!onCancel) return;\n\n    setIsLoading(true);\n    try {\n      await onCancel(cancelFeedback || undefined);\n      setShowCancelDialog(false);\n      setCancelFeedback(\"\");\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleReactivate = async () => {\n    if (!onReactivate) return;\n\n    setIsLoading(true);\n    try {\n      await onReactivate();\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleToggleAutoRenew = async (enabled: boolean) => {\n    if (!onToggleAutoRenew) return;\n\n    setIsLoading(true);\n    try {\n      await onToggleAutoRenew(enabled);\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  return (\n    <>\n      <Card className={cn(\"w-full shadow-xs\", className)}>\n        <CardHeader>\n          <div className=\"flex flex-col gap-1\">\n            <CardTitle>Subscription Settings</CardTitle>\n            <CardDescription>\n              Manage your subscription preferences and billing\n            </CardDescription>\n          </div>\n        </CardHeader>\n        <CardContent>\n          <div className=\"flex flex-col gap-6\">\n            <div className=\"flex flex-col gap-4\">\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"flex items-center justify-between\">\n                  <div className=\"flex flex-col gap-1\">\n                    <span className=\"font-medium text-sm\">Plan</span>\n                    <span className=\"text-muted-foreground text-sm\">\n                      {subscription.planName}\n                    </span>\n                  </div>\n                  <Badge\n                    className=\"shrink-0 text-xs\"\n                    variant={\n                      isActive\n                        ? \"default\"\n                        : isPaused\n                          ? \"secondary\"\n                          : \"destructive\"\n                    }\n                  >\n                    {subscription.status.charAt(0).toUpperCase() +\n                      subscription.status.slice(1)}\n                  </Badge>\n                </div>\n              </div>\n\n              <Separator />\n\n              <div className=\"flex flex-col gap-2\">\n                <div className=\"flex items-center justify-between\">\n                  <div className=\"flex flex-col gap-1\">\n                    <span className=\"font-medium text-sm\">Billing Period</span>\n                    <span className=\"text-muted-foreground text-sm\">\n                      {subscription.billingPeriod === \"monthly\"\n                        ? \"Monthly\"\n                        : \"Annual\"}\n                    </span>\n                  </div>\n                  {onChangeBillingPeriod && isActive && (\n                    <Select\n                      disabled={isLoading}\n                      onValueChange={(value) =>\n                        handleChangeBillingPeriod(value as \"monthly\" | \"annual\")\n                      }\n                      value={subscription.billingPeriod ?? \"monthly\"}\n                    >\n                      <SelectTrigger className=\"w-[140px]\">\n                        <SelectValue />\n                      </SelectTrigger>\n                      <SelectContent>\n                        <SelectItem value=\"monthly\">Monthly</SelectItem>\n                        <SelectItem value=\"annual\">Annual</SelectItem>\n                      </SelectContent>\n                    </Select>\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\">\n                  <div className=\"flex flex-col gap-1\">\n                    <div className=\"flex items-center gap-2\">\n                      <Calendar className=\"size-4 text-muted-foreground\" />\n                      <span className=\"font-medium text-sm\">Billing Date</span>\n                    </div>\n                    <span className=\"text-muted-foreground text-sm\">\n                      {subscription.nextBillingDate\n                        ? formatDate(subscription.nextBillingDate)\n                        : formatDate(subscription.currentBillingDate)}\n                    </span>\n                  </div>\n                </div>\n              </div>\n\n              {subscription.pausedUntil && (\n                <>\n                  <Separator />\n                  <div className=\"flex flex-col gap-2\">\n                    <div className=\"flex items-center justify-between\">\n                      <div className=\"flex flex-col gap-1\">\n                        <span className=\"font-medium text-sm\">\n                          Paused Until\n                        </span>\n                        <span className=\"text-muted-foreground text-sm\">\n                          {formatDate(subscription.pausedUntil)}\n                        </span>\n                      </div>\n                    </div>\n                  </div>\n                </>\n              )}\n\n              {subscription.canceledAt && (\n                <>\n                  <Separator />\n                  <div className=\"flex flex-col gap-2\">\n                    <div className=\"flex items-center justify-between\">\n                      <div className=\"flex flex-col gap-1\">\n                        <span className=\"font-medium text-sm\">Canceled On</span>\n                        <span className=\"text-muted-foreground text-sm\">\n                          {formatDate(subscription.canceledAt)}\n                        </span>\n                      </div>\n                    </div>\n                    {subscription.cancelAtPeriodEnd && (\n                      <p className=\"text-muted-foreground text-xs\">\n                        Your subscription will end on{\" \"}\n                        {subscription.nextBillingDate\n                          ? formatDate(subscription.nextBillingDate)\n                          : formatDate(subscription.currentBillingDate)}\n                      </p>\n                    )}\n                  </div>\n                </>\n              )}\n\n              <Separator />\n\n              <div className=\"flex items-center justify-between\">\n                <div className=\"flex flex-col gap-1\">\n                  <span className=\"font-medium text-sm\">Auto-renewal</span>\n                  <span className=\"text-muted-foreground text-xs\">\n                    {subscription.autoRenew\n                      ? \"Your subscription will renew automatically\"\n                      : \"Your subscription will not renew\"}\n                  </span>\n                </div>\n                {onToggleAutoRenew && isActive && (\n                  <Switch\n                    checked={subscription.autoRenew}\n                    disabled={isLoading}\n                    onCheckedChange={handleToggleAutoRenew}\n                  />\n                )}\n              </div>\n\n              {subscription.prorationPreview && (\n                <>\n                  <Separator />\n                  <div className=\"flex flex-col gap-2 rounded-lg border bg-muted/50 p-4\">\n                    <h4 className=\"font-medium text-sm\">Proration Preview</h4>\n                    <div className=\"flex flex-col gap-2 text-muted-foreground text-sm\">\n                      <div className=\"flex items-center justify-between\">\n                        <span>New Amount:</span>\n                        <span className=\"font-medium text-foreground\">\n                          {formatPrice(\n                            subscription.prorationPreview.newAmount,\n                            currency\n                          )}\n                        </span>\n                      </div>\n                      {subscription.prorationPreview.creditAmount > 0 && (\n                        <div className=\"flex items-center justify-between\">\n                          <span>Credit Applied:</span>\n                          <span className=\"font-medium text-green-600\">\n                            -\n                            {formatPrice(\n                              subscription.prorationPreview.creditAmount,\n                              currency\n                            )}\n                          </span>\n                        </div>\n                      )}\n                      <div className=\"flex items-center justify-between\">\n                        <span>Next Billing Date:</span>\n                        <span className=\"text-foreground\">\n                          {formatDate(\n                            subscription.prorationPreview.nextBillingDate\n                          )}\n                        </span>\n                      </div>\n                    </div>\n                  </div>\n                </>\n              )}\n            </div>\n\n            <Separator />\n\n            <div className=\"flex flex-col gap-2\">\n              {isPaused && onResume && (\n                <Button\n                  aria-busy={isLoading}\n                  className=\"w-full\"\n                  data-loading={isLoading}\n                  onClick={handleResume}\n                  type=\"button\"\n                >\n                  {isLoading ? (\n                    <>\n                      <Loader2 className=\"size-4 animate-spin\" />\n                      Resuming…\n                    </>\n                  ) : (\n                    <>\n                      <Play className=\"size-4\" />\n                      Resume Subscription\n                    </>\n                  )}\n                </Button>\n              )}\n\n              {isActive && onPause && (\n                <Button\n                  className=\"w-full\"\n                  onClick={() => setShowPauseDialog(true)}\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  <Pause className=\"size-4\" />\n                  Pause Subscription\n                </Button>\n              )}\n\n              {isCanceled && onReactivate && (\n                <Button\n                  aria-busy={isLoading}\n                  className=\"w-full\"\n                  data-loading={isLoading}\n                  onClick={handleReactivate}\n                  type=\"button\"\n                >\n                  {isLoading ? (\n                    <>\n                      <Loader2 className=\"size-4 animate-spin\" />\n                      Reactivating…\n                    </>\n                  ) : (\n                    <>\n                      <RefreshCw className=\"size-4\" />\n                      Reactivate Subscription\n                    </>\n                  )}\n                </Button>\n              )}\n\n              {isActive && onCancel && (\n                <Button\n                  className=\"w-full\"\n                  onClick={() => setShowCancelDialog(true)}\n                  type=\"button\"\n                  variant=\"destructive\"\n                >\n                  <X className=\"size-4\" />\n                  Cancel Subscription\n                </Button>\n              )}\n            </div>\n          </div>\n        </CardContent>\n      </Card>\n\n      <AlertDialog onOpenChange={setShowCancelDialog} open={showCancelDialog}>\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogTitle>Cancel Subscription</AlertDialogTitle>\n            <AlertDialogDescription>\n              Are you sure you want to cancel your subscription? You&apos;ll\n              lose access to all premium features at the end of your billing\n              period.\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex flex-col gap-2\">\n              <label className=\"font-medium text-sm\" htmlFor=\"cancel-feedback\">\n                Reason for canceling (optional)\n              </label>\n              <textarea\n                className=\"min-h-[80px] w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n                id=\"cancel-feedback\"\n                onChange={(e) => setCancelFeedback(e.target.value)}\n                placeholder=\"Help us improve by sharing why you're canceling...\"\n                value={cancelFeedback}\n              />\n            </div>\n          </div>\n          <AlertDialogFooter>\n            <AlertDialogCancel>Keep Subscription</AlertDialogCancel>\n            <AlertDialogAction\n              aria-busy={isLoading}\n              data-loading={isLoading}\n              onClick={handleCancel}\n              type=\"button\"\n            >\n              {isLoading ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Canceling…\n                </>\n              ) : (\n                \"Cancel Subscription\"\n              )}\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n\n      <AlertDialog onOpenChange={setShowPauseDialog} open={showPauseDialog}>\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogTitle>Pause Subscription</AlertDialogTitle>\n            <AlertDialogDescription>\n              Your subscription will be paused and you won&apos;t be charged\n              during the pause period. You can resume at any time.\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <div className=\"flex flex-col gap-4\">\n            <div className=\"flex flex-col gap-2\">\n              <label className=\"font-medium text-sm\" htmlFor=\"pause-duration\">\n                Pause Duration\n              </label>\n              <Select\n                onValueChange={(value) =>\n                  setPauseDuration(value as \"1\" | \"3\" | \"6\" | \"custom\")\n                }\n                value={pauseDuration}\n              >\n                <SelectTrigger id=\"pause-duration\">\n                  <SelectValue />\n                </SelectTrigger>\n                <SelectContent>\n                  <SelectItem value=\"1\">1 Month</SelectItem>\n                  <SelectItem value=\"3\">3 Months</SelectItem>\n                  <SelectItem value=\"6\">6 Months</SelectItem>\n                  <SelectItem value=\"custom\">Custom Date</SelectItem>\n                </SelectContent>\n              </Select>\n            </div>\n          </div>\n          <AlertDialogFooter>\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <AlertDialogAction\n              aria-busy={isLoading}\n              data-loading={isLoading}\n              onClick={handlePause}\n              type=\"button\"\n            >\n              {isLoading ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Pausing…\n                </>\n              ) : (\n                \"Pause Subscription\"\n              )}\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n    </>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}