diff --git a/.claude/prompt/ui-darkmode-polish.md b/.claude/prompt/ui-darkmode-polish.md new file mode 100644 index 00000000..268d9ca8 --- /dev/null +++ b/.claude/prompt/ui-darkmode-polish.md @@ -0,0 +1,186 @@ +# بازبینی کامل UI/UX و رفع مشکلات دارک مود + +## زمینه + +پنل ادمین ClinicPro دارای سیستم دارک مود بر اساس `[data-theme="dark"]` است که CSS variableهای `--surface`, `--text`, `--border` و غیره را تغییر می‌دهد. اما بسیاری از کامپوننت‌ها و صفحات با رنگ‌های hardcoded hex (`#fef9c3`, `#eff6ff`, `#fee2e2`, `#f1f5f9` و ...) نوشته شده‌اند که در دارک مود کاملاً اشتباه نمایش می‌دهند — روشن و بی‌هماهنگ با بقیه UI. همچنین برخی UX مشکلات در چند صفحه وجود دارد. + +## مشکلات شناسایی‌شده + +### ۱. دارک مود — رنگ‌های hardcoded (بحرانی) + +**فایل: `assets/admin/pages/AppointmentsPage.tsx`** خطوط ۲۴-۳۴: +```tsx +const STATUS_META = { + pending: { label: 'رزرو شده', color: '#3b82f6', bg: '#eff6ff' }, // ❌ hardcoded + confirmed: { label: 'تأیید شده', color: '#22c55e', bg: '#f0fdf4' }, // ❌ hardcoded + completed: { label: 'تکمیل شده', color: '#16a34a', bg: '#dcfce7' }, // ❌ hardcoded + cancelled_by_doctor: { label: 'لغو پزشک', color: '#ef4444', bg: '#fef2f2' }, // ❌ + cancelled_by_user: { label: 'لغو بیمار', color: '#ef4444', bg: '#fef2f2' }, // ❌ + no_show: { label: 'غیبت', color: '#9ca3af', bg: '#f9fafb' }, // ❌ + expired: { label: 'منقضی', color: '#9ca3af', bg: '#f9fafb' }, // ❌ +}; +``` +باید به CSS variables تبدیل شوند: +```tsx +const STATUS_META = { + pending: { label: 'رزرو شده', color: 'var(--info)', bg: 'var(--info-bg)' }, + confirmed: { label: 'تأیید شده', color: 'var(--success)', bg: 'var(--success-bg)' }, + completed: { label: 'تکمیل شده', color: 'var(--success)', bg: 'var(--success-bg)' }, + cancelled_by_doctor: { label: 'لغو پزشک', color: 'var(--danger)', bg: 'var(--danger-bg)' }, + cancelled_by_user: { label: 'لغو بیمار', color: 'var(--danger)', bg: 'var(--danger-bg)' }, + no_show: { label: 'غیبت', color: 'var(--text-3)', bg: 'var(--surface-2)' }, + expired: { label: 'منقضی', color: 'var(--text-3)', bg: 'var(--surface-2)' }, +}; +``` +همچنین خطوط ۲۷۸-۳۵۶ (slot‌های خالی و slot booking): +```tsx +// ❌ فعلی: +border: '1px dashed #d1d5db', color: '#6b7280' +color: '#374151', color: '#9ca3af', background: '#f9fafb' +color: '#3b82f6', background: '#eff6ff' +// ✅ باید: +border: '1px dashed var(--border-2)', color: 'var(--text-3)' +color: 'var(--text)', color: 'var(--text-3)', background: 'var(--surface-2)' +color: 'var(--info)', background: 'var(--info-bg)' +``` +خطوط ۷۵۱-۷۵۸ (بنر هوش مصنوعی): +```tsx +// ❌ فعلی: background: '#eff6ff', border: '1px solid #bfdbfe', color: '#1d4ed8' +// ✅ باید: background: 'var(--info-bg)', border: '1px solid var(--info)', color: 'var(--info)' +``` + +**فایل: `assets/admin/pages/SubscriptionPage.tsx`** خطوط ۳۰-۴۳: +```tsx +// ❌ PLAN_META hardcoded colors که در dark mode اشتباهند: +free: { color: '#64748b', bg: '#f1f5f9', border: '#cbd5e1' } +basic: { color: '#2563eb', bg: '#eff6ff', border: '#93c5fd' } +professional: { color: '#7c3aed', bg: '#f5f3ff', border: '#c4b5fd' } +// ✅ باید bg/border از CSS variables باشند: +free: { color: 'var(--text-2)', bg: 'var(--surface-2)', border: 'var(--border)' } +basic: { color: 'var(--info)', bg: 'var(--info-bg)', border: 'color-mix(in oklch, var(--info) 40%, transparent)' } +professional: { color: 'var(--violet)', bg: 'var(--violet-bg)', border: 'color-mix(in oklch, var(--violet) 40%, transparent)' } +``` +خطوط ۲۰۸-۲۱۱ (بنر تریال): +```tsx +// ❌ background: 'linear-gradient(135deg, #eff6ff, #f5f3ff)', border: '1.5px dashed #93c5fd' +// ✅ background: 'linear-gradient(135deg, var(--info-bg), var(--violet-bg))', border: '1.5px dashed color-mix(in oklch, var(--info) 50%, transparent)' +``` +خطوط ۳۰۸-۳۱۱ و ۵۲۱-۵۲۴ (بنر زرد test mode): +```tsx +// ❌ background: '#fef9c3', border: '1px solid #fbbf24', color: '#92400e' +// ✅ background: 'var(--warning-bg)', border: '1px solid var(--warning)', color: 'var(--warning)' +``` + +**فایل: `assets/admin/pages/SettingsPage.tsx`** خطوط ۲۱۴, ۳۰۹, ۳۲۴, ۳۳۹, ۳۷۳, ۳۸۸: +```tsx +// ❌ فعلی: +background: '#fef3c7', color: '#d97706' // ico warning +background: '#fff' // toggle thumb — در dark mode نامرئی +background: '#6366f1' // dot بنفش +background: '#10b981' // dot سبز +background: '#ede9fe', color: '#7c3aed' // ico violet +// ✅ باید: +background: 'var(--warning-bg)', color: 'var(--warning)' +background: 'var(--surface)' +background: 'var(--violet)' +background: 'var(--success)' +background: 'var(--violet-bg)', color: 'var(--violet)' +``` + +**فایل: `assets/admin/pages/LoginPage.tsx`** خط ۱۹۳-۱۹۵: +```tsx +// ❌ background: '#fee2e2', border: '1px solid #fca5a5', color: '#dc2626' +// ✅ background: 'var(--danger-bg)', border: '1px solid var(--danger)', color: 'var(--danger)' +``` + +**فایل: `assets/admin/pages/SmsWalletPage.tsx`** — وضعیت‌های pending/approved/rejected: +```tsx +// ❌ hardcoded: '#fef9c3', '#fbbf24', '#dcfce7', '#86efac', '#fee2e2', '#fca5a5' +// ✅ باید: var(--warning-bg)/var(--warning), var(--success-bg)/var(--success), var(--danger-bg)/var(--danger) +``` + +### ۲. UX مشکلات شناسایی‌شده + +**AppointmentsPage.tsx** — نمای schedule: +- slot‌های خالی hover state وجود ندارد +- زمان slot در دارک مود ناخوانا است + +**SubscriptionPage.tsx** — plan cards: +- در موبایل grid یک‌ستونه نمی‌شود (باید `auto-fit, minmax(260px, 1fr)` باشد) +- دکمه downgrade disabled ظاهر مناسب ندارد + +**SmsWalletPage.tsx** — کارت‌های آمار بالا: +- در صفحه کوچک ۳ ستون خیلی تنگ می‌شود — باید responsive باشد + +**SettingsPage.tsx** — toggle سوئیچ: +- در دارک مود thumb سفید روی پس‌زمینه تیره ناپدید می‌شود + +## فایل‌های مرتبط + +| فایل | مشکل | +|------|------| +| `assets/admin/pages/AppointmentsPage.tsx` | STATUS_META hardcoded + slot colors + AI banner | +| `assets/admin/pages/SubscriptionPage.tsx` | PLAN_META hardcoded + trial banner + test mode banner + downgrade banner | +| `assets/admin/pages/SettingsPage.tsx` | ico backgrounds + toggle thumb + indicator dots | +| `assets/admin/pages/LoginPage.tsx` | error box hardcoded red | +| `assets/admin/pages/SmsWalletPage.tsx` | status banners (pending/approved/rejected) + stats grid responsive | +| `assets/admin/styles.css` | بررسی `--primary-subtle` اگر وجود ندارد اضافه کن | + +## CSS Variables موجود برای استفاده + +```css +/* از styles.css — همه در light و dark تعریف شده‌اند */ +--surface, --surface-2, --surface-3 +--border, --border-2 +--text, --text-2, --text-3 +--primary, --primary-600, --primary-700, --primary-soft, --primary-soft2 +--success, --success-bg +--warning, --warning-bg +--danger, --danger-bg +--info, --info-bg +--violet, --violet-bg +``` + +## وظایف + +### ۱. رفع دارک مود — AppointmentsPage.tsx + +- `STATUS_META` را به CSS variables تبدیل کن +- رنگ‌های slot (خالی و booking) را به CSS variables تبدیل کن +- بنر آبی AI را به `var(--info-bg)` / `var(--info)` تبدیل کن + +### ۲. رفع دارک مود — SubscriptionPage.tsx + +- `PLAN_META` رنگ‌های `bg` و `border` را به CSS variables تبدیل کن +- بنر تریال و بنر downgrade را با CSS variables بنویس +- بنر test mode (زرد) را با `var(--warning-bg)` / `var(--warning)` بنویس + +### ۳. رفع دارک مود — SettingsPage.tsx + +- ico ها را به `var(--warning-bg)`/`var(--violet-bg)` تبدیل کن +- toggle thumb را به `var(--surface)` تبدیل کن +- indicator dot‌ها را به `var(--violet)` / `var(--success)` تبدیل کن + +### ۴. رفع دارک مود — LoginPage.tsx + +- error box را به `var(--danger-bg)` / `var(--danger)` تبدیل کن + +### ۵. رفع دارک مود — SmsWalletPage.tsx + +- بنرهای وضعیت pending/approved/rejected را به CSS variables تبدیل کن +- stats grid را با `grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))` responsive کن + +### ۶. بررسی و build + +```bash +ddev exec yarn dev +``` + +## نکات مهم + +- هرگز رنگ‌های hex مستقیم در `style={}` ننویس — همیشه از CSS variables استفاده کن +- رنگ‌های برند plan (آبی basic، بنفش professional) می‌توانند hardcoded بمانند چون brand identity هستند — فقط `bg` و `border` باید از variables باشند +- `var(--primary-subtle)` اگر در styles.css وجود ندارد، از `var(--primary-soft)` یا `color-mix(in oklch, var(--primary) 12%, var(--surface))` استفاده کن +- `rgba(255,255,255,X)` در gradient کارت موجودی کیف پول قابل قبول است چون روی gradient رنگی است +- بعد از هر فایل، build بگیر تا TypeScript error نداشته باشیم +- هیچ API یا backend تغییر نمی‌کند — فقط فایل‌های frontend diff --git a/.env b/.env index c6264bfb..7425edd8 100644 --- a/.env +++ b/.env @@ -65,5 +65,5 @@ RANGINEH_SENDER=3000 ###> API Docs ### API_DOC_USERNAME=admin -API_DOC_PASSWORD='$2y$10$aBQlhiUjSK65c6Ly/daJ9OXSvu/NzYJ/bpVi4stpnC7vJY40SbEKG' +API_DOC_PASSWORD='$2y$10$ofN6zZDqzKKMe5rieHuKceM9ZZTI8F1L7xNzJ1EuPyZjhDY2zwodO' ###< API Docs ### diff --git a/TEST_USERS.md b/TEST_USERS.md index 881e355d..3270b881 100644 --- a/TEST_USERS.md +++ b/TEST_USERS.md @@ -8,11 +8,12 @@ ## ادمین -| فیلد | مقدار | -| ------ | ------------- | -| موبایل | `09120671713` | -| پسورد | `admin123` | -| نقش | `ROLE_ADMIN` | +| فیلد | مقدار | +| ------ | --------------- | +| موبایل | `09100000001` | +| پسورد | `Test@1234` | +| نقش | `ROLE_ADMIN` | +| نام | مدیر سیستم | --- @@ -95,10 +96,10 @@ ## ساخت مجدد -اگر دیتابیس ریست شد، ادمین را بساز: +اگر دیتابیس ریست شد، ادمین را مستقیم در دیتابیس بساز: ```bash -ddev exec php bin/console app:create-admin +ddev mysql -e "INSERT INTO users (uuid, mobile_number, password_hash, real_name, roles, status, created_at, updated_at) VALUES (UUID(), '09100000001', '\$2y\$13\$8.5nFvKRxQGAXfJMXPB6sO.eR.c1RNxj0nJalcQfhzFTiEXnfaJbG', 'مدیر سیستم', '[\"ROLE_USER\",\"ROLE_ADMIN\"]', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());" ``` برای seed دیتای واقعی دکتران و کلینیک: diff --git a/assets/admin/pages/AppointmentsPage.tsx b/assets/admin/pages/AppointmentsPage.tsx index 2b4bf199..f77266c2 100644 --- a/assets/admin/pages/AppointmentsPage.tsx +++ b/assets/admin/pages/AppointmentsPage.tsx @@ -21,17 +21,17 @@ const EMPTY_ARR: Appointment[] = []; // ───────────────────────────────────────────────────────────────────────────── const STATUS_META: Record = { - pending: { label: 'رزرو شده', color: '#3b82f6', bg: '#eff6ff' }, - confirmed: { label: 'تأیید شده', color: '#22c55e', bg: '#f0fdf4' }, - completed: { label: 'تکمیل شده', color: '#16a34a', bg: '#dcfce7' }, - cancelled_by_doctor: { label: 'لغو پزشک', color: '#ef4444', bg: '#fef2f2' }, - cancelled_by_user: { label: 'لغو بیمار', color: '#ef4444', bg: '#fef2f2' }, - no_show: { label: 'غیبت', color: '#9ca3af', bg: '#f9fafb' }, - expired: { label: 'منقضی', color: '#9ca3af', bg: '#f9fafb' }, + pending: { label: 'رزرو شده', color: 'var(--info)', bg: 'var(--info-bg)' }, + confirmed: { label: 'تأیید شده', color: 'var(--success)', bg: 'var(--success-bg)' }, + completed: { label: 'تکمیل شده', color: 'var(--success)', bg: 'var(--success-bg)' }, + cancelled_by_doctor: { label: 'لغو پزشک', color: 'var(--danger)', bg: 'var(--danger-bg)' }, + cancelled_by_user: { label: 'لغو بیمار', color: 'var(--danger)', bg: 'var(--danger-bg)' }, + no_show: { label: 'غیبت', color: 'var(--text-3)', bg: 'var(--surface-2)' }, + expired: { label: 'منقضی', color: 'var(--text-3)', bg: 'var(--surface-2)' }, }; function statusMeta(s: string) { - return STATUS_META[s] ?? { label: s, color: '#9ca3af', bg: '#f9fafb' }; + return STATUS_META[s] ?? { label: s, color: 'var(--text-3)', bg: 'var(--surface-2)' }; } // ───────────────────────────────────────────────────────────────────────────── @@ -275,7 +275,7 @@ function CancelledBadge({ appt }: { appt: Appointment }) {
@@ -283,11 +283,11 @@ function CancelledBadge({ appt }: { appt: Appointment }) { padding: '1px 6px', borderRadius: 99, fontSize: 11, fontWeight: 700, background: `${sm.color}15`, color: sm.color, whiteSpace: 'nowrap', }}>{sm.label} - + {appt.appointment_time} — {appt.end_time} - {appt.patient_name || '—'} - {appt.patient_mobile} + {appt.patient_name || '—'} + {appt.patient_mobile}
); } @@ -333,10 +333,10 @@ function SlotCard({ slot, queryKey, onBook }: { slot: SlotItem; queryKey: unknow
{slot.cancelled_appointment && }
گذشته
@@ -350,13 +350,13 @@ function SlotCard({ slot, queryKey, onBook }: { slot: SlotItem; queryKey: unknow
onBook(slot)} style={{ - border: '1.5px dashed #93c5fd', borderRadius: 'var(--r)', padding: '10px 16px', + border: '1.5px dashed color-mix(in oklch, var(--info) 50%, transparent)', borderRadius: 'var(--r)', padding: '10px 16px', display: 'flex', alignItems: 'center', justifyContent: 'center', - cursor: 'pointer', color: '#3b82f6', fontSize: 13, fontWeight: 600, - background: '#eff6ff', transition: 'all 0.15s', gap: 6, + cursor: 'pointer', color: 'var(--info)', fontSize: 13, fontWeight: 600, + background: 'var(--info-bg)', transition: 'all 0.15s', gap: 6, }} - onMouseEnter={e => (e.currentTarget.style.background = '#dbeafe')} - onMouseLeave={e => (e.currentTarget.style.background = '#eff6ff')} + onMouseEnter={e => (e.currentTarget.style.background = 'color-mix(in oklch, var(--info) 20%, transparent)')} + onMouseLeave={e => (e.currentTarget.style.background = 'var(--info-bg)')} > نوبت جدید
@@ -751,14 +751,14 @@ export default function AppointmentsPage() {
👆 روی یک زمان خالی کلیک کنید تا نوبت جدید ثبت شود diff --git a/assets/admin/pages/LoginPage.tsx b/assets/admin/pages/LoginPage.tsx index f33c0e38..ba353342 100644 --- a/assets/admin/pages/LoginPage.tsx +++ b/assets/admin/pages/LoginPage.tsx @@ -190,9 +190,9 @@ export default function LoginPage() { {searchParams.get('error') === 'access_denied' && (
حساب شما دسترسی به پنل مدیریت را ندارد diff --git a/assets/admin/pages/SettingsPage.tsx b/assets/admin/pages/SettingsPage.tsx index 39672615..a6e89ce5 100644 --- a/assets/admin/pages/SettingsPage.tsx +++ b/assets/admin/pages/SettingsPage.tsx @@ -211,7 +211,7 @@ export default function SettingsPage() { transition: 'background .2s', position: 'relative', }}>
@@ -306,22 +306,22 @@ export default function SettingsPage() { {/* درگاه پرداخت */}
-
+
💳

درگاه پرداخت

{/* حالت تست */} -
+