Files
homelab-optimized/dashboard/ui/components/stat-card.tsx
Gitea Mirror Bot 2be8f1fe17
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m1s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-04-05 08:31:50 UTC
2026-04-05 08:31:50 +00:00

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>
);
}