import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { CheckIcon, XMarkIcon, SparklesIcon, RocketLaunchIcon, ShieldCheckIcon, CreditCardIcon, ClockIcon, ArrowPathIcon, } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import type { SubscriptionPlan, MySubscriptionData, SubscriptionPeriod } from '../types'; import { usePaymentConfig } from '../hooks/usePaymentConfig'; import { formatDate, formatRial, formatNumber } from '../lib/utils'; import Modal from '../components/ui/Modal'; // ── Constants ───────────────────────────────────────────────────────────── const PLAN_FEATURE_LABELS: Record = { patient_records: 'پرونده بیمار', services: 'مدیریت سرویس‌ها', sms_panel: 'پنل پیامک', }; const PLAN_META: Record = { free: { label: 'رایگان', desc: 'برای شروع کار با سیستم', color: 'var(--text-2)', bg: 'var(--surface-2)', border: 'var(--border)', icon: ShieldCheckIcon, }, basic: { label: 'پایه', desc: 'برای مطب‌های کوچک و متوسط', color: 'var(--info)', bg: 'var(--info-bg)', border: 'color-mix(in oklch, var(--info) 40%, transparent)', icon: RocketLaunchIcon, }, professional: { label: 'حرفه‌ای', desc: 'برای کلینیک‌های بزرگ', color: 'var(--violet)', bg: 'var(--violet-bg)', border: 'color-mix(in oklch, var(--violet) 40%, transparent)', icon: SparklesIcon, }, }; const GATEWAY_LABELS: Record = { mellat: 'بانک ملت', sep: 'سپ (صادرات)' }; // ── Main Page ───────────────────────────────────────────────────────────── export default function SubscriptionPage() { const qc = useQueryClient(); const [purchaseTarget, setPurchaseTarget] = useState<{ period: SubscriptionPeriod; planName: string } | null>(null); const [selectedGateway, setSelectedGateway] = useState<'mellat' | 'sep'>('mellat'); const { data: plansData, isLoading: plansLoading } = useQuery>({ queryKey: ['subscription-plans'], queryFn: () => api.get('/api/v1/subscription/plans'), }); const { data: myData, isLoading: myLoading } = useQuery>({ queryKey: ['subscription-my'], queryFn: () => api.get('/api/v1/subscription/my'), }); const { isTestMode } = usePaymentConfig(); const plans = plansData?.data ?? []; const myRaw = myData?.data; const my = myRaw?.subscription ?? null; const usedTrial = myRaw?.used_trial ?? false; const trialMutation = useMutation({ mutationFn: () => api.post('/api/v1/subscription/trial', {}), onSuccess: () => { qc.invalidateQueries({ queryKey: ['subscription-my'] }); toast.success('تریال رایگان با موفقیت فعال شد'); }, onError: (err: any) => toast.error(err.message), }); const purchaseMutation = useMutation({ mutationFn: ({ period_uuid, gateway, amount_rials }: { period_uuid: string; gateway: string; amount_rials: number }) => api.post<{ data: { payment_url: string } }>('/api/v1/subscription-payment', { period_uuid, gateway, amount_rials }), onSuccess: (res: any) => { const url = res?.data?.redirect_url ?? res?.data?.payment_url; if (url) window.location.href = url; else toast.error('خطا در دریافت لینک پرداخت'); }, onError: (err: any) => toast.error(err.message), }); const daysTotal = my?.expires_at && my?.starts_at ? Math.round((my.expires_at - my.starts_at) / 86400) : null; const daysLeft = my?.days_remaining ?? 0; const daysProgress = daysTotal ? Math.max(0, Math.min(100, ((daysTotal - daysLeft) / daysTotal) * 100)) : null; const planMeta = my ? (PLAN_META[my.plan.name] ?? PLAN_META.free) : null; const isExpiringSoon = daysLeft > 0 && daysLeft <= 7; return (
{/* ── Header ── */}

پنل اشتراکی

پنل خود را انتخاب کنید و از امکانات بیشتر بهره‌مند شوید

{/* ── وضعیت فعلی ── */} {!myLoading && my && (
{/* decorative circle */}
{/* icon */}
{planMeta && }
{/* info */}
پنل {planMeta!.label} {my.is_trial && ( تریال )} {isExpiringSoon && ( در حال انقضا )}
{my.expires_at ? (
انقضا {formatDate(my.expires_at)} ({formatNumber(daysLeft)} روز مانده)
) : (
بدون تاریخ انقضا
)} {/* progress bar */} {daysProgress !== null && (
{Math.round(daysProgress)}% گذشته
)}
{/* trial CTA */} {!usedTrial && ( )}
)} {/* ── بنر تریال (اگر هنوز استفاده نشده و اشتراکی ندارد) ── */} {!myLoading && !my && !usedTrial && (
یک ماه تریال رایگان دارید!
پنل پایه را به مدت یک ماه رایگان امتحان کنید
)} {/* ── کارت‌های پلن ── */} {plansLoading ? (
{[1, 2, 3].map((i) => (
))}
) : (
{plans.map((plan) => ( 0} usedTrial={usedTrial} onPurchase={(period) => setPurchaseTarget({ period, planName: plan.name })} onTrial={() => trialMutation.mutate()} trialPending={trialMutation.isPending} /> ))}
)} {/* ── فوتر راهنما ── */}
🔒 پرداخت امن از طریق درگاه‌های معتبر 🔄 تمدید از تاریخ انقضای قبلی محاسبه می‌شود 📦 داده‌ها پس از انقضا حفظ می‌شوند
{/* ── Modal پرداخت ── */} { setPurchaseTarget(null); setSelectedGateway('mellat'); }} title="پرداخت اشتراک" size="sm" > {purchaseTarget && (
{/* خلاصه خرید */}
دوره انتخابی
{PLAN_META[purchaseTarget.planName]?.label ?? purchaseTarget.planName} — {purchaseTarget.period.label}
{purchaseTarget.period.duration_months} ماه
مبلغ قابل پرداخت
{formatRial(purchaseTarget.period.price_rials)}
{/* انتخاب درگاه */} {isTestMode ? (
⚠️
درگاه آزمایشی فعال است
پول واقعی کسر نخواهد شد — این تراکنش آزمایشی است
) : (
انتخاب درگاه پرداخت
{(['mellat', 'sep'] as const).map((gw) => ( ))}
)} {/* دکمه‌ها */}
)}
); } // ── PlanCard ────────────────────────────────────────────────────────────── function PlanCard({ plan, currentPlanName, currentPlanLevel, currentPlanActive, usedTrial, onPurchase, onTrial, trialPending, }: { plan: SubscriptionPlan; currentPlanName: string; currentPlanLevel: number; currentPlanActive: boolean; usedTrial: boolean; onPurchase: (period: SubscriptionPeriod) => void; onTrial: () => void; trialPending: boolean; }) { const meta = PLAN_META[plan.name] ?? PLAN_META.free; const isCurrent = plan.name === currentPlanName; const isDowngrade = currentPlanActive && (plan.level ?? 0) < currentPlanLevel; const paidPeriods = plan.periods.filter((p) => !p.is_trial); const trialPeriod = plan.periods.find((p) => p.is_trial); const Icon = meta.icon; return (
{ (e.currentTarget as HTMLElement).style.boxShadow = 'var(--shadow)'; (e.currentTarget as HTMLElement).style.transform = 'translateY(-3px)'; }} onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.boxShadow = ''; (e.currentTarget as HTMLElement).style.transform = ''; }} > {/* توپ تزئینی */}
{/* سربرگ کارت */}
{isCurrent && ( پنل فعلی )}
{meta.label}
{meta.desc}
{plan.max_secretaries} منشی
{/* قابلیت‌ها */}
0 ? '1px solid var(--border)' : 'none', flex: 1 }}>
امکانات
    {Object.entries(plan.features).map(([key, enabled]) => (
  • {enabled ? : } {PLAN_FEATURE_LABELS[key] ?? key}
  • ))}
{/* تریال */} {trialPeriod && !usedTrial && plan.level > 0 && !isDowngrade && (
🎁 تریال {trialPeriod.duration_months} ماهه رایگان
)}
{/* دوره‌های پرداختی */} {paidPeriods.length > 0 && (
{isDowngrade && (
🔒 تا پایان اشتراک فعلی قابل انتخاب نیست
)} {paidPeriods.map((period) => (
{period.label}
{period.duration_months} ماه
{formatRial(period.price_rials)}
))}
)} {/* پنل رایگان — بدون دوره */} {paidPeriods.length === 0 && plan.level === 0 && (
{isCurrent ? '✓ شما اکنون در این پنل هستید' : 'رایگان — بدون هزینه'}
)}
); }