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

6.6 KiB
Raw Permalink Blame History

دسترسی صاحب کلینیک به پروفایل پزشکان عضو کلینیک

زمینه

وقتی صاحب کلینیک (ROLE_CLINIC) در صفحه کلینیک روی آیکون چشم کنار نام یک پزشک کلیک می‌کند، به جای مشاهده پروفایل پزشک، به داشبورد redirect می‌شود. دلیل: route doctors/:uuid در App.tsx فقط ['admin', 'doctor'] را قبول دارد و RoleRoute صاحب کلینیک را به /admin/dashboard می‌فرستد.

مشکل

دو لایه مشکل وجود دارد:

۱. Frontend — Route permission:

// assets/admin/App.tsx — خط 141
<Route path="doctors/:uuid" element={<RoleRoute roles={['admin', 'doctor']}><DoctorDetailPage /></RoleRoute>} />

RoleRoute فقط primaryRole را چک می‌کند — اگر clinic بود، redirect به dashboard می‌کند.

۲. Backend — API permission:

// 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()

وضعیت فعلی

// 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}</>;
}
// 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 مسیر پزشک

// App.tsx — خط 141 را تغییر بده
<Route
  path="doctors/:uuid"
  element={<RoleRoute roles={['admin', 'doctor', 'clinic']}><DoctorDetailPage /></RoleRoute>}
/>

۲. Backend — endpoint اختصاصی برای کلینیک

یک endpoint جدید در DoctorController اضافه کن که صاحب کلینیک بتواند پروفایل پزشکان عضو کلینیکش را ببیند، با تأیید عضویت:

// 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 جدید استفاده کن:

// در 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 جدید به‌روزرسانی کن