// // 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
Visualization
setComputers(vals.length ? vals : ["All"])}
/>
setIps(vals.length ? vals : ["All"])}
/>
{/* Viz */}
{vizType === "sankey" &&
}
{vizType === "table" && (
| Source |
Target |
Value |
{tableRows.map((r, idx) => (
| {r.source} |
{r.target} |
{r.value} |
))}
)}
{vizType === "topips" && (
{topIps.map(([ip, v]) => (
))}
)}
);
}