import React, { useEffect, useState, useRef } from 'react' import { Users, Server, HardDrive, Activity, AlertTriangle, CheckCircle2, Sun, Moon } from 'lucide-react' import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts' import { api } from '../api/client' import { useAuth } from '../contexts/AuthContext' import { useTheme } from '../contexts/ThemeContext' interface SystemStats { cpu: number memory: { total: number; used: number; free: number; percent: number } disk: { total: number; used: number; free: number; percent: number } load: [number, number, number] uptime: number } interface AccountStats { total: number active: number suspended: number terminated: number } interface SystemInfo { hostname: string os: string } interface ServiceStatus { name: string status: 'running' | 'stopped' | 'failed' | 'unknown' enabled: boolean } interface StatCard { label: string value: string | number sub?: string icon: React.ComponentType<{ size?: number | string; className?: string }> color: string } const formatBytes = (bytes: number) => { const gb = bytes / 1024 / 1024 / 1024 return gb >= 1 ? `${gb.toFixed(1)} GB` : `${(bytes / 1024 / 1024).toFixed(0)} MB` } // ─── Server Info Bar ───────────────────────────────────────────────────────── interface InfoCellProps { label: string value: React.ReactNode } function InfoCell({ label, value }: InfoCellProps) { return (
{label} {value}
) } function Divider() { return
} // ─── Component ─────────────────────────────────────────────────────────────── export default function Dashboard() { const { user } = useAuth() const { theme, toggleTheme } = useTheme() const [stats, setStats] = useState(null) const [accountStats, setAccountStats] = useState(null) const [cpuHistory, setCpuHistory] = useState<{ t: string; cpu: number; mem: number }[]>([]) const [systemInfo, setSystemInfo] = useState(null) const [version, setVersion] = useState(null) const [alertCount, setAlertCount] = useState(null) const wsRef = useRef(null) useEffect(() => { api.get('/nixserver/accounts/stats').then(r => setAccountStats(r.data.data)).catch(() => {}) api.get('/nixserver/system/stats').then(r => setStats(r.data.data)).catch(() => {}) api.get('/nixserver/system/info').then(r => setSystemInfo(r.data.data)).catch(() => {}) api.get('/nixserver/system/version').then(r => setVersion(r.data.data?.currentVersion ?? null)).catch(() => {}) api.get('/nixserver/services').then(r => { const services: ServiceStatus[] = r.data.data ?? [] setAlertCount(services.filter(s => s.status !== 'running').length) }).catch(() => { setAlertCount(0) }) // WebSocket live stats const token = localStorage.getItem('access_token') if (token) { const ws = new WebSocket(`ws://${window.location.hostname}:2087/ws/stats?token=${token}`) wsRef.current = ws ws.onmessage = e => { const msg = JSON.parse(e.data) if (msg.type === 'stats') { setStats(msg.data) setCpuHistory(prev => [ ...prev.slice(-29), { t: new Date().toLocaleTimeString(), cpu: msg.data.cpu, mem: msg.data.memory.percent }, ]) } } } return () => wsRef.current?.close() }, []) const cards: StatCard[] = [ { label: 'Total Accounts', value: accountStats?.total ?? '—', sub: `${accountStats?.active ?? 0} active`, icon: Users, color: 'text-indigo-400', }, { label: 'CPU Usage', value: stats ? `${stats.cpu}%` : '—', sub: `Load: ${stats?.load[0].toFixed(2) ?? '—'}`, icon: Activity, color: 'text-emerald-400', }, { label: 'Memory', value: stats ? `${stats.memory.percent}%` : '—', sub: stats ? `${formatBytes(stats.memory.used)} / ${formatBytes(stats.memory.total)}` : '—', icon: Server, color: 'text-blue-400', }, { label: 'Disk', value: stats ? `${stats.disk.percent}%` : '—', sub: stats ? `${formatBytes(stats.disk.used)} / ${formatBytes(stats.disk.total)}` : '—', icon: HardDrive, color: 'text-amber-400', }, ] const loadStr = stats ? `${stats.load[0].toFixed(2)}\u00a0\u00a0${stats.load[1].toFixed(2)}\u00a0\u00a0${stats.load[2].toFixed(2)}` : '…' const monitoringNode = alertCount === null ? ( … ) : alertCount === 0 ? ( All Clear ) : ( {alertCount} Alert{alertCount !== 1 ? 's' : ''} ) return (
{/* ── cPanel-style server info bar ─────────────────────────────────── */}
{/* ── Day / Night toggle — far right ── */}
{/* ── Stat cards ───────────────────────────────────────────────────── */}
{cards.map(card => (
{card.label}

{card.value}

{card.sub &&

{card.sub}

}
))}
{/* ── CPU / Memory chart ───────────────────────────────────────────── */}

CPU & Memory — Live

{cpuHistory.length > 0 ? ( ) : (
Connecting to live stats...
)}
{/* ── Account status breakdown ─────────────────────────────────────── */} {accountStats && (

Account Status

{[ { label: 'Active', value: accountStats.active, color: 'text-emerald-400' }, { label: 'Suspended', value: accountStats.suspended, color: 'text-amber-400' }, { label: 'Terminated', value: accountStats.terminated, color: 'text-red-400' }, ].map(s => (

{s.value}

{s.label}

))}
)}
) }