fix: harden widget error feedback

This commit is contained in:
Thomas Camlong committed 2026-08-02 17:34:52 +02:00
1 parent f0d8e97f8d
commit 5010ecf188
5 files changed
+29 -8

No files matched your search

@@ -45,6 +45,7 @@ import temperature from "./img/temperature.png";
### Interactions
- Toggle entity state by clicking on the entity (only when clickable enabled).
- If Homarr cannot refresh an entity, the last known state stays visible but toggling is disabled until the connection recovers.
<WidgetInteractionGuide />
+1
View File
@@ -2185,6 +2185,7 @@
}
},
"error": {
"loadFailed": "Unable to load entity",
"toggleFailed": "Unable to update entity"
}
},
+11 -5
View File
@@ -1,6 +1,6 @@
"use client";
import { Badge, Group, ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core";
import { Badge, Group, ScrollArea, SimpleGrid, Stack, Text, Tooltip } from "@mantine/core";
import { getQueryKey } from "@trpc/react-query";
import { clientApi } from "@homarr/api/client";
@@ -67,7 +67,7 @@ export default function AudioStatsWidget({
if (!currentlyPlaying) return null;
return (
<Group key={`${integrationId}:${session.sessionId}`} justify="space-between" wrap="nowrap" p="sm">
<Stack gap={0} style={{ minWidth: 0 }}>
<Stack gap={0} style={{ minWidth: 0, flex: 1 }}>
<Text size="sm" fw={600} truncate>
{currentlyPlaying.name}
</Text>
@@ -75,9 +75,15 @@ export default function AudioStatsWidget({
{currentlyPlaying.seasonName ?? currentlyPlaying.albumName ?? session.user.username}
</Text>
</Stack>
<Badge variant="light" style={{ flexShrink: 0 }}>
{session.sessionName}
</Badge>
<Tooltip
label={session.sessionName}
events={{ hover: true, focus: true, touch: true }}
position="left"
>
<Badge variant="light" maw="40%" style={{ flexShrink: 0 }} tabIndex={0}>
{session.sessionName}
</Badge>
</Tooltip>
</Group>
);
})}
@@ -50,7 +50,9 @@ export default function SmartHomeEntityStateWidget({
const apiUnit = entity?.attributes.unit_of_measurement;
const unit = options.entityUnit || (typeof apiUnit === "string" ? apiUnit : "");
const attribute = unit.length > 0 ? ` ${unit}` : "";
const isActionable = options.clickable && canInteract && entity !== undefined && !isEntityPending && !isPending;
const isActionable =
options.clickable && canInteract && entity !== undefined && !entityError && !isEntityPending && !isPending;
const queryErrorLabel = entityError ? t("widget.smartHome-entityState.error.loadFailed") : undefined;
const handleClick = useCallback(() => {
if (isEditMode) {
@@ -108,7 +110,7 @@ export default function SmartHomeEntityStateWidget({
onClick={handleClick}
disabled={!isActionable}
aria-label={`${displayName}: ${state}${attribute}`}
aria-description={error?.message}
aria-description={queryErrorLabel ?? (error ? t("widget.smartHome-entityState.error.toggleFailed") : undefined)}
w="100%"
h="100%"
styles={{
@@ -127,6 +129,11 @@ export default function SmartHomeEntityStateWidget({
<Text ta="center" c="dimmed" fw={500} size={isTiny ? "xs" : "sm"} lineClamp={2} maw="100%">
{displayName}
</Text>
{queryErrorLabel && (
<Text size="xs" c="orange" ta="center" lineClamp={1} maw="100%" aria-live="polite">
{queryErrorLabel}
</Text>
)}
{displayMode === "advanced" && entity && (
<>
<Group justify="center" gap={4}>
@@ -1,4 +1,4 @@
import { IconBinaryTree } from "@tabler/icons-react";
import { IconBinaryTree, IconServerOff } from "@tabler/icons-react";
import { getIntegrationKindsByCategory } from "@homarr/definitions";
@@ -22,4 +22,10 @@ export const { definition, componentLoader } = createWidgetDefinition("smartHome
}));
},
supportedIntegrations: getIntegrationKindsByCategory("smartHomeServer"),
errors: {
INTERNAL_SERVER_ERROR: {
icon: IconServerOff,
message: (t) => t("widget.smartHome-entityState.error.loadFailed"),
},
},
}).withDynamicImport(() => import("./component"));