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 { 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: '#64748b', bg: '#f1f5f9', border: '#cbd5e1', icon: ShieldCheckIcon, }, basic: { label: 'پایه', desc: 'برای مطب‌های کوچک و متوسط', color: '#2563eb', bg: '#eff6ff', border: '#93c5fd', icon: RocketLaunchIcon, }, professional: { label: 'حرفه‌ای', desc: 'برای کلینیک‌های بزرگ', color: '#7c3aed', bg: '#f5f3ff', border: '#c4b5fd', 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 { data: paymentConfigData } = useQuery>({ queryKey: ['payment-config'], queryFn: () => api.get('/api/v1/payment/config'), staleTime: 5 * 60 * 1000, }); const plans = plansData?.data ?? []; const myRaw = myData?.data; const my = myRaw?.subscription ?? null; const usedTrial = myRaw?.used_trial ?? false; const isTestMode = paymentConfigData?.data?.test_mode ?? 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 }: { period_uuid: string; gateway: string }) => api.post<{ data: { payment_url: string } }>('/api/v1/subscription-payment', { period_uuid, gateway }), onSuccess: (res: any) => { const 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) => ( 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, usedTrial, onPurchase, onTrial, trialPending, }: { plan: SubscriptionPlan; currentPlanName: string; 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 isUpgrade = (plan.level ?? 0) > ((PLAN_META[currentPlanName] ? plan.level : 0)); 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 && (
🎁 تریال {trialPeriod.duration_months} ماهه رایگان
)}
{/* دوره‌های پرداختی */} {paidPeriods.length > 0 && (
{paidPeriods.map((period) => (
{period.label}
{period.duration_months} ماه
{formatRial(period.price_rials)}
))}
)} {/* پنل رایگان — بدون دوره */} {paidPeriods.length === 0 && plan.level === 0 && (
{isCurrent ? '✓ شما اکنون در این پنل هستید' : 'رایگان — بدون هزینه'}
)}
); }