fix(beszel): address final review findings

- Gate stats/live queries on systems loading state to prevent stale ID
  requests during initial load
- Simplify useLiveStats: functional setState, stable integrationKey dep,
  remove unused includeDocker param
- Extract prepareRecords helper to deduplicate chart ordering logic
- Remove stale timePeriod type cast
This commit is contained in:
Thomas Camlong committed 2026-06-15 15:39:01 +02:00
1 parent ed333e1cbb
commit e29c46cc5a
3 files changed
+28 -22

No files matched your search

@@ -44,7 +44,7 @@ export default function BeszelSystemStatsWidget({
setOptions,
}: WidgetComponentProps<"beszelSystemStats">) {
const t = useScopedI18n("widget.beszelSystemStats");
const { data: systemsResult = [] } = clientApi.widget.beszel.getSystems.useQuery(
const { data: systemsResult = [], isPending: systemsPending } = clientApi.widget.beszel.getSystems.useQuery(
{ integrationIds },
{ staleTime: 30 * 1000 },
);
@@ -56,7 +56,8 @@ export default function BeszelSystemStatsWidget({
const selectedSystem = options.systemId || systems[0]?.value || "";
const selectedLabel = systems.find((s) => s.value === selectedSystem)?.label;
const systemExists = systems.length === 0 || systems.some((s) => s.value === selectedSystem);
const systemExists = systems.some((s) => s.value === selectedSystem);
const systemReady = !systemsPending && (selectedSystem === "" || systemExists);
const handleSelectSystem = useCallback(
(value: string) => setOptions({ newOptions: { systemId: value } }),
@@ -70,13 +71,13 @@ export default function BeszelSystemStatsWidget({
{
integrationIds,
systemId: selectedSystem,
timePeriod: options.timePeriod as "1h" | "12h" | "24h" | "1w" | "30d",
timePeriod: options.timePeriod,
includeDocker: showDocker,
},
{ staleTime: 30 * 1000, enabled: !isLive && systemExists && selectedSystem !== "" },
{ staleTime: 30 * 1000, enabled: !isLive && systemReady && selectedSystem !== "" },
);
const liveData = useLiveStats(integrationIds, selectedSystem, showDocker, isLive && systemExists);
const liveData = useLiveStats(integrationIds, selectedSystem, isLive && systemReady);
const activeStats = isLive ? liveData : statsResult;
@@ -122,7 +123,7 @@ export default function BeszelSystemStatsWidget({
const cols = width > 600 ? 2 : 1;
if (!systemExists && systems.length > 0) {
if (!systemsPending && !systemExists && systems.length > 0) {
return (
<Center h="100%">
<Stack align="center" gap="sm" p="md">
@@ -11,6 +11,13 @@ import type { BeszelContainerStatsRecord, BeszelSystemStatsRecord } from "@homar
const formatTime = (timestamp: string) => dayjs(timestamp).format("HH:mm");
const formatTimeLive = (timestamp: string) => dayjs(timestamp).format("HH:mm:ss");
function prepareRecords<T>(records: T[], live: boolean) {
return {
fmt: live ? formatTimeLive : formatTime,
ordered: live ? records : [...records].toReversed(),
};
}
const yAxisBase = { tickMargin: 0, tick: { fontSize: 10 } } as const;
interface ChartPanelProps {
@@ -82,8 +89,7 @@ export const useSystemChartData = (
) =>
useMemo(() => {
if (!systemStats) return [];
const fmt = live ? formatTimeLive : formatTime;
const ordered = live ? systemStats : [...systemStats].toReversed();
const { fmt, ordered } = prepareRecords(systemStats, live);
return ordered.map((r) => ({
time: fmt(r.created),
...mapFn(r.stats),
@@ -123,8 +129,7 @@ export const useDockerChartData = (
if (!containerStats?.length) return [];
const extract = defaultContainerExtractors[metric];
if (!extract) return [];
const fmt = live ? formatTimeLive : formatTime;
const ordered = live ? containerStats : [...containerStats].toReversed();
const { fmt, ordered } = prepareRecords(containerStats, live);
return ordered.map((record) => {
const point: Record<string, unknown> = { time: fmt(record.created) };
for (const name of containerNames) {
@@ -1,6 +1,6 @@
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { clientApi } from "@homarr/api/client";
import type { BeszelContainerStatsRecord, BeszelSystemStatsRecord } from "@homarr/integrations/types";
@@ -12,29 +12,29 @@ interface LiveStatsData {
containerStats: BeszelContainerStatsRecord[];
}
export const useLiveStats = (integrationIds: string[], systemId: string, includeDocker: boolean, enabled: boolean) => {
const bufferRef = useRef<LiveStatsData>({ systemStats: [], containerStats: [] });
export const useLiveStats = (integrationIds: string[], systemId: string, enabled: boolean) => {
const [data, setData] = useState<LiveStatsData | null>(null);
const integrationKey = integrationIds.join(",");
useEffect(() => {
bufferRef.current = { systemStats: [], containerStats: [] };
setData(null);
}, [systemId, enabled, ...integrationIds]);
}, [systemId, enabled, integrationKey]);
const onData = useCallback(
(incoming: { stats: BeszelSystemStatsRecord; containerStats: BeszelContainerStatsRecord | null }) => {
const buf = bufferRef.current;
buf.systemStats = [...buf.systemStats, incoming.stats].slice(-MAX_BUFFER);
if (incoming.containerStats) {
buf.containerStats = [...buf.containerStats, incoming.containerStats].slice(-MAX_BUFFER);
}
setData({ ...buf });
setData((prev) => {
const systemStats = [...(prev?.systemStats ?? []), incoming.stats].slice(-MAX_BUFFER);
const containerStats = incoming.containerStats
? [...(prev?.containerStats ?? []), incoming.containerStats].slice(-MAX_BUFFER)
: prev?.containerStats ?? [];
return { systemStats, containerStats };
});
},
[],
);
clientApi.widget.beszel.subscribeSystemStats.useSubscription(
{ integrationIds, systemId, includeDocker },
{ integrationIds, systemId },
{ enabled: enabled && systemId !== "", onData },
);