// // src/pages/Visualizations.jsx // import { useState } from "react"; // import SankeyPanel from "../components/SankeyPanel"; // // export default function Visualizations() { // const [env, setEnv] = useState("office1"); // // // ✅ All = празни низи // const [computers, setComputers] = useState([]); // const [ips, setIps] = useState([]); // // return ( //
//

Network Sankey Visualization

// // //
// ); // } import React, { useEffect, useMemo, useState } from "react"; import SankeyViz from "../components/SankeyViz"; function MultiSelect({ options, value, onChange, label }) { return (
{label}
(држи Ctrl за multi-select)
); } export default function Visualizations() { const [env, setEnv] = useState("office1"); // 👇 ако сакаш env листа динамички, додај endpoint. const envOptions = ["default", "office1"]; const [computerOptions, setComputerOptions] = useState(["All"]); const [ipOptions, setIpOptions] = useState(["All"]); const [computers, setComputers] = useState(["All"]); const [ips, setIps] = useState(["All"]); const [hours, setHours] = useState(48); const [limit, setLimit] = useState(200); const [vizType, setVizType] = useState("sankey"); // sankey | table | topips const [rows, setRows] = useState([]); const [loading, setLoading] = useState(false); // ✅ 1) load computers for env (користи твојот постоечки /api/computers) async function loadComputers() { const r = await fetch("/api/computers", { credentials: "include", headers: { "X-Env": env }, }); const data = await r.json(); const names = (Array.isArray(data) ? data : []).map((c) => c.name).filter(Boolean); setComputerOptions(["All", ...names]); } // ✅ 2) load ips list (ОВА треба да го имаш на backend како /api/ips) async function loadIps() { const r = await fetch(`/api/ips?env=${encodeURIComponent(env)}&hours=${hours}&limit=${limit}`, { credentials: "include", }); const data = await r.json(); const list = Array.isArray(data?.ips) ? data.ips : Array.isArray(data) ? data : []; setIpOptions(["All", ...list]); } // ✅ 3) load sankey rows (ОВА треба да го имаш на backend како /api/sankey) async function loadSankey() { setLoading(true); try { const params = new URLSearchParams(); params.set("env", env); params.set("hours", String(hours)); params.set("limit", String(limit)); params.set("computers", (computers?.length ? computers : ["All"]).join(",")); params.set("ips", (ips?.length ? ips : ["All"]).join(",")); const r = await fetch(`/api/sankey?${params.toString()}`, { credentials: "include" }); const data = await r.json(); setRows(Array.isArray(data) ? data : []); } finally { setLoading(false); } } useEffect(() => { loadComputers(); loadIps(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [env, hours, limit]); useEffect(() => { loadSankey(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [env, hours, limit, computers, ips]); // Table view const tableRows = useMemo(() => rows.slice(0, 200), [rows]); // Top IPs view const topIps = useMemo(() => { const map = new Map(); for (const r of rows) { map.set(r.target, (map.get(r.target) || 0) + Number(r.value || 0)); } return Array.from(map.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 20); }, [rows]); return (
Visualizations
{loading ? "Loading…" : `${rows.length} links`}
{/* Controls */}
Environment
Hours
setHours(Number(e.target.value))} />
Limit
setLimit(Number(e.target.value))} />
Visualization
setComputers(vals.length ? vals : ["All"])} /> setIps(vals.length ? vals : ["All"])} />
{/* Viz */} {vizType === "sankey" && } {vizType === "table" && (
{tableRows.map((r, idx) => ( ))}
Source Target Value
{r.source} {r.target} {r.value}
)} {vizType === "topips" && (
{topIps.map(([ip, v]) => (
{ip}
{v}
))}
)}
); }