"use client"; import { useEffect, useState, useRef } from "react"; import { useTranslations, useLocale } from "next-intl"; import { Card, CardContent } from "@/components/ui/card"; import { Users, Zap, Clock, Activity } from "lucide-react"; interface Stats { totalClaws: number; activeClaws: number; tasksToday: number; tasksTotal: number; avgTaskDuration: number; } function AnimatedNumber({ value, suffix = "" }: { value: number; suffix?: string }) { const [display, setDisplay] = useState(0); const prevRef = useRef(0); useEffect(() => { const start = prevRef.current; const diff = value - start; if (diff === 0) return; const duration = 800; const startTime = performance.now(); const animate = (currentTime: number) => { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const eased = 1 - Math.pow(1 - progress, 3); const current = start + diff * eased; setDisplay(current); if (progress < 1) { requestAnimationFrame(animate); } else { prevRef.current = value; } }; requestAnimationFrame(animate); }, [value]); const locale = useLocale(); const formatted = Number.isInteger(value) ? Math.round(display).toLocaleString(locale) : display.toFixed(1); return ( {formatted} {suffix} ); } const statCards = [ { key: "activeClaws" as const, labelKey: "onlineNow" as const, icon: Activity, color: "var(--accent-green)", glow: "0 0 20px rgba(16, 185, 129, 0.3)", }, { key: "totalClaws" as const, labelKey: "totalClaws" as const, icon: Users, color: "var(--accent-cyan)", glow: "var(--glow-cyan)", }, { key: "tasksToday" as const, labelKey: "tasksToday" as const, icon: Zap, color: "var(--accent-purple)", glow: "var(--glow-purple)", }, { key: "avgTaskDuration" as const, labelKey: "avgDuration" as const, icon: Clock, color: "var(--accent-orange)", glow: "0 0 20px rgba(245, 158, 11, 0.3)", suffix: "s", transform: (v: number) => v / 1000, }, ]; export function StatsPanel() { const t = useTranslations("stats"); const [stats, setStats] = useState({ totalClaws: 0, activeClaws: 0, tasksToday: 0, tasksTotal: 0, avgTaskDuration: 0, }); useEffect(() => { const fetchStats = async () => { try { const res = await fetch("/api/v1/stats"); if (res.ok) { const data = await res.json(); setStats(data); } } catch { // will retry on next interval } }; fetchStats(); const interval = setInterval(fetchStats, 10000); return () => clearInterval(interval); }, []); return ( {statCards.map(({ key, labelKey, icon: Icon, color, glow, suffix, transform }) => { const raw = stats[key]; const value = transform ? transform(raw) : raw; return ( {t(labelKey)} ); })} ); }
{t(labelKey)}