import React, { useEffect, useMemo, useState } from "react";
function severityForEventId(eventId) {
const id = parseInt(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";
}
export default function ComputerDetailsModal({ open, computer, onClose, onAskAI }) {
const [tab, setTab] = useState("overview");
const [loading, setLoading] = useState(false);
const [details, setDetails] = useState(null);
const [search, setSearch] = useState("");
// JSON viewer state
const [jsonOpen, setJsonOpen] = useState(false);
const [jsonTitle, setJsonTitle] = useState("");
const [jsonData, setJsonData] = useState(null);
useEffect(() => {
if (!open || !computer?.name) return;
let cancelled = false;
async function load() {
try {
setLoading(true);
const r = await fetch(`/api/computer/${encodeURIComponent(computer.name)}`);
const d = await r.json();
if (!cancelled) setDetails(d);
} catch (e) {
console.error("details error", e);
} 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 ?? "");
return msg.includes(q) || type.includes(q) || id.includes(q);
});
}, [details, search]);
function openJson(ev) {
// server ти враќа: { event_id, event_type, message, timestamp, details }
setJsonTitle(`Sysmon Event ${ev?.event_id ?? ""} • ${ev?.event_type ?? ""}`);
setJsonData(ev?.details ?? ev); // ако нема details, покажи цел event
setJsonOpen(true);
}
function closeJson() {
setJsonOpen(false);
setJsonTitle("");
setJsonData(null);
}
if (!open) return null;
return (
<>
e.stopPropagation()}>
{computer.name}
{computer.ip || "—"} • {computer.user || "—"} • {computer.status || "—"}
{loading &&
Loading…
}
{!loading && details && tab === "overview" && (
System Overview
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 && details && tab === "sysmon" && (
| Time |
ID |
Type |
Severity |
Message |
View |
{filteredSysmon.slice(0, 200).map((ev, idx) => {
const sev = severityForEventId(ev.event_id);
return (
| {ev.timestamp ? new Date(ev.timestamp).toLocaleTimeString() : "—"} |
{ev.event_id}
|
{ev.event_type} |
{sev.toUpperCase()}
|
{(ev.message || "").slice(0, 140)}
|
|
);
})}
{filteredSysmon.length === 0 && (
|
No sysmon events.
|
)}
{filteredSysmon.length > 200 && (
Showing first 200 (for speed).
)}
)}
{!loading && details && tab === "processes" && (
| Time |
PID |
Name |
User |
CPU% |
RAM MB |
Cmdline |
{(details.recent_processes || []).slice(0, 150).map((p, idx) => (
| {p.timestamp ? new Date(p.timestamp).toLocaleTimeString() : "—"} |
{p.pid}
|
{p.name} |
{p.username || "—"} |
{p.cpu_percent ?? 0} |
{p.memory_mb ?? 0} |
{(p.cmdline || "").slice(0, 120)}
|
))}
{(details.recent_processes || []).length === 0 && (
|
No process data.
|
)}
)}
{!loading && details && tab === "network" && (
| Time |
PID |
Process |
Local |
Remote |
Status |
{(details.network_connections || []).slice(0, 150).map((n, idx) => (
| {n.timestamp ? new Date(n.timestamp).toLocaleTimeString() : "—"} |
{n.pid}
|
{n.process_name || "—"} |
{n.local_address || "—"}
|
{n.remote_address || "—"}
|
{n.status || "—"} |
))}
{(details.network_connections || []).length === 0 && (
|
No network data.
|
)}
)}
{!loading && !details &&
No details.
}
{/* JSON VIEWER MODAL */}
{jsonOpen && (
e.stopPropagation()} style={{ width: "min(900px, 96vw)" }}>
{JSON.stringify(jsonData ?? {}, null, 2)}
)}
>
);
}