# دسترسی پزشک به آدرس‌های کلینیک‌های عضو + اجباری کردن فیلدهای آدرس

## زمینه

وقتی یک پزشک عضو یک کلینیک می‌شود، آدرس‌های آن کلینیک باید در پروفایل پزشک (بخش «آدرس‌های مطب») نمایش داده شود. این آدرس‌ها با `type: 'clinic'` در entity `DoctorAddress` ذخیره می‌شوند و `clinic_id` دارند، اما در پاسخ API فعلی `clinic_name` برگردانده نمی‌شود در حالی که frontend آن را از `a.clinic_name` می‌خواند.

همچنین پزشک باید بتواند آدرس‌های کلینیک‌هایی که عضو آن‌هاست را ببیند — در حال حاضر `GET /api/v1/clinic-pro/doctor-addresses/{doctorId}` فقط آدرس‌های مستقیم Doctor entity را برمی‌گرداند و آدرس‌های کلینیک‌های وابسته را شامل نمی‌شود.

علاوه بر این، در `AddressModal` (افزودن آدرس جدید در پروفایل پزشک)، فیلدهای `telephone`، `address`، `province_id` و `city_id` اختیاری هستند و باید اجباری شوند.

## مشکل / هدف

سه هدف مجزا:

**۱.** آدرس‌های کلینیک‌های عضو پزشک باید در لیست آدرس‌های پزشک نمایش داده شوند
**۲.** `DoctorAddress::toArray()` باید `clinic_name` را نیز برگرداند تا frontend بتواند آن را نشان دهد
**۳.** در `AddressModal`، فیلدهای تلفن، آدرس کامل، شهر و استان باید اجباری (required) باشند

## فایل‌های مرتبط

| فایل | نقش |
|------|-----|
| `src/Doctor/Entity/DoctorAddress.php` | entity آدرس — متد `toArray()` باید `clinic_name` اضافه کند |
| `src/Doctor/Controller/DoctorController.php` | `listAddresses()` — خط 671 — باید آدرس‌های کلینیک‌ها را هم شامل شود |
| `src/Clinic/Repository/ClinicRepository.php` | دارای `findByDoctor(Doctor $doctor): array` |
| `src/Clinic/Controller/ClinicController.php` | `listAddresses()` — خط 544 — PUBLIC، آدرس‌های کلینیک را برمی‌گرداند |
| `assets/admin/pages/DoctorDetailPage.tsx` | `addrSchema` در خط 772 — `AddressModal` — فرم افزودن آدرس |
| `docs/api/doctor.md` | مستندات endpoint پزشک |

## وضعیت فعلی

### Backend — DoctorAddress::toArray() (فایل: `src/Doctor/Entity/DoctorAddress.php`, خط 112)

```php
public function toArray(): array
{
    return [
        'id'         => (string) $this->id,
        'uuid'       => $this->uuid,
        'type'       => $this->type,
        'clinic_id'  => $this->clinicId,   // ← فقط ID، بدون نام
        'name'       => $this->name,
        // ...
    ];
}
```

### Backend — listAddresses در DoctorController (خط 671)

```php
#[Route('/api/v1/clinic-pro/doctor-addresses/{doctorId}', methods: ['GET'])]
public function listAddresses(int $doctorId): JsonResponse
{
    $doctor = $this->doctorRepo->find($doctorId);
    // ...
    $addresses = array_map(fn(DoctorAddress $a) => $a->toArray(), $doctor->getAddresses()->toArray());
    // ← فقط آدرس‌های مستقیم Doctor entity — آدرس‌های کلینیک‌های عضو شامل نمی‌شود
    return $this->success(['data' => $addresses]);
}
```

### Frontend — addrSchema در DoctorDetailPage.tsx (خط 772)

```tsx
const addrSchema = z.object({
  name:        z.string().optional(),
  address:     z.string().optional(),       // ← باید required باشد
  telephone:   z.string().max(20).optional(), // ← باید required باشد
  province_id: z.number().nullable().optional(), // ← باید required باشد
  city_id:     z.number().nullable().optional(), // ← باید required باشد
  latitude:    z.string().optional(),
  longitude:   z.string().optional(),
});
```

### Frontend — SessionEditor (خط 1082-1085)

```tsx
...addresses.filter(a => a.type === 'clinic').map(a => ({
  label: `🏨 ${a.clinic_name ? `${a.clinic_name}${a.name ? ` — ${a.name}` : ''}` : (a.name ?? a.address ?? `کلینیک ${a.id}`)}`,
}))
// ← a.clinic_name را استفاده می‌کند اما toArray() این فیلد را برنمی‌گرداند
```

## وضعیت ClinicRepository

```php
// src/Clinic/Repository/ClinicRepository.php — خط 25
public function findByDoctor(Doctor $doctor): array
{
    return $this->createQueryBuilder('c')
        ->innerJoin('c.doctors', 'd')
        ->where('d.id = :doctorId')
        ->setParameter('doctorId', $doctor->getId())
        ->getQuery()
        ->getResult();
}
```

متد `findByDoctor()` وجود دارد — از آن استفاده کن.

## وظایف

### ۱. Backend — اضافه کردن `clinic_name` به `DoctorAddress::toArray()`

در `src/Doctor/Entity/DoctorAddress.php`، `DoctorAddress` entity دارای فیلد `clinicId` (integer) است اما ارتباط مستقیم به entity `Clinic` ندارد. بنابراین `clinic_name` باید از طریق injection خارجی به toArray اضافه شود.

**روش پیشنهادی:** متد `toArray()` را با پارامتر اختیاری گسترش بده:

```php
public function toArray(?string $clinicName = null): array
{
    return [
        'id'          => (string) $this->id,
        'uuid'        => $this->uuid,
        'type'        => $this->type,
        'clinic_id'   => $this->clinicId,
        'clinic_name' => $clinicName,  // ← جدید
        'name'        => $this->name,
        // ... بقیه فیلدها بدون تغییر
    ];
}
```

### ۲. Backend — آدرس‌های کلینیک‌های عضو را در `listAddresses` شامل کن

در `src/Doctor/Controller/DoctorController.php`، متد `listAddresses` (خط 671):

1. `ClinicRepository` را به constructor اضافه کن (اگر از قبل وجود ندارد، بررسی کن)
2. آدرس‌های کلینیک‌های عضو پزشک را دریافت کن
3. آن‌ها را به لیست آدرس‌های پزشک اضافه کن

```php
public function listAddresses(int $doctorId): JsonResponse
{
    $doctor = $this->doctorRepo->find($doctorId);
    if ($doctor === null) {
        return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
    }

    // آدرس‌های شخصی پزشک
    $personalAddresses = array_map(
        fn(DoctorAddress $a) => $a->toArray(),
        $doctor->getAddresses()->toArray()
    );

    // آدرس‌های کلینیک‌هایی که پزشک عضو آن‌هاست
    $clinicAddresses = [];
    $clinics = $this->clinicRepo->findByDoctor($doctor);
    foreach ($clinics as $clinic) {
        $clinicAddrs = $this->addressRepo->findBy(['clinicId' => $clinic->getId()]);
        foreach ($clinicAddrs as $addr) {
            $clinicAddresses[] = $addr->toArray($clinic->getName());
        }
    }

    return $this->success(['data' => array_merge($personalAddresses, $clinicAddresses)]);
}
```

**نکته:** `$this->addressRepo` باید از نوع `DoctorAddressRepository` باشد (همان که در ClinicController استفاده می‌شود). بررسی کن که DoctorController این repo را دارد یا نه — اگر ندارد به constructor اضافه کن.

**نکته:** `$this->clinicRepo` از نوع `ClinicRepository` — بررسی کن DoctorController این dependency را دارد.

### ۳. Frontend — اجباری کردن فیلدها در `addrSchema`

در `assets/admin/pages/DoctorDetailPage.tsx`، خط 772، schema را به این شکل تغییر بده:

```tsx
const addrSchema = z.object({
  name:        z.string().optional(),
  address:     z.string().min(1, 'آدرس کامل اجباری است'),
  telephone:   z.string().min(1, 'تلفن اجباری است').max(20),
  province_id: z.number({ required_error: 'استان اجباری است' }),
  city_id:     z.number({ required_error: 'شهر اجباری است' }),
  latitude:    z.string().optional(),
  longitude:   z.string().optional(),
});
```

همچنین لیبل فیلدهای اجباری در form باید علامت ستاره (`*`) داشته باشند — فیلدهای `address`، `telephone`، `استان`، `شهر`.

در JSX، لیبل‌های مربوطه را به این شکل تغییر بده:
```tsx
// مثال:
<label>تلفن <span className="text-red-500">*</span></label>
<label>آدرس کامل <span className="text-red-500">*</span></label>
<label>استان <span className="text-red-500">*</span></label>
<label>شهر <span className="text-red-500">*</span></label>
```

و اضافه کردن نمایش خطا در زیر فیلدها با `formState: { errors }` از `useForm`:
```tsx
const { register, handleSubmit, watch, setValue, reset, formState: { errors } } = useForm<AddrForm>({
  resolver: zodResolver(addrSchema),
});
// ...
{errors.telephone && <p className="text-xs text-red-500 mt-1">{errors.telephone.message}</p>}
{errors.address && <p className="text-xs text-red-500 mt-1">{errors.address.message}</p>}
```

برای `province_id` و `city_id` که از `SearchableSelect` استفاده می‌کنند، error در زیر select نمایش بده.

## نکات مهم

- **Migration لازم نیست** — هیچ schema تغییر نمی‌کند، فقط response format و validation
- **`DoctorAddress::toArray(?string $clinicName = null)`** — پارامتر optional است، همه فراخوانی‌های فعلی بدون تغییر کار می‌کنند
- **DoctorController constructor** — بررسی کن آیا `ClinicRepository` و `DoctorAddressRepository` از قبل inject شده‌اند
- **ClinicController** دارای `$this->addressRepo` از نوع `DoctorAddressRepository` — در DoctorController باید نام variable یکسان باشد (بررسی کن)
- **frontend** در خط 1084: `a.clinic_name` — بعد از اضافه شدن این فیلد به response، این بخش کار می‌کند
- `GET /api/v1/clinic-pro/doctor-addresses/{doctorId}` PUBLIC است — نیازی به auth guard جدید نیست
- بعد از تغییر controller: `ddev exec php bin/console cache:clear`
- بعد از تغییر frontend: `ddev exec yarn dev` و TypeScript check
- مستندات: `docs/api/doctor.md` را با تغییرات endpoint `listAddresses` به‌روزرسانی کن (اضافه شدن clinic addresses به response)
