'use client'; import { useQueryClient } from '@tanstack/react-query'; 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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Link } from '@/i18n/navigation'; import { formatVND } from '@/lib/currency'; import { usePlans, useBillingHistory, useQuota, subscriptionKeys } from '@/lib/hooks/use-subscription'; import { subscriptionApi, type PlanDto, type QuotaCheckResult, } from '@/lib/subscription-api'; 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' }, }; const PAYMENT_STATUS_MAP: Record = { COMPLETED: { label: 'Thành công', variant: 'default' }, FAILED: { label: 'Thất bại', variant: 'destructive' }, PENDING: { label: 'Chờ xử lý', variant: 'secondary' }, CANCELLED: { label: 'Đã hủy', variant: 'outline' }, }; const PAYMENT_TYPE_LABELS: Record = { SUBSCRIPTION: 'Đăng ký gói', LISTING_FEE: 'Phí đăng tin', DEPOSIT: 'Đặt cọc', FEATURED_LISTING: 'Tin nổi bật', }; export default function SubscriptionPage() { const queryClient = useQueryClient(); const { data: plansData, isLoading: plansLoading } = usePlans(); const { data: billing, isLoading: billingLoading } = useBillingHistory(); const { data: listingsQuota } = useQuota('listings'); const { data: savedSearchesQuota } = useQuota('saved_searches'); const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly'); const [cancelProcessing, setCancelProcessing] = useState(false); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('plan'); // Checkout modal state const [checkoutPlan, setCheckoutPlan] = useState(null); const [checkoutOpen, setCheckoutOpen] = useState(false); const loading = plansLoading || billingLoading; const plans = (plansData ?? []).slice().sort( (a, b) => PLAN_TIER_ORDER.indexOf(a.tier) - PLAN_TIER_ORDER.indexOf(b.tier), ); const quotas = [listingsQuota, savedSearchesQuota].filter( (q): q is QuotaCheckResult => q != null, ); 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 = (plan: PlanDto) => { setCheckoutPlan(plan); setCheckoutOpen(true); }; const handleCancel = async () => { setCancelProcessing(true); setError(null); try { await subscriptionApi.cancelSubscription('Hủy từ trang quản lý'); await queryClient.invalidateQueries({ queryKey: subscriptionKeys.billing() }); } catch (e) { setError(e instanceof Error ? e.message : 'Hủy gói thất bại'); } finally { setCancelProcessing(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)}%` }} />
); })}
)} {/* Quick actions */}
{currentTier !== 'ENTERPRISE' && ( )} {billing?.subscription && billing.subscription.status === 'ACTIVE' && ( )}
{/* 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} {isCurrent && ( Hiện tại )}
{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}
{isCurrent ? ( ) : isUpgrade ? ( plan.tier === 'ENTERPRISE' ? ( ) : ( ) ) : ( )}
); })}
{/* 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) => { const pStatus = PAYMENT_STATUS_MAP[p.status] ?? { label: p.status, variant: 'secondary' as const }; return (

{PAYMENT_TYPE_LABELS[p.type] ?? p.type}

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

{formatVND(p.amountVND)}

{pStatus.label}
); })}
)}
)} {/* Checkout modal */} 0} currentTier={currentTier} />
); }