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