GET /billing/claims and GET /settlement now return only the first 50 rows by default (data.data unchanged, data.meta added). ClaimsPage and RepresentationSettlementPage read the full array with no pager, so rows beyond 50 were unreachable. Add page state + ?page/limit + the existing <Pagination> (reading data.meta.totalRecords). No change needed for: 422 on claim approve/pay (api.ts already surfaces the backend message via toast; the admin UI sends no amount so it's unreachable), the owner-only appointment-settings endpoints (admin user bypasses), and refresh rotation (authStore.refresh already persists the rotated refresh_token). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.6 KiB
تطبیق 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' => ...])) به این شکل است:
{ "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
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 — بدون صفحهبندی
const { data } = useQuery({
queryKey: ['settlements'],
queryFn: () => api.get<ApiResponse<SettlementRow[]>>('/api/v1/settlement'),
});
// data.data خوانده میشود بهعنوان آرایه
وظایف
۱. ClaimsPage — افزودن صفحهبندی
- یک
pagestate اضافه کن (پیشفرض ۱) و آن را به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را به ۱ برگردان.
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 ظاهر میشوند).