Add subcription feature
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
subscriptionService,
|
||||
type PlanMetadata,
|
||||
type SubscriptionTier,
|
||||
type BillingCycle,
|
||||
SUBSCRIPTION_TIERS
|
||||
} from '../../api';
|
||||
import { getRegisterUrl } from '../../utils/navigation';
|
||||
@@ -23,6 +24,8 @@ interface SubscriptionPricingCardsProps {
|
||||
pilotTrialMonths?: number;
|
||||
showComparison?: boolean;
|
||||
className?: string;
|
||||
billingCycle?: BillingCycle;
|
||||
onBillingCycleChange?: (cycle: BillingCycle) => void;
|
||||
}
|
||||
|
||||
export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> = ({
|
||||
@@ -33,14 +36,19 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
|
||||
pilotCouponCode,
|
||||
pilotTrialMonths = 3,
|
||||
showComparison = false,
|
||||
className = ''
|
||||
className = '',
|
||||
billingCycle: externalBillingCycle,
|
||||
onBillingCycleChange
|
||||
}) => {
|
||||
const { t } = useTranslation('subscription');
|
||||
const [plans, setPlans] = useState<Record<SubscriptionTier, PlanMetadata> | null>(null);
|
||||
const [billingCycle, setBillingCycle] = useState<BillingCycle>('monthly');
|
||||
const [internalBillingCycle, setInternalBillingCycle] = useState<BillingCycle>('monthly');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Use external billing cycle if provided, otherwise use internal state
|
||||
const billingCycle = externalBillingCycle || internalBillingCycle;
|
||||
|
||||
useEffect(() => {
|
||||
loadPlans();
|
||||
}, []);
|
||||
@@ -145,34 +153,48 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Billing Cycle Toggle */}
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="inline-flex rounded-lg border-2 border-[var(--border-primary)] p-1 bg-[var(--bg-secondary)]">
|
||||
<button
|
||||
onClick={() => setBillingCycle('monthly')}
|
||||
className={`px-6 py-2 rounded-md text-sm font-semibold transition-all ${
|
||||
billingCycle === 'monthly'
|
||||
? 'bg-[var(--color-primary)] text-white shadow-md'
|
||||
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-primary)] hover:text-[var(--text-primary)]'
|
||||
}`}
|
||||
>
|
||||
{t('billing.monthly', 'Mensual')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setBillingCycle('yearly')}
|
||||
className={`px-6 py-2 rounded-md text-sm font-semibold transition-all flex items-center gap-2 ${
|
||||
billingCycle === 'yearly'
|
||||
? 'bg-[var(--color-primary)] text-white shadow-md'
|
||||
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-primary)] hover:text-[var(--text-primary)]'
|
||||
}`}
|
||||
>
|
||||
{t('billing.yearly', 'Anual')}
|
||||
<span className="text-xs font-bold text-green-600 dark:text-green-400">
|
||||
{t('billing.save_percent', 'Ahorra 17%')}
|
||||
</span>
|
||||
</button>
|
||||
{/* Billing Cycle Toggle - Only show if not externally controlled */}
|
||||
{!externalBillingCycle && (
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="inline-flex rounded-lg border-2 border-[var(--border-primary)] p-1 bg-[var(--bg-secondary)]">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newCycle: BillingCycle = 'monthly';
|
||||
setInternalBillingCycle(newCycle);
|
||||
if (onBillingCycleChange) {
|
||||
onBillingCycleChange(newCycle);
|
||||
}
|
||||
}}
|
||||
className={`px-6 py-2 rounded-md text-sm font-semibold transition-all ${
|
||||
billingCycle === 'monthly'
|
||||
? 'bg-[var(--color-primary)] text-white shadow-md'
|
||||
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-primary)] hover:text-[var(--text-primary)]'
|
||||
}`}
|
||||
>
|
||||
{t('billing.monthly', 'Mensual')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const newCycle: BillingCycle = 'yearly';
|
||||
setInternalBillingCycle(newCycle);
|
||||
if (onBillingCycleChange) {
|
||||
onBillingCycleChange(newCycle);
|
||||
}
|
||||
}}
|
||||
className={`px-6 py-2 rounded-md text-sm font-semibold transition-all flex items-center gap-2 ${
|
||||
billingCycle === 'yearly'
|
||||
? 'bg-[var(--color-primary)] text-white shadow-md'
|
||||
: 'text-[var(--text-secondary)] hover:bg-[var(--bg-primary)] hover:text-[var(--text-primary)]'
|
||||
}`}
|
||||
>
|
||||
{t('billing.yearly', 'Anual')}
|
||||
<span className="text-xs font-bold text-green-600 dark:text-green-400">
|
||||
{t('billing.save_percent', 'Ahorra 17%')}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Simplified Plans Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 items-start lg:items-stretch">
|
||||
@@ -186,7 +208,7 @@ export const SubscriptionPricingCards: React.FC<SubscriptionPricingCardsProps> =
|
||||
const CardWrapper = mode === 'landing' ? Link : 'div';
|
||||
const isCurrentPlan = mode === 'settings' && selectedPlan === tier;
|
||||
const cardProps = mode === 'landing'
|
||||
? { to: getRegisterUrl(tier) }
|
||||
? { to: getRegisterUrl(tier, billingCycle) }
|
||||
: mode === 'selection' || (mode === 'settings' && !isCurrentPlan)
|
||||
? { onClick: () => handlePlanAction(tier, plan) }
|
||||
: {};
|
||||
|
||||
Reference in New Issue
Block a user