# تطبیق Admin SPA با تغییرات API برنچ `backend-audit`

## پروژه

`clinicpro` (Admin SPA — `assets/admin/`). پرامپت همتا برای سایت عمومی: `nobat724_front/.claude/prompt/adapt-backend-audit-api.md`.

## زمینه

برنچ `backend-audit` (۳۳ commit) چند endpoint را تغییر داد. مقدارهای wire خطاها حفظ شده‌اند (M21)، ولی **چند لیست حالا صفحه‌بندی شده‌اند** (پیش‌فرض ۵۰ ردیف، قبلاً همه) و یک `data.meta` افزوده‌اند، و تعدادی مسیر سخت‌گیری امنیتی جدید دارند. این پرامپت فقط مصرف‌کننده‌های **Admin SPA** را اصلاح می‌کند.

پاکت پاسخ این لیست‌ها (`$this->success(['data' => $rows, 'meta' => ...])`) به این شکل است:

```json
{ "success": true, "data": { "data": [ /* ردیف‌ها */ ], "meta": { "totalRecords": 124, "totalPages": 3, "currentPage": 1 } } }
```

یعنی `data.data` (آرایه) **دست‌نخورده** است — خواندن فعلی نمی‌شکند — اما حالا فقط **۵۰ ردیف اول** برمی‌گردد مگر اینکه `?page`/`?limit` بفرستی. بدون اصلاح، کاربر ادمین بقیه‌ی ردیف‌ها را نمی‌بیند.

## جدول کامل تغییرات API (همه‌ی endpointهای تغییریافته)

| Endpoint | تغییر | مصرف در Admin SPA | اقدام |
|---|---|---|---|
| `GET /api/v1/billing/claims` | صفحه‌بندی + `data.meta`، پیش‌فرض ۵۰ | `pages/ClaimsPage.tsx` | **صفحه‌بندی اضافه شود** |
| `GET /api/v1/settlement` | صفحه‌بندی + `data.meta`، پیش‌فرض ۵۰ | `pages/RepresentationSettlementPage.tsx` | **صفحه‌بندی اضافه شود** |
| `GET /api/v1/wallet/transactions` | صفحه‌بندی + `?page` + `data.meta`، پیش‌فرض ۵۰ | مصرف نمی‌شود (فقط tauri) | — |
| `GET /api/v1/admin/comments/pending` | صفحه‌بندی + `data.meta`، پیش‌فرض ۵۰ | مصرف نشد (تأیید کن) | اگر صفحه‌ای دارد، صفحه‌بندی اضافه شود |
| `POST /api/v1/billing/claims/{uuid}/{approve|pay}` | اگر `approved_rials`/`paid_rials` خارج بازه باشد → **`422`** (`field` در پاسخ) | `ClaimsPage.tsx` transitionMut | هندل خطای ۴۲۲ |
| `GET /api/v1/appointment-settings/available-locations/{uuid}` | حالا فقط owner doctor یا `ROLE_ADMIN` (وگرنه `403`) | `pages/DoctorDetailPage.tsx` | بدون اصلاح — کاربر ادمین bypass دارد (مستند شود) |
| `GET /api/v1/appointment-settings/date-override/list/{uuid}` | همان — owner/admin | `DoctorDetailPage.tsx` | بدون اصلاح (admin bypass) |
| `GET /api/v1/appointment-settings/holidays/list/{uuid}` | همان — owner/admin | `DoctorDetailPage.tsx` | بدون اصلاح (admin bypass) |
| `GET /api/v1/insurance/{id}` | حالا owner/admin (`403`) | مصرف نشد | — |
| `GET /api/v1/clinic-pro/doctor-address/{id}` | حالا owner/admin (`403`) | مصرف نشد (تأیید کن) | — |
| `POST /api/v1/pre-registration` | `200 → 201` | اگر مصرف دارد، چک کن `res.ok`/2xx باشد نه `=== 200` | تأیید |
| کدهای خطای legacy (M21) | مقدار wire **بدون تغییر** (`SLOT_TAKEN`, `VALIDATION`, …) | — | بدون اصلاح |

> همه‌ی اصلاح‌های دیگر برنچ (N+1، unique، integrity) خروجی API را تغییر نداده‌اند.

## فایل‌های مرتبط

| فایل | نقش |
|------|-----|
| `assets/admin/pages/ClaimsPage.tsx` | لیست مطالبات بیمه — اکنون ۵۰-cap |
| `assets/admin/pages/RepresentationSettlementPage.tsx` | لیست تسویه‌ها — اکنون ۵۰-cap |
| `assets/admin/pages/DoctorDetailPage.tsx` | فقط مستندسازی (admin bypass) |
| `assets/admin/lib/api.ts` | `ApiResponse`/`PaginatedResponse` types |
| `assets/admin/components/ui/Pagination` | کامپوننت صفحه‌بندی موجود |

## وضعیت فعلی (کد واقعی)

### ClaimsPage.tsx — بدون صفحه‌بندی، فقط `data.data.data`

```tsx
const claimsQuery = useQuery<{ data: { data: Claim[] } }>({
  queryKey: ['claims', queryString],
  queryFn: () => api.get(`/api/v1/billing/claims${queryString}`),
});
// ...
const claims = (claimsQuery.data as any)?.data?.data ?? [];

const transitionMut = useMutation({
  mutationFn: ({ uuid, action, body }) =>
    api.post(`/api/v1/billing/claims/${uuid}/${action}`, body ?? {}),
  onError: (e: Error) => toast.error(e.message),   // ۴۲۲ جدید اینجا نمایش داده می‌شود
});
```

`queryString` از فیلترهای موجود (`status`, `insurance_id`, `from`, `to`, `q`) ساخته می‌شود ولی `page`/`limit` ندارد.

### RepresentationSettlementPage.tsx — بدون صفحه‌بندی

```tsx
const { data } = useQuery({
  queryKey: ['settlements'],
  queryFn: () => api.get<ApiResponse<SettlementRow[]>>('/api/v1/settlement'),
});
// data.data خوانده می‌شود به‌عنوان آرایه
```

## وظایف

### ۱. ClaimsPage — افزودن صفحه‌بندی

- یک `page` state اضافه کن (پیش‌فرض ۱) و آن را به `queryString` تزریق کن (`p.set('page', String(page))`). `page` را در `queryKey` بگذار تا refetch شود.
- total را از پاسخ بخوان: `const total = (claimsQuery.data as any)?.data?.meta?.totalRecords ?? 0;`
- کامپوننت `<Pagination>` موجود را زیر جدول مطالبات با `total`/`page`/`onPageChange` رندر کن (الگوی بقیه‌ی صفحه‌های لیست admin).
- وقتی فیلترها عوض شد، `page` را به ۱ برگردان.

```tsx
const [page, setPage] = useState(1);
// queryString: const s = p.toString(); p.set('page', String(page)); ...
const total = (claimsQuery.data as any)?.data?.meta?.totalRecords ?? 0;
// <Pagination currentPage={page} totalRecords={total} onPageChange={setPage} />
```

### ۲. ClaimsPage — هندل خطای ۴۲۲ روی approve/pay

اکنون اگر `approved_rials > total_claimed_rials` یا `paid_rials > total_approved_rials` (یا منفی) باشد، پاسخ `422` با `errors[0].field` (`approved_rials`/`paid_rials`) است. `onError` فعلی فقط `e.message` را toast می‌کند — کافی است، ولی مطمئن شو پیام فارسی backend («مبلغ تأییدشده باید بین ۰ و مبلغ مطالبه‌شده باشد») به کاربر نشان داده می‌شود (نه «خطای ناشناخته»). اگر فرم مبلغ دارد، خطا را کنار فیلد مربوطه با استفاده از `field` نشان بده.

### ۳. RepresentationSettlementPage — افزودن صفحه‌بندی

مثل ClaimsPage: `page` state + `?page=` در URL + خواندن `data.meta.totalRecords` + `<Pagination>`. خواندن `data.data` به‌عنوان آرایه دست‌نخورده می‌ماند.

### ۴. تأیید عدم مصرف

با grep تأیید کن Admin SPA این‌ها را **مصرف نمی‌کند** (اگر می‌کند، همان الگوی صفحه‌بندی را اعمال کن):
`/api/v1/wallet/transactions`، `/api/v1/admin/comments/pending`، `/api/v1/clinic-pro/doctor-address/`، `/api/v1/insurance/{id}` (GET تکی).

### ۵. مستندسازی (بدون تغییر کد)

در صفحه‌ی `DoctorDetailPage.tsx` این سه endpoint حالا owner-or-admin‌اند: `available-locations`، `date-override/list`، `holidays/list`. چون کاربر Admin SPA همیشه `ROLE_ADMIN` است، **bypass دارد و چیزی نمی‌شکند** — فقط در صورت اضافه‌شدن نقش‌های غیرادمین به این صفحه در آینده حواست باشد.

## نکات مهم

- پاکت پاسخ این لیست‌ها double-nest است: آرایه در `data.data`، متادیتا در `data.meta`. (نه `PaginatedResponse<T>` که `data` را آرایه‌ی تخت می‌گیرد — این endpointها از `success(['data'=>..., 'meta'=>...])` استفاده می‌کنند، نه `paginated()`.)
- پیش‌فرض `limit` سمت backend ۵۰، حداکثر ۱۰۰ است؛ `page` ۱-based.
- از کامپوننت `<Pagination>` و `<DataTable>` موجود استفاده کن؛ کتابخانه‌ی جدید اضافه نکن؛ RTL.
- تاریخ‌ها Unix timestamp؛ نمایش با `formatDate()` شمسی.
- بعد از تغییر، با `ddev exec yarn dev` بیلد را چک کن (خطاهای TS فقط در خروجی tsc ظاهر می‌شوند).
```
