diff --git a/client/src/Components/Charts/Utils/chartUtils.jsx b/client/src/Components/Charts/Utils/chartUtils.jsx
index bbf81ae38..af5f27c66 100644
--- a/client/src/Components/Charts/Utils/chartUtils.jsx
+++ b/client/src/Components/Charts/Utils/chartUtils.jsx
@@ -88,7 +88,7 @@ PercentTick.propTypes = {
*/
const getFormattedPercentage = (value) => {
if (typeof value !== "number") return value;
- return `${(value * 100).toFixed(2)}.%`;
+ return `${(value * 100).toFixed(2)}%`;
};
/**
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
index 9ef548b2e..343f7681b 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx
@@ -1,111 +1,130 @@
// NetworkCharts.jsx
-import { Grid, Card, CardContent, Typography } from "@mui/material";
-import { useTheme } from "@mui/material/styles";
-import AreaChart from "../../../../../Components/Charts/AreaChart";
-import { TzTick, InfrastructureTooltip } from '../../../../../Components/Charts/Utils/chartUtils';
+import PropTypes from "prop-types";
+import { Stack } from "@mui/material";
+import InfraAreaChart from "../../../../../Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart";
-const BytesTick = ({ x, y, payload }) => {
- const value = payload.value;
- const label =
- value >= 1024 ** 3
- ? `${(value / 1024 ** 3).toFixed(2)} GB`
- : value >= 1024 ** 2
- ? `${(value / 1024 ** 2).toFixed(2)} MB`
- : `${(value / 1024).toFixed(2)} KB`;
-
- return {label};
-};
+// Utils
+import {
+ TzTick,
+ InfrastructureTooltip,
+} from "../../../../../Components/Charts/Utils/chartUtils";
+import { useTheme } from "@emotion/react";
const getFormattedNetworkMetric = (value) => {
- if (typeof value !== "number" || isNaN(value)) return "0";
- if (value >= 1024 ** 3) return `${(value / 1024 ** 3).toFixed(1)} GB/s`;
- if (value >= 1024 ** 2) return `${(value / 1024 ** 2).toFixed(1)} MB/s`;
- if (value >= 1024) return `${(value / 1024).toFixed(1)} KB/s`;
- return `${Math.round(value)} B/s`;
+ if (typeof value !== "number" || isNaN(value)) return "0";
+ if (value >= 1024 ** 3) return `${(value / 1024 ** 3).toFixed(1)} GB/s`;
+ if (value >= 1024 ** 2) return `${(value / 1024 ** 2).toFixed(1)} MB/s`;
+ if (value >= 1024) return `${(value / 1024).toFixed(1)} KB/s`;
+ return `${Math.round(value)} B/s`;
};
const NetworkCharts = ({ eth0Data, dateRange }) => {
- const theme = useTheme();
- const textColor = theme.palette.primary.contrastTextTertiary;
+ const theme = useTheme();
- const charts = [
- { title: "Bytes per second", key: "bytesPerSec", color: theme.palette.info.main, yTick: },
- { title: "Packets per second", key: "packetsPerSec", color: theme.palette.success.main },
- { title: "Errors", key: "errors", color: theme.palette.error.main },
- { title: "Drops", key: "drops", color: theme.palette.warning.main }
- ];
+ const configs = [
+ {
+ type: "network-bytes",
+ data: eth0Data,
+ dataKeys: ["bytesPerSec"],
+ heading: "Bytes per second",
+ strokeColor: theme.palette.info.main,
+ gradientStartColor: theme.palette.info.main,
+ yLabel: "Bytes per second",
+ xTick: ,
+ toolTip: (
+
+ ),
+ },
+ {
+ type: "network-packets",
+ data: eth0Data,
+ dataKeys: ["packetsPerSec"],
+ heading: "Packets per second",
+ strokeColor: theme.palette.success.main,
+ gradientStartColor: theme.palette.success.main,
+ yLabel: "Packets per second",
+ xTick: ,
+ toolTip: (
+ Math.round(value).toLocaleString()}
+ />
+ ),
+ },
+ {
+ type: "network-errors",
+ data: eth0Data,
+ dataKeys: ["errors"],
+ heading: "Errors",
+ strokeColor: theme.palette.error.main,
+ gradientStartColor: theme.palette.error.main,
+ yLabel: "Errors",
+ xTick: ,
+ toolTip: (
+ Math.round(value).toLocaleString()}
+ />
+ ),
+ },
+ {
+ type: "network-drops",
+ data: eth0Data,
+ dataKeys: ["drops"],
+ heading: "Drops",
+ strokeColor: theme.palette.warning.main,
+ gradientStartColor: theme.palette.warning.main,
+ yLabel: "Drops",
+ xTick: ,
+ toolTip: (
+ Math.round(value).toLocaleString()}
+ />
+ ),
+ },
+ ];
- const formatYAxis = (key, value) => {
- if (key === "bytesPerSec") {
- // Format as MB/s or GB/s if large
- if (value >= 1024 ** 3) return `${(value / 1024 ** 3).toFixed(1)} GB/s`;
- if (value >= 1024 ** 2) return `${(value / 1024 ** 2).toFixed(1)} MB/s`;
- if (value >= 1024) return `${(value / 1024).toFixed(1)} KB/s`;
- return `${Math.round(value)} B/s`;
- }
- return Math.round(value).toLocaleString();
- };
-
- const CustomTick = ({ x, y, payload, chartKey }) => {
- // Ensure value is always rounded for display
- let value = payload.value;
- if (typeof value === 'number') {
- value = Math.round(value);
- }
- return (
-
- {formatYAxis(chartKey, value)}
-
- );
- };
-
- const chartConfigs = charts.map((chart) => ({
- data: eth0Data,
- dataKeys: [chart.key],
- heading: chart.title,
- strokeColor: chart.color,
- gradientStartColor: chart.color,
- yTick: chart.yTick || ,
- xTick: ,
- toolTip: (
-
- ),
- }));
-
- return (
-
- {chartConfigs.map((config, idx) => (
-
-
-
-
- {config.heading}
-
-
-
-
-
- ))}
-
- );
+ return (
+ *": {
+ flexBasis: `calc(50% - ${theme.spacing(8)})`,
+ maxWidth: `calc(50% - ${theme.spacing(8)})`,
+ },
+ }}
+ >
+ {configs.map((config) => (
+
+ ))}
+
+ );
};
-export default NetworkCharts;
\ No newline at end of file
+NetworkCharts.propTypes = {
+ eth0Data: PropTypes.array.isRequired,
+ dateRange: PropTypes.string.isRequired,
+};
+
+export default NetworkCharts;
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
index 5253742a6..b2f2f7841 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx
@@ -1,16 +1,9 @@
// NetworkStatBoxes.jsx
-import DataUsageIcon from "@mui/icons-material/DataUsage";
-import NetworkCheckIcon from "@mui/icons-material/NetworkCheck";
-import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
+import PropTypes from "prop-types";
import StatusBoxes from "../../../../../Components/StatusBoxes";
import StatBox from "../../../../../Components/StatBox";
import { Typography } from "@mui/material";
-const INTERFACE_LABELS = {
- en0: "Ethernet/Wi-Fi (Primary)",
- wlan0: "Wi-Fi (Secondary)",
-};
-
function formatBytes(bytes) {
if (bytes === 0 || bytes == null) return "0 B";
const k = 1024;
@@ -37,48 +30,57 @@ const NetworkStatBoxes = ({ shouldRender, net }) => {
shouldRender={shouldRender}
flexWrap="wrap"
>
- {filtered.map((iface) => (
- <>
+ {filtered
+ .map((iface) => [
+ />,
+ />,
+ />,
+ />,
+ />,
- >
- ))}
+ />,
+ ])
+ .flat()}
);
};
+NetworkStatBoxes.propTypes = {
+ shouldRender: PropTypes.bool.isRequired,
+ net: PropTypes.arrayOf(
+ PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ bytes_sent: PropTypes.number,
+ bytes_recv: PropTypes.number,
+ packets_sent: PropTypes.number,
+ packets_recv: PropTypes.number,
+ err_in: PropTypes.number,
+ err_out: PropTypes.number,
+ })
+ ),
+};
+
export default NetworkStatBoxes;
diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
index 60825be0f..6b8ccb85a 100644
--- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
+++ b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx
@@ -1,34 +1,10 @@
+import PropTypes from "prop-types";
import NetworkStatBoxes from "./NetworkStatBoxes";
import NetworkCharts from "./NetworkCharts";
import MonitorTimeFrameHeader from "../../../../../Components/MonitorTimeFrameHeader";
-function filterByDateRange(data, dateRange) {
- if (!Array.isArray(data)) return [];
- const now = Date.now();
- let cutoff;
- switch (dateRange) {
- case "recent":
- cutoff = now - 2 * 60 * 60 * 1000; // last 2 hours
- break;
- case "day":
- cutoff = now - 24 * 60 * 60 * 1000; // last 24 hours
- break;
- case "week":
- cutoff = now - 7 * 24 * 60 * 60 * 1000; // last 7 days
- break;
- case "month":
- cutoff = now - 30 * 24 * 60 * 60 * 1000; // last 30 days
- break;
- default:
- cutoff = 0;
- }
- return data.filter((d) => new Date(d.time).getTime() >= cutoff);
-}
-
const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => {
const eth0Data = getEth0TimeSeries(checks);
- const xAxisFormatter = getXAxisFormatter(checks);
- const filteredEth0Data = filterByDateRange(eth0Data, dateRange);
return (
<>
@@ -42,66 +18,120 @@ const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => {
setDateRange={setDateRange}
/>
>
);
};
+Network.propTypes = {
+ net: PropTypes.array,
+ checks: PropTypes.array,
+ isLoading: PropTypes.bool.isRequired,
+ dateRange: PropTypes.string.isRequired,
+ setDateRange: PropTypes.func.isRequired,
+};
+
export default Network;
/* ---------- Helper functions ---------- */
function getEth0TimeSeries(checks) {
+ console.log(`[NetworkStats] Processing ${checks?.length || 0} checks`);
+ if (checks && checks.length > 0) {
+ console.log("[NetworkStats] First check _id:", checks[0]._id);
+ console.log("[NetworkStats] Last check _id:", checks[checks.length - 1]._id);
+ console.log(
+ "[NetworkStats] Sample check structure:",
+ JSON.stringify(checks[0], null, 2)
+ );
+ }
+
const sorted = [...(checks || [])].sort((a, b) => new Date(a._id) - new Date(b._id));
const series = [];
let prev = null;
for (const check of sorted) {
+ console.log(`[NetworkStats] Processing check: ${check._id}`);
const eth = (check.net || []).find((iface) => iface.name === "en0");
if (!eth) {
+ console.log("[NetworkStats] No en0 interface found in check:", check._id);
prev = check;
continue;
}
+
+ console.log(`[NetworkStats] Found en0 interface in check ${check._id}:`, eth);
+
if (prev) {
+ console.log(`[NetworkStats] Have previous check: ${prev._id}`);
const prevEth = (prev.net || []).find((iface) => iface.name === "en0");
const t1 = new Date(check._id);
const t0 = new Date(prev._id);
+ console.log(`[NetworkStats] Time difference: ${t1 - t0}ms`);
+
if (!prevEth || isNaN(t1) || isNaN(t0)) {
+ console.log("[NetworkStats] Skipping - invalid prev data or time");
prev = check;
continue;
}
+
const dt = (t1 - t0) / 1000;
+ console.log(`[NetworkStats] Delta time: ${dt}s`);
+
if (dt > 0) {
- series.push({
- time: check._id,
- bytesPerSec: (eth.bytesSent - prevEth.bytesSent) / dt,
- packetsPerSec: (eth.packetsSent - prevEth.packetsSent) / dt,
- errors: (eth.errIn ?? 0) + (eth.errOut ?? 0),
- drops: (eth.dropIn ?? 0) + (eth.dropOut ?? 0),
+ const bytesField = eth.avgBytesSent;
+ const prevBytesField = prevEth.avgBytesSent;
+
+ console.log(`[NetworkStats] Bytes comparison:`, {
+ current: bytesField,
+ previous: prevBytesField,
+ diff: bytesField - prevBytesField,
});
+
+ if (bytesField !== undefined && prevBytesField !== undefined) {
+ const dataPoint = {
+ _id: check._id, // Use _id instead of time to match AreaChartBoxes
+ bytesPerSec: (bytesField - prevBytesField) / dt,
+ packetsPerSec: (eth.avgPacketsSent - prevEth.avgPacketsSent) / dt,
+ errors: (eth.avgErrIn ?? 0) + (eth.avgErrOut ?? 0),
+ drops: 0, // Skip drops for now since we don't have avgDropIn/Out
+ };
+ console.log(`[NetworkStats] Adding data point:`, dataPoint);
+ series.push(dataPoint);
+ } else {
+ console.warn("[NetworkStats] Missing bytes fields:", { eth, prevEth });
+ }
+ } else {
+ console.log("[NetworkStats] Skipping - zero or negative time delta");
}
+ } else {
+ console.log("[NetworkStats] No previous check yet, setting as prev");
}
prev = check;
}
+ console.log(`[NetworkStats] Generated ${series.length} time series data points`);
+
+ // If we only have one check, create a single data point with absolute values
+ if (series.length === 0 && sorted.length === 1) {
+ const check = sorted[0];
+ const eth = (check.net || []).find((iface) => iface.name === "en0");
+ if (eth) {
+ console.log(
+ "[NetworkStats] Only one data point available, showing absolute values"
+ );
+ series.push({
+ _id: check._id, // Use _id instead of time to match AreaChartBoxes
+ bytesPerSec: eth.avgBytesSent || 0, // Show absolute value instead of rate
+ packetsPerSec: eth.avgPacketsSent || 0, // Show absolute value instead of rate
+ errors: (eth.avgErrIn ?? 0) + (eth.avgErrOut ?? 0),
+ drops: 0,
+ });
+ }
+ }
+
+ if (series.length > 0) {
+ console.log("[NetworkStats] Sample series data:", series[0]);
+ }
return series;
}
-
-function getXAxisFormatter(checks) {
- if (!checks || checks.length === 0) return (val) => val;
- const sorted = [...checks].sort((a, b) => new Date(a._id) - new Date(b._id));
- const first = new Date(sorted[0]._id);
- const last = new Date(sorted[sorted.length - 1]._id);
- const diffDays = (last - first) / (1000 * 60 * 60 * 24);
-
- return diffDays > 2
- ? (val) =>
- new Date(val).toLocaleDateString(undefined, { month: "short", day: "numeric" })
- : (val) =>
- new Date(val).toLocaleTimeString(undefined, {
- hour: "2-digit",
- minute: "2-digit",
- hour12: false,
- });
-}