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 => (
))}
)}
)
}