Files
clinicpro/.claude/prompt/clinic-view-doctor-profile.md
T

148 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# دسترسی صاحب کلینیک به پروفایل پزشکان عضو کلینیک
## زمینه
وقتی صاحب کلینیک (`ROLE_CLINIC`) در صفحه کلینیک روی آیکون چشم کنار نام یک پزشک کلیک می‌کند، به جای مشاهده پروفایل پزشک، به داشبورد redirect می‌شود. دلیل: route `doctors/:uuid` در `App.tsx` فقط `['admin', 'doctor']` را قبول دارد و `RoleRoute` صاحب کلینیک را به `/admin/dashboard` می‌فرستد.
## مشکل
دو لایه مشکل وجود دارد:
**۱. Frontend — Route permission:**
```tsx
// assets/admin/App.tsx — خط 141
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />
```
`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
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />
// RoleRoute خط 70-74
function RoleRoute({ roles, children }: { roles: string[]; children: React.ReactNode }) {
const primaryRole = useAuthStore((s) => s.primaryRole);
if (!primaryRole) return <div>در حال بارگذاری...</div>;
if (!roles.includes(primaryRole)) return <Navigate to="/admin/dashboard" replace />;
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 را تغییر بده
<Route
path="doctors/:uuid"
element={<RoleRoute roles={['admin', 'doctor', 'clinic']}><DoctorDetailPage /></RoleRoute>}
/>
```
### ۲. 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 جدید به‌روزرسانی کن