# پیاده‌سازی فرانت‌اند فاز ۲ — TASK-10 تا TASK-16

## زمینه

بک‌اند فاز ۲ کاملاً پیاده‌سازی و تست شده است. این پرامپت فقط فرانت‌اند ۷ تسک را پیاده‌سازی می‌کند.
هر تسک که تمام شد → `git commit` بزن (message فارسی مختصر).
بعد از هر تسک بلافاصله `yarn dev` اجرا کن تا خطاهای TypeScript مشخص شوند.

## قوانین ثابت پروژه

- **Paginated**: items از `data?.data`، total از `data?.meta?.totalRecords`
- **Single resource**: از `data?.data` (ممکن است double-nested باشد — `data?.data?.data`)
- **Category API**: triple-nested → `data?.data?.data ?? []`
- **تاریخ نمایش**: همیشه با `formatDate()` یا `formatDateTime()` از `lib/utils`
- **Forms**: React Hook Form + Zod resolver
- **Data fetching**: TanStack Query v5 (`useQuery` / `useMutation`)
- **UI**: از `DataTable`, `Pagination`, `Modal`, `ConfirmDialog`, `PageHeader` استفاده کن — کتابخانه جدید اضافه نکن
- **RTL**: همه استایل‌ها RTL باشند
- **کامیت بعد از هر تسک**: `git add` فایل‌های تغییر‌یافته → `git commit -m "feat: ..."`

---

## TASK-10 — مدیریت پرسنل (Staff)

### API

```
GET    /api/v1/staff                   → لیست پرسنل فعال entity جاری
POST   /api/v1/staff                   → ایجاد
PATCH  /api/v1/staff/{uuid}            → ویرایش
PATCH  /api/v1/staff/{uuid}/toggle     → فعال/غیرفعال
```

**Response GET:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "full_name": "علی رضایی",
            "phone": "09121234567",
            "job_title": "پرستار",
            "address": null,
            "national_code": "0012345678",
            "active": true,
            "created_at": 1718000000
        }
    ]
}
```

**Response PATCH toggle:**

```json
{ "success": true, "data": { "active": false } }
```

### فایل‌های جدید

**`assets/admin/pages/StaffPage.tsx`** — صفحه مدیریت پرسنل:

- `useQuery(['staff'])` → `GET /api/v1/staff` → `data?.data ?? []`
- جدول با ستون‌ها: نام، سمت، تلفن، کد ملی، وضعیت (badge)، عملیات
- دکمه «افزودن پرسنل» → Modal ایجاد
- دکمه ویرایش (PencilIcon) → Modal ویرایش
- دکمه toggle (EyeIcon/EyeSlashIcon) با `ConfirmDialog` برای غیرفعال‌سازی
- حذف سخت نداریم — فقط toggle

**فیلدهای فرم:**

```ts
interface StaffFormData {
    full_name: string; // required
    phone?: string;
    job_title?: string;
    address?: string;
    national_code?: string;
}
```

**Zod schema:**

```ts
const schema = z.object({
    full_name: z.string().min(2, "نام حداقل ۲ کاراکتر باید باشد"),
    phone: z.string().optional(),
    job_title: z.string().optional(),
    address: z.string().optional(),
    national_code: z.string().optional(),
});
```

### Route و Sidebar

در `App.tsx` اضافه کن:

```tsx
<Route
    path="staff"
    element={
        <RoleRoute roles={["doctor", "clinic"]}>
            <StaffPage />
        </RoleRoute>
    }
/>
```

در Sidebar (فایل `components/layout/AdminLayout.tsx` یا `Sidebar.tsx`) لینک «پرسنل» را برای نقش‌های `doctor` و `clinic` اضافه کن.

### کامیت

```
git add assets/admin/pages/StaffPage.tsx assets/admin/App.tsx assets/admin/components/layout/...
git commit -m "feat: add staff management page (TASK-10)"
```

---

## TASK-11 — پنل اشتراکی (Subscription)

### API

```
GET  /api/v1/subscription/plans    → لیست پلن‌ها (public)
GET  /api/v1/subscription/my       → اشتراک فعال من
POST /api/v1/subscription/trial    → فعال‌سازی تریال
POST /api/v1/subscription-payment  → شروع پرداخت { period_uuid, gateway }
```

**Response GET /plans:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "name": "basic",
            "level": 1,
            "max_secretaries": 2,
            "features": { "patient_records": true, "services": true },
            "periods": [
                {
                    "uuid": "...",
                    "label": "تریال ۱ ماهه",
                    "duration_months": 1,
                    "price_rials": 0,
                    "is_trial": true
                },
                {
                    "uuid": "...",
                    "label": "۱ ماهه",
                    "duration_months": 1,
                    "price_rials": 250000,
                    "is_trial": false
                }
            ]
        }
    ]
}
```

**Response GET /my:**

```json
{
    "success": true,
    "data": {
        "plan": {
            "name": "basic",
            "level": 1,
            "features": { "patient_records": true, "services": true }
        },
        "period": { "label": "۶ ماهه", "duration_months": 6 },
        "is_trial": false,
        "starts_at": 1718000000,
        "expires_at": 1733360000,
        "used_trial": true,
        "days_remaining": 42
    }
}
```

### فایل‌های جدید

**`assets/admin/pages/SubscriptionPage.tsx`** — صفحه اشتراک:

**بخش ۱ — وضعیت فعلی:**

- نمایش پلن فعلی (نام، تاریخ انقضا به شمسی، روزهای باقی‌مانده)
- اگر `used_trial = false` → دکمه «فعال‌سازی تریال رایگان» (Basic یک ماهه)
- نوار پیشرفت روزهای باقیمانده (اگر `expires_at` دارد)

**بخش ۲ — کارت‌های پلن:**

- سه کارت: Free / Basic / Professional
- هر کارت: نام پلن، ویژگی‌ها (لیست چک‌مارک)، دوره‌های قیمت به صورت badge/tab
- دکمه «خرید» برای هر دوره → modal انتخاب gateway → `POST /api/v1/subscription-payment` → redirect به URL برگشتی
- اگر پلن فعلی همان پلن است → دکمه «تمدید»

**Types جدید (`assets/admin/types/index.ts`):**

```ts
export interface SubscriptionPlan {
    uuid: string;
    name: string;
    level: number;
    max_secretaries: number;
    features: Record<string, boolean>;
    periods: SubscriptionPeriod[];
}
export interface SubscriptionPeriod {
    uuid: string;
    label: string;
    duration_months: number;
    price_rials: number;
    is_trial: boolean;
}
export interface MySubscription {
    plan: { name: string; level: number; features: Record<string, boolean> };
    period?: { label: string; duration_months: number };
    is_trial: boolean;
    starts_at?: number;
    expires_at?: number | null;
    used_trial: boolean;
    days_remaining?: number;
}
```

### Route

```tsx
<Route
    path="subscription"
    element={
        <RoleRoute roles={["doctor", "clinic"]}>
            <SubscriptionPage />
        </RoleRoute>
    }
/>
```

### کامیت

```
git commit -m "feat: add subscription management page (TASK-11)"
```

---

## TASK-12 — تکمیل Permissions منشی

**هیچ صفحه جدیدی لازم نیست** — فقط `assets/admin/pages/SecretariesPage.tsx` تغییر می‌کند.

### وضعیت فعلی SecretariesPage.tsx

فایل موجود است و لیست منشی ها را نشان می‌دهد. باید:

1. **Modal ویرایش permissions** اضافه کن:
    - دکمه «دسترسی‌ها» (ShieldCheckIcon) کنار هر ردیف
    - Modal با checkbox matrix:

```tsx
// ساختار permissions که باید از API بخوانی و ذخیره کنی
const DEFAULT_PERMISSIONS = {
    appointments: {
        view: true,
        create: false,
        cancel: false,
        update_status: false,
    },
    addresses: { view: true, create: false, update: false, delete: false },
    clinic_info: { view: true, update: false },
    insurances: { view: true, create: false, update: false, delete: false },
};

const PERMISSION_LABELS = {
    appointments: {
        label: "نوبت‌ها",
        actions: [
            { key: "view", label: "مشاهده" },
            { key: "create", label: "ایجاد" },
            { key: "cancel", label: "لغو" },
            { key: "update_status", label: "تغییر وضعیت" },
        ],
    },
    addresses: {
        label: "آدرس‌ها",
        actions: [
            { key: "view", label: "مشاهده" },
            { key: "create", label: "ایجاد" },
            { key: "update", label: "ویرایش" },
            { key: "delete", label: "حذف" },
        ],
    },
    clinic_info: {
        label: "اطلاعات کلینیک",
        actions: [
            { key: "view", label: "مشاهده" },
            { key: "update", label: "ویرایش" },
        ],
    },
    insurances: {
        label: "بیمه‌ها",
        actions: [
            { key: "view", label: "مشاهده" },
            { key: "create", label: "ایجاد" },
            { key: "update", label: "ویرایش" },
            { key: "delete", label: "حذف" },
        ],
    },
};
```

2. **PATCH `/api/v1/secretary/{uuid}`** با body:

```json
{ "permissions": { "appointments": { "view": true, ... }, ... } }
```

3. **خطای `ERR_SECRETARY_LIMIT_REACHED` (403)** را هنگام ایجاد منشی handle کن:

```tsx
// در onError useMutation create:
if (err.code === "ERR_SECRETARY_LIMIT_REACHED") {
    toast.error(
        "سقف تعداد منشی پنل فعلی شما پر است. برای افزودن منشی بیشتر پنل را ارتقا دهید.",
    );
}
```

### کامیت

```
git commit -m "feat: add secretary permissions matrix and subscription limit handling (TASK-12)"
```

---

## TASK-13 — سرویس‌های کلینیک

### API

```
GET    /api/v1/service-sections              → لیست بخش‌ها
POST   /api/v1/service-section               → ایجاد بخش { name }
PATCH  /api/v1/service-section/{uuid}        → ویرایش { name }
DELETE /api/v1/service-section/{uuid}        → حذف بخش

GET    /api/v1/service-items/{sectionUuid}   → لیست آیتم‌های یک بخش
POST   /api/v1/service-item                  → ایجاد آیتم
PATCH  /api/v1/service-item/{uuid}           → ویرایش آیتم
DELETE /api/v1/service-item/{uuid}           → حذف آیتم
```

**Response GET /service-sections:**

```json
{
    "success": true,
    "data": [{ "uuid": "...", "name": "تزریقات", "active": true }]
}
```

**Response GET /service-items/{sectionUuid}:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "name": "سرم ۵۰۰cc",
            "price_rials": 85000,
            "staff": { "uuid": "...", "full_name": "علی رضایی" },
            "active": true
        }
    ]
}
```

### فایل‌های جدید

**`assets/admin/pages/ClinicServicesPage.tsx`** — صفحه دو‌بخشی:

**ستون چپ** — لیست بخش‌ها (ServiceSection):

- لیست ساده با دکمه‌های ویرایش/حذف
- دکمه «+ بخش جدید» → Modal با فیلد `name`
- کلیک روی بخش → بارگذاری آیتم‌های آن در ستون راست

**ستون راست** — لیست آیتم‌های بخش انتخابی (ServiceItem):

- جدول: نام، قیمت (`formatRial()`), پرسنل مسئول، وضعیت
- دکمه «+ آیتم جدید»
- حذف: اگر `ERR_SERVICE_ITEM_IN_USE` (409) → toast.error('این سرویس در پرونده بیمار استفاده شده و قابل حذف نیست')

**فرم آیتم:**

```ts
interface ServiceItemForm {
    section_uuid: string; // hidden — از state
    name: string; // required
    price_rials: number; // required
    staff_uuid?: string; // SearchableSelect از GET /api/v1/staff
}
```

**Types جدید:**

```ts
export interface ServiceSection {
    uuid: string;
    name: string;
    active: boolean;
}
export interface ServiceItem {
    uuid: string;
    name: string;
    price_rials: number;
    staff: { uuid: string; full_name: string } | null;
    active: boolean;
}
```

### Route

```tsx
<Route
    path="clinic-services"
    element={
        <RoleRoute roles={["doctor", "clinic"]}>
            <ClinicServicesPage />
        </RoleRoute>
    }
/>
```

### کامیت

```
git commit -m "feat: add clinic services management page (TASK-13)"
```

---

## TASK-14 — پنل پیامکی

### API

```
GET   /api/v1/sms/wallet/balance   → موجودی
POST  /api/v1/sms/wallet/charge    → شارژ { gateway, amount_rials } → redirect URL
GET   /api/v1/sms/wallet/logs      → تاریخچه (paginated)
GET   /api/v1/sms/settings         → تنظیمات
PATCH /api/v1/sms/settings         → ذخیره تنظیمات
```

**Response GET /sms/wallet/balance:**

```json
{
    "success": true,
    "data": {
        "balance_rials": 150000,
        "sms_price_rials": 250,
        "estimated_sms_count": 600
    }
}
```

**Response GET /sms/wallet/logs:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "type": "credit",
            "amount_rials": 500000,
            "description": "شارژ",
            "created_at": 1718000000
        },
        {
            "uuid": "...",
            "type": "debit",
            "amount_rials": 250,
            "description": "ارسال پیامک یادآوری",
            "created_at": 1718001000
        }
    ],
    "meta": { "totalRecords": 45, "totalPages": 5, "currentPage": 1 }
}
```

**Response GET /sms/settings:**

```json
{
    "success": true,
    "data": {
        "reminder_enabled": true,
        "reminder_hours_before": 3,
        "post_visit_enabled": false,
        "post_visit_text": null
    }
}
```

### فایل‌های جدید

**`assets/admin/pages/SmsWalletPage.tsx`** — صفحه کیف پول پیامک:

**بخش ۱ — کارت موجودی:**

- نمایش `balance_rials` با `formatRial()`
- نمایش `estimated_sms_count` (تعداد پیامک قابل ارسال)
- دکمه «شارژ کیف پول» → Modal با فیلد مبلغ + انتخاب gateway → redirect به URL

**بخش ۲ — تنظیمات:**

- Toggle «یادآوری قبل از نوبت» + فیلد عددی «چند ساعت قبل»
- Toggle «پیامک بعد از ویزیت» + textarea متن پیامک
- دکمه ذخیره → `PATCH /api/v1/sms/settings`

**بخش ۳ — تاریخچه تراکنش‌ها:**

- جدول: نوع (badge سبز credit / قرمز debit)، مبلغ، توضیحات، تاریخ
- Pagination

**Types جدید:**

```ts
export interface SmsWalletBalance {
    balance_rials: number;
    sms_price_rials: number;
    estimated_sms_count: number;
}
export interface SmsWalletLog {
    uuid: string;
    type: "credit" | "debit";
    amount_rials: number;
    description: string;
    created_at: number;
}
export interface SmsSettings {
    reminder_enabled: boolean;
    reminder_hours_before: number;
    post_visit_enabled: boolean;
    post_visit_text: string | null;
}
```

### Route

```tsx
<Route
    path="sms-wallet"
    element={
        <RoleRoute roles={["doctor", "clinic"]}>
            <SmsWalletPage />
        </RoleRoute>
    }
/>
```

### کامیت

```
git commit -m "feat: add SMS wallet and settings page (TASK-14)"
```

---

## TASK-15 — پرونده بیمار

### API

```
GET   /api/v1/patients                     → لیست بیماران (paginated, ?search=)
POST  /api/v1/patient                      → ایجاد پرونده { user_uuid }
GET   /api/v1/patient/{uuid}               → جزئیات پرونده
GET   /api/v1/patient/{uuid}/sessions      → لیست مراجعات (paginated)
POST  /api/v1/patient/{uuid}/session       → ثبت مراجعه جدید
PATCH /api/v1/session/{uuid}               → ویرایش مراجعه
```

**Response GET /patients:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "user": {
                "uuid": "...",
                "fullName": "علی محمدی",
                "phone": "09121234567"
            },
            "created_at": 1710000000
        }
    ],
    "meta": { "totalRecords": 12, "totalPages": 2, "currentPage": 1 }
}
```

**Response GET /patient/{uuid}/sessions:**

```json
{
    "success": true,
    "data": [
        {
            "uuid": "...",
            "appointment_uuid": null,
            "visit_price_rials": 500000,
            "base_insurance_discount_percent": "30.00",
            "supplementary_discount_percent": "0.00",
            "services_total_rials": 130000,
            "final_price_rials": 445000,
            "payment_method": "cash",
            "notes": "...",
            "created_at": 1718000000,
            "updated_at": 1718000000
        }
    ],
    "meta": { "totalRecords": 5, "totalPages": 1, "currentPage": 1 }
}
```

**Request POST /patient/{uuid}/session:**

```json
{
    "visit_price_rials": 500000,
    "base_insurance_discount_percent": 30,
    "supplementary_discount_percent": 0,
    "payment_method": "cash",
    "notes": "...",
    "services": [{ "service_item_uuid": "...", "staff_uuid": null }]
}
```

### فایل‌های جدید/تغییر

**`assets/admin/pages/MyPatientsPage.tsx`** — بازنویسی کامل:

**نمای لیست بیماران:**

- جستجو (`?search=`) با debounce ۴۰۰ms
- جدول: نام بیمار، تلفن، تاریخ ثبت، عملیات
- دکمه «+ بیمار جدید» → Modal با `SearchableSelect` برای پیدا کردن کاربر (search by phone)
- کلیک روی ردیف → نمای جزئیات پرونده (state محلی یا route `/my-patients/:uuid`)

**نمای جزئیات پرونده:**

- مشخصات بیمار + دکمه «+ مراجعه جدید»
- لیست مراجعات (accordion یا جدول):
    - قیمت ویزیت، تخفیف بیمه پایه، تخفیف تکمیلی، مجموع سرویس‌ها، قیمت نهایی
    - وضعیت پرداخت (`payment_method`)
    - یادداشت
    - دکمه ویرایش (فقط `notes` و `payment_method`)

**Modal ثبت مراجعه جدید:**

```ts
interface SessionFormData {
    visit_price_rials: number;
    base_insurance_discount_percent: number; // 0-100
    supplementary_discount_percent: number; // 0-100
    payment_method: "cash" | "card" | "insurance" | "online" | "pending";
    notes?: string;
    services: { service_item_uuid: string; staff_uuid?: string }[];
}
```

- محاسبه `final_price_rials` به صورت live در فرانت:

```ts
const afterBase = visitPrice * (1 - baseDiscount / 100);
const afterSupp = afterBase * (1 - suppDiscount / 100);
const servicesTotal = services.reduce(
    (sum, s) => sum + (s.price_rials || 0),
    0,
);
const finalPrice = Math.round(afterSupp) + servicesTotal;
```

- افزودن سرویس: `SearchableSelect` از `GET /api/v1/service-items/{sectionUuid}` (باید ابتدا بخش را انتخاب کند)

**Types جدید:**

```ts
export interface PatientRecord {
    uuid: string;
    user: { uuid: string; fullName: string; phone: string };
    created_at: number;
}
export interface PatientSession {
    uuid: string;
    appointment_uuid: string | null;
    visit_price_rials: number;
    base_insurance_discount_percent: string;
    supplementary_discount_percent: string;
    services_total_rials: number;
    final_price_rials: number;
    payment_method: string;
    notes: string | null;
    created_at: number;
    updated_at: number;
}
```

### Route

```tsx
// مسیر موجود کافی است:
<Route
    path="my-patients"
    element={
        <RoleRoute roles={["doctor", "secretary", "clinic"]}>
            <MyPatientsPage />
        </RoleRoute>
    }
/>
```

### کامیت

```
git commit -m "feat: implement patient records and sessions page (TASK-15)"
```

---

## TASK-16 — داشبورد هوشمند با فیلتر زمانی

### تغییر API

```
GET /api/v1/dashboard/clinic?from=UNIX&to=UNIX   → فیلدهای جدید در stats
GET /api/v1/dashboard/doctor?from=UNIX&to=UNIX   → فیلدهای جدید در stats
GET /api/v1/admin/dashboard/charts?from=UNIX&to=UNIX  → سری‌های زمانی
```

**فیلدهای جدید در Response clinic/doctor:**

```json
{
  "stats": {
    "...فیلدهای قبلی...",
    "sms_wallet_balance": 150000,
    "unique_patients_count": 45,
    "revenue_period_rials": 12500000
  },
  "period": { "from": 1717200000, "to": 1719792000 }
}
```

**Response GET /admin/dashboard/charts?from=UNIX&to=UNIX:**

```json
{
  "success": true,
  "data": {
    "appointments_by_day": [
      { "date": "06/01", "count": 12 }
    ],
    "revenue_by_day": [
      { "date": "06/01", "amount": 3500000 }
    ],
    "subscription_sales_by_plan": [
      { "plan": "basic", "count": 15, "revenue": 3750000 }
    ],
    "appointment_status": [...],
    "top_specialties": [...]
  }
}
```

### تغییر `assets/admin/pages/DashboardPage.tsx`

**۱. فیلتر زمانی — اضافه کن:**

```tsx
type Preset = "this_week" | "this_month" | "3_months" | "custom";

// تبدیل به Unix:
function getRange(preset: Preset): { from: number; to: number } {
    const now = Math.floor(Date.now() / 1000);
    switch (preset) {
        case "this_week":
            // شنبه‌ی این هفته — ساده‌ترین راه: now - روزهای گذشته از شنبه
            const dayOfWeek = new Date().getDay(); // 0=Sun,6=Sat
            const daysSinceSat = (dayOfWeek + 1) % 7;
            return { from: now - daysSinceSat * 86400, to: now };
        case "this_month":
            const d = new Date();
            d.setDate(1);
            d.setHours(0, 0, 0, 0);
            return { from: Math.floor(d.getTime() / 1000), to: now };
        case "3_months":
            return { from: now - 90 * 86400, to: now };
        default:
            return { from: now - 30 * 86400, to: now };
    }
}
```

preset buttons:

```tsx
<div style={{ display: "flex", gap: 8, marginBottom: 16 }}>
    {(["this_week", "this_month", "3_months"] as Preset[]).map((p) => (
        <button
            key={p}
            className={`btn sm ${preset === p ? "primary" : ""}`}
            onClick={() => setPreset(p)}
        >
            {p === "this_week"
                ? "این هفته"
                : p === "this_month"
                  ? "این ماه"
                  : "۳ ماه"}
        </button>
    ))}
</div>
```

**۲. فیلدهای جدید در کارت‌های آمار:**

- کارت «موجودی پیامک»: `stats.sms_wallet_balance` → `formatRial()`
- کارت «بیماران یکتا»: `stats.unique_patients_count`
- کارت «درآمد دوره»: `stats.revenue_period_rials` → `formatRial()`

**۳. نمودارها (برای admin role):**

از **SVG inline** موجود در DashboardPage استفاده کن (کلاس `SvgLineChart` که در فایل موجود است):

```tsx
// appointments_by_day و revenue_by_day داده‌های خطی دارند
// فقط آرایه عددی را استخراج کن:
const apptData = charts?.appointments_by_day?.map((d: any) => d.count) ?? [];
const revData  = charts?.revenue_by_day?.map((d: any) => d.amount) ?? [];

<SvgLineChart data={apptData} color="#3b82f6" />
<SvgLineChart data={revData} color="#22c55e" />
```

**subscription_sales_by_plan** — جدول ساده:

```tsx
<table>
    <thead>
        <tr>
            <th>پلن</th>
            <th>تعداد</th>
            <th>درآمد</th>
        </tr>
    </thead>
    <tbody>
        {subData.map((row) => (
            <tr key={row.plan}>
                <td>{row.plan}</td>
                <td>{row.count}</td>
                <td>{formatRial(row.revenue)}</td>
            </tr>
        ))}
    </tbody>
</table>
```

**API call با from/to:**

```tsx
const [preset, setPreset] = useState<Preset>("this_month");
const range = getRange(preset);

// برای admin:
const { data: charts } = useQuery({
    queryKey: ["dashboard-charts", range.from, range.to],
    queryFn: () =>
        api.get(
            `/api/v1/admin/dashboard/charts?from=${range.from}&to=${range.to}`,
        ),
});

// برای doctor:
const { data: doctorDash } = useQuery({
    queryKey: ["dashboard-doctor", range.from, range.to],
    queryFn: () =>
        api.get(`/api/v1/dashboard/doctor?from=${range.from}&to=${range.to}`),
    enabled: primaryRole === "doctor",
});
```

### کامیت

```
git commit -m "feat: add date range filter and new stats to dashboard (TASK-16)"
```

---

## ترتیب اجرا و چک‌لیست

```
[ ] TASK-10: StaffPage.tsx → route → sidebar → yarn dev → commit
[ ] TASK-11: SubscriptionPage.tsx → route → sidebar → yarn dev → commit
[ ] TASK-12: SecretariesPage.tsx (permissions modal + limit error) → yarn dev → commit
[ ] TASK-13: ClinicServicesPage.tsx → route → sidebar → yarn dev → commit
[ ] TASK-14: SmsWalletPage.tsx → route → sidebar → yarn dev → commit
[ ] TASK-15: MyPatientsPage.tsx (بازنویسی) → yarn dev → commit
[ ] TASK-16: DashboardPage.tsx (date range + new stats + charts) → yarn dev → commit
```

## قوانین اجرا

1. **یک تسک در یک زمان** — هرگز دو تسک را همزمان شروع نکن
2. **بعد از هر تسک `ddev exec yarn dev`** — هر TypeScript error را همان‌جا رفع کن
3. **اگر API error داشت** — فایل `docs/api/` مربوطه را بخوان
4. **Types جدید** را در `assets/admin/types/index.ts` اضافه کن
5. **Sidebar** — برای هر صفحه جدید، لینک sidebar را هم اضافه کن (در `AdminLayout.tsx` یا `Sidebar.tsx`)
6. **کامیت** بعد از `yarn dev` بدون خطا — نه قبل از آن

## فایل‌های مرجع مهم

| فایل                                              | نقش                                                          |
| ------------------------------------------------- | ------------------------------------------------------------ |
| `assets/admin/lib/api.ts`                         | fetch wrapper — `api.get/post/patch/delete`                  |
| `assets/admin/lib/utils.ts`                       | `formatRial`, `formatDate`, `formatDateTime`, `formatNumber` |
| `assets/admin/types/index.ts`                     | همه TypeScript interfaces                                    |
| `assets/admin/stores/authStore.ts`                | `primaryRole`, `dbUuid`, `doctorUuid`                        |
| `assets/admin/components/ui/DataTable.tsx`        | جدول — `Column<T>[]`                                         |
| `assets/admin/components/ui/Modal.tsx`            | Modal                                                        |
| `assets/admin/components/ui/SearchableSelect.tsx` | dropdown با جستجو                                            |
| `docs/api/staff.md`                               | API staff                                                    |
| `docs/api/subscription.md`                        | API subscription                                             |
| `docs/api/clinic-services.md`                     | API services                                                 |
| `docs/api/sms.md`                                 | API SMS wallet                                               |
| `docs/api/patient.md`                             | API patient records                                          |
| `docs/api/dashboard.md`                           | API dashboard                                                |
