diff --git a/assets/admin/pages/DashboardPage.tsx b/assets/admin/pages/DashboardPage.tsx
index 3fe7cf66..6bc250c5 100644
--- a/assets/admin/pages/DashboardPage.tsx
+++ b/assets/admin/pages/DashboardPage.tsx
@@ -12,6 +12,7 @@ import type { ApiResponse } from '../lib/api';
import { formatNumber, formatRial, formatDateTime } from '../lib/utils';
import { useAuthStore } from '../stores/authStore';
import InviteDoctorModal from '../components/ui/InviteDoctorModal';
+import StatCard from '../components/ui/StatCard';
// ── Shared Status Maps ────────────────────────────────────────────────────
@@ -593,14 +594,15 @@ function ClinicDashboard() {
if (q.isLoading) return ;
- const kpiCards = [
- { label: 'پزشکان', value: formatNumber(d?.stats.total_doctors ?? 0), icon: HeartIcon, color: 'var(--success)', bg: 'var(--success-bg)' },
- { label: 'نوبتهای امروز', value: formatNumber(d?.stats.today_appointments ?? 0), icon: CalendarDaysIcon, color: 'var(--warning)', bg: 'var(--warning-bg)' },
- { label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon: ClockIcon, color: 'var(--info)', bg: 'var(--info-bg)' },
- { label: 'دعوتنامه در انتظار', value: formatNumber(d?.stats.pending_invitations ?? 0), icon: BellAlertIcon, color: 'var(--danger)', bg: 'var(--danger-bg)' },
- ...(d?.stats.sms_wallet_balance != null ? [{ label: 'موجودی پیامک', value: formatRial(d.stats.sms_wallet_balance), icon: CreditCardIcon, color: 'var(--primary)', bg: 'var(--primary-soft)' }] : []),
- ...(d?.stats.unique_patients_count != null ? [{ label: 'بیماران یکتا', value: formatNumber(d.stats.unique_patients_count), icon: UserGroupIcon, color: 'var(--violet)', bg: 'var(--violet-bg)' }] : []),
- ...(d?.stats.revenue_period_rials != null ? [{ label: 'درآمد دوره', value: formatRial(d.stats.revenue_period_rials), icon: CreditCardIcon, color: 'var(--success)', bg: 'var(--success-bg)' }] : []),
+ const statCards: { tone: 'amber' | 'violet' | 'green' | 'pink'; label: string; value: React.ReactNode; icon: React.ReactNode }[] = [
+ { tone: 'amber', label: 'تعداد نوبتهای امروز', value: `${formatNumber(d?.stats.today_appointments ?? 0)}+`, icon: },
+ { tone: 'violet', label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon: },
+ { tone: 'green', label: d?.stats.revenue_period_rials != null ? 'درآمد دوره' : 'پزشکان',
+ value: d?.stats.revenue_period_rials != null ? formatRial(d.stats.revenue_period_rials) : formatNumber(d?.stats.total_doctors ?? 0),
+ icon: d?.stats.revenue_period_rials != null ? : },
+ { tone: 'pink', label: d?.stats.unique_patients_count != null ? 'بیماران یکتا' : 'دعوتنامه در انتظار',
+ value: d?.stats.unique_patients_count != null ? formatNumber(d.stats.unique_patients_count) : formatNumber(d?.stats.pending_invitations ?? 0),
+ icon: },
];
return (
@@ -627,15 +629,9 @@ function ClinicDashboard() {
-
- {kpiCards.map(c => (
-
-
-
-
-
{c.label}
-
{c.value}
-
+
+ {statCards.map(c => (
+
))}
@@ -823,13 +819,13 @@ function DoctorDashboard() {
if (q.isLoading) return
;
- const kpiCards = [
- { label: 'نوبتهای امروز', value: formatNumber(d?.stats.today_appointments ?? 0), icon: CalendarDaysIcon, color: 'var(--warning)', bg: 'var(--warning-bg)' },
- { label: 'نوبتهای فردا', value: formatNumber(d?.stats.tomorrow_appointments ?? 0), icon: ClockIcon, color: 'var(--info)', bg: 'var(--info-bg)' },
- { label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon: CalendarDaysIcon, color: 'var(--success)', bg: 'var(--success-bg)' },
- { label: 'میانگین امتیاز', value: d?.stats.avg_rating != null ? String(d.stats.avg_rating) : '—', icon: StarIcon, color: 'var(--primary)', bg: 'var(--primary-soft)' },
- ...(d?.stats.unique_patients_count != null ? [{ label: 'بیماران یکتا', value: formatNumber(d.stats.unique_patients_count), icon: UserGroupIcon, color: 'var(--violet)', bg: 'var(--violet-bg)' }] : []),
- ...(d?.stats.revenue_period_rials != null ? [{ label: 'درآمد دوره', value: formatRial(d.stats.revenue_period_rials), icon: CreditCardIcon, color: 'var(--success)', bg: 'var(--success-bg)' }] : []),
+ const statCards: { tone: 'amber' | 'violet' | 'green' | 'pink'; label: string; value: React.ReactNode; icon: React.ReactNode }[] = [
+ { tone: 'amber', label: 'تعداد نوبتهای امروز', value: `${formatNumber(d?.stats.today_appointments ?? 0)}+`, icon:
},
+ { tone: 'violet', label: 'نوبتهای فردا', value: `${formatNumber(d?.stats.tomorrow_appointments ?? 0)}+`, icon:
},
+ { tone: 'green', label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon:
},
+ { tone: 'pink', label: d?.stats.revenue_period_rials != null ? 'درآمد دوره' : 'میانگین امتیاز',
+ value: d?.stats.revenue_period_rials != null ? formatRial(d.stats.revenue_period_rials) : (d?.stats.avg_rating != null ? String(d.stats.avg_rating) : '—'),
+ icon: d?.stats.revenue_period_rials != null ?
:
},
];
return (
@@ -852,21 +848,13 @@ function DoctorDashboard() {
-
- {kpiCards.map(c => (
-
-
-
-
-
{c.label}
-
{c.value}
- {c.label === 'میانگین امتیاز' && d?.stats.total_ratings ? (
-
{formatNumber(d.stats.total_ratings)} نظر
- ) : null}
-
+
+ {statCards.map(c => (
+
))}
+
diff --git a/assets/admin/styles.css b/assets/admin/styles.css
index f6bb27c5..16e66c54 100644
--- a/assets/admin/styles.css
+++ b/assets/admin/styles.css
@@ -47,11 +47,11 @@
--accent: #f0682a;
--accent-600: #db5a1f;
--accent-bg: #fdeee4;
- /* رنگهای پاستلی stat-cards داشبورد (همخوان با tauri) */
- --stat-amber-bg: #fdf0e3; --stat-amber-fg: #e08a1e;
- --stat-violet-bg: #efeefb; --stat-violet-fg: #6d5cf0;
- --stat-green-bg: #e7f6ef; --stat-green-fg: #16a35a;
- --stat-pink-bg: #fdeee9; --stat-pink-fg: #f0682a;
+ /* رنگهای پاستلی stat-cards داشبورد (دقیقاً مطابق clinic-pro-tauri) */
+ --stat-amber-bg: rgba(255,192,81,0.11); --stat-amber-fg: #FFC051;
+ --stat-violet-bg: rgba(85,89,206,0.08); --stat-violet-fg: #5559CE;
+ --stat-green-bg: rgba(0,157,121,0.10); --stat-green-fg: #009D79;
+ --stat-pink-bg: rgba(241,119,50,0.10); --stat-pink-fg: #F17732;
--ring: color-mix(in oklch, var(--primary) 32%, transparent);
--shadow-sm: 0 1px 2px rgba(15,27,46,.06), 0 1px 3px rgba(15,27,46,.05);
--shadow: 0 2px 6px rgba(15,27,46,.06), 0 8px 24px rgba(15,27,46,.06);