Skip to content

Commit

Permalink
update code pricing component 🔥
Browse files Browse the repository at this point in the history
  • Loading branch information
Codehagen committed Jan 5, 2025
1 parent 008318d commit 29587f6
Show file tree
Hide file tree
Showing 8 changed files with 763 additions and 7 deletions.
11 changes: 11 additions & 0 deletions __registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,16 @@ export const Index: Record<string, any> = {
subcategory: "overlay",
chunks: []
},
"pricing": {
name: "pricing",
type: "registry:ui",
registryDependencies: undefined,
files: ["src/components/prismui/pricing.tsx"],
component: React.lazy(() => import("@/components/prismui/pricing.tsx")),
source: "",
category: "sections",
subcategory: "marketing",
chunks: []
},
},
}
17 changes: 17 additions & 0 deletions public/r/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,5 +211,22 @@
],
"category": "components",
"subcategory": "overlay"
},
{
"name": "pricing",
"type": "registry:ui",
"code": "\"use client\";\n\n// ... (paste the entire component code here)\n",
"files": [
{
"path": "components/prismui/pricing.tsx",
"type": "registry:ui"
}
],
"dependencies": [
"framer-motion",
"canvas-confetti"
],
"category": "sections",
"subcategory": "marketing"
}
]
12 changes: 7 additions & 5 deletions public/r/styles/default/pricing.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{
"name": "pricing",
"type": "registry:block",
"type": "registry:ui",
"code": "\"use client\";\n\n// ... (paste the entire component code here)\n",
"files": [
{
"path": "section/pricing.tsx",
"type": "registry:block",
"content": "\"use client\";\n\nimport Section from \"@/components/section\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { Label } from \"@/components/ui/label\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { siteConfig } from \"@/lib/config\";\nimport useWindowSize from \"@/lib/hooks/use-window-size\";\nimport { cn } from \"@/lib/utils\";\nimport { motion } from \"framer-motion\";\nimport { Check } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { useState, useRef } from \"react\";\nimport { FaStar } from \"react-icons/fa\";\nimport confetti from \"canvas-confetti\";\nimport NumberFlow from \"@number-flow/react\";\n\nexport default function Pricing() {\n const [isMonthly, setIsMonthly] = useState(true);\n const { isDesktop } = useWindowSize();\n const switchRef = useRef<HTMLButtonElement>(null);\n\n const handleToggle = (checked: boolean) => {\n setIsMonthly(!checked);\n if (checked && switchRef.current) {\n const rect = switchRef.current.getBoundingClientRect();\n const x = rect.left + rect.width / 2;\n const y = rect.top + rect.height / 2;\n\n confetti({\n particleCount: 50,\n spread: 60,\n origin: {\n x: x / window.innerWidth,\n y: y / window.innerHeight,\n },\n colors: [\n \"hsl(var(--primary))\",\n \"hsl(var(--accent))\",\n \"hsl(var(--secondary))\",\n \"hsl(var(--muted))\",\n ],\n ticks: 200,\n gravity: 1.2,\n decay: 0.94,\n startVelocity: 30,\n shapes: [\"circle\"],\n });\n }\n };\n\n return (\n <Section>\n <div className=\"text-center space-y-4 mb-12\">\n <h2 className=\"text-4xl font-bold tracking-tight sm:text-5xl\">\n Simple, Transparent Pricing\n </h2>\n <p className=\"text-muted-foreground text-lg\">\n Choose the plan that works for you\n <br />\n All plans include access to our platform, lead generation tools, and\n dedicated support.\n </p>\n </div>\n\n <div className=\"flex justify-center mb-10\">\n <label className=\"relative inline-flex items-center cursor-pointer\">\n <Label>\n <Switch\n ref={switchRef}\n checked={!isMonthly}\n onCheckedChange={handleToggle}\n className=\"relative\"\n />\n </Label>\n </label>\n <span className=\"ml-2 font-semibold\">\n Annual billing <span className=\"text-primary\">(Save 20%)</span>\n </span>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-3 sm:2 gap-4\">\n {[\n {\n name: \"STARTER\",\n price: \"50\",\n yearlyPrice: \"40\",\n period: \"per month\",\n features: [\n \"Up to 10 projects\",\n \"Basic analytics\",\n \"48-hour support response time\",\n \"Limited API access\",\n \"Community support\",\n ],\n description: \"Perfect for individuals and small projects\",\n buttonText: \"Start Free Trial\",\n href: \"/sign-up\",\n isPopular: false,\n },\n {\n name: \"PROFESSIONAL\",\n price: \"99\",\n yearlyPrice: \"79\",\n period: \"per month\",\n features: [\n \"Unlimited projects\",\n \"Advanced analytics\",\n \"24-hour support response time\",\n \"Full API access\",\n \"Priority support\",\n \"Team collaboration\",\n \"Custom integrations\",\n ],\n description: \"Ideal for growing teams and businesses\",\n buttonText: \"Get Started\",\n href: \"/sign-up\",\n isPopular: true,\n },\n {\n name: \"ENTERPRISE\",\n price: \"299\",\n yearlyPrice: \"239\",\n period: \"per month\",\n features: [\n \"Everything in Professional\",\n \"Custom solutions\",\n \"Dedicated account manager\",\n \"1-hour support response time\",\n \"SSO Authentication\",\n \"Advanced security\",\n \"Custom contracts\",\n \"SLA agreement\",\n ],\n description: \"For large organizations with specific needs\",\n buttonText: \"Contact Sales\",\n href: \"/contact\",\n isPopular: false,\n },\n ].map((plan, index) => (\n <motion.div\n key={index}\n initial={{ y: 50, opacity: 1 }}\n whileInView={\n isDesktop\n ? {\n y: plan.isPopular ? -20 : 0,\n opacity: 1,\n x: index === 2 ? -30 : index === 0 ? 30 : 0,\n scale: index === 0 || index === 2 ? 0.94 : 1.0,\n }\n : {}\n }\n viewport={{ once: true }}\n transition={{\n duration: 1.6,\n type: \"spring\",\n stiffness: 100,\n damping: 30,\n delay: 0.4,\n opacity: { duration: 0.5 },\n }}\n className={cn(\n `rounded-2xl border-[1px] p-6 bg-background text-center lg:flex lg:flex-col lg:justify-center relative`,\n plan.isPopular ? \"border-primary border-2\" : \"border-border\",\n \"flex flex-col\",\n !plan.isPopular && \"mt-5\",\n index === 0 || index === 2\n ? \"z-0 transform translate-x-0 translate-y-0 -translate-z-[50px] rotate-y-[10deg]\"\n : \"z-10\",\n index === 0 && \"origin-right\",\n index === 2 && \"origin-left\"\n )}\n >\n {plan.isPopular && (\n <div className=\"absolute top-0 right-0 bg-primary py-0.5 px-2 rounded-bl-xl rounded-tr-xl flex items-center\">\n <FaStar className=\"text-primary-foreground\" />\n <span className=\"text-primary-foreground ml-1 font-sans font-semibold\">\n Popular\n </span>\n </div>\n )}\n <div className=\"flex-1 flex flex-col\">\n <p className=\"text-base font-semibold text-muted-foreground\">\n {plan.name}\n </p>\n <div className=\"mt-6 flex items-center justify-center gap-x-2\">\n <span className=\"text-5xl font-bold tracking-tight text-foreground\">\n <NumberFlow\n value={\n isMonthly ? Number(plan.price) : Number(plan.yearlyPrice)\n }\n format={{\n style: \"currency\",\n currency: \"USD\",\n minimumFractionDigits: 0,\n maximumFractionDigits: 0,\n }}\n formatter={(value) => `$${value}`}\n transformTiming={{\n duration: 500,\n easing: \"ease-out\",\n }}\n willChange\n className=\"font-variant-numeric: tabular-nums\"\n />\n </span>\n {plan.period !== \"Next 3 months\" && (\n <span className=\"text-sm font-semibold leading-6 tracking-wide text-muted-foreground\">\n / {plan.period}\n </span>\n )}\n </div>\n\n <p className=\"text-xs leading-5 text-muted-foreground\">\n {isMonthly ? \"billed monthly\" : \"billed annually\"}\n </p>\n\n <ul className=\"mt-5 gap-2 flex flex-col\">\n {plan.features.map((feature, idx) => (\n <li key={idx} className=\"flex items-center\">\n <Check className=\"mr-2 h-4 w-4 text-primary\" />\n <span>{feature}</span>\n </li>\n ))}\n </ul>\n\n <hr className=\"w-full my-4\" />\n\n <Link\n href={plan.href}\n className={cn(\n buttonVariants({\n variant: \"outline\",\n }),\n \"group relative w-full gap-2 overflow-hidden text-lg font-semibold tracking-tighter\",\n \"transform-gpu ring-offset-current transition-all duration-300 ease-out hover:ring-2 hover:ring-primary hover:ring-offset-1 hover:bg-primary hover:text-primary-foreground\",\n plan.isPopular\n ? \"bg-primary text-primary-foreground\"\n : \"bg-background text-foreground\"\n )}\n >\n {plan.buttonText}\n </Link>\n <p className=\"mt-6 text-xs leading-5 text-muted-foreground\">\n {plan.description}\n </p>\n </div>\n </motion.div>\n ))}\n </div>\n </Section>\n );\n}\n"
"path": "components/prismui/pricing.tsx",
"type": "registry:ui",
"content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport NumberFlow from \"@/components/prismui/number-flow\";\nimport confetti from \"canvas-confetti\";\n\ninterface PricingPlan {\n name: string;\n description: string;\n price: {\n monthly: number;\n yearly: number;\n };\n features: string[];\n isPopular?: boolean;\n buttonText?: string;\n buttonVariant?: \"default\" | \"outline\";\n}\n\ninterface PricingProps {\n title?: string;\n description?: string;\n plans: PricingPlan[];\n className?: string;\n}\n\nexport default function Pricing({\n title = \"Simple, transparent pricing\",\n description = \"Choose the plan that's right for you\",\n plans,\n className,\n}: PricingProps) {\n const [isYearly, setIsYearly] = useState(false);\n\n const handleToggle = () => {\n setIsYearly(!isYearly);\n if (!isYearly) {\n confetti({\n particleCount: 100,\n spread: 70,\n origin: { y: 0.6 },\n });\n }\n };\n\n return (\n <section className={cn(\"py-16 md:py-24\", className)}>\n <div className=\"container px-4 md:px-6\">\n <div className=\"flex flex-col items-center space-y-4 text-center\">\n <h2 className=\"text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl\">\n {title}\n </h2>\n <p className=\"max-w-[700px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400\">\n {description}\n </p>\n <div className=\"flex items-center space-x-2\">\n <span className=\"text-sm font-medium\">Monthly</span>\n <Switch checked={isYearly} onCheckedChange={handleToggle} />\n <span className=\"text-sm font-medium\">Yearly</span>\n {isYearly && (\n <Badge variant=\"secondary\" className=\"ml-2\">\n Save 20%\n </Badge>\n )}\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-6 mt-12 md:grid-cols-3 lg:gap-8\">\n {plans.map((plan, index) => (\n <motion.div\n key={plan.name}\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ delay: index * 0.1 }}\n className=\"relative\"\n >\n <Card\n className={cn(\n \"flex h-full flex-col p-6\",\n plan.isPopular && \"border-primary shadow-lg\"\n )}\n >\n {plan.isPopular && (\n <Badge\n className=\"absolute -top-2 right-4\"\n variant=\"secondary\"\n >\n Most Popular\n </Badge>\n )}\n <div className=\"flex-1 space-y-4\">\n <h3 className=\"text-xl font-bold\">{plan.name}</h3>\n <p className=\"text-sm text-gray-500 dark:text-gray-400\">\n {plan.description}\n </p>\n <div className=\"flex items-baseline\">\n <span className=\"text-3xl font-bold\">$</span>\n <AnimatePresence mode=\"wait\">\n <NumberFlow\n key={isYearly ? \"yearly\" : \"monthly\"}\n value={\n isYearly ? plan.price.yearly : plan.price.monthly\n }\n format={{ minimumFractionDigits: 0 }}\n />\n </AnimatePresence>\n <span className=\"ml-1 text-sm text-gray-500\">\n /{isYearly ? \"year\" : \"month\"}\n </span>\n </div>\n <ul className=\"space-y-2\">\n {plan.features.map((feature) => (\n <li\n key={feature}\n className=\"flex items-center text-sm text-gray-500\"\n >\n <Check className=\"mr-2 h-4 w-4 text-primary\" />\n {feature}\n </li>\n ))}\n </ul>\n </div>\n <Button\n className=\"mt-6 w-full\"\n variant={plan.buttonVariant || \"default\"}\n >\n {plan.buttonText || \"Get Started\"}\n </Button>\n </Card>\n </motion.div>\n ))}\n </div>\n </div>\n </section>\n );\n}\n"
}
],
"dependencies": [
"@/components/ui/button"
"framer-motion",
"canvas-confetti"
]
}
Loading

0 comments on commit 29587f6

Please sign in to comment.