Files
hamed bdc00fe267 refactor: update UserDetailPage styles and introduce avatar gradient utility
- Refactored role metadata styles in UserDetailPage for consistency with design tokens.
- Replaced hardcoded avatar colors with a utility function to generate gradients based on user ID.
- Improved InfoCard component styles for better hover effects and accessibility.
- Removed deprecated color classes and adjusted background gradients for various components.
- Updated theme token tests to reflect the removal of deferred files and ensure compliance with design standards.
- Added new avatarColors utility file to manage avatar gradient definitions.
2026-07-27 16:58:03 +03:30

24 KiB
Raw Permalink Blame History

ClinicPro Admin — UI Design Specification

سبک بصری: Panelix Premium React Admin Dashboard


1. Design System پایه

رنگ‌بندی (Color Palette)

منبع حقیقت assets/admin/styles.css است. توکن‌ها در :root تعریف و در [data-theme="dark"] بازتعریف می‌شوند؛ نسخهٔ oklch/color-mix زیر @supports می‌آید و مرورگر قدیمی روی همان مقادیر sRGB می‌ماند. هیچ توکنی نباید فقط داخل @supports تعریف شود.

/* Brand — indigo (نه بنفشِ این سند در نسخه‌های قدیمی) */
--primary  --primary-600  --primary-700  --primary-soft  --primary-soft2  --on-primary
/* Accent — نارنجی هم‌خانواده با clinic-pro-tauri */
--accent  --accent-600  --accent-bg
/* سطوح و متن */
--bg  --bg-2  --surface  --surface-2  --surface-3  --border  --border-2
--text  --text-2  --text-3
/* وضعیت */
--success/-bg  --warning/-bg  --danger/-bg  --info/-bg  --violet/-bg
/* کارت‌های آمار داشبورد */
--stat-{amber,violet,green,pink}-{bg,fg}

قاعدهٔ الزامی: در assets/admin رنگ ثابت ممنوع است — نه hex، نه کلاس پالت Tailwind (bg-white, text-gray-800, border-slate-200, …). به‌جایش:

به‌جای بنویس
style={{ color: '#525252' }} style={{ color: 'var(--text)' }}
className="text-[#525252] dark:text-[#D7D8ED]" className="text-[var(--text)]"
className="bg-white border border-gray-100" className="bg-[var(--surface)] border border-[var(--border)]"
stroke="#616161" روی SVG style={{ stroke: 'var(--text-2)' }} یا stroke="currentColor"

نگاشت hexهای قدیمی به توکن (از آدیت ۱۴۰۵/۰۵):

hex توکن
#525252 #3b3b3b #2f2f2f #111827 #374151 · دارک: #d7d8ed --text
#616161 #6b7280 #7e7e7e #858d9d · دارک: #a1a1a1 --text-2
#9ca3af #94a3b8 #9b9b9b --text-3
#5559ce --primary · #494cb3--primary-600 · #3b3f9f--primary-700
#f17732 #f0682a #f97316 --accent
#ffffff (سطح کارت) · دارک: #222433 --surface
#efefef #ededed #e7e7e7 · دارک: #35343d #343645 --border
#e0e0e0 #e1e1e1 #dbdbdb #d7d7d7 · دارک: #404040 --border-2
#ef4444 #d32f2f #e53935 --danger (پس‌زمینه: --danger-bg)
#2e7d32 #3c9a4f #22c55e --success (پس‌زمینه: --success-bg)
#f59e0b #fdd835 #ffc051 --warning (پس‌زمینه: --warning-bg)
#f4f5fd #f7f8ff #eef0ff --primary-soft · #e8ebff #c7cef4--primary-soft2

استثناهای پذیرفته‌شده (در تست گارد ثبت شده‌اند): پالت انتخابی برچسب‌ها (TagsSettingsPage)، اسکریم مودال (bg-black/50، مثل .overlay)، و BrandFixed هیوی نارنجی در stores/uiStore.ts.

تایپوگرافی

Font Family: "Vazirmatn", "Inter", sans-serif   ← فارسی + لاتین
Direction: RTL

Heading 1:  28px / font-bold   / gray-900
Heading 2:  22px / font-bold   / gray-900
Heading 3:  18px / font-semibold / gray-800
Heading 4:  16px / font-semibold / gray-700
Body:       14px / font-normal / gray-600
Caption:    12px / font-normal / gray-500
Label:      12px / font-medium / gray-700 / uppercase + tracking-wide

Spacing & Border Radius

Spacing scale: 4px base (4, 8, 12, 16, 20, 24, 32, 40, 48, 64)
Border radius:
  sm:   6px   (badges, chips)
  md:   10px  (inputs, buttons)
  lg:   16px  (cards)
  xl:   24px  (modals)
  full: 9999px (avatars, toggles)
Box shadow:
  card:   0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06)
  modal:  0 20px 60px rgba(0,0,0,.15)
  dropdown: 0 4px 20px rgba(0,0,0,.10)

2. Layout Structure

┌─────────────────────────────────────────────────────────┐
│                      TOPBAR (64px)                       │
├────────────┬────────────────────────────────────────────┤
│            │                                            │
│  SIDEBAR   │              MAIN CONTENT                  │
│  (260px)   │                                            │
│            │  ┌──────────────────────────────────────┐  │
│  collapsed │  │  Page Header (title + breadcrumb)    │  │
│  → 72px    │  ├──────────────────────────────────────┤  │
│            │  │                                      │  │
│            │  │     Content Area (padding 24px)      │  │
│            │  │                                      │  │
│            │  └──────────────────────────────────────┘  │
└────────────┴────────────────────────────────────────────┘

3. Sidebar

حالت باز (260px)

┌──────────────────────────────┐
│  ◉ ClinicPro   [← collapse]  │  ← logo + toggle button
├──────────────────────────────┤
│  🔍 جستجوی سریع...           │  ← search input
├──────────────────────────────┤
│  GENERAL                     │  ← section label (gray-500, 11px, uppercase)
│  ◉ داشبورد                   │  ← active item (purple bg + purple text + bold)
│  ○ کاربران                   │
│  ○ پزشکان                    │
│  ○ کلینیک‌ها                 │
├──────────────────────────────┤
│  MANAGEMENT                  │
│  ○ نوبت‌ها              [3]  │  ← badge count
│  ○ پرداخت‌ها                 │
│  ○ تسویه‌حساب          [5]  │
├──────────────────────────────┤
│  CONTENT                     │
│  ○ نظرات               [12] │
│  ○ امتیازها                  │
│  ○ بلاگ                      │
│  ○ پیامک                     │
├──────────────────────────────┤
│  SYSTEM                      │
│  ○ دسته‌بندی‌ها              │
│  ○ نمایندگان                 │
│  ○ منشی‌ها                   │
├──────────────────────────────┤
│  ┌────────────────────────┐  │
│  │ 👤 Admin               │  ← admin profile card at bottom
│  │ admin@clinicpro.ir     │  
│  │ [تنظیمات] [خروج]      │  
│  └────────────────────────┘  │
└──────────────────────────────┘

حالت جمع‌شده (72px) — Flyout on hover

┌──────┐
│  ◉   │   ← logo icon
├──────┤
│  🔍  │   ← hover → flyout search
├──────┤
│  ⊞   │   ← icon only, hover → flyout label + submenu
│  👥  │
│  🩺  │
│  🏥  │
│  📅  │
│  💳  │
│  🏦  │   ← badge dot (نه عدد)
│  💬  │   ← badge dot
│  ⭐  │
│  📝  │
│  📱  │
│  🗂  │
│  🤝  │
│  🔐  │
└──────┘

رفتار sidebar:

  • transition: width 300ms cubic-bezier(0.4, 0, 0.2, 1)
  • Overlay در موبایل (< 768px)
  • Active item: bg-primary-500/15 + right border 4px solid #8b5cf6
  • Hover item: bg-gray-700/40

4. Topbar

┌─────────────────────────────────────────────────────────────┐
│  ≡  [Breadcrumb: داشبورد / پزشکان]        🔔 5  👤 Admin ▾ │
└─────────────────────────────────────────────────────────────┘
  • ارتفاع: 64px
  • پس‌زمینه: سفید + box-shadow: 0 1px 0 #e5e7eb
  • Notification Bell: dropdown با لیست آخرین رویدادها
  • User Menu: تصویر آواتار + نام + dropdown (پروفایل / تنظیمات / خروج)

5. Cards

Stat Card (آمار خلاصه)

┌──────────────────────────────────┐
│  ┌────┐                          │
│  │ 🩺 │   کل پزشکان              │  ← icon در مربع رنگی (purple-100)
│  └────┘   1,284                  │  ← عدد بزرگ (28px bold)
│           ↑ 12% نسبت به ماه قبل │  ← trend badge (سبز/قرمز)
└──────────────────────────────────┘
bg: white, radius: 16px, shadow: card, padding: 24px

Data Card (محتوا / جداول)

┌────────────────────────────────────────────┐
│  عنوان کارت                    [اقدام ▾]  │  ← header
├────────────────────────────────────────────┤
│                                            │
│   محتوا (جدول / نمودار / فرم)             │
│                                            │
└────────────────────────────────────────────┘

6. DataTable (جدول داده)

┌─────────────────────────────────────────────────────────────────┐
│  [🔍 جستجو...]    [فیلتر ▾]    [ستون‌ها ▾]    [صادرکردن ↓]     │
├──────────┬────────────┬──────────┬────────┬─────────────────────┤
│  ☐  نام  │   موبایل   │  نقش     │ وضعیت  │      اقدامات        │
├──────────┼────────────┼──────────┼────────┼─────────────────────┤
│ ☐ علی م. │ 0912***    │ پزشک     │ ● فعال │ 👁 ✏️ 🗑            │
│ ☐ سارا ح │ 0935***    │ کلینیک   │ ○ غیر  │ 👁 ✏️ 🗑            │
├──────────┴────────────┴──────────┴────────┴─────────────────────┤
│  نمایش 1-10 از 284          [← قبلی]  1 2 3 ... 29  [بعدی →]   │
└─────────────────────────────────────────────────────────────────┘

ویژگی‌ها:

  • Sortable columns (کلیک روی header → ↑↓)
  • Row hover: bg-gray-50
  • Sticky header هنگام scroll
  • Loading state: skeleton rows (shimmer animation)
  • Empty state: آیکون + پیام توصیفی + دکمه اقدام
  • Bulk actions: با انتخاب checkbox ها → نوار بالا ظاهر می‌شود

7. Status Badges

// وضعیت نوبت
<Badge variant="yellow">در انتظار پرداخت</Badge>     /* waiting_for_payment */
<Badge variant="blue">رزرو شده</Badge>               /* reserved */
<Badge variant="purple">ورود به مطب</Badge>           /* checked_in */
<Badge variant="orange">در صف انتظار</Badge>          /* waiting */
<Badge variant="indigo">در حال ویزیت</Badge>          /* in_progress */
<Badge variant="green">ویزیت شده</Badge>              /* visited / completed */
<Badge variant="red">لغو شده</Badge>                  /* cancelled_* */
<Badge variant="gray">لغو خودکار</Badge>              /* auto_cancel_unpaid */
<Badge variant="rose">غیبت</Badge>                    /* no_show */

// وضعیت پرداخت
<Badge variant="yellow">در انتظار</Badge>             /* pending */
<Badge variant="green">موفق</Badge>                   /* received */
<Badge variant="red">لغو شده</Badge>                  /* canceled */
<Badge variant="blue">استرداد</Badge>                 /* refund */

// وضعیت SMS Template
<Badge variant="gray">پیشنویس</Badge>               /* draft */
<Badge variant="yellow">در انتظار تأیید</Badge>       /* pending_approval */
<Badge variant="green">تأیید شده</Badge>              /* approved */
<Badge variant="red">رد شده</Badge>                   /* rejected */

ساختار badge:

padding: 2px 10px
border-radius: 9999px
font-size: 12px / font-medium
با dot رنگی (●) در ابتدا

8. فرم‌ها (Forms)

Input

┌─────────────────────────────────┐
│ برچسب                           │
│ ┌─────────────────────────────┐ │
│ │ 🔍  placeholder...          │ │  ← icon اختیاری
│ └─────────────────────────────┘ │
│ پیام خطا (قرمز، 12px)           │
└─────────────────────────────────┘
  • Border: 1px solid #d1d5db → focus: 2px solid #8b5cf6
  • Height input: 44px
  • Border-radius: 10px
  • Error state: border قرمز + shake animation
  • Disabled: opacity 50%

Select / Dropdown

  • کتابخانه: react-select با استایل custom (RTL support)
  • Multi-select برای تخصص‌ها، بیمه‌ها، تگ‌ها

Permission Matrix (منشی)

                    مشاهده   ایجاد   ویرایش   حذف
نوبت‌ها              ☑        ☑       ☐        ☐
آدرس‌ها             ☑        ☐       ☐        ☐
اطلاعات کلینیک      ☑        —       ☐        —
بیمه‌ها             ☑        ☐       ☐        ☐

9. نمودارها (Charts)

داشبورد اصلی

Row 1: [Stat Card x4] ← کاربران / پزشکان / نوبت امروز / درآمد امروز

Row 2: [Area Chart — درآمد ماهانه (60%)] | [Donut Chart — نوبت‌ها بر اساس وضعیت (40%)]

Row 3: [Bar Chart — آمار ماهانه نمایندگان (60%)] | [لیست آخرین نوبت‌ها (40%)]

کتابخانه: Recharts یا ApexCharts

  • رنگ اصلی نمودارها: shades of purple + secondary colors
  • Tooltip: سفید با سایه، اعداد فارسی
  • X-axis: نام ماه‌های شمسی (فروردین ... اسفند)
  • Responsive: <ResponsiveContainer width="100%" height={300}>

10. Modal / Dialog

┌──────────────────────────────────────────────────┐
│                                                  │  ← backdrop: rgba(0,0,0,.4)
│  ┌────────────────────────────────────────────┐  │
│  │ عنوان Modal                           ✕   │  │  ← header: border-bottom
│  ├────────────────────────────────────────────┤  │
│  │                                            │  │
│  │  محتوا                                    │  │
│  │                                            │  │
│  ├────────────────────────────────────────────┤  │
│  │             [لغو]    [تأیید / ذخیره]      │  │  ← footer: border-top
│  └────────────────────────────────────────────┘  │
│                                                  │
└──────────────────────────────────────────────────┘
  • انیمیشن ورود: scale(0.95) → scale(1) + opacity 0 → 1 (200ms)
  • Confirm Dialog برای حذف: دکمه «حذف» قرمز + آیکون هشدار
  • Width: sm=400px / md=600px / lg=800px / xl=1000px

11. Toast Notifications

موقعیت: top-left (RTL)
┌─────────────────────────────────┐
│ ✓  پزشک با موفقیت ویرایش شد.   │  ← success (سبز)
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ ✕  خطا در ذخیره اطلاعات.       │  ← error (قرمز)
└─────────────────────────────────┘
  • Auto dismiss: 4 ثانیه
  • Stack: حداکثر 3 نوتیفیکیشن همزمان
  • کتابخانه: react-hot-toast یا sonner

12. Empty States & Loading

Loading (Skeleton)

┌──────────────────────────────┐
│  ▓▓▓▓▓▓▓▓▓  ░░░░░░░░░░░     │  ← shimmer animation
│  ░░░░░░░░░░░░░░░░░░░░░░░░   │
│  ░░░░░░░░░░░  ▓▓▓▓▓▓▓▓▓▓   │
└──────────────────────────────┘
  • animate-pulse با رنگ gray-200

Empty State

┌──────────────────────────────────┐
│                                  │
│          [SVG Illustration]      │
│                                  │
│    هیچ موردی یافت نشد            │
│    توضیح کوتاه...                │
│                                  │
│         [افزودن اولین مورد]      │
│                                  │
└──────────────────────────────────┘

13. Page Header (هر صفحه)

┌─────────────────────────────────────────────────────────┐
│  پزشکان                             [+ افزودن پزشک]     │
│  داشبورد / پزشکان                                        │  ← breadcrumb
└─────────────────────────────────────────────────────────┘

14. تکنولوژی Stack

لایه کتابخانه
Framework React 19 + TypeScript
Routing React Router v7
Styling Tailwind CSS v4
State (server) TanStack Query v5
State (client) Zustand
Forms React Hook Form + Zod
Charts Recharts
Table TanStack Table v8
Icons Heroicons v2
Date (Jalali) @date-io/date-fns-jalali + react-datepicker
Numbers react-number-format
Toast sonner
Select react-select
Rich Text @tiptap/react
File Upload react-dropzone
RTL dir="rtl" + Tailwind rtl: variants
Font Vazirmatn (از Google Fonts یا CDN)

15. Responsive Breakpoints

نام عرض رفتار
mobile < 768px Sidebar → Drawer overlay
tablet 768px1024px Sidebar collapsed (72px)
desktop > 1024px Sidebar باز (260px)

16. Dark Mode (پیاده‌شده)

دارک‌مود کامل شیپ شده است. stores/uiStore.tsapplyTheme() روی <html> هم‌زمان کلاس dark (برای واریانت Tailwind) و data-theme="dark" (برای توکن‌های CSS) را ست می‌کند و مقدارش در localStorage['clinicpro-ui'] می‌ماند. applyBrand() هم --brand-h/--brand-c و در صورت وجود BrandFixed مقادیر --primary* را inline می‌گذارد.

/* styles.css */
@custom-variant dark (&:where(.dark, .dark *));
:root { --surface: #ffffff; --text: #3b3b3b; /* … */ }
[data-theme="dark"] { --surface: #222433; --text: #D7D8ED; /* … */ }

قواعد:

  • رنگ از توکن بیاید؛ آن‌وقت دارک‌مود خودکار درست است و dark: لازم نیست.
  • dark: فقط وقتی که واقعاً توکنِ متفاوتی در دو تم لازم است — نه برای جبران hex ثابت.
  • inline style همیشه بر کلاس غالب است. style={{ color: '#525252' }} کنار className="dark:text-[#D7D8ED]" یعنی کلاس هیچ‌وقت اعمال نمی‌شود؛ این الگو در تست گارد assets/admin/test/theme-tokens.test.ts fail می‌دهد.
  • var(--token) داخل style کار می‌کند؛ برای SVG یا stroke="currentColor" بگذار و رنگ را از والد بگیر، یا style={{ stroke: 'var(--…)' }}.
  • الفای هگزی (${color}30) روی توکن بی‌معنی است — color-mix(in srgb, var(--x) 19%, transparent).

کل assets/admin مهاجرت کرده است؛ تست گارد DEFERRED ندارد و فقط سه استثنای عمدی دارد: پالت برچسب‌ها، BrandFixed در uiStore، و گرادیان تزئینی آواتار (lib/avatarColors.ts). اسکریم bg-black/NN هم مجاز است.

نکتهٔ تاریخی: کلاس‌هایی مثل text-primary-600 / bg-primary-100 هیچ‌وقت رنگی تولید نمی‌کردند — بلوک @theme فقط --font-sans را تعریف می‌کند و مقیاس primary-* در Tailwind وجود ندارد. ۴۳ نمونه از این کلاس‌های مرده در آدیت به توکن تبدیل شدند.


17. نمونه رنگ‌بندی صفحه داشبورد

[صفحه]  bg: #f1f5f9
│
├── Sidebar (bg: #0f172a, text: gray-400, active: purple-500)
│
└── Main
    ├── Topbar (bg: white, border-bottom: gray-200)
    │
    └── Content (padding: 24px)
        ├── [Stat Card]  bg:white, icon-box: purple-100
        ├── [Stat Card]  bg:white, icon-box: green-100
        ├── [Stat Card]  bg:white, icon-box: blue-100
        └── [Stat Card]  bg:white, icon-box: orange-100

منابع