- Refactor color palette in `ui-design-spec.md` to utilize CSS variables exclusively, eliminating fixed hex values and Tailwind utility classes. - Complete dark mode implementation in `uiStore.ts`, ensuring proper theme application via `applyTheme()` and `applyBrand()`. - Create `admin-theme-dark-light-audit.md` to document the transition process, outlining issues with inline styles and fixed colors. - Introduce `theme-tokens.test.ts` to enforce rules against fixed hex colors and ensure compliance with the design system. - Update various components and styles to replace inline styles and fixed colors with CSS variables, ensuring consistent theming across light and dark modes. - Ensure all changes maintain visual integrity in both light and dark modes, with a focus on accessibility and contrast standards.
104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
/**
|
|
* Stat cards row — ported pixel-for-pixel from clinic-pro-tauri
|
|
* `src/components/dashboard/cards/` (index + Card). Exact Tailwind arbitrary
|
|
* classes / colors kept; icons are the ported SVGs in `dashboardIcons`.
|
|
*/
|
|
import React from 'react';
|
|
import { UserAddIcon, CardIcon, CardTickIcon, CalendarIcon } from './dashboardIcons';
|
|
|
|
export interface StatCardModel {
|
|
/** background tint of the whole card (source `card_color`) */
|
|
cardColor: string;
|
|
/** solid background of the round icon bubble (source `icon_color`) */
|
|
iconColor: string;
|
|
icon: React.ReactNode;
|
|
value: React.ReactNode;
|
|
label: string;
|
|
}
|
|
|
|
function TauriStatCard({ data }: { data: StatCardModel }) {
|
|
return (
|
|
<li
|
|
className={`px-[8px] md:px-[16px] lg:px-[24px] py-[18px] sm:py-[20px] md:py-[22px] lg:py-[24px] rounded-[8px] flex items-center justify-start gap-[4px] md:gap-[8px] lg:gap-[12px] ${data.cardColor} dark:bg-[var(--surface)]`}
|
|
>
|
|
<div
|
|
className={`p-[8px] w-[calc(18px+8px)] sm:w-[calc(20px+8px)] md:w-[calc(22px+8px)] lg:w-[calc(24px+8px)] min-w-[calc(18px+8px)] sm:min-w-[calc(20px+8px)] md:min-w-[calc(22px+8px)] lg:min-w-[calc(24px+8px)] h-[calc(18px+8px)] sm:h-[calc(20px+8px)] md:h-[calc(22px+8px)] lg:h-[calc(24px+8px)] grid place-items-center rounded-full ${data.iconColor}`}
|
|
>
|
|
{data.icon}
|
|
</div>
|
|
<div className="flex flex-col w-full items-start justify-center gap-[8px]">
|
|
<p className="text-[var(--text)] text-wrap text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium">
|
|
{data.value}
|
|
</p>
|
|
<p className="text-[var(--text-2)] text-[12px] md:text-[14px] lg:text-[16px] font-normal">
|
|
{data.label}
|
|
</p>
|
|
</div>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
export interface DashboardStats {
|
|
/** تعداد کل مراجعین */
|
|
totalPatients: number;
|
|
/** کل پرداختیها (ریال) */
|
|
totalPaymentsRials: number;
|
|
/** پرداختیهای امروز (ریال) */
|
|
todayPaymentsRials: number;
|
|
/** تعداد نوبتهای امروز */
|
|
todayAppointments: number;
|
|
}
|
|
|
|
/**
|
|
* 4-card grid. `formatNumber`/`formatRial` are passed in so this stays a pure
|
|
* presentational component (SRP) — no coupling to the app's utils.
|
|
*/
|
|
export function TauriStatCards({
|
|
stats,
|
|
formatNumber,
|
|
formatRial,
|
|
}: {
|
|
stats: DashboardStats;
|
|
formatNumber: (n: number) => string;
|
|
formatRial: (rial: number) => string;
|
|
}) {
|
|
const cards: StatCardModel[] = [
|
|
{
|
|
cardColor: 'bg-[rgba(241,119,50,0.10)]',
|
|
iconColor: 'bg-[var(--accent)]',
|
|
icon: <UserAddIcon />,
|
|
value: `${formatNumber(stats.totalPatients)}+`,
|
|
label: 'تعداد کل مراجعین',
|
|
},
|
|
{
|
|
cardColor: 'bg-[rgba(0,157,121,0.10)]',
|
|
iconColor: 'bg-[var(--success)]',
|
|
icon: <CardIcon />,
|
|
value: formatRial(stats.totalPaymentsRials),
|
|
label: 'کل پرداختیها',
|
|
},
|
|
{
|
|
cardColor: 'bg-[rgba(85,89,206,0.08)]',
|
|
iconColor: 'bg-[var(--primary)]',
|
|
icon: <CardTickIcon />,
|
|
value: formatRial(stats.todayPaymentsRials),
|
|
label: 'پرداختیهای امروز',
|
|
},
|
|
{
|
|
cardColor: 'bg-[rgba(255,192,81,0.11)]',
|
|
iconColor: 'bg-[var(--warning)]',
|
|
icon: <CalendarIcon />,
|
|
value: `${formatNumber(stats.todayAppointments)}+`,
|
|
label: 'تعداد نوبتهای امروز',
|
|
},
|
|
];
|
|
|
|
return (
|
|
<ul className="grid w-full grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-[16px] sm:gap-[23px] md:gap-[30px] lg:gap-[36px]">
|
|
{cards.map((c) => (
|
|
<TauriStatCard key={c.label} data={c} />
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|