import { Card, CardContent } from "@/components/ui/card"; type StatColor = "blue" | "green" | "violet" | "amber" | "emerald"; const gradientMap: Record = { blue: "from-blue-300 to-blue-500", green: "from-green-300 to-green-500", violet: "from-violet-300 to-violet-500", amber: "from-amber-300 to-amber-500", emerald: "from-emerald-300 to-emerald-500", }; const accentLineMap: Record = { blue: "#3b82f6", green: "#22c55e", violet: "#8b5cf6", amber: "#f59e0b", emerald: "#10b981", }; const strokeColorMap: Record = { blue: ["#93c5fd", "#3b82f6"], green: ["#86efac", "#22c55e"], violet: ["#c4b5fd", "#8b5cf6"], amber: ["#fcd34d", "#f59e0b"], emerald: ["#6ee7b7", "#10b981"], }; interface StatCardProps { label: string; value: string | number; sub?: React.ReactNode; color?: StatColor; gauge?: number; // 0-100 percentage for ring gauge } function colorizeSubText(sub: React.ReactNode): React.ReactNode { if (typeof sub !== "string") return sub; const keywords: [RegExp, string][] = [ [/\b(running|online|healthy|clean|clear|all good|ok)\b/gi, "text-green-400"], [/\b(error|alert|fail|errors detected)\b/gi, "text-red-400"], ]; for (const [pattern, cls] of keywords) { if (pattern.test(sub)) { return ( <> {sub.split(pattern).map((part, i) => pattern.test(part) ? ( {part} ) : ( {part} ) )} ); } } return sub; } function GaugeRing({ pct, color, size = 64, stroke = 4 }: { pct: number; color: StatColor; size?: number; stroke?: number }) { const radius = (size - stroke) / 2; const circumference = 2 * Math.PI * radius; const offset = circumference - (pct / 100) * circumference; const gradientId = `gauge-grad-${color}`; const [stopStart, stopEnd] = strokeColorMap[color] ?? ["#93c5fd", "#3b82f6"]; return ( ); } export function StatCard({ label, value, sub, color = "blue", gauge }: StatCardProps) { const hasGauge = gauge != null && gauge >= 0; const accent = accentLineMap[color]; return ( {/* Top accent line */} {hasGauge ? (
{value}
) : (

{value}

)}

{label}

{sub &&
{colorizeSubText(sub)}
}
); }