mirror of
https://github.com/AeThex-Corporation/AeThex-OS.git
synced 2026-04-17 22:27:19 +00:00
118 lines
4.9 KiB
TypeScript
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>
|
|
);
|
|
}
|