feat(ui-kit): tauri pastel stat cards on doctor/clinic dashboards
Exact tauri stat-card palette tokens; doctor and clinic dashboards now render 4 pastel StatCards (amber/violet/green/pink) matching the clinic-pro-tauri reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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 <LoadingSkeleton />;
|
||||
|
||||
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: <CalendarDaysIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ tone: 'violet', label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon: <ClockIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ 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 ? <CreditCardIcon style={{ width: 22, height: 22 }} /> : <HeartIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ 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: <UserGroupIcon style={{ width: 22, height: 22 }} /> },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -627,15 +629,9 @@ function ClinicDashboard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat-grid">
|
||||
{kpiCards.map(c => (
|
||||
<div key={c.label} className="stat">
|
||||
<div className="ico" style={{ background: c.bg, color: c.color }}>
|
||||
<c.icon style={{ width: 21, height: 21 }} />
|
||||
</div>
|
||||
<div className="lbl">{c.label}</div>
|
||||
<div className="val">{c.value}</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16 }}>
|
||||
{statCards.map(c => (
|
||||
<StatCard key={c.label} tone={c.tone} label={c.label} value={c.value} icon={c.icon} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -823,13 +819,13 @@ function DoctorDashboard() {
|
||||
|
||||
if (q.isLoading) return <LoadingSkeleton />;
|
||||
|
||||
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: <CalendarDaysIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ tone: 'violet', label: 'نوبتهای فردا', value: `${formatNumber(d?.stats.tomorrow_appointments ?? 0)}+`, icon: <ClockIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ tone: 'green', label: 'نوبتهای این ماه', value: formatNumber(d?.stats.this_month_appointments ?? 0), icon: <UserGroupIcon style={{ width: 22, height: 22 }} /> },
|
||||
{ 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 ? <CreditCardIcon style={{ width: 22, height: 22 }} /> : <StarIcon style={{ width: 22, height: 22 }} /> },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -852,21 +848,13 @@ function DoctorDashboard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat-grid">
|
||||
{kpiCards.map(c => (
|
||||
<div key={c.label} className="stat">
|
||||
<div className="ico" style={{ background: c.bg, color: c.color }}>
|
||||
<c.icon style={{ width: 21, height: 21 }} />
|
||||
</div>
|
||||
<div className="lbl">{c.label}</div>
|
||||
<div className="val">{c.value}</div>
|
||||
{c.label === 'میانگین امتیاز' && d?.stats.total_ratings ? (
|
||||
<div className="hint">{formatNumber(d.stats.total_ratings)} نظر</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 16 }}>
|
||||
{statCards.map(c => (
|
||||
<StatCard key={c.label} tone={c.tone} label={c.label} value={c.value} icon={c.icon} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ height: 'var(--gap)' }} />
|
||||
<DoctorClinicInvitationsCard />
|
||||
|
||||
<div className="grid-2" style={{ marginTop: 'var(--gap)' }}>
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user