import { useEffect, useState } from 'react' import { useParams, useNavigate } from 'react-router-dom' import api from '../api/client' import StatusBadge from '../components/StatusBadge' import { Cpu, HardDrive, MemoryStick, Network, Clock, Shield, ArrowLeft, Terminal, Download, } from 'lucide-react' export default function AgentDetail() { const { id } = useParams() const navigate = useNavigate() const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const reload = () => { api.getAgent(id).then(setData).finally(() => setLoading(false)) } useEffect(() => { reload() const interval = setInterval(reload, 30000) return () => clearInterval(interval) }, [id]) if (loading) return
Laden...
if (!data) return
Agent nicht gefunden
const { agent, system_data: sys, status } = data return (
{/* Header */}

{agent.name}

{agent.ip} — {agent.opnsense_version || 'Version unbekannt'}
api.triggerBackup(id)} /> {}} />
{!sys ? (
Noch keine Systemdaten vorhanden
) : ( <> {/* Quick Stats */}
{/* Network Interfaces */} {sys.network_interfaces && sys.network_interfaces.length > 0 && (
{sys.network_interfaces .filter((i) => i.addresses && i.addresses.length > 0) .map((iface) => (
{iface.name} {iface.status}
{iface.addresses?.filter((a) => !a.includes(':')).join(', ')}
RX: {formatBytes(iface.rx_bytes)}
TX: {formatBytes(iface.tx_bytes)}
))}
)} {/* Services */} {sys.services && sys.services.length > 0 && (
{sys.services.map((svc) => ( {svc.name} ))}
)} {/* Gateways */} {sys.gateways && sys.gateways.length > 0 && (
{sys.gateways.map((gw) => ( ))}
Name Gateway Status RTT Loss
{gw.name} {gw.gateway} {gw.status === 'none' ? 'OK' : gw.status} {gw.delay || '—'} {gw.loss || '—'}
)} {/* Disks */} {sys.disks && sys.disks.length > 0 && (
{sys.disks.map((d) => { const pct = d.total_bytes > 0 ? (d.used_bytes / d.total_bytes * 100) : 0 return ( ) })}
Dataset Mountpoint Belegt Gesamt %
{d.filesystem} {d.mount_point} {formatBytes(d.used_bytes)} {formatBytes(d.total_bytes)}
90 ? 'bg-red-500' : pct > 70 ? 'bg-yellow-500' : 'bg-green-500'}`} style={{ width: `${pct}%` }} />
{pct.toFixed(0)}%
)} )}
) } function Section({ title, icon: Icon, children }) { return (
{Icon && } {title}
{children}
) } function ActionButton({ icon: Icon, label, onClick }) { return ( ) } function QuickStat({ icon: Icon, label, value, sub }) { return (
{label}
{value}
{sub &&
{sub}
}
) } function formatBytes(bytes) { if (!bytes || bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB', 'TB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}` } function formatUptime(seconds) { const d = Math.floor(seconds / 86400) const h = Math.floor((seconds % 86400) / 3600) if (d > 0) return `${d}d ${h}h` const m = Math.floor((seconds % 3600) / 60) return `${h}h ${m}m` }