import React, {useEffect, useMemo, useState} from "react"; import "./styles/theme.css"; import Login from "./components/Login"; import TopBar from "./components/TopBar"; import ComputerCard from "./components/ComputerCard"; import ComputerDetailsModal from "./components/ComputerDetailsModal"; import AIAssistantDrawer from "./components/AIAssistantDrawer"; export default function App() { const [stats, setStats] = useState(null); const [computers, setComputers] = useState([]); const [loading, setLoading] = useState(true); const [lastUpdated, setLastUpdated] = useState(null); const [query, setQuery] = useState(""); const [status, setStatus] = useState(""); const [selectedComputer, setSelectedComputer] = useState(null); const [aiOpen, setAiOpen] = useState(false); const [aiScope, setAiScope] = useState(null); const [selectedEnv, setSelectedEnv] = useState("default"); const [me, setMe] = useState(null); const [meLoading, setMeLoading] = useState(true); // const [openAI, setOpenAI] = useState(false); // const [scope, setScope] = useState(null); async function loadMe() { setMeLoading(true); try { const r = await fetch("/api/me", {credentials: "include"}); if (r.ok) { const data = await r.json(); setMe(data.user); } else { setMe(null); } } catch (e) { console.error("loadMe error", e); setMe(null); } finally { setMeLoading(false); } } async function refresh() { try { setLoading(true); const [s, c] = await Promise.all([ fetch("/api/stats", {credentials: "include"}).then((r) => r.json()), fetch("/api/computers", { credentials: "include", headers: {"X-Env": selectedEnv}, }).then((r) => r.json()), ]); setStats(s); setComputers(Array.isArray(c) ? c : []); setLastUpdated(new Date().toISOString()); } catch (e) { console.error("refresh error", e); } finally { setLoading(false); } } // on mount: check session useEffect(() => { loadMe(); }, []); // when logged in OR env changed, refresh data useEffect(() => { if (me) refresh(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [me, selectedEnv]); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); return (computers || []) .filter((c) => (status ? c.status === status : true)) .filter((c) => { if (!q) return true; const hay = [c.name, c.user, c.ip, c.os, c.status] .filter(Boolean) .join(" ") .toLowerCase(); return hay.includes(q); }); }, [computers, query, status]); // Gate UI if (meLoading) return
Loading…
; if (!me) return ; return (
setAiOpen(true)} onRefresh={refresh} lastUpdated={lastUpdated} onEnvChange={(env) => setSelectedEnv(env)} selectedEnv={selectedEnv} me={me} onLoggedOut={loadMe} /> {/* STATS */}
Total Computers
{computers.length}
Monitored Systems
Sysmon Events (24h)
{stats?.total_sysmon_events ?? "—"}
Security Events
Network Connections (24h)
{stats?.total_network_connections ?? "—"}
Captured Connections
{/* FILTERS */}
Filters
{loading ? "Refreshing…" : `${filtered.length} shown`}
setQuery(e.target.value)} placeholder="Search by name / user / ip / os…" />
{/* COMPUTERS */}
Connected Computers
{filtered.length} computers
{filtered.map((c) => ( { setSelectedComputer(pc); setAiScope(pc.name); }} /> ))} {filtered.length === 0 && (
No computers match filter.
)}
{/* DETAILS MODAL */} setSelectedComputer(null)} onAskAI={(name) => { setAiScope(name); setAiOpen(true); }} /> {/* AI DRAWER */} {/**/} {/* setOpenAI(false)}*/} {/* computers={computers}*/} {/* scope={scope}*/} {/* setScope={setScope}*/} {/* apiBase={import.meta.env.VITE_API_BASE}*/} {/*/>*/} setAiOpen(false)} computers={computers} scope={aiScope} setScope={setAiScope} apiBase={import.meta.env.VITE_API_BASE} />
); }