Files
clinicpro/.claude/prompt/admin-mobile-responsive.md
hamed 1c81c6f2e5 Add JSON files for Payment Gateway Interface and API documentation updates
- Created a new JSON file for the PaymentGatewayInterface.php, detailing its methods and return types.
- Added a JSON file for payment.md, documenting various API endpoints and their parameters, responses, and errors.
- Introduced a JSON file for fix-admin-modal-and-calendar.md, outlining issues and solutions related to admin modal and calendar functionalities.
2026-07-02 21:22:14 +03:30

11 KiB
Raw Permalink Blame History

ریسپانسیو کردن پنل ادمین برای موبایل (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<div className="stat-grid" style={{ gridTemplateColumns: 'repeat(4,1fr)' }}>؛ روی ۳۷۵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 300pxDoctorDetailPage (1fr 1fr autoAppointmentsPage (1fr autoSettingsPage (1fr autoServiceTariffModal (150px 1fr autoDashboardPage ردیف 160px 1fr 52px. (PersianCalendar گریدهای repeat(7,1fr)/repeat(3,1fr) داخل popover کوچک‌اند و مشکل‌ساز نیستند — دست نزن.)

وضعیت فعلی

CSS (assets/admin/styles.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)

// assets/admin/components/layout/Topbar.tsx:26
onClick={typeof window !== 'undefined' && window.innerWidth < 1024 ? onMobileMenuOpen : toggleSidebar}

< 1024 منوی موبایل را باز می‌کند، ولی drawer CSS فقط ≤ 860 است → در بازهٔ ۸۶۱–۱۰۲۳ hamburger «باز» می‌کند اما سایدبار حالت drawer ندارد (منطقهٔ خراب).

نمونهٔ گرید inline (تکرارشده در ۲۰ فایل)

<div className="stat-grid" style={{ gridTemplateColumns: 'repeat(4,1fr)' }}>   // PaymentsPage:116
<div className="stat-grid" style={{ gridTemplateColumns: 'repeat(5,1fr)' }}>   // DoctorsPage:212
<div style={{ display: 'grid', gridTemplateColumns: '1fr 300px', gap: 'var(--gap)' }}>  // ClinicDetailPage:784

وظایف

۱. .stat-grid را ذاتاً ریسپانسیو کن (CSS)

در styles.css به .stat-grid یک base با auto-fit بده تا بدون نیاز به media و بدون تأثیرپذیری از تعداد ستون خودش جمع شود:

.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ها

در همهٔ صفحه‌هایی که <div className="stat-grid" style={{ gridTemplateColumns: 'repeat(N,1fr)', ... }}> دارند، فقط کلید gridTemplateColumns را از inline style حذف کن (بقیهٔ propها مثل marginBottom بمانند). با تسک ۱، خودکار ریسپانسیو می‌شوند.

فایل‌ها: PaymentsPage, DoctorsPage, StaffPage, FinancialReportPage, RepresentationFinancePage, RepresentationSettlementPage, DashboardPage (خطوط ۹۵۰/۱۰۵۶/۱۰۷۲).

مثال:

// قبل
<div className="stat-grid" style={{ gridTemplateColumns: 'repeat(4,1fr)' }}>
// بعد
<div className="stat-grid">
// اگر propهای دیگری بود:
<div className="stat-grid" style={{ marginBottom: 'var(--gap)' }}>

۳. لایه‌های دو/سه‌ستونهٔ inline → ریسپانسیو

برای گریدهای غیرِ stat که ستون ثابت دارند و باید روی موبایل عمودی شوند، به‌جای inline از کلاس ریسپانسیو استفاده کن. یک کلاس عمومی در 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:

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> عریض باید داخل .table-wrap (که overflow-x: auto دارد) باشد تا فقط جدول اسکرول شود نه کل صفحه. فایل‌های دارای <table> بدون wrap را بررسی و در صورت نبودِ wrapper، دور جدول <div className="table-wrap"> بگذار: 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 می‌دهد، همان الگو را اعمال کن.