Files
clinicpro/.claude/prompt/admin-mobile-responsive.md
T
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

158 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ریسپانسیو کردن پنل ادمین برای موبایل (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 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
<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 و بدون تأثیرپذیری از تعداد ستون** خودش جمع شود:
```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ها
در همهٔ صفحه‌هایی که `<div className="stat-grid" style={{ gridTemplateColumns: 'repeat(N,1fr)', ... }}>` دارند، فقط **کلید `gridTemplateColumns` را از inline style حذف کن** (بقیهٔ propها مثل `marginBottom` بمانند). با تسک ۱، خودکار ریسپانسیو می‌شوند.
فایل‌ها: `PaymentsPage`, `DoctorsPage`, `StaffPage`, `FinancialReportPage`, `RepresentationFinancePage`, `RepresentationSettlementPage`, `DashboardPage` (خطوط ۹۵۰/۱۰۵۶/۱۰۷۲).
مثال:
```tsx
// قبل
<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 اضافه کن:
```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>` عریض باید داخل `.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 می‌دهد، همان الگو را اعمال کن.