feat: add doctor profile page with sidebar integration and routing

This commit is contained in:
hamed
2026-06-11 19:06:59 +03:30
parent 704075770b
commit 277922d4ae
7 changed files with 347 additions and 76 deletions
+202
View File
@@ -0,0 +1,202 @@
# صفحه پروفایل پزشک (Doctor Profile Page)
## هدف
پزشک باید صفحه‌ای داشته باشد که بتواند تمام اطلاعات مربوط به خودش را مشاهده و ویرایش کند — دقیقاً مشابه `/admin/doctors/{uuid}` که ادمین می‌بیند، اما:
- به صورت خودکار UUID پزشک لاگین‌شده از `authStore.dbUuid` خوانده می‌شود
- آدرس صفحه: `/admin/profile`
- در سایدبار دکتر یک آیتم "پروفایل من" اضافه می‌شود
---
## وضعیت فعلی (چه داریم)
### صفحه موجود
- `DoctorDetailPage.tsx` در مسیر `/admin/doctors/:uuid` برای **ادمین + دکتر** قابل دسترسی است
- صفحه کامل است و شامل: اطلاعات پروفایل، آدرس مطب، تنظیمات نوبت‌دهی، تعطیلات، تاریخ‌های خاص
- دکتر با UUID خودش می‌تواند این صفحه را ببیند اما **لینکی در منوی دکتر وجود ندارد**
### سایدبار دکتر (فعلی)
```
عمومی:
- داشبورد
مدیریت:
- نوبت‌های من
```
### Auth Store
- `authStore.dbUuid` = UUID دکتر لاگین‌شده
- `authStore.primaryRole` = 'doctor'
---
## API‌های موجود (بررسی‌شده)
| Endpoint | وضعیت | کاربرد |
|----------|-------|---------|
| `GET /api/v1/doctor/{uuid}` | ✅ PUBLIC | خواندن پروفایل (double-nested: `data?.data?.data`) |
| `PATCH /api/v1/doctor/{uuid}` | ✅ AUTH | ویرایش پروفایل |
| `POST /api/v1/clinic-pro/doctor-address` | ✅ AUTH | افزودن آدرس مطب |
| `PATCH /api/v1/clinic-pro/doctor-address/{id}` | ✅ AUTH | ویرایش آدرس |
| `DELETE /api/v1/clinic-pro/doctor-address/{id}` | ✅ AUTH | حذف آدرس |
| `GET /api/v1/clinic-pro/doctor-addresses/{doctorId}` | ✅ AUTH | لیست آدرس‌ها |
| `GET /api/v1/appointment-settings/weekly-schedule/{doctorUuid}` | ✅ AUTH | برنامه هفتگی |
| `POST /api/v1/appointment-settings/weekly-schedule` | ✅ AUTH | ساخت برنامه هفتگی |
| `PATCH /api/v1/appointment-settings/weekly-schedule/{uuid}` | ✅ AUTH | ویرایش برنامه هفتگی |
| `GET /api/v1/appointment-settings/date-override/list/{doctorUuid}` | ✅ AUTH | تاریخ‌های خاص |
| `GET /api/v1/appointment-settings/holidays/list/{doctorUuid}` | ✅ AUTH | تعطیلات |
| `GET /api/v1/my/appointments/today-stats?date=...` | ✅ AUTH | آمار نوبت‌ها |
---
## قابلیت‌هایی که باید ساخته شود
### قابلیت ۱ — Route + Sidebar
**فایل‌ها:**
- `assets/admin/App.tsx` — افزودن route `/admin/profile`
- `assets/admin/components/layout/Sidebar.tsx` — افزودن "پروفایل من" به منوی دکتر
**Route:**
```tsx
<Route path="profile" element={<DoctorProfilePage />} />
```
**Sidebar (doctor):**
```
عمومی:
- داشبورد
پروفایل:
- پروفایل من ← /admin/profile
مدیریت:
- نوبت‌های من
- تنظیمات نوبت‌دهی ← /admin/appointment-settings (اختیاری)
```
---
### قابلیت ۲ — `DoctorProfilePage.tsx`
**ساختار صفحه** (با tab برای سازماندهی):
```
┌──────────────────────────────────────────────────────────────┐
│ [عکس] دکتر [نام] ویرایش پروفایل │
│ [تخصص] [درجه] │
└──────────────────────────────────────────────────────────────┘
[ اطلاعات عمومی ] [ آدرس مطب ] [ تنظیمات نوبت‌دهی ]
↑ تب‌ها
──── محتوای هر تب ────
```
**تب ۱ — اطلاعات عمومی** (ویرایش inline):
- نام کامل
- جنسیت (مرد/زن)
- درجه علمی (عمومی / متخصص / فوق‌تخصص / فلوشیپ)
- کد نظام پزشکی
- تخصص‌ها (multi-select از `GET /api/v1/categorys/specially_doctor`)
- سرویس‌ها (multi-select از `GET /api/v1/categorys/doctor_services` یا `GET /api/v1/doctor-services`)
- بیوگرافی (textarea)
- بارگذاری عکس پروفایل
فرم: React Hook Form + Zod
Submit: `PATCH /api/v1/doctor/{uuid}`
**تب ۲ — آدرس مطب** (CRUD):
- لیست آدرس‌ها با نام + آدرس + تلفن
- دکمه "افزودن آدرس" → Modal با فرم:
- نام مطب
- آدرس
- تلفن
- استان + شهر (از category API)
- نقشه (Leaflet — اختیاری اگر پیچیده باشد)
- ویرایش + حذف هر آدرس
**تب ۳ — تنظیمات نوبت‌دهی** (مشابه آنچه در DoctorDetailPage وجود دارد):
- برنامه هفتگی (weekly schedule) — مشاهده + ویرایش
- تعطیلات (holidays) — لیست + افزودن + حذف
- تاریخ‌های خاص (date overrides) — لیست + مدیریت
---
## نکات پیاده‌سازی
### گرفتن UUID دکتر
```tsx
const uuid = useAuthStore(s => s.dbUuid);
```
### Double-nested response برای GET doctor
```tsx
// پاسخ: { success: true, data: { data: { uuid, name, ... } } }
const doctor = data?.data?.data;
```
### استفاده مجدد از DoctorDetailPage
اگر `DoctorDetailPage.tsx` قابل reuse است:
- می‌توان یک wrapper ساده ساخت که فقط `uuid` را از `authStore.dbUuid` می‌گیرد و پاس می‌دهد
- اما اگر رفتار صفحه برای ادمین و دکتر تفاوت دارد (مثلاً ادمین می‌تواند دکتر را غیرفعال کند ولی دکتر نمی‌تواند) باید برخی بخش‌ها را با `isAdmin` شرطی کرد
### بخش‌هایی که فقط ادمین می‌بیند (باید پنهان شوند):
- دکمه "غیرفعال کردن حساب دکتر"
- دکمه "حذف دکتر"
- لیست کلینیک‌هایی که دکتر عضو است (فقط نمایش — بدون حذف از کلینیک)
### بخش‌هایی که دکتر اضافه می‌بیند (اختیاری):
- آمار خلاصه نوبت‌های امروز (از `GET /api/v1/my/appointments/today-stats`)
---
## فایل‌های تأثیرپذیر
| فایل | تغییر |
|------|-------|
| `assets/admin/pages/DoctorProfilePage.tsx` | **جدید** — صفحه پروفایل پزشک |
| `assets/admin/App.tsx` | افزودن route `/admin/profile` |
| `assets/admin/components/layout/Sidebar.tsx` | افزودن "پروفایل من" به منوی دکتر |
| `assets/admin/pages/DoctorDetailPage.tsx` | اختیاری — اگر نیاز به تغییر رفتار ادمین/دکتر |
---
## رویکرد پیشنهادی
**ساده‌ترین راه** (بدون duplicate کد):
1. در `App.tsx` route جدید:
```tsx
<Route path="profile" element={<Navigate to={`/admin/doctors/${dbUuid}`} replace />} />
```
اما این روش نیاز به دسترسی به `dbUuid` در سطح App دارد.
**روش بهتر:**
```tsx
// DoctorProfilePage.tsx — wrapper
export default function DoctorProfilePage() {
const dbUuid = useAuthStore(s => s.dbUuid);
if (!dbUuid) return <div>در حال بارگذاری...</div>;
return <DoctorDetailPage overrideUuid={dbUuid} isOwnProfile={true} />;
}
```
و در `DoctorDetailPage.tsx` یک prop `isOwnProfile` اضافه کن که:
- دکمه‌های ادمین‌-only را پنهان کند
- عنوان صفحه را تغییر دهد ("پروفایل من" به جای "جزئیات پزشک")
**یا ساده‌تر:** فقط لینک sidebar را به `/admin/doctors/{dbUuid}` تنظیم کن و DoctorDetailPage را بدون تغییر reuse کن — چون دکتر از طریق `RoleRoute roles={['admin', 'doctor']}` به آن صفحه دسترسی دارد.
---
## الزامات
1. وقتی دکتر وارد پنل می‌شود، از سایدبار به "پروفایل من" دسترسی داشته باشد
2. پروفایل خودش را ببیند و ویرایش کند (نه پروفایل دکتر دیگری)
3. تنظیمات نوبت‌دهی‌اش را مدیریت کند
4. آدرس مطب‌هایش را مدیریت کند
5. دکمه‌های ادمین‌-only (حذف/غیرفعال کردن) نمایش داده نشوند
6. همه تاریخ‌ها شمسی نمایش داده شوند
+41 -9
View File
@@ -10,13 +10,14 @@ description: اجرای یک فایل پرامپت .md به صورت گام‌ب
---
## قبل از شروع — تحلیل پرامپت
## قبل از شروع — تحلیل پرامپت و ساخت Todo
۱. فایل پرامپت را کامل بخوان
۲. مستندات موجود را بررسی کن: `docs/api/`
۳. کد مرتبط در `src/` و `assets/admin/` را مرور کن
۴. لیست قابلیت‌ها را از پرامپت استخراج کن
۵. به کاربر نمایش بده:
۵. **با `TodoWrite` یک todo کامل بساز** — یک آیتم به ازای هر قابلیت
۶. به کاربر نمایش بده:
```
📋 قابلیت‌های شناسایی‌شده:
@@ -31,23 +32,44 @@ description: اجرای یک فایل پرامپت .md به صورت گام‌ب
## قوانین اجرا (اجباری — هیچ استثنایی ندارد)
### ۱. یک قابلیت در هر مرحله
### ۱. Todo — ستون فقرات اجرا
**در ابتدای هر اجرا** یک todo کامل با `TodoWrite` بساز:
- یک آیتم به ازای هر قابلیت با وضعیت `pending`
- آیتم‌های زیر-مجموعه اضافه کن اگر قابلیت چند بخش مستقل دارد
**در طول اجرا:**
- هر قابلیت را که شروع می‌کنی → وضعیتش را `in_progress` کن
- هر قابلیت را که تست و تأیید شد → فوری `completed` کن
- هرگز دو آیتم را همزمان `in_progress` نگذار
- اگر خطا داری که قابلیت را block کرده → یک آیتم `in_progress` باقی بماند تا رفع شود
**نمونه todo اولیه:**
```
[ ] قابلیت ۱: Stats Bar backend endpoint
[ ] قابلیت ۲: Stats Bar frontend component
[ ] قابلیت ۳: PersianCalendar popup
[ ] قابلیت ۴: AppointmentStatusDropdown
[ ] قابلیت ۵: بازطراحی AppointmentsPage
```
### ۲. یک قابلیت در هر مرحله
- هرگز دو قابلیت را همزمان پیاده‌سازی نکن
- هرگز بدون تأیید موفقیت مرحله قبل به مرحله بعد نرو
### ۲. ترتیب اجرای هر قابلیت
### ۳. ترتیب اجرای هر قابلیت
```
① تحلیل ② طراحی ③ پیاده‌سازی ④ تست ⑤ رفع خطا ⑥ مستندسازی ⑦ گزارش
① تحلیل ② طراحی ③ پیاده‌سازی ④ تست ⑤ رفع خطا ⑥ مستندسازی ⑦ Todo + گزارش
```
### ۳. سازگاری با پروژه
### ۴. سازگاری با پروژه
- همه تغییرات باید با Symfony 7 + React 19 سازگار باشند
- از الگوهای موجود پروژه پیروی کن (BaseController، TanStack Query، Zod، ...)
- اگر Entity تغییر کرد: `doctrine:migrations:diff` و `doctrine:migrations:migrate` اجرا کن
- اگر API تغییر کرد: همه بخش‌های وابسته (frontend types، api calls، ...) را اصلاح کن
### ۴. اصول کدنویسی
### ۵. اصول کدنویسی
- SOLID و Clean Code
- هیچ کامنت غیرضروری اضافه نکن
- نام‌گذاری معنادار
@@ -74,6 +96,7 @@ description: اجرای یک فایل پرامپت .md به صورت گام‌ب
### مرحله ③ — پیاده‌سازی
- قابلیت را در todo روی `in_progress` بگذار
- **Backend اول**: Entity → Migration → Repository → Service → Controller
- **Frontend بعد**: Types → API call → Component → Route
- یک فایل در هر Edit/Write — نه bulk
@@ -138,9 +161,12 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
4. تمام status codes و error codes
5. اگر پارامتر query داشت → همه را مستند کن
### مرحله ⑦ — گزارش
### مرحله ⑦ — Todo + گزارش
بعد از اتمام هر قابلیت، گزارش کوتاه:
بعد از موفقیت تست و مستندسازی:
۱. **آیتم todo را `completed` کن** با `TodoWrite`
۲. گزارش کوتاه نمایش بده:
```
✅ قابلیت [شماره]: [نام] — تکمیل شد
@@ -150,6 +176,12 @@ ddev exec npx tsc --noEmit --project tsconfig.json 2>&1 | head -30
• assets/admin/...
• docs/api/...
📊 پیشرفت کلی:
✅ قابلیت ۱: [نام] — تکمیل
✅ قابلیت ۲: [نام] — تکمیل
⏳ قابلیت ۳: [نام] — در صف
...
─────────────────────────────────
▶ قابلیت بعدی: [شماره] — [نام]
آیا ادامه دهم؟