# رفع دو باگ Modal و تقویم شمسی در پنل ادمین ## پروژه `clinicpro` (Admin SPA — React 19 داخل Symfony/Encore). ## زمینه در پنل ادمین دو باگ UI مستقل وجود دارد که هر دو در `/admin/profile` و سایر صفحات دیده می‌شوند: 1. **مودال وسط صفحه باز نمی‌شود و به پایین صفحه می‌چسبد** — در همهٔ صفحاتی که از کامپوننت مشترک `Modal` استفاده می‌کنند. 2. **تقویم شمسی هنگام کار با دکمه‌های ناوبری بسته می‌شود** — مثلاً در فیلد «تاریخ شروع فعالیت» وقتی وارد نمای انتخاب سال می‌شوی و روی دکمه‌های `<` / `>` (تغییر بازهٔ سال) کلیک می‌کنی، به‌جای جابه‌جایی بازه، کل فرم submit و مودال بسته می‌شود. ## مشکل / هدف ### باگ ۱ — علت کامپوننت `Modal` محتوای خود را **inline در همان‌جای درخت DOM** رندر می‌کند (بدون Portal). کلاس `.overlay` از `position: fixed; inset: 0; display: grid; place-items: center` استفاده می‌کند که باید نسبت به viewport وسط‌چین کند؛ اما وقتی یکی از عناصر والد یک containing-block برای `position: fixed` بسازد (هر عنصری با `transform` / `filter` / `perspective` / `contain: paint` / `will-change`)، مبنای `fixed` از viewport به آن والد تغییر می‌کند و overlay داخل جعبهٔ بلندِ آن والد کشیده می‌شود؛ در نتیجه `place-items: center` مودال را در وسط آن جعبهٔ بلند (که پایین‌تر از دید کاربر است) قرار می‌دهد، نه وسط صفحه. راه‌حل قطعی و مستقل از اینکه کدام والد مقصر است: رندر Modal با **React Portal روی `document.body`**. ### باگ ۲ — علت در `PersianCalendar.tsx` هیچ‌کدام از `
{children}
{footer &&
{footer}
} ); } ``` ### `PersianCalendar.tsx` (buttonها بدون `type`) — نمونه‌ها ```tsx // دکمه‌های ناوبری هدر // ... // سلول روز // سلول ماه // سلول سال ``` `mini-btn` بستن در `Modal` هم بدون `type` است و باید اصلاح شود (اگر مودالی داخل فرم قرار گیرد). ## وظایف ### ۱. رندر `Modal` با Portal روی `document.body` `createPortal` را از `react-dom` وارد کن و کل markup مودال را داخل آن بپیچ: ```tsx import React, { useEffect } from 'react'; import { createPortal } from 'react-dom'; import { XMarkIcon } from '@heroicons/react/24/outline'; // ... داخل کامپوننت، بعد از `if (!open) return null;` return createPortal(
e.stopPropagation()}> {/* ... بدون تغییر ... */}
, document.body ); ``` نکته‌ها: - Portal تضمین می‌کند overlay فرزندِ مستقیم `body` باشد، پس `position: fixed` همیشه نسبت به viewport محاسبه می‌شود و باگ چسبیدن به پایین در همهٔ صفحات رفع می‌شود. - منطق Escape و `onClick` overlay و `stopPropagation` مودال بدون تغییر بماند. - دکمهٔ بستن `mini-btn` را `type="button"` کن تا اگر مودالی داخل یک `` قرار گرفت، submit ناخواسته رخ ندهد. ### ۲. افزودن `type="button"` به همهٔ ` ``` این کار از submit ناخواستهٔ فرمِ دربرگیرنده جلوگیری می‌کند و تقویم هنگام کار با `<` / `>` و انتخاب سال/ماه باز می‌ماند؛ فقط انتخاب «روز» (که `onChange` + `onClose` را صدا می‌زند) آن را می‌بندد. ### ۳. (اختیاری، اگر جای دیگری هم مشکل مشابه بود) بررسی سریر سایر تقویم‌ها `PersianDatePicker.tsx` (والدِ `PersianCalendar`) دکمهٔ باز/بستن‌اش `
` است نه `