From c8a2be553a6f822ea45ba621c373ea3ef5e5817d Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Wed, 10 Jun 2026 18:45:25 +0200 Subject: [PATCH] refactor(widgets): migrate new integration widgets from useSuspenseQuery to useQuery Convert Uptime Kuma, Paperless-ngx, audio-stats, and UPS widgets to use useQuery with WidgetEmptyState fallback, consistent with the query cache persistence pattern from PR #5811. --- packages/widgets/src/audio-stats/component.tsx | 10 +++++++--- packages/widgets/src/paperless-ngx/component.tsx | 10 +++++++--- packages/widgets/src/ups/component.tsx | 8 +++++++- packages/widgets/src/uptime-kuma/component.tsx | 8 +++++++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/widgets/src/audio-stats/component.tsx b/packages/widgets/src/audio-stats/component.tsx index ff2b56863..d9d8d26d3 100644 --- a/packages/widgets/src/audio-stats/component.tsx +++ b/packages/widgets/src/audio-stats/component.tsx @@ -2,13 +2,17 @@ import { clientApi } from "@homarr/api/client"; +import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; import { AudioStatsContent } from "./audio-stats-content"; export default function AudioStatsWidget({ integrationIds, options, width }: WidgetComponentProps<"audioStats">) { - const [response] = clientApi.widget.audioStats.getStats.useSuspenseQuery({ - integrationId: integrationIds[0] ?? "", - }); + const { data: response } = clientApi.widget.audioStats.getStats.useQuery( + { integrationId: integrationIds[0] ?? "" }, + { staleTime: 5 * 60 * 1000 }, + ); + + if (!response) return ; return ; } diff --git a/packages/widgets/src/paperless-ngx/component.tsx b/packages/widgets/src/paperless-ngx/component.tsx index 406c50605..3b476aa0d 100644 --- a/packages/widgets/src/paperless-ngx/component.tsx +++ b/packages/widgets/src/paperless-ngx/component.tsx @@ -8,6 +8,7 @@ import { IconFileDescription, IconFileText, IconInbox, IconTag, IconUsers } from import { clientApi } from "@homarr/api/client"; import { useScopedI18n } from "@homarr/translation/client"; +import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; import classes from "./component.module.css"; @@ -71,9 +72,12 @@ const heroPartVisibility = { export default function PaperlessNgxWidget({ integrationIds, options, width }: WidgetComponentProps<"paperlessNgx">) { const t = useScopedI18n("widget.paperlessNgx"); - const [stats] = clientApi.widget.paperlessNgx.getStats.useSuspenseQuery({ - integrationId: integrationIds[0] ?? "", - }); + const { data: stats } = clientApi.widget.paperlessNgx.getStats.useQuery( + { integrationId: integrationIds[0] ?? "" }, + { staleTime: 5 * 60 * 1000 }, + ); + + if (!stats) return ; const canShowInboxHero = options.showDocumentsInbox && options.showDocumentsTotal; const showHero = options.showInboxRatio && canShowInboxHero; diff --git a/packages/widgets/src/ups/component.tsx b/packages/widgets/src/ups/component.tsx index 2b98a1437..3ae515b41 100644 --- a/packages/widgets/src/ups/component.tsx +++ b/packages/widgets/src/ups/component.tsx @@ -8,6 +8,7 @@ import type { UpsStatus, UpsSummary } from "@homarr/integrations/types"; import type { ScopedTranslationFunction } from "@homarr/translation"; import { useScopedI18n } from "@homarr/translation/client"; +import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; import { NoIntegrationDataError } from "../errors/no-data-integration"; import { NoIntegrationSelectedError } from "../errors/no-integration-selected"; @@ -39,7 +40,10 @@ interface UpsContentProps { function UpsContent({ integrationIds, options, isEditMode, width }: UpsContentProps) { const t = useScopedI18n("widget.ups"); - const [data] = clientApi.widget.ups.getSummaries.useSuspenseQuery({ integrationIds }); + const { data } = clientApi.widget.ups.getSummaries.useQuery( + { integrationIds }, + { staleTime: 30 * 1000 }, + ); const utils = clientApi.useUtils(); clientApi.widget.ups.subscribeSummaries.useSubscription( @@ -59,6 +63,8 @@ function UpsContent({ integrationIds, options, isEditMode, width }: UpsContentPr }, ); + if (!data) return ; + const devices = data.flatMap((instance) => instance.summaries.map((summary) => ({ key: `${instance.integrationId}:${summary.id}`, summary })), ); diff --git a/packages/widgets/src/uptime-kuma/component.tsx b/packages/widgets/src/uptime-kuma/component.tsx index 4d484138a..2a3b6136b 100644 --- a/packages/widgets/src/uptime-kuma/component.tsx +++ b/packages/widgets/src/uptime-kuma/component.tsx @@ -9,6 +9,7 @@ import { formatNumber } from "@homarr/common"; import type { UptimeKumaDashboardData } from "@homarr/integrations/types"; import { useScopedI18n } from "@homarr/translation/client"; +import { WidgetEmptyState } from "../common/empty-state"; import type { WidgetComponentProps } from "../definition"; import { NoIntegrationDataError } from "../errors/no-data-integration"; import classes from "./component.module.css"; @@ -106,7 +107,10 @@ interface UptimeKumaContentProps { function UptimeKumaContent({ integrationIds, isEditMode, options, width }: UptimeKumaContentProps) { const t = useScopedI18n("widget.uptimeKuma"); - const [dashboardData] = clientApi.widget.uptimeKuma.getDashboard.useSuspenseQuery({ integrationIds }); + const { data: dashboardData } = clientApi.widget.uptimeKuma.getDashboard.useQuery( + { integrationIds }, + { staleTime: 30 * 1000 }, + ); const utils = clientApi.useUtils(); clientApi.widget.uptimeKuma.subscribeToDashboard.useSubscription( @@ -127,6 +131,8 @@ function UptimeKumaContent({ integrationIds, isEditMode, options, width }: Uptim }, ); + if (!dashboardData) return ; + const combined = dashboardData.reduce( (acc, item) => ({ totalMonitors: acc.totalMonitors + item.dashboard.totalMonitors,