# بازطراحی UI/UX مودال «ثبت مراجعه جدید»

## پروژه

`clinicpro` (admin frontend)

## زمینه

مودال «ثبت مراجعه جدید» در صفحه‌ی پرونده بیمار، UX ضعیفی دارد: فیلدها بدون گروه‌بندی، label و input بدون فاصله‌ی منطقی، بخش خدمات و خلاصه‌ی قیمت بی‌سازمان، و ارتباط بین «قیمت ویزیت / تخفیف بیمه پایه / تخفیف مکمل» با بیمه‌ی واقعی بیمار مشخص نیست. (تصویر فعلی توسط کاربر ارائه شده.)

## مشکل / هدف

بازطراحی مودال با اصول UI/UX حرفه‌ای:

- گروه‌بندی منطقی: «بیمه و مبلغ» / «خدمات» / «پرداخت و یادداشت» / «خلاصه»
- انتخاب بیمه‌ی پایه و مکمل به‌صورت select (از بیمه‌های entity)، و **پرکردن خودکار قیمت ویزیت و تخفیف/سهم** بر اساس قیمت‌گذاری بیمه‌ی پروفایل (خروجی پرامپت `insurance-visit-pricing.md`)
- نمایش زنده‌ی محاسبه: آزاد → با بیمه پایه → با بیمه مکمل → مبلغ نهایی
- واحد پول یکدست (تومان یا ریال — یکی، نه مخلوط؛ الان «ریال» در ورودی و «تومان» در خلاصه مخلوط است)
- فاصله‌گذاری، عنوان بخش‌ها، حالت‌های loading/disabled درست

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

| فایل | نقش |
|------|-----|
| `assets/admin/pages/MyPatientsPage.tsx` | مودال `sessionModal` (حدود خط ۸۰۹ به بعد) و `handleSubmitSession` |
| `assets/admin/components/ui/Modal.tsx` | کامپوننت مودال (props: `size` = sm/md/lg/xl) |
| `assets/admin/components/ui/SearchableSelect.tsx` | select قابل‌جستجو (برای بیمه/خدمات) |
| `assets/admin/lib/utils.ts` | `formatRial`, `formatNumber` |
| `src/Patient/Controller/PatientController.php` | endpoint `POST /api/v1/patient/{uuid}/session` |
| `src/Patient/Service/PatientService.php` | محاسبه‌ی نهایی سمت سرور |

## وضعیت فعلی

- اسکیمای فرم:

```tsx
const sessionSchema = z.object({
    visit_price_rials: z.coerce.number().min(0),
    base_insurance_discount_percent: z.coerce.number().min(0).max(100),
    supplementary_discount_percent: z.coerce.number().min(0).max(100),
    payment_method: z.enum(["cash", "card", "insurance", "online", "pending"]),
    notes: z.string().optional(),
});
```

- محاسبه‌ی قیمت سمت کلاینت:

```tsx
function calcFinalPrice(visitPrice, baseDiscount, suppDiscount, servicesTotal) {
    const afterBase = visitPrice * (1 - baseDiscount / 100);
    const afterSupp = afterBase * (1 - suppDiscount / 100);
    return Math.round(afterSupp) + servicesTotal;
}
```

- خلاصه‌ی پایین مودال «جمع خدمات» و «مبلغ نهایی» را با «تومان» نشان می‌دهد ولی ورودی‌ها «ریال» هستند — ناهماهنگ.
- backend (`PatientService::createSession`) همین فیلدها + `insurance_base_id` / `insurance_supplementary_id` را می‌پذیرد (موجودیت `PatientSession` این دو ستون را دارد) اما فرم فعلی آن‌ها را ارسال نمی‌کند.

## وظایف

### ۱. ساختار بصری مودال

مودال را به بخش‌های واضح با عنوان تقسیم کن (می‌توانی `size="lg"` بدهی):

1. **بیمه و مبلغ ویزیت**: select بیمه‌ی پایه + select بیمه‌ی مکمل + قیمت ویزیت + تخفیف/سهم
2. **خدمات**: انتخاب بخش/سرویس + chipهای انتخاب‌شده (الگوی فعلی، فقط منظم‌تر)
3. **پرداخت و یادداشت**: روش پرداخت + یادداشت
4. **خلاصه**: آزاد / با بیمه پایه / با بیمه مکمل / جمع خدمات / مبلغ نهایی

### ۲. اتصال بیمه به قیمت‌گذاری پروفایل

- بیمه‌های entity و قیمت‌گذاری را از endpoint پرامپت `insurance-visit-pricing.md` (`GET /api/v1/insurance-pricing`) بگیر.
- با انتخاب بیمه‌ی پایه/مکمل، قیمت ویزیت آزاد و سهم بیمار خودکار پر شود (کاربر بتواند دستی override کند).
- `insurance_base_id` و `insurance_supplementary_id` را در body ارسال کن (backend از قبل پشتیبانی می‌کند).

### ۳. واحد پول یکدست

همه‌جا یک واحد. توصیه: نمایش به تومان با `formatRial` اگر این util تومان می‌دهد را بررسی کن؛ ورودی و خلاصه باید یکی باشند.

### ۴. تست

`ddev exec npx tsc --noEmit` و `ddev exec yarn dev`؛ بدون خطا.

## نکات مهم

- وابسته به `insurance-visit-pricing.md`؛ اگر آن endpoint هنوز نیست، حداقل ساختار UI و select بیمه‌ها از `GET /api/v1/insurances` را پیاده کن و auto-fill را با TODO علامت بزن.
- فرم با React Hook Form + Zod؛ server state با TanStack Query.
- از کامپوننت‌های `ui/` موجود استفاده کن؛ کتابخانه‌ی CSS جدید اضافه نکن؛ RTL.
- این تغییر صرفاً frontend است؛ تغییر API لازم نیست (مگر اینکه فیلد بیمه‌ی session در پاسخ کم باشد).
