feat: implement useUrlState hook for managing URL-based state in admin pages

- Refactor multiple admin pages (BlogsPage, ClinicsPage, DoctorsPage, etc.) to utilize the new useUrlState hook for managing pagination, search, and filter states via URL.
- Ensure that the state persists in the URL, allowing users to return to the same state when navigating back from detail pages.
- Update relevant components to handle state changes appropriately and maintain clean URLs by removing default values.
- Add SlotPicker component for selecting appointment slots based on availability.
- Create tests for useUrlState to validate its functionality and ensure correct behavior when interacting with the URL.
- Update API documentation to reflect changes in appointment creation and slot selection processes.
This commit is contained in:
hamed
2026-07-29 21:04:40 +03:30
parent e0e8fbd1e4
commit 684cf1f783
20 changed files with 668 additions and 79 deletions
+4
View File
@@ -735,6 +735,10 @@ Create a new appointment for a patient. Used by doctor/clinic/secretary to book
> **هویت بیمار بر پایه‌ی کد ملی:** کد ملی روی **پروفایل** بیمار ذخیره می‌شود (`profiles.national_code`، یکتا). بیمار **اول با کد ملیِ پروفایل** پیدا می‌شود، سپس با موبایل. پس یک شخص می‌تواند چند موبایل داشته باشد ولی پرونده‌اش (`PatientRecord`) یکتا می‌ماند. اگر موبایلی که پروفایلش کد ملی دیگری دارد دوباره با کد ملی متفاوت ارسال شود، خطای 422 برمی‌گردد. اگر هیچ بیماری یافت نشود، کاربر جدید (`ROLE_USER`) به‌همراه پروفایلِ حاملِ همان کد ملی ساخته می‌شود. موبایلِ واردشده در هر نوبت به‌صورت snapshot روی خودِ نوبت (`patient_mobile`) هم ذخیره می‌شود.
>
> **نامِ نمایش‌داده‌شده‌ی بیمار:** چون بیمار با کد ملی به پروفایل واقعی‌اش resolve می‌شود، snapshotِ نامِ نوبت (`patient_name`) از **نامِ همان پروفایل** (`User.realName`) پر می‌شود، نه از نامِ تایپ‌شده در مودال. نامِ ورودی فقط وقتی روی نوبت می‌نشیند که بیمار کاملاً جدید باشد و نامی نداشته باشد. لیستِ `GET /api/v1/my/appointments` هم همین را نشان می‌دهد (`override_name` تنها برای رزروِ عمومیِ «برای شخص دیگر» — که `user` صاحب حساب است — از `realName` جدا می‌شود).
>
> **وضعیت نوبتِ ساخته‌شده:** این endpoint نوبت را همیشه `pending` می‌سازد. صفحهٔ «افزودن نوبت» پنل (`/admin/appointments/new`) نوبتِ **قطعی** می‌سازد، پس بلافاصله پس از ساخت، خودش `POST /api/v1/appointment/{uuid}/confirm` را با `payments: []` صدا می‌زند. اگر آن مرحله شکست بخورد، نوبت `pending` می‌ماند (اسلات همچنان اشغال است) و به کاربر گفته می‌شود از لیست نوبت‌ها قطعی کند.
>
> **انتخاب زمان در پنل:** در حالت نوبت‌دهی **اسلاتی**، صفحهٔ افزودن نوبت زمان را از `GET /api/v1/appointment-slots` می‌گیرد و فقط اسلاتِ `is_available` قابل انتخاب است؛ ورود دستیِ ساعت فقط به‌عنوان «ثبت خارج از برنامه» باقی مانده (مثلاً روزی که پزشک برنامهٔ کاری ندارد). در حالت **سرویسی**، زمان‌ها از `GET /api/v1/appointment-service-slots` می‌آیند.
### Response `201`
```json