# ریسپانسیو کردن پنل ادمین برای موبایل (iPhone 8 / ۳۷۵px) ## پروژه `clinicpro` (admin frontend — React 19 + `assets/admin/styles.css`) ## زمینه پنل `/admin` روی صفحه‌های کوچک (مثل iPhone 8 عرض ۳۷۵px) به‌هم می‌ریزد و افقی اسکرول می‌شود. زیرساخت ریسپانسیو **تا حدی هست** (viewport meta درست است، drawer سایدبار + scrim در `@media (max-width: 860px)`، `.settings-grid`/`.stat-grid` در media collapse می‌شوند)، ولی **بی‌اثر شده** چون در بیشتر صفحه‌ها `grid-template-columns` به‌صورت **inline `style={{}}`** روی المان‌ها نوشته شده و **inline style بر قوانین `@media` در CSS اولویت دارد** → گریدها روی موبایل جمع نمی‌شوند و از عرض صفحه بیرون می‌زنند. نمونهٔ واقعی: `PaymentsPage.tsx` → `
`؛ روی ۳۷۵px می‌شود ۴ ستون ۹۰px‌ای که overflow می‌دهد. `.stat-grid` در CSS در `@media (max-width:560px)` به `1fr 1fr` می‌رود ولی inline آن را باطل می‌کند. ## مشکل / هدف پنل ادمین روی ۳۷۵px بدون اسکرول افقی و خوانا شود: 1. گریدهای inline ثابت که media را می‌شکنند اصلاح شوند (بهترین راه: `repeat(auto-fit, minmax(...))` که بدون media خودکار جمع می‌شود، یا حذف inline و واگذاری به CSS). 2. breakpoint منوی موبایل (hamburger) با drawer یکسان شود. 3. جدول‌ها و محتوای عریض داخل کانتینر اسکرول‌شونده باشند، نه کل صفحه. 4. گارد سراسری ضد-overflow افقی. ## فایل‌های مرتبط | فایل | نقش | |------|-----| | `clinicpro/assets/admin/styles.css` | `.stat-grid` auto-fit، breakpoint drawer، گارد overflow، paddingهای موبایل | | `clinicpro/assets/admin/components/layout/Topbar.tsx` | هماهنگی breakpoint hamburger با CSS | | صفحه‌های با گرید inline (لیست پایین) | حذف/ریسپانسیو کردن `gridTemplateColumns` inline | **صفحه‌ها/کامپوننت‌های دارای `gridTemplateColumns` inline** (`grep -rn "gridTemplateColumns:" assets/admin`): `PaymentsPage`, `DoctorsPage`, `StaffPage`, `FinancialReportPage`, `RepresentationFinancePage`, `RepresentationSettlementPage`, `DashboardPage` (چند مورد)، `ClinicDetailPage` (`1fr 300px`)، `DoctorDetailPage` (`1fr 1fr auto`)، `AppointmentsPage` (`1fr auto`)، `SettingsPage` (`1fr auto`)، `ServiceTariffModal` (`150px 1fr auto`)، `DashboardPage` ردیف `160px 1fr 52px`. (`PersianCalendar` گریدهای `repeat(7,1fr)`/`repeat(3,1fr)` داخل popover کوچک‌اند و مشکل‌ساز نیستند — دست نزن.) ## وضعیت فعلی ### CSS (`assets/admin/styles.css`) ```css :root { --sidebar-w: 252px; } .main { flex: 1; min-width: 0; margin-right: var(--sidebar-w); } .stat-grid { /* base بدون grid-template-columns صریح؟ inlineها آن را ست می‌کنند */ } .scrim { display: none; } @media (max-width: 1080px) { .stat-grid { grid-template-columns: repeat(3, 1fr); } .dash-main, .dash-3 { grid-template-columns: 1fr; } } @media (max-width: 860px) { /* ← drawer فقط تا 860 */ .main { margin-right: 0 !important; } .sidebar { transform: translateX(100%); box-shadow: var(--shadow-lg); } .app[data-mobile-open="true"] .sidebar { transform: translateX(0); width: var(--sidebar-w); } .app[data-mobile-open="true"] .scrim { display: block; position: fixed; inset: 0; /* ... */ } .topbar-search { display: none; } .grid-2 { grid-template-columns: 1fr; } } @media (max-width: 560px) { .stat-grid { grid-template-columns: 1fr 1fr; } } ``` ### Topbar breakpoint (ناهماهنگ با CSS) ```tsx // assets/admin/components/layout/Topbar.tsx:26 onClick={typeof window !== 'undefined' && window.innerWidth < 1024 ? onMobileMenuOpen : toggleSidebar} ``` `< 1024` منوی موبایل را باز می‌کند، ولی drawer CSS فقط `≤ 860` است → در بازهٔ ۸۶۱–۱۰۲۳ hamburger «باز» می‌کند اما سایدبار حالت drawer ندارد (منطقهٔ خراب). ### نمونهٔ گرید inline (تکرارشده در ۲۰ فایل) ```tsx
// PaymentsPage:116
// DoctorsPage:212
// ClinicDetailPage:784 ``` ## وظایف ### ۱. `.stat-grid` را ذاتاً ریسپانسیو کن (CSS) در `styles.css` به `.stat-grid` یک base با `auto-fit` بده تا **بدون نیاز به media و بدون تأثیرپذیری از تعداد ستون** خودش جمع شود: ```css .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--gap); } ``` و قوانین media مربوط به `.stat-grid` (خطوط ۷۰۴ و ۷۲۰ فعلی) را **حذف** کن (دیگر لازم نیستند؛ auto-fit همه را پوشش می‌دهد). ### ۲. حذف `gridTemplateColumns` inline از stat-gridها در همهٔ صفحه‌هایی که `
` دارند، فقط **کلید `gridTemplateColumns` را از inline style حذف کن** (بقیهٔ propها مثل `marginBottom` بمانند). با تسک ۱، خودکار ریسپانسیو می‌شوند. فایل‌ها: `PaymentsPage`, `DoctorsPage`, `StaffPage`, `FinancialReportPage`, `RepresentationFinancePage`, `RepresentationSettlementPage`, `DashboardPage` (خطوط ۹۵۰/۱۰۵۶/۱۰۷۲). مثال: ```tsx // قبل
// بعد
// اگر propهای دیگری بود:
``` ### ۳. لایه‌های دو/سه‌ستونهٔ inline → ریسپانسیو برای گریدهای غیرِ stat که ستون ثابت دارند و باید روی موبایل عمودی شوند، به‌جای inline از کلاس ریسپانسیو استفاده کن. یک کلاس عمومی در CSS اضافه کن: ```css /* لایهٔ دوستونه که زیر ۸۶۰ عمودی می‌شود */ .split-2 { display: grid; grid-template-columns: 1fr minmax(0, 320px); gap: var(--gap); align-items: start; } @media (max-width: 860px) { .split-2 { grid-template-columns: 1fr; } } /* ردیف فیلد + اکشن که زیر ۶۰۰ عمودی می‌شود */ .form-row { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: end; } @media (max-width: 600px) { .form-row { grid-template-columns: 1fr; } } ``` سپس: - `ClinicDetailPage:784` (`1fr 300px`) → `className="split-2"` (حذف inline grid). - `DoctorDetailPage:1107` (`1fr 1fr auto`) و `AppointmentsPage:409` (`1fr auto`) و `SettingsPage:431` (`1fr auto`) و `ServiceTariffModal:95` (`150px 1fr auto`) → از `form-row` استفاده کن یا اگر ساختار خاص است، همان inline را با wrap در media نگه‌دار ولی مطمئن شو زیر ۶۰۰ تک‌ستون می‌شود. (برای المان‌های inline که کلاس‌گذاری سخت است، می‌توانی `gridTemplateColumns` را با `repeat(auto-fit, minmax(140px, 1fr))` جایگزین کنی تا خودکار بشکند.) - `DashboardPage:119` (`160px 1fr 52px`) و `DashboardPage:514` (`quick-grid` `repeat(3,1fr)`): `quick-grid` را هم مثل stat به auto-fit ببر؛ ردیف ۱۶۰px را زیر ۵۶۰ به `1fr auto` یا wrap تبدیل کن. ### ۴. هماهنگی breakpoint منوی موبایل drawer را با hamburger یکسان کن. **سریع‌ترین راه:** در `styles.css` بلوک `@media (max-width: 860px)` را به `@media (max-width: 1024px)` تغییر بده (drawer/scrim/main-margin/topbar-search همگی زیر ۱۰۲۴). این با `window.innerWidth < 1024` در `Topbar` هم‌خوان می‌شود و منطقهٔ خرابِ ۸۶۱–۱۰۲۳ رفع می‌شود. > اگر ترجیح می‌دهی سایدبار روی تبلت بماند، به‌جای آن `1024` در `Topbar.tsx` را به `860` تغییر بده. یکی از دو راه — نه هر دو. (توصیه: drawer تا ۱۰۲۴.) ### ۵. گارد سراسری ضد-overflow افقی + padding موبایل در `styles.css`: ```css html, body { max-width: 100%; overflow-x: hidden; } .main { min-width: 0; } /* اجازهٔ کوچک‌شدن flex child */ @media (max-width: 560px) { .page, .main > * { padding-left: 12px; padding-right: 12px; } /* اگر padding بزرگ‌تری دارند */ .page-head, .toolbar { flex-wrap: wrap; gap: 10px; } /* هدر/تولبار در چند خط */ } ``` (کلاس‌های واقعی padding صفحه را از `styles.css` پیدا کن — اگر نام `.page`/`.content` فرق دارد، همان را هدف بگیر.) ### ۶. جدول‌ها داخل کانتینر اسکرول‌شونده هر `` عریض باید داخل `.table-wrap` (که `overflow-x: auto` دارد) باشد تا فقط جدول اسکرول شود نه کل صفحه. فایل‌های دارای `
` بدون wrap را بررسی و در صورت نبودِ wrapper، دور جدول `
` بگذار: `PreRegistrationsPage`, `UsersPage`, `ClinicsPage`, `RepresentationFinancePage`, `AppointmentsPage`, `RepresentationSettlementPage`, `AdminSubscriptionPage`, `DoctorsPage`, `SmsWalletPage`, `RepresentationProfilePage`. (اگر از کامپوننت `DataTable` مشترک استفاده می‌کنند و آن خودش wrap دارد، نیازی نیست — اول بررسی کن.) ## نکات مهم - **چرا inline بد است:** `style={{gridTemplateColumns}}` روی المان، specificity بالاتر از هر `@media` در stylesheet دارد؛ پس یا حذف شود یا خودش `auto-fit`/ریسپانسیو باشد. این هستهٔ باگ است. - `auto-fit + minmax` بهترین انتخاب برای stat/quick گریدهاست: بدون media، با هر تعداد کارت، روی هر عرض درست جمع می‌شود. - **RTL**: از `padding-inline`/`margin-inline` و رفتار موجود پیروی کن؛ چیزی که RTL را بشکند اضافه نکن. - کلاس CSS جدید فقط در حد `.split-2`/`.form-row` (یا معادل)؛ کتابخانهٔ CSS جدید نصب نکن. - بعد از تغییر: `ddev exec yarn dev` و `ddev exec npx tsc --noEmit` بدون خطا؛ سپس در DevTools با عرض **375px (iPhone 8)** این صفحه‌ها را چک کن: Dashboard، Payments، Doctors، Appointments، Settings، یک Detail (Clinic/Doctor) — نباید اسکرول افقی داشته باشند و کارت‌ها/جدول‌ها باید درست بچینند. - تغییر فقط frontend/CSS است؛ backend و docs تغییری ندارند. - این‌ها فایل‌های `.tsx`/CSS پنل‌اند؛ اگر جای دیگری هم `gridTemplateColumns` inline ثابت دیدی که overflow می‌دهد، همان الگو را اعمال کن.