import React, { useEffect, useMemo, useState } from "react"; function severityForEventId(eventId) { const id = Number.parseInt(String(eventId ?? ""), 10); const high = new Set([1, 3, 8, 10]); const med = new Set([5, 6, 7, 11, 22]); if (high.has(id)) return "high"; if (med.has(id)) return "medium"; return "low"; } function fmtTime(ts) { if (!ts) return "—"; try { return new Date(ts).toLocaleTimeString(); } catch { return String(ts); } } export default function ComputerDetailsModal({ open, computer, onClose, onAskAI }) { const [tab, setTab] = useState("overview"); const [loading, setLoading] = useState(false); const [details, setDetails] = useState(null); const [error, setError] = useState(""); const [search, setSearch] = useState(""); // JSON viewer const [jsonOpen, setJsonOpen] = useState(false); const [jsonTitle, setJsonTitle] = useState(""); const [jsonData, setJsonData] = useState(null); // reset when opening / switching pc useEffect(() => { if (!open) return; setTab("overview"); setSearch(""); setError(""); setDetails(null); setJsonOpen(false); setJsonTitle(""); setJsonData(null); }, [open, computer?.name]); // close on ESC useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === "Escape") onClose?.(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); // load details useEffect(() => { if (!open || !computer?.name) return; let cancelled = false; async function load() { setLoading(true); setError(""); try { const r = await fetch(`/api/computer/${encodeURIComponent(computer.name)}`, { credentials: "include", }); const d = await r.json().catch(() => ({})); if (!r.ok) throw new Error(d?.error || `HTTP ${r.status}`); if (!cancelled) setDetails(d); } catch (e) { if (!cancelled) { setError(String(e?.message || e)); setDetails(null); } } finally { if (!cancelled) setLoading(false); } } load(); return () => { cancelled = true; }; }, [open, computer?.name]); const filteredSysmon = useMemo(() => { const list = details?.sysmon_events || []; const q = search.trim().toLowerCase(); if (!q) return list; return list.filter((ev) => { const msg = (ev.message || "").toLowerCase(); const type = (ev.event_type || "").toLowerCase(); const id = String(ev.event_id ?? "").toLowerCase(); return msg.includes(q) || type.includes(q) || id.includes(q); }); }, [details, search]); function openJson(ev) { setJsonTitle(`Sysmon Event ${ev?.event_id ?? ""} • ${ev?.event_type ?? ""}`); setJsonData(ev?.details ?? ev); setJsonOpen(true); } function closeJson() { setJsonOpen(false); setJsonTitle(""); setJsonData(null); } if (!open) return null; return ( <>
e.stopPropagation()}>
{computer?.name || "Computer"}
{computer?.ip || "—"} • {computer?.user || "—"} • {computer?.status || "—"}
{loading &&
Loading…
} {!loading && error && (
Error
{error}
)} {!loading && !error && details && tab === "overview" && (
System Overview
Env: {details.computer?.env_name || "default"}
User: {details.computer?.user || "—"}
IP: {details.computer?.ip || "—"}
OS: {details.computer?.os || "—"}
First seen: {details.computer?.first_seen || "—"}
Last seen: {details.computer?.last_seen || "—"}
Total logs: {details.computer?.total_logs ?? "—"}
)} {!loading && !error && details && tab === "sysmon" && (
Sysmon Events
{filteredSysmon.length} events setSearch(e.target.value)} />
{filteredSysmon.slice(0, 400).map((ev, idx) => { const sev = severityForEventId(ev.event_id); return ( ); })} {filteredSysmon.length === 0 && ( )}
Time ID Type Severity Message View
{fmtTime(ev.timestamp)} {ev.event_id ?? "—"} {ev.event_type || "—"} {sev.toUpperCase()} {(ev.message || "").slice(0, 220)}
No sysmon events.
{filteredSysmon.length > 400 && (
Showing first 400 (for speed).
)}
)} {!loading && !error && details && tab === "processes" && (
Recent Processes
{(details.recent_processes || []).length} rows
{(details.recent_processes || []).slice(0, 400).map((p, idx) => ( ))} {(details.recent_processes || []).length === 0 && ( )}
Time PID Name User CPU% RAM MB Cmdline
{fmtTime(p.timestamp)} {p.pid ?? "—"} {p.name || "—"} {p.username || "—"} {p.cpu_percent ?? 0} {p.memory_mb ?? 0} {(p.cmdline || "").slice(0, 220)}
No process data.
)} {!loading && !error && details && tab === "network" && (
Network Connections
{(details.network_connections || []).length} rows
{(details.network_connections || []).slice(0, 400).map((n, idx) => ( ))} {(details.network_connections || []).length === 0 && ( )}
Time PID Process Local Remote Status
{fmtTime(n.timestamp)} {n.pid ?? "—"} {n.process_name || "—"} {n.local_address || "—"} {n.remote_address || "—"} {n.status || "—"}
No network data.
)} {!loading && !error && !details && (
No details.
)}
{/* JSON VIEWER */} {jsonOpen && (
e.stopPropagation()}>
View JSON
{jsonTitle}
{JSON.stringify(jsonData ?? {}, null, 2)}
)} ); }