"use client"; import { useState, useEffect } from "react"; import { fetchAgentDetails, AgentDetails } from "@/lib/api"; import { fmtBytes } from "@/lib/utils"; import { DeviceDetailModal } from "@/components/ui/DeviceDetailModal"; import { IpDetails } from "@/components/ui/IpDetails"; import { Loader2, X, Server, Monitor, Activity, Globe, Shield, Bell, Wifi, AlertTriangle, Lock, Unlock, ChevronRight, Eye, Router } from "lucide-react"; interface Props { agentUuid: string; agentLabel: string; onClose: () => void; } type Tab = "devices" | "flows" | "apps" | "security" | "events" | "mac"; // ── Severity colour helper ───────────────────────────────────────────────── function severityStyle(sev: string | null) { switch ((sev || "").toLowerCase()) { case "critical": return { badge: "bg-red-500/15 text-red-400 ring-red-500/30", dot: "bg-red-400" }; case "high": return { badge: "bg-orange-500/15 text-orange-400 ring-orange-500/30", dot: "bg-orange-400" }; case "warning": return { badge: "bg-yellow-500/15 text-yellow-400 ring-yellow-500/30", dot: "bg-yellow-400" }; case "info": return { badge: "bg-blue-500/15 text-blue-400 ring-blue-500/30", dot: "bg-blue-400" }; default: return { badge: "bg-slate-500/15 text-slate-400 ring-slate-500/30", dot: "bg-slate-400" }; } } function riskStyle(level: string | null) { switch ((level || "").toLowerCase()) { case "rawan": case "critical": return { badge: "bg-red-500/15 text-red-400 ring-red-500/30", dot: "bg-red-400" }; case "sedang": case "medium": return { badge: "bg-yellow-500/15 text-yellow-400 ring-yellow-500/30", dot: "bg-yellow-400" }; case "aman": case "low": return { badge: "bg-green-500/15 text-green-400 ring-green-500/30", dot: "bg-green-400" }; default: return { badge: "bg-slate-500/15 text-slate-400 ring-slate-500/30", dot: "bg-slate-400" }; } } // ── Shared table wrapper ──────────────────────────────────────────────────── function TableWrap({ children, head }: { children: React.ReactNode; head: string[] }) { return (
{head.map((h) => ( ))} {children}
{h}
); } function EmptyState({ msg }: { msg: string }) { return

{msg}

; } // ── Main component ────────────────────────────────────────────────────────── export function AgentDetailModal({ agentUuid, agentLabel, onClose }: Props) { const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState("devices"); const [selectedDevice, setSelectedDevice] = useState<{ ip: string; label: string | null; mac: string | null } | null>(null); useEffect(() => { setIsLoading(true); setError(null); fetchAgentDetails(agentUuid) .then(setData) .catch((e) => setError(e.message)) .finally(() => setIsLoading(false)); }, [agentUuid]); // Count security alerts const secCount = data ? (data.security?.insecure_protocols?.length ?? 0) + (data.security?.unencrypted_passwords?.length ?? 0) + (data.security?.tor_detections?.length ?? 0) + (data.security?.vpn_detections?.length ?? 0) : 0; const tabs: { key: Tab; label: string; icon: React.ReactNode; count?: number }[] = [ { key: "devices", label: "Devices", icon: , count: data?.devices.length }, { key: "flows", label: "Flows", icon: , count: data?.flows.length }, { key: "apps", label: "Top Apps", icon: , count: data?.top_apps.length }, { key: "security", label: "Security", icon: , count: secCount || undefined }, { key: "events", label: "Events", icon: , count: data?.events.length }, { key: "mac", label: "MAC Bandwidth", icon: , count: data?.mac_bandwidth.length }, ]; return ( <>
e.stopPropagation()} > {/* ── Header ── */}

{agentLabel}

{agentUuid}

{/* ── Body ── */}
{isLoading ? (

Loading agent data…

) : error ? (
{error}
) : data ? ( <> {/* ── KPI row ── */}
{[ { label: "Devices", value: data.devices.length, color: "text-white" }, { label: "Flows", value: data.summary?.active_flows ?? data.flows.length, color: "text-white" }, { label: "Download", value: fmtBytes(data.summary?.bandwidth_down ?? 0), color: "text-cyan-400" }, { label: "Upload", value: fmtBytes(data.summary?.bandwidth_up ?? 0), color: "text-green-400" }, ].map((kpi) => (

{kpi.label}

{kpi.value}

))}
{/* ── Security alert strip ── */} {secCount > 0 && (
{secCount} security alerts detected on this agent — check the Security tab
)} {/* ── Tabs ── */}
{tabs.map((tab) => ( ))}
{/* ── Tab content ── */}
{/* DEVICES */} {activeTab === "devices" && ( data.devices.length === 0 ? : (
{data.devices.map((d, i) => { const rc = riskStyle(d.risk_level); return ( ); })}
) )} {/* FLOWS */} {activeTab === "flows" && ( data.flows.length === 0 ? : ( {data.flows.map((f, i) => ( {f.src_ip || "—"}
{f.dst_ip || "—"}{f.dst_port ? :{f.dst_port} : ""} {f.dst_ip && }
{f.app_label || f.domain || "—"} {f.protocol || "—"} {fmtBytes(f.download)} {fmtBytes(f.upload)} {f.last_seen?.slice(0, 16) || "—"} ))}
) )} {/* TOP APPS */} {activeTab === "apps" && ( data.top_apps.length === 0 ? : (
{data.top_apps.map((a, i) => { const total = (a.download ?? 0) + (a.upload ?? 0); const maxTotal = Math.max(...data.top_apps.map(x => (x.download ?? 0) + (x.upload ?? 0)), 1); const pct = (total / maxTotal) * 100; return (
{a.favicon ? {a.app_label} :
} {a.app_label} {a.category && {a.category}} {fmtBytes(total)}
↓ {fmtBytes(a.download ?? 0)} ↑ {fmtBytes(a.upload ?? 0)}
); })}
) )} {/* SECURITY */} {activeTab === "security" && (
{/* Encryption Audit */}

Encryption Audit {data.security?.encryption_audit.length ?? 0} devices

{(data.security?.encryption_audit.length ?? 0) === 0 ? : ( {data.security!.encryption_audit.map((r, i) => { const rc = riskStyle(r.risk_level); return ( {r.ip_address || "—"} {r.device_label || "—"}
{r.encrypted_pct ?? 0}%
{r.risk_level || "—"} {fmtBytes(r.encrypted ?? 0)} {fmtBytes(r.unencrypted ?? 0)} ); })} )}
{/* Insecure Protocols */}

Insecure Protocols {data.security?.insecure_protocols.length ?? 0} records

{(data.security?.insecure_protocols.length ?? 0) === 0 ? : ( {data.security!.insecure_protocols.map((r, i) => { const sc = severityStyle(r.risk); return ( {r.ip_address || "—"} {r.protocol || "—"} {r.risk || "—"} {r.app_label || "—"}
{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"} {r.dst_ip && }
{fmtBytes(r.download)} {fmtBytes(r.upload)} ); })}
)}
{/* Unencrypted Passwords */} {(data.security?.unencrypted_passwords.length ?? 0) > 0 && (

Unencrypted Passwords Detected! {data.security!.unencrypted_passwords.length} records

{data.security!.unencrypted_passwords.map((r, i) => { const sc = severityStyle(r.severity); return ( {r.ip_address || "—"}
{r.dst_ip ? `${r.dst_ip}:${r.dst_port ?? "?"}` : "—"} {r.dst_ip && }
{r.protocol || "—"} {r.username || "—"} {r.severity || "—"} ); })}
)} {/* IP Reputation */} {(data.security?.ip_reputation.length ?? 0) > 0 && (

IP Reputation {data.security!.ip_reputation.length} records

{data.security!.ip_reputation.map((r, i) => ( {r.ip_address || "—"} {r.local_ip || "—"} {r.reputation || "—"} {r.score ?? "—"} {r.country || "—"} {r.blacklisted ? ⛔ Yes : ✓ No } ))}
)} {/* Tor & VPN side-by-side */}
{/* Tor */}

Tor Detections {data.security?.tor_detections.length ?? 0}

{(data.security?.tor_detections.length ?? 0) === 0 ? :
{data.security!.tor_detections.map((r, i) => (
{r.ip_address || "—"} {r.country || "—"}
Exit: {r.exit_node || "—"}
))}
}
{/* VPN */}

VPN Detections {data.security?.vpn_detections.length ?? 0}

{(data.security?.vpn_detections.length ?? 0) === 0 ? :
{data.security!.vpn_detections.map((r, i) => (
{r.ip_address || "—"} {r.vpn_type || "—"}
Remote: {r.remote_ip || "—"} • {r.country || "—"} {r.remote_ip && }
{r.confidence &&
Confidence: {(r.confidence * 100).toFixed(0)}%
}
))}
}
{/* Server Discovery */} {(data.server_discovery?.length ?? 0) > 0 && (

Servers Discovered {data.server_discovery!.length} records

{data.server_discovery!.map((r, i) => (
{r.ip_address || "—"} {r.ip_address && }
{r.hostname || "—"} {r.server_type || "—"} {r.port ?? "—"} {r.protocol || "—"} {r.os_label || "—"} ))}
)}
)} {/* EVENTS */} {activeTab === "events" && ( data.events.length === 0 ? : (
{data.events.map((ev, i) => { const sc = severityStyle(ev.severity); return (
{ev.severity || "—"}
{ev.event_type || "Unknown"} {ev.ip_address || "—"} {ev.mac_address && {ev.mac_address}}
{ev.description &&

{ev.description}

}
{ev.event_at?.slice(0, 16) || "—"}
); })}
) )} {/* MAC BANDWIDTH */} {activeTab === "mac" && ( data.mac_bandwidth.length === 0 ? : (
{data.mac_bandwidth.map((m, i) => { const maxTotal = Math.max(...data.mac_bandwidth.map(x => x.total ?? 0), 1); const pct = ((m.total ?? 0) / maxTotal) * 100; return (

{m.mac_address}

{m.manufacturer || "Unknown Manufacturer"}

{fmtBytes(m.total ?? 0)}

Total

↓ {fmtBytes(m.download ?? 0)} ↑ {fmtBytes(m.upload ?? 0)}
); })}
) )}
) : null}
{selectedDevice && selectedDevice.ip && ( setSelectedDevice(null)} /> )} ); }