From 473bdbae7734665ccb947dfe30527bb52884c7c4 Mon Sep 17 00:00:00 2001 From: Developer Date: Tue, 28 Jul 2026 09:19:42 +0700 Subject: [PATCH] feat: add stat card with micro sparkline chart --- .../src/components/dashboard/stat-card.tsx | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 services/frontend/src/components/dashboard/stat-card.tsx diff --git a/services/frontend/src/components/dashboard/stat-card.tsx b/services/frontend/src/components/dashboard/stat-card.tsx new file mode 100644 index 0000000..4dc1ca0 --- /dev/null +++ b/services/frontend/src/components/dashboard/stat-card.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { type LucideIcon } from "lucide-react"; +import { GlassCard } from "@/components/glass/card"; +import { cn } from "@/lib/utils"; +import { Area, AreaChart, ResponsiveContainer } from "recharts"; + +interface StatCardProps { + label: string; + value: number | string; + icon: LucideIcon; + variant?: "default" | "danger" | "success"; + sparklineData?: { value: number }[]; + formatter?: (v: number) => string; +} + +export function StatCard({ + label, + value, + icon: Icon, + variant = "default", + sparklineData, + formatter = (v) => (typeof v === "number" ? v.toLocaleString() : v), +}: StatCardProps) { + const accentColor = { + default: "var(--color-primary)", + danger: "var(--color-destructive)", + success: "oklch(0.6 0.18 160)", + }[variant]; + + const bgAccent = { + default: "bg-primary/10 text-primary", + danger: "bg-destructive/10 text-destructive", + success: "bg-emerald-500/10 text-emerald-500", + }[variant]; + + const numValue = typeof value === "number" ? value : Number(value); + + return ( + +
+
+ +
+
+
+ {formatter(numValue)} +
+
+ {label} +
+ + {/* Sparkline background */} + {sparklineData && sparklineData.length > 0 && ( +
+ + + + + + + + + + + +
+ )} +
+ ); +}