"use client"; import { useCallback, useMemo, useState } from "react"; import { ScrollArea, Select, SimpleGrid, Stack } from "@mantine/core"; import { clientApi } from "@homarr/api/client"; import { useScopedI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "../definition"; import { containerColors } from "../beszel/_shared/colors"; import { BeszelAreaChart, ChartPanel, useContainerNames, useDockerChartData, useSystemChartData, } from "../beszel/_shared/chart"; import { chartAxisFormatters, formatByteRate, formatGB, formatPercent, formatStorageBytes, } from "../beszel/_shared/format"; import { makeTooltipProps } from "../beszel/_shared/tooltip"; const tooltipPercent = makeTooltipProps(formatPercent); const tooltipGB = makeTooltipProps(formatGB, true); const tooltipRate = makeTooltipProps(formatByteRate, true); const tooltipPercentTotal = makeTooltipProps(formatPercent, true); const tooltipBytesTotal = makeTooltipProps(formatStorageBytes, true); const CHART_HEIGHT = 180; export default function BeszelSystemStatsWidget({ options, integrationIds, isEditMode, width, }: WidgetComponentProps<"beszelSystemStats">) { const t = useScopedI18n("widget.beszelSystemStats"); const { data: systemsResult = [] } = clientApi.widget.beszel.getSystems.useQuery( { integrationIds }, { staleTime: 30 * 1000 }, ); const systems = useMemo( () => systemsResult.flatMap((r) => r.systems.map((s) => ({ value: s.id, label: s.name }))), [systemsResult], ); const [selectedSystem, setSelectedSystem] = useState(systems[0]?.value ?? null); const showDocker = options.showDockerCpu || options.showDockerMemory || options.showDockerNetwork; const { data: statsResult } = clientApi.widget.beszel.getSystemStats.useQuery( { integrationIds, systemId: selectedSystem ?? systems[0]?.value ?? "", timePeriod: options.timePeriod as "1h" | "12h" | "24h" | "1w" | "30d", includeDocker: showDocker, }, { staleTime: 30 * 1000 }, ); // BeszelSystemStats fields: storage values (m, mu, mb, d, du, s, su) are already in GB. // Disk I/O (dr, dw) is in MB/s — scale to bytes/s for display formatters. // Network (ns, nr, b) is in bytes/s. Container memory (m) is in MB. const cpuMap = useCallback((s: { cpu: number }) => ({ [t("chart.cpu.series")]: s.cpu }), [t]); const memoryMap = useCallback( (s: { mu: number; mb: number }) => ({ [t("chart.memory.series")]: s.mu, [t("chart.memory.cache")]: s.mb ?? 0, }), [t], ); const diskMap = useCallback((s: { du: number }) => ({ [t("chart.disk.series")]: s.du }), [t]); const diskIOMap = useCallback( (s: { dr?: number; dw?: number }) => ({ [t("chart.diskIO.read")]: (s.dr ?? 0) * (1024 * 1024), [t("chart.diskIO.write")]: (s.dw ?? 0) * (1024 * 1024), }), [t], ); // b = public interface bandwidth (preferred), ns/nr = all-interface traffic (fallback) const networkMap = useCallback( (s: { ns: number; nr: number; b?: [number, number] }) => ({ [t("chart.network.sent")]: s.b?.[0] ?? s.ns, [t("chart.network.recv")]: s.b?.[1] ?? s.nr, }), [t], ); const cpuData = useSystemChartData(statsResult?.systemStats, cpuMap); const memoryData = useSystemChartData(statsResult?.systemStats, memoryMap); const diskData = useSystemChartData(statsResult?.systemStats, diskMap); const diskIOData = useSystemChartData(statsResult?.systemStats, diskIOMap); const networkData = useSystemChartData(statsResult?.systemStats, networkMap); const containerNames = useContainerNames(statsResult?.containerStats); const dockerCpuData = useDockerChartData(statsResult?.containerStats, containerNames, "cpu"); const dockerMemData = useDockerChartData(statsResult?.containerStats, containerNames, "memory"); const dockerNetData = useDockerChartData(statsResult?.containerStats, containerNames, "network"); const containerSeries = containerNames.map((name, i) => ({ name, color: containerColors[i % containerColors.length] as string, })); const cols = width > 600 ? 2 : 1; return (