AeThex-OS/client/src/os/apps/MetricsDashboardApp.tsx

118 lines
4.9 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { motion } from 'framer-motion';
import { Activity, TrendingUp, ArrowUp } from 'lucide-react';
function Skeleton({ className = "", animate = true }: { className?: string; animate?: boolean }) {
return <div className={`bg-white/5 rounded animate-pulse ${className}`}></div>;
}
function useLayout() {
return {};
}
export function MetricsDashboardApp() {
const layout = useLayout();
const { data: metrics, isLoading } = useQuery({
queryKey: ['os-dashboard-metrics'],
queryFn: async () => {
const res = await fetch('/api/metrics');
return res.json();
},
refetchInterval: 30000,
});
const [animatedValues, setAnimatedValues] = useState({ profiles: 0, projects: 0, xp: 0 });
useEffect(() => {
if (metrics) {
const duration = 1000;
const steps = 20;
const interval = duration / steps;
let step = 0;
const timer = setInterval(() => {
step++;
const progress = step / steps;
setAnimatedValues({
profiles: Math.round(progress * (metrics.totalProfiles || 0)),
projects: Math.round(progress * (metrics.totalProjects || 0)),
xp: Math.round(progress * (metrics.totalXP || 0)),
});
if (step >= steps) clearInterval(timer);
}, interval);
return () => clearInterval(timer);
}
}, [metrics]);
if (isLoading) {
return (
<div className="min-h-full bg-slate-950 p-3 md:p-4">
<div className="flex items-center gap-2 mb-4">
<Activity className="w-5 h-5 text-cyan-400" />
<Skeleton className="h-6 w-32" />
</div>
<div className="grid grid-cols-2 gap-3 md:gap-4">
<Skeleton className="h-24 md:h-28 rounded-lg" />
<Skeleton className="h-24 md:h-28 rounded-lg" />
<Skeleton className="h-24 md:h-28 rounded-lg" />
<Skeleton className="h-24 md:h-28 rounded-lg" />
</div>
</div>
);
}
return (
<div className="min-h-full bg-slate-950 p-3 md:p-4 overflow-auto">
<div className="flex items-center gap-2 mb-4">
<Activity className="w-5 h-5 text-cyan-400" />
<h2 className="text-base md:text-lg font-display text-white uppercase tracking-wider">Live Metrics</h2>
</div>
<div className="grid grid-cols-2 gap-3 md:gap-4 mb-4">
<div className="bg-gradient-to-br from-cyan-500/20 to-cyan-500/5 rounded-lg p-3 md:p-4 border border-cyan-500/30">
<div className="text-xs text-cyan-400 uppercase">Architects</div>
<div className="text-2xl md:text-3xl font-bold text-white font-mono">{animatedValues.profiles}</div>
<div className="flex items-center gap-1 text-green-400 text-xs mt-1">
<ArrowUp className="w-3 h-3" /> +{Math.floor(Math.random() * 5) + 1} today
</div>
</div>
<div className="bg-gradient-to-br from-purple-500/20 to-purple-500/5 rounded-lg p-3 md:p-4 border border-purple-500/30">
<div className="text-xs text-purple-400 uppercase">Projects</div>
<div className="text-2xl md:text-3xl font-bold text-white font-mono">{animatedValues.projects}</div>
<div className="flex items-center gap-1 text-green-400 text-xs mt-1">
<TrendingUp className="w-3 h-3" /> Active
</div>
</div>
<div className="bg-gradient-to-br from-green-500/20 to-green-500/5 rounded-lg p-3 md:p-4 border border-green-500/30">
<div className="text-xs text-green-400 uppercase">Total XP</div>
<div className="text-2xl md:text-3xl font-bold text-white font-mono">{animatedValues.xp.toLocaleString()}</div>
</div>
<div className="bg-gradient-to-br from-yellow-500/20 to-yellow-500/5 rounded-lg p-3 md:p-4 border border-yellow-500/30">
<div className="text-xs text-yellow-400 uppercase">Online</div>
<div className="text-2xl md:text-3xl font-bold text-white font-mono">{metrics?.onlineUsers || 0}</div>
<div className="flex items-center gap-1 text-yellow-400 text-xs mt-1">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" /> Live
</div>
</div>
</div>
<div className="bg-white/5 rounded-lg p-3 md:p-4 border border-white/10">
<div className="text-xs text-white/50 uppercase mb-3">Network Activity</div>
<div className="flex items-end gap-1 h-24">
{Array.from({ length: 20 }).map((_, i) => {
const height = Math.random() * 80 + 20;
return (
<motion.div
key={i}
initial={{ height: 0 }}
animate={{ height: `${height}%` }}
transition={{ delay: i * 0.05, duration: 0.3 }}
className="flex-1 bg-gradient-to-t from-cyan-500 to-purple-500 rounded-t"
/>
);
})}
</div>
</div>
</div>
);
}