The notice now triggers on interface names (loopback / container-bridge only), not on zero traffic. Update the variable, i18n key, and English copy to match: - showNoTrafficNotice -> showLoopbackOnlyNotice - pages.infrastructure.charts.labels.netNoTraffic -> pages.infrastructure.charts.labels.loopbackOnlyNotice - English string rewritten to describe the loopback-only condition rather than "zero traffic on every interface" Other locales still hold the old string under the old key and will fall back to English until the translation pipeline picks up the new key. Tracked separately.
136 lines
3.8 KiB
TypeScript
136 lines
3.8 KiB
TypeScript
import Grid from "@mui/material/Grid";
|
|
import Stack from "@mui/material/Stack";
|
|
import { NoticeBanner } from "@/Components/design-elements";
|
|
import { HistogramInfrastructure } from "@/Components/monitors";
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
import type { HardwareCheckStats } from "@/Types/Monitor";
|
|
import { useMemo } from "react";
|
|
import { useTheme } from "@mui/material/styles";
|
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
import { LAYOUT } from "@/Utils/Theme/constants";
|
|
|
|
const formatBytesToMB = (value: number) => `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
|
|
interface ChartConfig {
|
|
title: string;
|
|
type: string;
|
|
dataKeys: string[];
|
|
strokeColor: string;
|
|
gradientStartColor: string;
|
|
idx: number | null;
|
|
interfaceName?: string;
|
|
}
|
|
|
|
const getChartConfigs = (
|
|
theme: any,
|
|
checks: HardwareCheckStats[],
|
|
t: any
|
|
): ChartConfig[] => {
|
|
const configs: ChartConfig[] = [];
|
|
|
|
// 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(
|
|
{
|
|
title: t("pages.infrastructure.charts.labels.netBytesSent", {
|
|
name: iface.name,
|
|
}),
|
|
type: "netBytesSent",
|
|
dataKeys: [`net[${idx}].bytesSentPerSecond`],
|
|
strokeColor: theme.palette.primary.main,
|
|
gradientStartColor: theme.palette.primary.main,
|
|
idx,
|
|
interfaceName: iface.name,
|
|
},
|
|
{
|
|
title: t("pages.infrastructure.charts.labels.netBytesRecv", {
|
|
name: iface.name,
|
|
}),
|
|
type: "netBytesRecv",
|
|
dataKeys: [`net[${idx}].deltaBytesRecv`],
|
|
strokeColor: theme.palette.success.main,
|
|
gradientStartColor: theme.palette.success.main,
|
|
idx,
|
|
interfaceName: iface.name,
|
|
}
|
|
);
|
|
});
|
|
|
|
return configs;
|
|
};
|
|
|
|
// Loopback / bridge names Capture sees when it can only observe the container's
|
|
// own network namespace. When ALL reported interfaces match this set we assume
|
|
// the "no real interfaces visible" case and surface the notice.
|
|
const CONTAINER_ONLY_IFACE_NAMES = new Set(["lo", "lo0", "docker0"]);
|
|
|
|
const onlyContainerVisibleInterfaces = (checks: HardwareCheckStats[]): boolean => {
|
|
const named = new Set<string>();
|
|
checks.forEach((c) => c.net?.forEach((iface) => named.add(iface.name)));
|
|
if (named.size === 0) return false;
|
|
for (const name of named) {
|
|
if (!CONTAINER_ONLY_IFACE_NAMES.has(name)) return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
export const InfraNetworkCharts = ({
|
|
checks,
|
|
dateRange,
|
|
}: {
|
|
checks: HardwareCheckStats[];
|
|
dateRange: string;
|
|
}) => {
|
|
const theme = useTheme();
|
|
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
|
const { t } = useTranslation();
|
|
const chartConfigs = useMemo(
|
|
() => getChartConfigs(theme, checks, t),
|
|
[theme, checks, t]
|
|
);
|
|
const showLoopbackOnlyNotice =
|
|
chartConfigs.length > 0 && onlyContainerVisibleInterfaces(checks);
|
|
|
|
return (
|
|
<Stack gap={theme.spacing(LAYOUT.XS)}>
|
|
{showLoopbackOnlyNotice && (
|
|
<NoticeBanner severity="warning">
|
|
{t("pages.infrastructure.charts.labels.loopbackOnlyNotice")}
|
|
</NoticeBanner>
|
|
)}
|
|
<Grid
|
|
container
|
|
spacing={theme.spacing(LAYOUT.MD)}
|
|
>
|
|
{chartConfigs.map((config) => {
|
|
return (
|
|
<Grid
|
|
size={isSmall ? 12 : 6}
|
|
key={`${config.type}-${config.interfaceName ?? config.idx ?? ""}`}
|
|
>
|
|
<HistogramInfrastructure
|
|
dateRange={dateRange}
|
|
title={config.title}
|
|
type={config.type}
|
|
idx={config.idx}
|
|
checks={checks}
|
|
xKey="bucketDate"
|
|
dataKeys={config.dataKeys}
|
|
gradient={true}
|
|
gradientStartColor={config.gradientStartColor}
|
|
gradientEndColor="#ffffff"
|
|
strokeColor={config.strokeColor}
|
|
yAxisFormatter={formatBytesToMB}
|
|
/>
|
|
</Grid>
|
|
);
|
|
})}
|
|
</Grid>
|
|
</Stack>
|
|
);
|
|
};
|