{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "billing-upgrade-prompt",
  "title": "Billing Upgrade Prompt",
  "description": "Prompt users to upgrade their plan with feature highlights.",
  "registryDependencies": [
    "badge",
    "button",
    "card",
    "separator"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/billing/billing-upgrade-prompt.tsx",
      "content": "\"use client\";\n\nimport { Check, Loader2, Sparkles, TrendingUp, X } 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 {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/new-york/ui/card\";\nimport { Separator } from \"@/registry/new-york/ui/separator\";\n\nexport interface UpgradeFeature {\n  name: string;\n  description?: string;\n  icon?: React.ComponentType<{ className?: string }>;\n}\n\nexport interface BillingUpgradePromptProps {\n  currentPlan: {\n    id: string;\n    name: string;\n  };\n  recommendedPlan: {\n    id: string;\n    name: string;\n    price: number;\n    currency?: string;\n    billingPeriod?: \"monthly\" | \"annual\";\n  };\n  features?: UpgradeFeature[];\n  reason?: \"usage_limit\" | \"feature_unlock\" | \"recommended\" | \"custom\";\n  customMessage?: string;\n  onUpgrade?: () => void;\n  onDismiss?: () => void;\n  onLearnMore?: () => void;\n  className?: string;\n  variant?: \"banner\" | \"card\" | \"modal\";\n  showSavings?: boolean;\n  savingsAmount?: number;\n  limitedTime?: boolean;\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\nexport default function BillingUpgradePrompt({\n  currentPlan,\n  recommendedPlan,\n  features = [],\n  reason = \"recommended\",\n  customMessage,\n  onUpgrade,\n  onDismiss,\n  onLearnMore,\n  className,\n  variant = \"card\",\n  showSavings = false,\n  savingsAmount = 0,\n  limitedTime = false,\n}: BillingUpgradePromptProps) {\n  const [isLoading, setIsLoading] = useState(false);\n  const [isDismissed, setIsDismissed] = useState(false);\n\n  const handleUpgrade = async () => {\n    setIsLoading(true);\n    try {\n      await onUpgrade?.();\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleDismiss = () => {\n    setIsDismissed(true);\n    onDismiss?.();\n  };\n\n  if (isDismissed) return null;\n\n  const getReasonMessage = () => {\n    switch (reason) {\n      case \"usage_limit\":\n        return `You've used 90% of your ${currentPlan.name} plan limit. Upgrade to continue using all features.`;\n      case \"feature_unlock\":\n        return `Unlock advanced features with ${recommendedPlan.name}.`;\n      case \"recommended\":\n        return `${recommendedPlan.name} is recommended for your usage.`;\n      case \"custom\":\n        return (\n          customMessage ||\n          `Upgrade to ${recommendedPlan.name} for more features.`\n        );\n      default:\n        return `Upgrade to ${recommendedPlan.name} for more features.`;\n    }\n  };\n\n  const currency = recommendedPlan.currency || \"USD\";\n  const price = formatPrice(recommendedPlan.price, currency);\n  const period =\n    recommendedPlan.billingPeriod === \"annual\" ? \"/year\" : \"/month\";\n\n  if (variant === \"banner\") {\n    return (\n      <div\n        className={cn(\n          \"flex flex-col gap-4 rounded-lg border bg-linear-to-r from-primary/10 to-primary/5 p-4 sm:flex-row sm:items-center sm:justify-between\",\n          className\n        )}\n      >\n        <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n          <div className=\"flex flex-wrap items-center gap-2\">\n            <Sparkles className=\"size-5 shrink-0 text-primary\" />\n            <h3 className=\"font-semibold text-sm\">\n              Upgrade to {recommendedPlan.name}\n            </h3>\n            {limitedTime && (\n              <Badge className=\"bg-primary text-primary-foreground text-xs\">\n                Limited time\n              </Badge>\n            )}\n          </div>\n          <p className=\"wrap-break-word text-muted-foreground text-sm\">\n            {getReasonMessage()}\n          </p>\n          {features.length > 0 && (\n            <div className=\"flex flex-wrap items-center gap-2 text-sm\">\n              {features.slice(0, 3).map((feature, idx) => (\n                <div className=\"flex items-center gap-1\" key={idx}>\n                  <Check className=\"size-3.5 text-primary\" />\n                  <span className=\"wrap-break-word\">{feature.name}</span>\n                </div>\n              ))}\n              {features.length > 3 && (\n                <span className=\"text-muted-foreground\">\n                  +{features.length - 3} more\n                </span>\n              )}\n            </div>\n          )}\n        </div>\n        <div className=\"flex shrink-0 flex-col gap-2 sm:flex-row\">\n          {onDismiss && (\n            <Button\n              aria-label=\"Dismiss upgrade prompt\"\n              onClick={handleDismiss}\n              size=\"icon\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X className=\"size-4\" />\n            </Button>\n          )}\n          {onLearnMore && (\n            <Button onClick={onLearnMore} type=\"button\" variant=\"outline\">\n              Learn more\n            </Button>\n          )}\n          {onUpgrade && (\n            <Button\n              aria-busy={isLoading}\n              data-loading={isLoading}\n              onClick={handleUpgrade}\n              type=\"button\"\n            >\n              {isLoading ? (\n                <>\n                  <Loader2 className=\"size-4 animate-spin\" />\n                  Upgrading…\n                </>\n              ) : (\n                <>\n                  <TrendingUp className=\"size-4\" />\n                  Upgrade to {price}\n                  {period}\n                </>\n              )}\n            </Button>\n          )}\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <Card className={cn(\"relative w-full shadow-xs\", className)}>\n      {onDismiss && (\n        <Button\n          aria-label=\"Dismiss upgrade prompt\"\n          className=\"absolute top-4 right-4 z-10\"\n          onClick={handleDismiss}\n          size=\"icon\"\n          type=\"button\"\n          variant=\"ghost\"\n        >\n          <X className=\"size-4\" />\n        </Button>\n      )}\n      <CardHeader>\n        <div className=\"flex min-w-0 flex-1 flex-col gap-2 pr-8\">\n          <div className=\"flex flex-wrap items-center gap-2\">\n            <CardTitle className=\"wrap-break-word\">\n              Upgrade to {recommendedPlan.name}\n            </CardTitle>\n            {limitedTime && (\n              <Badge className=\"shrink-0 bg-primary text-primary-foreground text-xs\">\n                Limited time\n              </Badge>\n            )}\n          </div>\n          <CardDescription className=\"wrap-break-word\">\n            {getReasonMessage()}\n          </CardDescription>\n        </div>\n      </CardHeader>\n      <CardContent>\n        <div className=\"flex flex-col gap-6\">\n          <div className=\"flex flex-col gap-2 sm:flex-row sm:items-baseline sm:gap-3\">\n            <div className=\"flex items-baseline gap-2\">\n              <span className=\"font-semibold text-3xl\">{price}</span>\n              <span className=\"text-muted-foreground text-sm\">{period}</span>\n            </div>\n            {showSavings && savingsAmount > 0 && (\n              <Badge className=\"shrink-0 text-xs\" variant=\"secondary\">\n                Save {formatPrice(savingsAmount, currency)}/year\n              </Badge>\n            )}\n          </div>\n\n          {features.length > 0 && (\n            <>\n              <Separator />\n              <div className=\"flex flex-col gap-3\">\n                <h4 className=\"font-medium text-sm\">What you&apos;ll get:</h4>\n                <div className=\"flex flex-col gap-2\">\n                  {features.map((feature, idx) => (\n                    <div className=\"flex items-start gap-3\" key={idx}>\n                      {feature.icon ? (\n                        <feature.icon className=\"size-4 shrink-0 text-primary\" />\n                      ) : (\n                        <Check className=\"size-4 shrink-0 text-primary\" />\n                      )}\n                      <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n                        <span className=\"wrap-break-word text-sm\">\n                          {feature.name}\n                        </span>\n                        {feature.description && (\n                          <span className=\"wrap-break-word text-muted-foreground text-xs\">\n                            {feature.description}\n                          </span>\n                        )}\n                      </div>\n                    </div>\n                  ))}\n                </div>\n              </div>\n            </>\n          )}\n\n          <Separator />\n\n          <div className=\"flex flex-col gap-2\">\n            {onUpgrade && (\n              <Button\n                aria-busy={isLoading}\n                className=\"w-full\"\n                data-loading={isLoading}\n                onClick={handleUpgrade}\n                type=\"button\"\n              >\n                {isLoading ? (\n                  <>\n                    <Loader2 className=\"size-4 animate-spin\" />\n                    Upgrading…\n                  </>\n                ) : (\n                  <>\n                    <TrendingUp className=\"size-4\" />\n                    Upgrade now\n                  </>\n                )}\n              </Button>\n            )}\n            {onLearnMore && (\n              <Button\n                className=\"w-full\"\n                onClick={onLearnMore}\n                type=\"button\"\n                variant=\"outline\"\n              >\n                Learn more\n              </Button>\n            )}\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "billing"
  ],
  "type": "registry:ui"
}