CrawlerX-frontend/components/pricing-table/components-pricing-table-toggle.tsx
2025-09-27 13:36:36 +05:30

169 lines
6.0 KiB
TypeScript

'use client';
import React, { useState } from 'react';
import { loadStripe, Stripe } from '@stripe/stripe-js';
import axios from 'axios';
interface Plan {
name: string;
desc: string;
monthly: number;
yearly: number;
features: string[];
btnStyle: string;
popular?: boolean;
planId: string;
}
// ⚡ Lazy load Stripe to avoid undefined.match errors
let stripePromise: Promise<Stripe | null> | null = null;
const getStripe = () => {
if (!stripePromise) {
const key = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
if (!key) throw new Error('Stripe publishable key is not defined!');
stripePromise = loadStripe(key);
}
return stripePromise;
};
const ComponentsPricingTableToggle: React.FC = () => {
const [yearlyPrice, setYearlyPrice] = useState(false);
const plans: Plan[] = [
{
name: 'Starter SEO Crawl',
desc: 'Perfect for small websites. Crawl up to 5,000 pages per month.',
monthly: 19,
yearly: 19 * 12 * 0.8,
features: ['5,000 Pages/Month', 'Basic Crawl Reports', 'Email Support'],
btnStyle: 'btn-dark',
planId: 'starter-seo-crawl',
},
{
name: 'Pro SEO Crawl',
desc: 'Best for medium websites. Crawl up to 50,000 pages per month.',
monthly: 49,
yearly: 49 * 12 * 0.8,
features: ['50,000 Pages/Month', 'Advanced Crawl Reports', 'Priority Support'],
btnStyle: 'btn-primary',
popular: true,
planId: 'pro-seo-crawl',
},
{
name: 'Enterprise SEO Crawl',
desc: 'For large-scale websites. Unlimited crawling and dedicated support.',
monthly: 199,
yearly: 199 * 12 * 0.8,
features: ['Unlimited Pages', 'Custom Integrations', 'Dedicated Account Manager'],
btnStyle: 'btn-dark',
planId: 'enterprise-seo-crawl',
},
];
// 🔹 Stripe Checkout handler
const handleBuyNow = async (plan: Plan) => {
try {
const stripe = await getStripe();
if (!stripe) return;
// 🔹 Call backend API to create Checkout Session
const { data } = await axios.post(
'https://api.crawlerx.co/api/payment/create-intent',
{
planId: plan.planId,
price: yearlyPrice ? plan.yearly : plan.monthly,
billing: yearlyPrice ? 'yearly' : 'monthly',
}
);
if (!data.sessionId) {
console.error('No sessionId returned from backend!');
return;
}
// 🔹 Redirect to Stripe Checkout
const { error } = await stripe.redirectToCheckout({ sessionId: data.sessionId });
if (error) console.error('Stripe redirect error:', error.message);
} catch (err: any) {
console.error('Error creating checkout session:', err.response?.data || err.message);
}
};
return (
<div className="mb-5 panel">
<div className="mx-auto max-w-[320px] dark:text-white-dark md:max-w-[1140px]">
{/* Toggle */}
<div className="mt-5 flex justify-center space-x-4 text-center text-base font-semibold md:mt-10">
<span className={`${!yearlyPrice ? 'text-primary' : 'text-white-dark'}`}>Monthly</span>
<label className="relative h-6 w-12">
<input
type="checkbox"
className="custom_switch peer absolute top-0 z-10 h-full w-full cursor-pointer opacity-0"
onChange={() => setYearlyPrice(!yearlyPrice)}
/>
<span className="outline_checkbox bg-icon block h-full rounded-full border-2 border-[#ebedf2]
before:absolute before:bottom-1 before:h-4 before:w-4 before:rounded-full before:bg-[#ebedf2]
before:transition-all before:duration-300 peer-checked:border-primary peer-checked:before:bg-primary
ltr:before:left-1 ltr:peer-checked:before:left-7">
</span>
</label>
<span className={`relative ${yearlyPrice ? 'text-primary' : ' text-white-dark'} `}>
Yearly
<span className="badge absolute my-auto hidden whitespace-nowrap rounded-full bg-success ltr:left-full ltr:ml-2">
20% Off
</span>
</span>
</div>
{/* Plans */}
<div className="mt-5 space-y-4 text-white-dark md:mt-16 md:flex md:space-y-0">
{plans.map((plan, idx) => (
<div
key={idx}
className="relative rounded-md border border-white-light p-4 transition-all duration-300
hover:shadow-[0_0_15px_1px_rgba(113,106,202,0.20)] dark:border-[#1b2e4b] lg:p-9"
>
{plan.popular && (
<div className="absolute inset-x-0 top-0 flex h-10 items-center justify-center rounded-t-md bg-primary text-base text-white md:-top-[30px]">
Most Popular
</div>
)}
<h3 className="mb-5 text-xl font-semibold text-black dark:text-white-light">{plan.name}</h3>
<p>{plan.desc}</p>
<div className="my-7 p-2.5 text-center text-lg">
<strong
className={`text-xl ${
plan.popular ? 'text-primary lg:text-4xl'
: 'text-[#3b3f5c] dark:text-white-light lg:text-3xl'
}`}
>
${yearlyPrice ? plan.yearly.toFixed(0) : plan.monthly}
</strong>{' '}
/ {yearlyPrice ? 'yearly' : 'monthly'}
</div>
<div className="mb-6">
<strong className="mb-3 inline-block text-[15px] text-black dark:text-white-light">
{plan.name} Features
</strong>
<ul className="space-y-3">
{plan.features.map((f, i) => (
<li key={i}>{f}</li>
))}
</ul>
</div>
<button
type="button"
className={`btn ${plan.btnStyle} w-full`}
onClick={() => handleBuyNow(plan)}
>
Buy Now
</button>
</div>
))}
</div>
</div>
</div>
);
};
export default ComponentsPricingTableToggle;