79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import { Card, CardContent } from "@/components/ui/card";
|
|
|
|
const accentColors: Record<string, string> = {
|
|
Containers: "#3b82f6",
|
|
"Hosts Online": "#22c55e",
|
|
GPU: "#a855f7",
|
|
"Emails Today": "#f59e0b",
|
|
Alerts: "#ef4444",
|
|
"Total Spend": "#3b82f6",
|
|
Transactions: "#22c55e",
|
|
"Top Vendor": "#f59e0b",
|
|
"Total Queries": "#3b82f6",
|
|
Blocked: "#ef4444",
|
|
"Avg Response": "#22c55e",
|
|
};
|
|
|
|
interface StatCardProps {
|
|
label: string;
|
|
value: string | number;
|
|
sub?: React.ReactNode;
|
|
pct?: number; // Optional 0-100 for ring gauge
|
|
}
|
|
|
|
function GaugeRing({ pct, color, size = 72, stroke = 5 }: { pct: number; color: string; size?: number; stroke?: number }) {
|
|
const radius = (size - stroke) / 2;
|
|
const circumference = 2 * Math.PI * radius;
|
|
const offset = circumference - (pct / 100) * circumference;
|
|
|
|
return (
|
|
<svg width={size} height={size} className="absolute inset-0 m-auto -rotate-90">
|
|
<circle
|
|
className="gauge-track"
|
|
cx={size / 2}
|
|
cy={size / 2}
|
|
r={radius}
|
|
strokeWidth={stroke}
|
|
/>
|
|
<circle
|
|
className="gauge-fill"
|
|
cx={size / 2}
|
|
cy={size / 2}
|
|
r={radius}
|
|
strokeWidth={stroke}
|
|
stroke={color}
|
|
strokeDasharray={circumference}
|
|
strokeDashoffset={offset}
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function StatCard({ label, value, sub, pct }: StatCardProps) {
|
|
const color = accentColors[label] ?? "#3b82f6";
|
|
const hasPct = pct != null && pct >= 0;
|
|
|
|
return (
|
|
<Card className="card-hover-lift overflow-hidden relative group">
|
|
<CardContent className="pt-5 pb-4 px-4 relative flex flex-col items-center justify-center text-center min-h-[110px]">
|
|
{hasPct ? (
|
|
<div className="relative w-[72px] h-[72px] flex items-center justify-center mb-1">
|
|
<GaugeRing pct={pct} color={color} />
|
|
<span className="text-2xl font-bold text-foreground tabular-nums-transition stat-glow">
|
|
{value}
|
|
</span>
|
|
</div>
|
|
) : (
|
|
<p className="text-3xl font-bold text-foreground tabular-nums-transition stat-glow mb-1">
|
|
{value}
|
|
</p>
|
|
)}
|
|
<p className="text-[10px] uppercase tracking-wider text-muted-foreground font-medium">
|
|
{label}
|
|
</p>
|
|
{sub && <div className="mt-0.5 text-xs text-muted-foreground/70">{sub}</div>}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|