{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "update-plan-card",
  "title": "Update Plan Card",
  "description": "A update plan card component",
  "dependencies": ["lucide-react", "motion"],
  "registryDependencies": [
    "button",
    "badge",
    "radio-group",
    "toggle",
    "label",
    "card",
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/billingsdk/update-plan-card.tsx",
      "content": "\"use client\";\n\nimport { useState, useCallback } from \"react\";\nimport { motion, AnimatePresence } from \"motion/react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { Toggle } from \"@/components/ui/toggle\";\nimport { Label } from \"@/components/ui/label\";\nimport { type Plan } from \"@/lib/billingsdk-config\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface UpdatePlanCardProps {\n  currentPlan: Plan;\n  plans: Plan[];\n  onPlanChange: (planId: string) => void;\n  className?: string;\n  title?: string;\n}\n\nconst easing = [0.4, 0, 0.2, 1] as const;\n\nexport function UpdatePlanCard({\n  currentPlan,\n  plans,\n  onPlanChange,\n  className,\n  title,\n}: UpdatePlanCardProps) {\n  const [isYearly, setIsYearly] = useState(false);\n  const [selectedPlan, setSelectedPlan] = useState<string | undefined>(\n    undefined,\n  );\n\n  const getCurrentPrice = useCallback(\n    (plan: Plan) => (isYearly ? `${plan.yearlyPrice}` : `${plan.monthlyPrice}`),\n    [isYearly],\n  );\n\n  const handlePlanChange = useCallback((planId: string) => {\n    setSelectedPlan((prev) => (prev === planId ? undefined : planId));\n  }, []);\n\n  return (\n    <Card\n      className={cn(\n        \"mx-auto w-full max-w-xl overflow-hidden text-left shadow-lg\",\n        className,\n      )}\n    >\n      <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n        <CardTitle className=\"text-base font-semibold\">\n          {title || \"Upgrade Plan\"}\n        </CardTitle>\n        <div className=\"flex items-center gap-2 text-sm\">\n          <Toggle\n            size=\"sm\"\n            pressed={!isYearly}\n            onPressedChange={(pressed) => setIsYearly(!pressed)}\n            className=\"px-3\"\n          >\n            Monthly\n          </Toggle>\n          <Toggle\n            pressed={isYearly}\n            onPressedChange={(pressed) => setIsYearly(pressed)}\n            className=\"px-3\"\n          >\n            Yearly\n          </Toggle>\n        </div>\n      </CardHeader>\n      <CardContent className=\"space-y-3\">\n        <RadioGroup value={selectedPlan} onValueChange={handlePlanChange}>\n          <div className=\"space-y-2.5 sm:space-y-3\">\n            {plans.map((plan, index) => (\n              <motion.div\n                key={plan.id}\n                layout\n                initial={{ opacity: 0, y: 20 }}\n                animate={{ opacity: 1, y: 0 }}\n                transition={{\n                  layout: { duration: 0.3, ease: easing },\n                  opacity: { delay: index * 0.05, duration: 0.3, ease: easing },\n                  y: { delay: index * 0.05, duration: 0.3, ease: easing },\n                }}\n                onClick={() => handlePlanChange(plan.id)}\n                onKeyDown={(e) => {\n                  if (e.key === \"Enter\" || e.key === \" \") {\n                    e.preventDefault();\n                    handlePlanChange(plan.id);\n                  }\n                }}\n                role=\"button\"\n                tabIndex={0}\n                aria-pressed={selectedPlan === plan.id}\n                className={cn(\n                  \"relative cursor-pointer overflow-hidden rounded-lg border transition-all duration-200 sm:rounded-xl\",\n                  \"focus-visible:ring-primary touch-manipulation focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none\",\n                  selectedPlan === plan.id\n                    ? \"border-primary from-muted/60 to-muted/30 bg-gradient-to-br shadow-sm\"\n                    : \"border-border hover:border-primary/50\",\n                )}\n              >\n                <motion.div layout=\"position\" className=\"p-3 sm:p-4\">\n                  <div className=\"flex items-start justify-between gap-2 sm:gap-3\">\n                    <div className=\"flex min-w-0 flex-1 gap-2 sm:gap-3\">\n                      <RadioGroupItem\n                        value={plan.id}\n                        id={plan.id}\n                        className=\"pointer-events-none mt-0.5 flex-shrink-0 sm:mt-1\"\n                      />\n                      <div className=\"min-w-0 flex-1\">\n                        <div className=\"flex flex-wrap items-center gap-1.5 sm:gap-2\">\n                          <Label\n                            htmlFor={plan.id}\n                            className=\"cursor-pointer text-sm leading-tight font-semibold sm:text-base sm:font-medium\"\n                          >\n                            {plan.title}\n                          </Label>\n                          {plan.badge && (\n                            <Badge\n                              variant=\"secondary\"\n                              className=\"h-5 flex-shrink-0 px-1.5 py-0 text-[10px] sm:h-auto sm:px-2 sm:py-0.5 sm:text-xs\"\n                            >\n                              {plan.badge}\n                            </Badge>\n                          )}\n                        </div>\n                        <p className=\"text-muted-foreground mt-1 text-[11px] leading-relaxed sm:text-xs\">\n                          {plan.description}\n                        </p>\n                        {plan.features.length > 0 && (\n                          <div className=\"pt-2 sm:pt-3\">\n                            <div className=\"flex flex-wrap gap-1.5 sm:gap-2\">\n                              {plan.features.map((feature, featureIndex) => (\n                                <div\n                                  key={featureIndex}\n                                  className=\"bg-muted/20 border-border/30 flex flex-shrink-0 items-center gap-1.5 rounded-md border px-2 py-1 sm:gap-2 sm:rounded-lg\"\n                                >\n                                  <div className=\"bg-primary h-1 w-1 flex-shrink-0 rounded-full sm:h-1.5 sm:w-1.5\" />\n                                  <span className=\"text-muted-foreground text-[10px] leading-none whitespace-nowrap sm:text-xs\">\n                                    {feature.name}\n                                  </span>\n                                </div>\n                              ))}\n                            </div>\n                          </div>\n                        )}\n                      </div>\n                    </div>\n                    <div className=\"min-w-[60px] flex-shrink-0 text-right sm:min-w-[80px]\">\n                      <div className=\"text-base leading-tight font-bold sm:text-xl sm:font-semibold\">\n                        {parseFloat(getCurrentPrice(plan)) >= 0\n                          ? `${plan.currency}${getCurrentPrice(plan)}`\n                          : getCurrentPrice(plan)}\n                      </div>\n                      <div className=\"text-muted-foreground mt-0.5 text-[10px] sm:text-xs\">\n                        /{isYearly ? \"year\" : \"month\"}\n                      </div>\n                    </div>\n                  </div>\n                </motion.div>\n\n                <AnimatePresence initial={false}>\n                  {selectedPlan === plan.id && (\n                    <motion.div\n                      initial={{ height: 0, opacity: 0 }}\n                      animate={{\n                        height: \"auto\",\n                        opacity: 1,\n                        transition: {\n                          height: { duration: 0.3, ease: easing },\n                          opacity: {\n                            duration: 0.25,\n                            delay: 0.05,\n                            ease: easing,\n                          },\n                        },\n                      }}\n                      exit={{\n                        height: 0,\n                        opacity: 0,\n                        transition: {\n                          height: { duration: 0.25, ease: easing },\n                          opacity: { duration: 0.15, ease: easing },\n                        },\n                      }}\n                      className=\"overflow-hidden\"\n                    >\n                      <motion.div\n                        initial={{ y: -8 }}\n                        animate={{\n                          y: 0,\n                          transition: {\n                            duration: 0.25,\n                            delay: 0.05,\n                            ease: easing,\n                          },\n                        }}\n                        exit={{ y: -8 }}\n                        className=\"px-3 pb-3 sm:px-4 sm:pb-4\"\n                      >\n                        <Button\n                          className=\"h-10 w-full touch-manipulation text-sm font-medium sm:h-11 sm:text-base\"\n                          disabled={selectedPlan === currentPlan.id}\n                          onClick={(e) => {\n                            e.stopPropagation();\n                            onPlanChange(plan.id);\n                          }}\n                        >\n                          {selectedPlan === currentPlan.id\n                            ? \"Current Plan\"\n                            : \"Upgrade\"}\n                        </Button>\n                      </motion.div>\n                    </motion.div>\n                  )}\n                </AnimatePresence>\n              </motion.div>\n            ))}\n          </div>\n        </RadioGroup>\n      </CardContent>\n    </Card>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/billingsdk/update-plan-card.tsx"
    },
    {
      "path": "src/registry/billingsdk/demo/update-plan-card-demo.tsx",
      "content": "\"use client\";\n\nimport { UpdatePlanCard } from \"@/components/billingsdk/update-plan-card\";\nimport { plans } from \"@/lib/billingsdk-config\";\n\nexport function UpdatePlanCardDemo() {\n  return (\n    <main className=\"flex w-full flex-1 flex-col justify-center text-center\">\n      <UpdatePlanCard\n        currentPlan={plans[0]}\n        plans={plans}\n        onPlanChange={(planId) => {\n          console.log(\"Upgrade plan to\", planId);\n        }}\n      />\n    </main>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/update-plan-card-demo.tsx"
    },
    {
      "path": "src/registry/lib/billingsdk-config.ts",
      "content": "export interface Plan {\n  id: string;\n  title: string;\n  description: string;\n  highlight?: boolean;\n  type?: \"monthly\" | \"yearly\";\n  currency?: string;\n  monthlyPrice: string;\n  yearlyPrice: string;\n  buttonText: string;\n  badge?: string;\n  features: {\n    name: string;\n    icon: string;\n    iconColor?: string;\n  }[];\n}\n\nexport interface CurrentPlan {\n  plan: Plan;\n  type: \"monthly\" | \"yearly\" | \"custom\";\n  price?: string;\n  nextBillingDate: string;\n  paymentMethod: string;\n  status: \"active\" | \"inactive\" | \"past_due\" | \"cancelled\";\n}\n\nexport const plans: Plan[] = [\n  {\n    id: \"starter\",\n    title: \"Starter\",\n    description: \"For developers testing out Liveblocks locally.\",\n    currency: \"$\",\n    monthlyPrice: \"0\",\n    yearlyPrice: \"0\",\n    buttonText: \"Start today for free\",\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n  {\n    id: \"pro\",\n    title: \"Pro\",\n    description: \"For companies adding collaboration in production.\",\n    currency: \"$\",\n    monthlyPrice: \"20\",\n    yearlyPrice: \"199\",\n    buttonText: \"Sign up\",\n    badge: \"Most popular\",\n    highlight: true,\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n  {\n    id: \"enterprise\",\n    title: \"Enterprise\",\n    description:\n      \"For organizations that need more support and compliance features.\",\n    currency: \"$\",\n    monthlyPrice: \"Custom\",\n    yearlyPrice: \"Custom\",\n    buttonText: \"Contact sales\",\n    features: [\n      {\n        name: \"Presence\",\n        icon: \"check\",\n        iconColor: \"text-green-500\",\n      },\n      {\n        name: \"Comments\",\n        icon: \"check\",\n        iconColor: \"text-orange-500\",\n      },\n      {\n        name: \"Notifications\",\n        icon: \"check\",\n        iconColor: \"text-teal-500\",\n      },\n      {\n        name: \"Text Editor\",\n        icon: \"check\",\n        iconColor: \"text-blue-500\",\n      },\n      {\n        name: \"Sync Datastore\",\n        icon: \"check\",\n        iconColor: \"text-zinc-500\",\n      },\n    ],\n  },\n];\n",
      "type": "registry:lib",
      "target": "lib/billingsdk-config.ts"
    }
  ],
  "type": "registry:block"
}
