# بازطراحی 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 در پاسخ کم باشد).