# جریان کاربری — تسک ۱۱: پنل اشتراکی

## جریان مشاهده پنل‌ها و خرید

```
کاربر وارد صفحه اشتراک می‌شود
          │
          ▼
GET /api/v1/subscription/my
  → نمایش پنل فعلی + تاریخ انقضا (شمسی) + days_remaining
  → اگر used_trial=false و trial_enabled=true → بنر تریال نمایش داده می‌شود
          │
          ▼
GET /api/v1/subscription/plans
  → جدول مقایسه پنل‌ها (free | basic | professional)
  → هر پنل: لیست دوره‌ها با قیمت — دروه تریال جدا نمایش داده می‌شود
          │
          ├─► کاربر «تریال» انتخاب می‌کند:
          │         │
          │         ▼
          │   POST /api/v1/subscription/trial
          │         ├─► 200: فعال شد → صفحه refresh
          │         └─► 400: قبلاً استفاده شده → toast خطا
          │
          └─► کاربر یک دوره پولی انتخاب می‌کند:
                    │
                    ▼
              POST /api/v1/subscription-payment (موجود)
                { period_uuid, gateway: 'mellat' }
                    │
                    ▼
              redirect به gateway بانک
                    │
                    ▼
              GET|POST /api/v1/subscription-payment/callback/{gateway}
                    │
                    ▼
              SubscriptionService::createFromPayment()
              → ClinicSubscription ساخته می‌شود
                    │
                    ▼
              redirect به /admin/subscription?success=1
```

## جریان تمدید اشتراک

```
اشتراک ۶ ماهه فعال است (expires_at = آینده)
          │
          ▼
کاربر دوره جدید ۶ ماهه انتخاب می‌کند و پرداخت می‌کند
          │
          ▼
calculateExpiresAt():
  base = max(expires_at_فعلی, now)   ← از تاریخ انقضا (نه الان)
  new_expires = base + 6 × 30 × 86400
          │
          ▼
اشتراک ۶ ماه دیگر تمدید می‌شود بدون اتلاف زمان باقی‌مانده
```

## جریان gate check در تسک‌های بعدی

```
کاربر Free تلاش می‌کند پرونده بیمار بسازد
          │
          ▼
POST /api/v1/patient
          │
          ▼
PatientController::create():
  $hasFeature = $subscriptionService->hasFeature($entityType, $entityId, 'patient_records')
          │
          ├─► false → 403 ERR_SUBSCRIPTION_REQUIRED
          │          { "errors": [{ "code": "ERR_SUBSCRIPTION_REQUIRED",
          │                         "message": "این قابلیت نیاز به پنل Basic یا بالاتر دارد" }] }
          │
          └─► true → ادامه پردازش
```

## جریان مدیریت پنل‌ها توسط ادمین

```
ادمین وارد صفحه مدیریت اشتراک می‌شود
          │
          ├─► تب «پنل‌ها»:
          │   GET /api/v1/admin/subscription/plans
          │   → جدول پنل‌ها با دوره‌ها و قیمت‌ها
          │   → PATCH /api/v1/admin/subscription/period/{uuid}  ← ویرایش inline قیمت
          │   → POST /api/v1/admin/subscription/period          ← افزودن دوره جدید
          │
          └─► تب «گزارش»:
              GET /api/v1/admin/subscription/report?from=UNIX&to=UNIX
              → تعداد خریدها + مجموع درآمد + تعداد تریال‌ها
              → breakdown بر اساس plan و period
```

## هشدار انقضا (frontend)

```
در هر بار لود صفحه:
  اگر days_remaining <= 7 و expires_at != null:
    → نمایش banner هشدار: «اشتراک شما X روز دیگر منقضی می‌شود — تمدید کنید»
```

## نمایش در Frontend (SubscriptionPage.tsx)

```
┌─────────────────────────────────────────────────────┐
│ 🎁 یک ماه تریال رایگان — فعال‌سازی                 │  ← فقط اگر used_trial=false
├─────────────────────────────────────────────────────┤
│ پنل فعلی: Basic  |  انقضا: ۱۴۰۵/۰۶/۲۳  |  ۴۲ روز │
├───────────┬──────────────────┬─────────────────────┤
│ Free      │ Basic ★          │ Professional        │
│ رایگان    │ پرونده بیمار ✅   │ پرونده بیمار ✅     │
│ ۱ منشی    │ سرویس‌ها ✅       │ سرویس‌ها ✅         │
│           │ ۲ منشی           │ ۵ منشی              │
│           │ ┌──────────┐    │                     │
│           │ │ ۱ ماهه   │    │ [انتخاب دوره ▼]     │
│           │ │ ۲۵۰,۰۰۰  │    │                     │
│           │ │ ۶ ماهه   │    │                     │
│           │ │ ۱,۲۰۰,۰۰۰│    │                     │
│           │ └──────────┘    │                     │
│           │ [خرید اشتراک]   │ [خرید اشتراک]       │
└───────────┴──────────────────┴─────────────────────┘
```
