'use client'; import { Check, Crown, Rocket, Shield, X, Zap } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; import { CheckoutModal } from '@/components/subscription/checkout-modal'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Link } from '@/i18n/navigation'; import { useAuthStore } from '@/lib/auth-store'; import { formatVND } from '@/lib/currency'; import { usePlans, useBillingHistory } from '@/lib/hooks/use-subscription'; import type { PlanDto } from '@/lib/subscription-api'; import { cn } from '@/lib/utils'; // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const PLAN_TIER_ORDER = ['FREE', 'AGENT_PRO', 'INVESTOR', 'ENTERPRISE']; const TIER_ICONS: Record = { FREE: , AGENT_PRO: , INVESTOR: , ENTERPRISE: , }; const TIER_COLORS: Record = { FREE: 'text-muted-foreground', AGENT_PRO: 'text-blue-600', INVESTOR: 'text-purple-600', ENTERPRISE: 'text-amber-600', }; /** Fallback data when API is unavailable */ const FALLBACK_PLANS: PlanDto[] = [ { id: 'fallback-free', tier: 'FREE', name: 'Miễn phí', priceMonthlyVND: '0', priceYearlyVND: '0', maxListings: 3, maxSavedSearches: 5, features: { basicSearch: true, listingPost: true, maxPhotos: 5, analytics: false, prioritySupport: false, aiValuation: false, featuredListing: false, }, isActive: true, }, { id: 'fallback-agent', tier: 'AGENT_PRO', name: 'Agent Pro', priceMonthlyVND: '499000', priceYearlyVND: '4990000', maxListings: 50, maxSavedSearches: 30, features: { basicSearch: true, listingPost: true, maxPhotos: 30, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: true, leadManagement: true, agentProfile: true, }, isActive: true, }, { id: 'fallback-investor', tier: 'INVESTOR', name: 'Investor', priceMonthlyVND: '999000', priceYearlyVND: '9990000', maxListings: 20, maxSavedSearches: 100, features: { basicSearch: true, listingPost: true, maxPhotos: 15, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: false, marketReports: true, priceAlerts: true, portfolioTracking: true, }, isActive: true, }, { id: 'fallback-enterprise', tier: 'ENTERPRISE', name: 'Enterprise', priceMonthlyVND: '4990000', priceYearlyVND: '49900000', maxListings: -1, maxSavedSearches: -1, features: { basicSearch: true, listingPost: true, maxPhotos: 100, analytics: true, prioritySupport: true, aiValuation: true, featuredListing: true, leadManagement: true, agentProfile: true, marketReports: true, priceAlerts: true, portfolioTracking: true, apiAccess: true, whiteLabel: true, dedicatedSupport: true, }, isActive: true, }, ]; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- // Feature labels mapped for the comparison table const FEATURE_LABELS: Record = { maxListings: 'Tin đăng', maxSavedSearches: 'Tìm kiếm đã lưu', maxPhotos: 'Ảnh/tin đăng', analytics: 'Phân tích thị trường', prioritySupport: 'Hỗ trợ ưu tiên', aiValuation: 'Định giá AI', featuredListing: 'Tin đăng nổi bật', leadManagement: 'Quản lý khách hàng', agentProfile: 'Hồ sơ môi giới', marketReports: 'Báo cáo thị trường', priceAlerts: 'Cảnh báo giá', portfolioTracking: 'Theo dõi danh mục', apiAccess: 'Truy cập API', whiteLabel: 'Giao diện riêng', dedicatedSupport: 'Hỗ trợ chuyên biệt', }; const COMPARISON_FEATURES = [ 'maxListings', 'maxSavedSearches', 'maxPhotos', 'analytics', 'aiValuation', 'featuredListing', 'prioritySupport', 'leadManagement', 'agentProfile', 'marketReports', 'priceAlerts', 'portfolioTracking', 'apiAccess', 'whiteLabel', 'dedicatedSupport', ]; function getFeatureValue( plan: PlanDto, key: string, ): boolean | number | string { if (key === 'maxListings') { return plan.maxListings === -1 ? 'Không giới hạn' : plan.maxListings; } if (key === 'maxSavedSearches') { return plan.maxSavedSearches === -1 ? 'Không giới hạn' : plan.maxSavedSearches; } if (key === 'maxPhotos') { return plan.features['maxPhotos'] ?? false; } return plan.features[key] ?? false; } // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export default function PricingPage() { const t = useTranslations('pricing'); const { data: plansData, isLoading, error } = usePlans(); const { isAuthenticated } = useAuthStore(); const { data: billing } = useBillingHistory(); const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>( 'monthly', ); // Checkout modal state const [checkoutPlan, setCheckoutPlan] = useState(null); const [checkoutOpen, setCheckoutOpen] = useState(false); const plans = (plansData ?? (error ? FALLBACK_PLANS : [])) .slice() .sort( (a, b) => PLAN_TIER_ORDER.indexOf(a.tier) - PLAN_TIER_ORDER.indexOf(b.tier), ); // Current subscription info for logged-in users const currentTier = billing?.subscription?.planTier ?? 'FREE'; const currentTierIndex = PLAN_TIER_ORDER.indexOf(currentTier); const handleSelectPlan = (plan: PlanDto) => { if (plan.tier === 'ENTERPRISE') return; // Enterprise uses contact form if (plan.tier === 'FREE') return; // Free doesn't need payment if (!isAuthenticated) { // Redirect to register with plan context return; } setCheckoutPlan(plan); setCheckoutOpen(true); }; const getPlanCta = (plan: PlanDto) => { const tierIndex = PLAN_TIER_ORDER.indexOf(plan.tier); if (plan.tier === 'FREE') { if (isAuthenticated && currentTier === 'FREE') { return { label: t('ctaCurrentPlan'), disabled: true, variant: 'outline' as const }; } return { label: t('ctaFree'), disabled: false, variant: 'outline' as const }; } if (plan.tier === 'ENTERPRISE') { return { label: t('ctaEnterprise'), disabled: false, variant: 'outline' as const }; } if (isAuthenticated) { if (plan.tier === currentTier) { return { label: t('ctaCurrentPlan'), disabled: true, variant: 'outline' as const }; } if (tierIndex > currentTierIndex) { return { label: t('ctaUpgrade'), disabled: false, variant: 'default' as const }; } // Downgrade not supported from pricing page return { label: t('ctaDowngrade'), disabled: true, variant: 'outline' as const }; } return { label: t('ctaUpgrade'), disabled: false, variant: 'default' as const }; }; return (
{/* Hero section */}
{t('badge')}

{t('title')}

{t('subtitle')}

{/* Current plan indicator for logged-in users */} {isAuthenticated && billing?.subscription && (
{t('currentPlanBadge', { plan: t(`tiers.${currentTier}`), })}
)} {/* Billing cycle toggle */}
{/* Pricing cards */}
{isLoading ? (
{t('loading')}
) : (
{plans.map((plan) => { const isPopular = plan.tier === 'AGENT_PRO'; const isCurrent = isAuthenticated && plan.tier === currentTier; const price = billingCycle === 'monthly' ? plan.priceMonthlyVND : plan.priceYearlyVND; const cta = getPlanCta(plan); return ( {isPopular && (
{t('popular')}
)} {isCurrent && (
{t('currentPlan')}
)}
{TIER_ICONS[plan.tier]} {t(`tiers.${plan.tier}`)}
{t(`tierDescriptions.${plan.tier}`)}
{/* Price */}
{formatVND(price)} {Number(price) > 0 && ( /{billingCycle === 'monthly' ? t('perMonth') : t('perYear')} )}
{/* Key features list */}
  • {plan.maxListings === -1 ? t('unlimited') : `${plan.maxListings} ${t('listingsCount')}`}
  • {plan.maxSavedSearches === -1 ? t('unlimited') : `${plan.maxSavedSearches} ${t('savedSearchesCount')}`}
  • {plan.features['maxPhotos']} {t('photosPerListing')}
  • {plan.features['analytics'] && (
  • {t('features.analytics')}
  • )} {plan.features['aiValuation'] && (
  • {t('features.aiValuation')}
  • )} {plan.features['prioritySupport'] && (
  • {t('features.prioritySupport')}
  • )} {plan.features['featuredListing'] && (
  • {t('features.featuredListing')}
  • )} {plan.features['leadManagement'] && (
  • {t('features.leadManagement')}
  • )} {plan.features['marketReports'] && (
  • {t('features.marketReports')}
  • )} {plan.features['portfolioTracking'] && (
  • {t('features.portfolioTracking')}
  • )} {plan.features['apiAccess'] && (
  • {t('features.apiAccess')}
  • )}
{/* CTA */} {plan.tier === 'FREE' && !isAuthenticated ? ( ) : plan.tier === 'ENTERPRISE' ? ( ) : !isAuthenticated ? ( ) : ( )}
); })}
)}
{/* Feature comparison table */}

{t('comparisonTitle')}

{t('comparisonSubtitle')}

{plans.map((plan) => ( ))} {COMPARISON_FEATURES.map((featureKey) => ( {plans.map((plan) => { const val = getFeatureValue(plan, featureKey); return ( ); })} ))}
{t('feature')} {t(`tiers.${plan.tier}`)} {isAuthenticated && plan.tier === currentTier && ( {t('currentPlan')} )}
{FEATURE_LABELS[featureKey]} {typeof val === 'boolean' ? ( val ? ( ) : ( ) ) : ( {String(val)} )}
{/* FAQ / CTA section */}

{t('ctaTitle')}

{t('ctaDescription')}

{isAuthenticated ? ( ) : ( )}
{/* Checkout modal */} 0} currentTier={currentTier} />
); }