# دسترسی صاحب کلینیک به پروفایل پزشکان عضو کلینیک ## زمینه وقتی صاحب کلینیک (`ROLE_CLINIC`) در صفحه کلینیک روی آیکون چشم کنار نام یک پزشک کلیک می‌کند، به جای مشاهده پروفایل پزشک، به داشبورد redirect می‌شود. دلیل: route `doctors/:uuid` در `App.tsx` فقط `['admin', 'doctor']` را قبول دارد و `RoleRoute` صاحب کلینیک را به `/admin/dashboard` می‌فرستد. ## مشکل دو لایه مشکل وجود دارد: **۱. Frontend — Route permission:** ```tsx // assets/admin/App.tsx — خط 141 } /> ``` `RoleRoute` فقط `primaryRole` را چک می‌کند — اگر `clinic` بود، redirect به dashboard می‌کند. **۲. Backend — API permission:** ```php // src/Doctor/Controller/DoctorController.php — خط 140-141 #[Route('/api/v1/doctor/{uuid}', methods: ['GET'])] public function show(string $uuid): JsonResponse // بدون IsGranted — PUBLIC است ``` `GET /api/v1/doctor/{uuid}` عمومی است، اما برای اطمینان از امنیت باید یک endpoint اختصاصی برای کلینیک وجود داشته باشد که تأیید کند پزشک واقعاً عضو کلینیک مربوطه است. ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `assets/admin/App.tsx` | تعریف route و RoleRoute — جای اصلی مشکل frontend | | `assets/admin/pages/DoctorDetailPage.tsx` | صفحه پروفایل پزشک | | `src/Doctor/Controller/DoctorController.php` | `GET /api/v1/doctor/{uuid}` | | `src/Clinic/Repository/ClinicRepository.php` | دارای `findByDoctor()` و `findByUser()` | ## وضعیت فعلی ```tsx // App.tsx خط 141 } /> // RoleRoute خط 70-74 function RoleRoute({ roles, children }: { roles: string[]; children: React.ReactNode }) { const primaryRole = useAuthStore((s) => s.primaryRole); if (!primaryRole) return
در حال بارگذاری...
; if (!roles.includes(primaryRole)) return ; return <>{children}; } ``` ```php // DoctorController.php خط 140-163 — PUBLIC, no auth check #[Route('/api/v1/doctor/{uuid}', methods: ['GET'])] public function show(string $uuid): JsonResponse { $doctor = $this->doctorRepo->findByUuid($uuid); if ($doctor === null) { return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404); } // ... بدون چک مالکیت یا عضویت در کلینیک return $this->success(['data' => array_merge($doctor->toDetailArray(), ['clinics' => $clinicData])]); } ``` ## وظایف ### ۱. Frontend — اضافه کردن `clinic` به RoleRoute مسیر پزشک ```tsx // App.tsx — خط 141 را تغییر بده } /> ``` ### ۲. Backend — endpoint اختصاصی برای کلینیک یک endpoint جدید در `DoctorController` اضافه کن که صاحب کلینیک بتواند پروفایل پزشکان عضو کلینیکش را ببیند، با تأیید عضویت: ```php // src/Doctor/Controller/DoctorController.php #[Route('/api/v1/clinic/my-doctor/{doctorUuid}', methods: ['GET'])] #[IsGranted('ROLE_CLINIC')] public function showForClinic(string $doctorUuid, #[CurrentUser] User $user): JsonResponse { $clinic = $this->clinicRepo->findByUser($user); if ($clinic === null) { return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کلینیک یافت نشد', 404); } $doctor = $this->doctorRepo->findByUuid($doctorUuid); if ($doctor === null) { return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404); } // فقط پزشکانی که عضو این کلینیک هستند قابل مشاهده‌اند if (!$clinic->getDoctors()->contains($doctor)) { return $this->error(ErrorCodes::ERR_AUTH_006, 'این پزشک عضو کلینیک شما نیست', 403); } $clinicData = [[ 'id' => (string) $clinic->getId(), 'uuid' => $clinic->getUuid(), 'name' => $clinic->getName(), 'address' => $clinic->getAddress(), 'telephone' => $clinic->getTelephone(), ]]; return $this->success(['data' => array_merge($doctor->toDetailArray(), ['clinics' => $clinicData])]); } ``` **وابستگی‌های constructor**: `ClinicRepository $clinicRepo` باید به `DoctorController` اضافه شود (بررسی کن آیا از قبل موجود است). ### ۳. Frontend — DoctorDetailPage: fetch با endpoint مناسب در `DoctorDetailPage.tsx`، وقتی `primaryRole === 'clinic'`، از endpoint جدید استفاده کن: ```tsx // در DoctorDetailPage.tsx — در useQuery که doctor را fetch می‌کند const { data: doctorData } = useQuery({ queryKey: ['doctor', uuid, primaryRole], queryFn: () => primaryRole === 'clinic' ? api.get(`/api/v1/clinic/my-doctor/${uuid}`) : api.get(`/api/v1/doctor/${uuid}`), enabled: !!uuid, }); ``` ### ۴. Frontend — DoctorDetailPage: محدود کردن قابلیت‌های ویرایش برای clinic وقتی `primaryRole === 'clinic'`: - دکمه ویرایش پروفایل نمایش داده **نشود** (فقط مشاهده) - دکمه حذف نمایش داده **نشود** - بخش دعوتنامه‌ها (`ClinicInvitationsSection`) نمایش داده **نشود** بررسی کن چه بخش‌هایی با `isOwnProfile` یا `primaryRole === 'admin'` guard شده‌اند و `clinic` را به عنوان read-only viewer handle کن. ## نکات مهم - `ClinicRepository::findByUser(User $user)` وجود دارد — از آن برای پیدا کردن کلینیک صاحب استفاده کن - `Clinic::getDoctors()` یک `Collection` است — از `->contains($doctor)` برای چک عضویت استفاده کن - `GET /api/v1/doctor/{uuid}` را تغییر نده — این endpoint عمومی است و frontend برای admin/doctor از آن استفاده می‌کند - migration لازم نیست — هیچ entity تغییر نمی‌کند - بعد از تغییر route باید `ddev exec php bin/console cache:clear` اجرا شود - مستندات: `docs/api/doctor.md` را با endpoint جدید به‌روزرسانی کن