'use client'; import { useEffect, useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { subscriptionApi, type BillingHistoryDto, type PlanDto, type QuotaCheckResult, } from '@/lib/subscription-api'; function formatVND(amount: string | number): string { const num = typeof amount === 'string' ? Number(amount) : amount; if (num === 0) return 'Miễn phí'; if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(0)} triệu đ`; return num.toLocaleString('vi-VN') + ' đ'; } const PLAN_TIER_ORDER = ['FREE', 'AGENT_PRO', 'INVESTOR', 'ENTERPRISE']; const PLAN_TIER_LABELS: Record = { FREE: 'Miễn phí', AGENT_PRO: 'Môi giới Pro', INVESTOR: 'Nhà đầu tư', ENTERPRISE: 'Doanh nghiệp', }; const STATUS_MAP: Record = { ACTIVE: { label: 'Đang hoạt động', variant: 'default' }, PAST_DUE: { label: 'Quá hạn', variant: 'destructive' }, CANCELLED: { label: 'Đã hủy', variant: 'outline' }, EXPIRED: { label: 'Hết hạn', variant: 'secondary' }, }; export default function SubscriptionPage() { const [plans, setPlans] = useState([]); const [billing, setBilling] = useState(null); const [quotas, setQuotas] = useState([]); const [loading, setLoading] = useState(true); const [upgradeTarget, setUpgradeTarget] = useState(null); const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly'); const [processing, setProcessing] = useState(false); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('plan'); useEffect(() => { setLoading(true); Promise.all([ subscriptionApi.getPlans().catch(() => []), subscriptionApi.getBillingHistory().catch(() => null), Promise.all([ subscriptionApi.checkQuota('listings').catch(() => null), subscriptionApi.checkQuota('saved_searches').catch(() => null), ]), ]) .then(([plansData, billingData, quotaResults]) => { setPlans(plansData.sort((a, b) => PLAN_TIER_ORDER.indexOf(a.tier) - PLAN_TIER_ORDER.indexOf(b.tier))); setBilling(billingData); setQuotas(quotaResults.filter((q): q is QuotaCheckResult => q !== null)); }) .finally(() => setLoading(false)); }, []); const currentTier = billing?.subscription?.planTier ?? 'FREE'; const currentTierIndex = PLAN_TIER_ORDER.indexOf(currentTier); const subStatus = billing?.subscription?.status ? STATUS_MAP[billing.subscription.status] ?? { label: 'Đang hoạt động', variant: 'default' as const } : null; const handleUpgrade = async () => { if (!upgradeTarget) return; setProcessing(true); setError(null); try { if (billing?.subscription) { await subscriptionApi.upgradeSubscription(upgradeTarget.tier); } else { await subscriptionApi.createSubscription(upgradeTarget.tier, billingCycle); } // Reload billing data const newBilling = await subscriptionApi.getBillingHistory().catch(() => null); setBilling(newBilling); setUpgradeTarget(null); } catch (e) { setError(e instanceof Error ? e.message : 'Nâng cấp thất bại'); } finally { setProcessing(false); } }; return (

Gói dịch vụ

Quản lý gói đăng ký và theo dõi hạn mức sử dụng

{error && (
{error}
)} {loading ? (
Đang tải...
) : ( Gói hiện tại So sánh gói Lịch sử thanh toán {/* Current plan tab */}
Gói {PLAN_TIER_LABELS[currentTier] ?? currentTier} {billing?.subscription ? `Kỳ hiện tại: ${new Date(billing.subscription.currentPeriodStart).toLocaleDateString('vi-VN')} — ${new Date(billing.subscription.currentPeriodEnd).toLocaleDateString('vi-VN')}` : 'Bạn đang sử dụng gói miễn phí'}
{subStatus && {subStatus.label}}
{/* Quota usage */} {quotas.length > 0 && (

Hạn mức sử dụng

{quotas.map((q) => { const pct = q.limit > 0 ? (q.used / q.limit) * 100 : 0; return (
{q.metric === 'listings' ? 'Tin đăng' : q.metric === 'saved_searches' ? 'Tìm kiếm đã lưu' : q.metric} {q.used}/{q.limit}
90 ? 'bg-red-500' : pct > 70 ? 'bg-yellow-500' : 'bg-primary'}`} style={{ width: `${Math.min(pct, 100)}%` }} />
); })}
)} {/* Plan comparison tab */} {/* Billing cycle toggle */}
{plans.map((plan) => { const tierIndex = PLAN_TIER_ORDER.indexOf(plan.tier); const isCurrent = plan.tier === currentTier; const isUpgrade = tierIndex > currentTierIndex; const price = billingCycle === 'monthly' ? plan.priceMonthlyVND : plan.priceYearlyVND; return ( {PLAN_TIER_LABELS[plan.tier] ?? plan.name} {formatVND(price)} {Number(price) > 0 && ( /{billingCycle === 'monthly' ? 'tháng' : 'năm'} )}
Tin đăng {plan.maxListings === -1 ? 'Không giới hạn' : plan.maxListings}
Tìm kiếm lưu {plan.maxSavedSearches === -1 ? 'Không giới hạn' : plan.maxSavedSearches}
{plan.features && Object.entries(plan.features).map(([key, val]) => (
{key} {typeof val === 'boolean' ? (val ? '✓' : '✗') : String(val)}
))}
{isCurrent ? ( ) : isUpgrade ? ( ) : ( )}
); })}
{/* Payment history tab */} Lịch sử thanh toán Các giao dịch liên quan đến gói dịch vụ {!billing || billing.payments.length === 0 ? (
Chưa có giao dịch nào
) : (
{billing.payments.map((p) => (

{p.type}

{new Date(p.createdAt).toLocaleDateString('vi-VN')} — {p.provider}

{formatVND(p.amountVND)}

{p.status === 'COMPLETED' ? 'Thành công' : p.status === 'FAILED' ? 'Thất bại' : p.status === 'PENDING' ? 'Chờ xử lý' : p.status}
))}
)}
)} {/* Upgrade dialog */} !o && setUpgradeTarget(null)}> Nâng cấp lên {PLAN_TIER_LABELS[upgradeTarget?.tier ?? ''] ?? upgradeTarget?.name} Xác nhận nâng cấp gói dịch vụ. Bạn sẽ được chuyển hướng đến trang thanh toán.
Gói {PLAN_TIER_LABELS[upgradeTarget?.tier ?? ''] ?? upgradeTarget?.name}
Chu kỳ {billingCycle === 'monthly' ? 'Hàng tháng' : 'Hàng năm'}
Giá {upgradeTarget && formatVND( billingCycle === 'monthly' ? upgradeTarget.priceMonthlyVND : upgradeTarget.priceYearlyVND, )}
); }