diff --git a/client/src/Components/v2/monitors/charts/HistogramInfrastructure.tsx b/client/src/Components/v2/monitors/charts/HistogramInfrastructure.tsx index 7ef82ba0c..e65a58bd9 100644 --- a/client/src/Components/v2/monitors/charts/HistogramInfrastructure.tsx +++ b/client/src/Components/v2/monitors/charts/HistogramInfrastructure.tsx @@ -127,7 +127,7 @@ export const HistogramInfrastructure = ({ }) => { const theme = useTheme(); const uniqueId = useId(); - let data = checks.reverse(); + const data = checks; let avgTemps: { bucketDate: string; avg_temp: number | null }[] = []; let tempYDomain: number[] = []; diff --git a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx b/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx deleted file mode 100644 index ee100bb0e..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/InfraAreaChart.jsx +++ /dev/null @@ -1,37 +0,0 @@ -// Components -import { Typography } from "@mui/material"; -import BaseContainer from "../BaseContainer/index.jsx"; -import AreaChart from "@/Components/v1/Charts/AreaChart/index.jsx"; -// Utils -import { useTheme } from "@emotion/react"; -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; -const InfraAreaChart = ({ config }) => { - const theme = useTheme(); - const { getDimensions } = useHardwareUtils(); - return ( - - - {config.heading} - - - - ); -}; - -export default InfraAreaChart; diff --git a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx b/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx deleted file mode 100644 index 3465683d9..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/index.jsx +++ /dev/null @@ -1,140 +0,0 @@ -// Components -import { Stack } from "@mui/material"; -import InfraAreaChart from "./InfraAreaChart.jsx"; -import SkeletonLayout from "./skeleton.jsx"; - -// Utils -import { - PercentTick, - TzTick, - InfrastructureTooltip, - TemperatureTooltip, -} from "@/Components/v1/Charts/Utils/chartUtils.jsx"; -import { useTheme } from "@emotion/react"; -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; -import { useTranslation } from "react-i18next"; -const AreaChartBoxes = ({ shouldRender, monitor, dateRange }) => { - const theme = useTheme(); - const { t } = useTranslation(); - const { buildTemps } = useHardwareUtils(); - - if (!shouldRender) { - return ; - } - - const { stats } = monitor ?? {}; - const { checks } = stats; - - let latestCheck = checks[0]; - const { temps, tempKeys } = buildTemps(checks); - - const configs = [ - { - type: "memory", - data: checks, - dataKeys: ["avgMemoryUsage"], - heading: t("memoryUsage"), - strokeColor: theme.palette.accent.main, // CAIO_REVIEW - gradientStartColor: theme.palette.accent.main, // CAIO_REVIEW - yLabel: t("memoryUsage"), - yDomain: [0, 1], - yTick: , - xTick: , - toolTip: ( - - ), - }, - { - type: "cpu", - data: checks, - dataKeys: ["avgCpuUsage"], - heading: t("cpuUsage"), - strokeColor: theme.palette.success.main, - gradientStartColor: theme.palette.success.main, - yLabel: t("cpuUsage"), - yDomain: [0, 1], - yTick: , - xTick: , - toolTip: ( - - ), - }, - { - type: "temperature", - data: temps, - dataKeys: tempKeys, - strokeColor: theme.palette.error.main, - gradientStartColor: theme.palette.error.main, - heading: t("cpuTemperature"), - yLabel: "Temperature", - xTick: , - yDomain: [ - 0, - Math.max(Math.max(...temps.flatMap((t) => tempKeys.map((k) => t[k]))) * 1.1, 200), - ], - toolTip: ( - - ), - }, - ...(latestCheck?.disks?.map((disk, idx) => ({ - type: "disk", - data: checks, - diskIndex: idx, - dataKeys: [`disks[${idx}].usagePercent`], - heading: `Disk${idx} usage`, - strokeColor: theme.palette.warning.main, - gradientStartColor: theme.palette.warning.main, - yLabel: t("diskUsage"), - yDomain: [0, 1], - yTick: , - xTick: , - toolTip: ( - - ), - })) || []), - ]; - - return ( - *": { - flexBasis: `calc(50% - ${theme.spacing(8)})`, - maxWidth: `calc(50% - ${theme.spacing(8)})`, - }, - }} - > - {configs.map((config) => ( - - ))} - - ); -}; - -export default AreaChartBoxes; diff --git a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx b/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx deleted file mode 100644 index f787b88ec..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/AreaChartBoxes/skeleton.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; -import { useTheme } from "@emotion/react"; -const SkeletonLayout = () => { - const theme = useTheme(); - return ( - - - - - ); -}; - -export default SkeletonLayout; diff --git a/client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx b/client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx deleted file mode 100644 index 9c0fe1574..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/BaseContainer/index.jsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Renders a base box with consistent styling - * @param {Object} props - Component properties - * @param {React.ReactNode} props.children - Child components to render inside the box - * @param {Object} props.sx - Additional styling for the box - * @returns {React.ReactElement} Styled box component - */ - -// Components -import { Box } from "@mui/material"; - -// Utils -import { useTheme } from "@emotion/react"; -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; -import PropTypes from "prop-types"; - -const BaseContainer = ({ children, sx = {}, shouldExpand = false }) => { - const theme = useTheme(); - const { getDimensions } = useHardwareUtils(); - return ( - - {children} - - ); -}; - -BaseContainer.propTypes = { - children: PropTypes.node.isRequired, - sx: PropTypes.object, - shouldExpand: PropTypes.bool, -}; - -export default BaseContainer; diff --git a/client/src/Pages/Infrastructure/Details/Components/ChartsNetwork.tsx b/client/src/Pages/Infrastructure/Details/Components/ChartsNetwork.tsx index a911e00e3..d20bf6181 100644 --- a/client/src/Pages/Infrastructure/Details/Components/ChartsNetwork.tsx +++ b/client/src/Pages/Infrastructure/Details/Components/ChartsNetwork.tsx @@ -26,7 +26,9 @@ const getChartConfigs = ( ): ChartConfig[] => { const configs: ChartConfig[] = []; - const netInterfaces = checks[0]?.net || []; + // Find the first check that has network data to get interface names + const checkWithNet = checks.find((c) => c.net && c.net.length > 0); + const netInterfaces = checkWithNet?.net || []; netInterfaces.forEach((iface, idx) => { configs.push( diff --git a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx b/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx deleted file mode 100644 index c762eeb32..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/Gauge.jsx +++ /dev/null @@ -1,127 +0,0 @@ -// Components -import CustomGauge from "@/Components/v1/Charts/CustomGauge/index.jsx"; -import BaseContainer from "../BaseContainer/index.jsx"; -import { Stack, Typography, Box } from "@mui/material"; -// Utils -import { useTheme } from "@emotion/react"; -import PropTypes from "prop-types"; - -const Gauge = ({ - value, - heading, - metricOne, - valueOne, - metricTwo, - valueTwo, - metricThree, - valueThree, - metricFour, - valueFour, - shouldExpand = false, -}) => { - const theme = useTheme(); - - const valueStyle = { - borderRadius: theme.spacing(2), - backgroundColor: theme.palette.tertiary.main, - minWidth: "40%", - maxWidth: "60%", - mb: theme.spacing(2), - mt: theme.spacing(2), - pr: theme.spacing(2), - textAlign: "right", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - }; - - return ( - - - - - - {heading} - - - - - {metricOne} - {valueOne} - - - {metricTwo} - {valueTwo} - - - {metricThree} - {valueThree} - - - {metricFour} - {valueFour} - - - - - ); -}; - -Gauge.propTypes = { - value: PropTypes.number, - heading: PropTypes.string, - metricOne: PropTypes.string, - valueOne: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - metricTwo: PropTypes.string, - valueTwo: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - metricThree: PropTypes.string, - valueThree: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - metricFour: PropTypes.string, - valueFour: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - shouldExpand: PropTypes.bool, -}; - -export default Gauge; diff --git a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx b/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx deleted file mode 100644 index 3859fe040..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/index.jsx +++ /dev/null @@ -1,112 +0,0 @@ -// Components -import { Box } from "@mui/material"; -import Gauge from "./Gauge.jsx"; -import SkeletonLayout from "./skeleton.jsx"; -import PropTypes from "prop-types"; - -// Utils -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; - -const Gauges = ({ isLoading = false, monitor }) => { - const { decimalToPercentage, formatBytes, formatDeviceName, formatMountpoint } = - useHardwareUtils(); - const theme = useTheme(); - const { t } = useTranslation(); - - if (isLoading) { - return ; - } - - const latestCheck = monitor?.recentChecks?.[0]; - const memoryUsagePercent = latestCheck?.memory?.usage_percent ?? 0; - const memoryUsedBytes = latestCheck?.memory?.used_bytes ?? 0; - const memoryTotalBytes = latestCheck?.memory?.total_bytes ?? 0; - const cpuUsagePercent = latestCheck?.cpu?.usage_percent ?? 0; - const cpuPhysicalCores = latestCheck?.cpu?.physical_core ?? 0; - const cpuFrequency = latestCheck?.cpu?.frequency ?? 0; - - const gauges = [ - { - type: "memory", - value: decimalToPercentage(memoryUsagePercent), - heading: t("memoryUsage"), - metricOne: t("used"), - valueOne: formatBytes(memoryUsedBytes, true), - metricTwo: t("total"), - valueTwo: formatBytes(memoryTotalBytes, true), - }, - { - type: "cpu", - value: decimalToPercentage(cpuUsagePercent), - heading: t("cpuUsage"), - metricOne: t("cores"), - valueOne: cpuPhysicalCores ?? 0, - metricTwo: t("frequency"), - valueTwo: `${(cpuFrequency / 1000).toFixed(2)} Ghz`, - }, - ...(latestCheck?.disk ?? []) - .filter((disk) => { - if (!monitor?.selectedDisks || monitor.selectedDisks.length === 0) { - return true; - } - return monitor.selectedDisks.includes(disk.mountpoint || disk.device); - }) - .map((disk, idx) => ({ - type: "disk", - diskIndex: idx, - value: decimalToPercentage(disk.usage_percent), - heading: `Disk${idx} usage`, - metricOne: t("used"), - valueOne: formatBytes(disk.total_bytes - disk.free_bytes, true), - metricTwo: t("total"), - valueTwo: formatBytes(disk.total_bytes, true), - metricThree: t("device"), - valueThree: formatDeviceName(disk.device), - metricFour: t("mountedOn"), - valueFour: formatMountpoint(disk.mountpoint), - })), - ]; - - // Only expand gauges to fill row when there are 4 or more - const shouldExpand = gauges.length >= 4; - - return ( - - {gauges.map((gauge) => { - return ( - - ); - })} - - ); -}; - -Gauges.propTypes = { - isLoading: PropTypes.bool, - monitor: PropTypes.object, -}; - -export default Gauges; diff --git a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx b/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx deleted file mode 100644 index 347a52901..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/GaugeBoxes/skeleton.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Stack, Skeleton } from "@mui/material"; -import { useTheme } from "@emotion/react"; - -const SkeletonLayout = () => { - const theme = useTheme(); - - return ( - - {Array.from({ length: 3 }).map((_, idx) => { - return ( - - ); - })} - - ); -}; - -export default SkeletonLayout; diff --git a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx b/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx deleted file mode 100644 index d9f93311c..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkCharts.jsx +++ /dev/null @@ -1,132 +0,0 @@ -import PropTypes from "prop-types"; -import { Stack, Typography } from "@mui/material"; -import InfraAreaChart from "../AreaChartBoxes/InfraAreaChart.jsx"; - -import { - TzTick, - InfrastructureTooltip, - NetworkTick, -} from "@/Components/v1/Charts/Utils/chartUtils.jsx"; -import { useTheme } from "@emotion/react"; -import { useTranslation } from "react-i18next"; -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; - -const NetworkCharts = ({ ethernetData, dateRange }) => { - const theme = useTheme(); - const { t } = useTranslation(); - - const { formatBytesPerSecondString, formatPacketsPerSecondString } = useHardwareUtils(); - - if (!ethernetData?.length) { - return {t("noNetworkStatsAvailable")}; - } - - const configs = [ - { - type: "network-bytes", - data: ethernetData, - dataKeys: ["bytesPerSec"], - heading: t("dataReceived"), - strokeColor: theme.palette.info.main, - gradientStartColor: theme.palette.info.main, - yLabel: t("rate"), - xTick: , - yTick: , - toolTip: ( - - ), - }, - { - type: "network-packets", - data: ethernetData, - dataKeys: ["packetsPerSec"], - heading: t("packetsReceivedRate"), - strokeColor: theme.palette.success.main, - gradientStartColor: theme.palette.success.main, - yLabel: t("rate"), - xTick: , - yTick: , - toolTip: ( - - ), - }, - { - type: "network-errors", - data: ethernetData, - dataKeys: ["errors"], - heading: t("networkErrors"), - strokeColor: theme.palette.error.main, - gradientStartColor: theme.palette.error.main, - yLabel: t("errors"), - xTick: , - toolTip: ( - Math.round(value).toLocaleString()} - /> - ), - }, - { - type: "network-drops", - data: ethernetData, - dataKeys: ["drops"], - heading: t("networkDrops"), - strokeColor: theme.palette.warning.main, - gradientStartColor: theme.palette.warning.main, - yLabel: t("drops"), - xTick: , - toolTip: ( - Math.round(value).toLocaleString()} - /> - ), - }, - ]; - - return ( - *": { - flexBasis: `calc(50% - ${theme.spacing(8)})`, - maxWidth: `calc(50% - ${theme.spacing(8)})`, - }, - }} - > - {configs.map((config) => ( - - ))} - - ); -}; - -NetworkCharts.propTypes = { - ethernetData: 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 deleted file mode 100644 index 0747dfcd1..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/NetworkStatBoxes.jsx +++ /dev/null @@ -1,81 +0,0 @@ -import PropTypes from "prop-types"; -import StatusBoxes from "@/Components/v1/StatusBoxes/index.jsx"; -import StatBox from "@/Components/v1/StatBox/index.jsx"; -import { Typography } from "@mui/material"; -import { useTranslation } from "react-i18next"; -import { useHardwareUtils } from "../../Hooks/useHardwareUtils.jsx"; - -function formatNumber(num) { - return num != null ? num.toLocaleString() : "0"; -} - -const NetworkStatBoxes = ({ shouldRender, net, ifaceName }) => { - const { t } = useTranslation(); - const { formatBytes } = useHardwareUtils(); - - const filtered = net?.filter((iface) => iface.name === ifaceName) || []; - - if (!net?.length) { - return {t("noNetworkStatsAvailable")}; - } - - return ( - - {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, - }) - ), - ifaceName: PropTypes.string.isRequired, -}; - -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 deleted file mode 100644 index 78db333e6..000000000 --- a/client/src/Pages/Infrastructure/Details/Components/NetworkStats/index.jsx +++ /dev/null @@ -1,101 +0,0 @@ -import PropTypes from "prop-types"; -import { useState, useEffect } from "react"; -import { Box } from "@mui/material"; -import { useTranslation } from "react-i18next"; -import { useTheme } from "@emotion/react"; -import Select from "@/Components/v1/Inputs/Select/index.jsx"; -import NetworkStatBoxes from "./NetworkStatBoxes.jsx"; -import NetworkCharts from "./NetworkCharts.jsx"; -import MonitorTimeFrameHeader from "@/Components/v1/MonitorTimeFrameHeader/index.jsx"; - -const getAvailableInterfaces = (net) => { - return (net || []).map((iface) => iface.name).filter(Boolean); -}; - -const getNetworkInterfaceData = (checks, ifaceName) => { - if (!ifaceName) return []; - - // Transform backend data structure for the selected interface - // Backend already calculates deltas, we just reshape the data - return (checks || []) - .map((check) => { - const networkInterface = (check.net || []).find( - (iface) => iface.name === ifaceName - ); - if (!networkInterface) return null; - return { - _id: check._id, - bytesPerSec: networkInterface.deltaBytesRecv, - packetsPerSec: networkInterface.deltaPacketsRecv, - errors: networkInterface.deltaErrOut ?? 0, - drops: networkInterface.deltaDropOut ?? 0, - }; - }) - .filter(Boolean); -}; - -const Network = ({ net, checks, isLoading, dateRange, setDateRange }) => { - const { t } = useTranslation(); - const theme = useTheme(); - - const availableInterfaces = getAvailableInterfaces(net); - const [selectedInterface, setSelectedInterface] = useState(""); - - // Set default interface when data loads - useEffect(() => { - if (availableInterfaces.length > 0 && !selectedInterface) { - setSelectedInterface(availableInterfaces[0]); - } - }, [availableInterfaces, selectedInterface]); - - const ethernetData = getNetworkInterfaceData(checks, selectedInterface); - - return ( - <> - - - {availableInterfaces.length > 0 && ( -