"use client"; import { useEffect, useRef, useState } from "react"; import { Box, Group, Stack } from "@mantine/core"; import { useElementSize } from "@mantine/hooks"; import { clientApi } from "@homarr/api/client"; import type { WidgetComponentProps } from "../definition"; import { CombinedNetworkTrafficChart } from "./chart/combined-network-traffic"; import { SystemResourceCPUChart } from "./chart/cpu-chart"; import { SystemResourceGPUChart } from "./chart/gpu-chart"; import { SystemResourceMemoryChart } from "./chart/memory-chart"; import { NetworkTrafficChart } from "./chart/network-traffic"; const MAX_QUEUE_SIZE = 15; const toChartItem = (healthInfo: { cpuUtilization: number; memUsedInBytes: number; gpu: { processorUtilization: number }[]; network: { up: number; down: number } | null; }) => ({ cpu: healthInfo.cpuUtilization, memory: healthInfo.memUsedInBytes, gpu: healthInfo.gpu.length > 0 ? healthInfo.gpu.reduce((acc, g) => acc + g.processorUtilization, 0) / healthInfo.gpu.length : 0, network: healthInfo.network, }); export default function SystemResources({ integrationIds, options }: WidgetComponentProps<"systemResources">) { const { ref, width } = useElementSize(); const { data = [], dataUpdatedAt } = clientApi.widget.healthMonitoring.getSystemHealthStatus.useQuery({ integrationIds, }); const memoryCapacityInBytes = (data[0]?.healthInfo.memAvailableInBytes ?? 0) + (data[0]?.healthInfo.memUsedInBytes ?? 0); const [items, setItems] = useState< { cpu: number; memory: number; gpu: number; network: { up: number; down: number } | null }[] >(() => (data[0] ? [toChartItem(data[0].healthInfo)] : [])); const prevIntegrationIds = useRef(integrationIds); useEffect(() => { if (prevIntegrationIds.current === integrationIds) return; prevIntegrationIds.current = integrationIds; setItems([]); }, [integrationIds]); const lastUpdatedAt = useRef(dataUpdatedAt); useEffect(() => { if (dataUpdatedAt === lastUpdatedAt.current) return; lastUpdatedAt.current = dataUpdatedAt; const firstItem = data[0]; if (!firstItem) return; setItems((prev) => [...prev, toChartItem(firstItem.healthInfo)].slice(-MAX_QUEUE_SIZE)); }, [dataUpdatedAt, data]); const showNetwork = items.length === 0 || (items.every((item) => item.network !== null) && options.visibleCharts.includes("network")); const rowHeight = `calc((100% - ${(options.visibleCharts.length - 1) * 8}px) / ${options.visibleCharts.length})`; return ( {options.visibleCharts.includes("cpu") && ( item.cpu)} hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> )} {options.visibleCharts.includes("memory") && ( item.memory)} totalCapacityInBytes={memoryCapacityInBytes} hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> )} {options.visibleCharts.includes("gpu") && ( item.gpu)} hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> )} {showNetwork && (width > 256 ? ( item.network!.down)} isUp={false} hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> item.network!.up)} isUp hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> ) : ( item.network!)} hasShadow={options.hasShadow} labelDisplayMode={options.labelDisplayMode} /> ))} ); }