import { useState } from 'react';
export interface PricingPlan {
id?: number;
name: string;
price: string | number; // Current displayed price (will be calculated based on period)
monthlyPrice?: string | number; // Base monthly price (used for annual discount calculation)
annualDiscountPercent?: number; // Annual discount percentage from backend (default 15%)
originalPrice?: string | number;
period?: string; // "/month", "/year", "/Lifetime"
description?: string;
features: string[];
buttonText?: string;
highlighted?: boolean; // For featured/popular plan
icon?: React.ReactNode;
disabled?: boolean;
recommended?: boolean; // For "Recommended" badge
}
export interface PricingTableProps {
variant?: '1' | '2' | '3'; // Three different table styles
title?: string;
subtitle?: string;
plans: PricingPlan[];
showToggle?: boolean; // Monthly/Annually toggle
onPlanSelect?: (plan: PricingPlan) => void;
className?: string;
}
// Checkmark SVG Icon
const CheckIcon = () => (
);
// X Icon for excluded features
const XIcon = () => (
);
export default function PricingTable({
variant = '1',
title,
subtitle,
plans,
showToggle = false,
onPlanSelect,
className = '',
}: PricingTableProps) {
const [billingPeriod, setBillingPeriod] = useState<'monthly' | 'annually'>('monthly');
const handlePlanClick = (plan: PricingPlan) => {
if (plan.disabled) return;
onPlanSelect?.(plan);
};
const formatPrice = (price: string | number) => {
if (typeof price === 'number') {
return price.toFixed(2);
}
return price;
};
// Calculate price based on billing period with discount from backend
const getDisplayPrice = (plan: PricingPlan): { price: number; originalPrice?: number } => {
const monthlyPrice = typeof plan.monthlyPrice === 'number'
? plan.monthlyPrice
: typeof plan.price === 'number'
? plan.price
: parseFloat(String(plan.price || 0));
if (billingPeriod === 'annually' && showToggle) {
// Get discount percentage from plan (default 15%)
const discountPercent = plan.annualDiscountPercent || 15;
const discountMultiplier = (100 - discountPercent) / 100;
// Annual price: monthly * 12 * discount multiplier
const annualPrice = monthlyPrice * 12 * discountMultiplier;
const originalAnnualPrice = monthlyPrice * 12;
return { price: annualPrice, originalPrice: originalAnnualPrice };
}
// Monthly price
return { price: monthlyPrice };
};
// Variant 1: With toggle and highlighted center card
if (variant === '1') {
return (
{title && (
{title}
)}
{showToggle && (
{billingPeriod === 'annually' && (
Save 15% with annual billing
)}
)}
{plans.map((plan, index) => {
const isHighlighted = plan.highlighted || false; // Use explicit highlighted prop
const displayPrice = getDisplayPrice(plan);
const period = billingPeriod === 'annually' && showToggle ? '/year' : (plan.period || '/month');
return (
{plan.name}
${formatPrice(displayPrice.price)}
{period}
{(displayPrice.originalPrice || plan.originalPrice) && (
${formatPrice(displayPrice.originalPrice || plan.originalPrice || 0)}
)}
{plan.description && (
{plan.description}
)}
{plan.features.map((feature, idx) => {
const isExcluded = feature.startsWith('!');
const featureText = isExcluded ? feature.substring(1) : feature;
return (
-
{isExcluded ? : }
{featureText}
);
})}
);
})}
);
}
// Variant 2: With icons and border highlight
if (variant === '2') {
return (
{plans.map((plan, index) => {
const isHighlighted = plan.highlighted || index === 1;
return (
{plan.name}
{plan.icon && (
{plan.icon}
)}
${formatPrice(plan.price)}
{plan.period || ' / Lifetime'}
{plan.description && (
{plan.description}
)}
{plan.features.map((feature, idx) => {
const isExcluded = feature.startsWith('!');
const featureText = isExcluded ? feature.substring(1) : feature;
return (
-
{isExcluded ? : }
{featureText}
);
})}
);
})}
);
}
// Variant 3: Compact with recommended badge
if (variant === '3') {
return (
{plans.map((plan, index) => {
const isRecommended = plan.recommended || index === 2;
return (
{isRecommended && (
Recommended
)}
{plan.name}
{plan.description && (
{plan.description}
)}
{typeof plan.price === 'string' && plan.price.toLowerCase() === 'free'
? 'Free'
: `$${formatPrice(plan.price)}`}
{plan.period || 'For a Lifetime'}
{plan.features.map((feature, idx) => {
const isExcluded = feature.startsWith('!');
const featureText = isExcluded ? feature.substring(1) : feature;
return (
-
{isExcluded ? (
) : (
)}
{featureText}
);
})}
);
})}
);
}
return null;
}