diff --git a/docs/configs/services.md b/docs/configs/services.md index 0a867dd7a..29c135626 100644 --- a/docs/configs/services.md +++ b/docs/configs/services.md @@ -120,7 +120,7 @@ Each widget can optionally provide a list of which fields should be visible via ### Block Highlighting -Widgets can tint their metric blocks automatically based on rules defined alongside the service. Attach a `highlight` section to the widget configuration and map each block (use the translation key from the widget, or the explicit `field` prop if it is present) to one or more numeric or string rules. The first matching rule wins and its level controls the colours. +Widgets can tint their metric blocks automatically based on rules defined alongside the service. Attach a `highlight` section to the widget configuration and map each block to one or more numeric or string rules using the field key (for example, `queued`, `lan_users`). ```yaml - Sonarr: @@ -131,7 +131,7 @@ Widgets can tint their metric blocks automatically based on rules defined alongs url: http://sonarr.host.or.ip key: ${SONARR_API_KEY} highlight: - sonarr.queued: + queued: numeric: - level: danger when: gte @@ -142,7 +142,7 @@ Widgets can tint their metric blocks automatically based on rules defined alongs - level: good when: eq value: 0 - sonarr.status: + status: string: - level: danger when: regex diff --git a/src/components/services/widget/container.jsx b/src/components/services/widget/container.jsx index 6be5cdb7d..1496c2b3a 100644 --- a/src/components/services/widget/container.jsx +++ b/src/components/services/widget/container.jsx @@ -14,8 +14,8 @@ export default function Container({ error = false, children, service }) { const { settings } = useContext(SettingsContext); const highlightConfig = useMemo( - () => buildHighlightConfig(settings?.blockHighlights, service?.widget?.highlight), - [settings?.blockHighlights, service?.widget?.highlight], + () => buildHighlightConfig(settings?.blockHighlights, service?.widget?.highlight, service?.widget?.type), + [settings?.blockHighlights, service?.widget?.highlight, service?.widget?.type], ); if (error) { diff --git a/src/utils/highlights.js b/src/utils/highlights.js index adc4ba756..c20ef945c 100644 --- a/src/utils/highlights.js +++ b/src/utils/highlights.js @@ -4,7 +4,23 @@ const DEFAULT_LEVEL_CLASSES = { danger: "bg-rose-700/45 text-rose-200 dark:bg-rose-950/70 dark:text-rose-400", }; -export const buildHighlightConfig = (globalConfig, widgetConfig) => { +const normalizeFieldKeys = (fields, widgetType) => { + if (!fields || typeof fields !== "object") return {}; + + return Object.entries(fields).reduce((acc, [key, value]) => { + if (value === null || value === undefined) return acc; + if (typeof key !== "string") return acc; + const trimmedKey = key.trim(); + if (trimmedKey === "") return acc; + + const targetKey = widgetType ? `${widgetType}.${trimmedKey}` : trimmedKey; + acc[targetKey] = value; + + return acc; + }, {}); +}; + +export const buildHighlightConfig = (globalConfig, widgetConfig, widgetType) => { const levels = { ...DEFAULT_LEVEL_CLASSES, ...(globalConfig?.levels || {}), @@ -12,19 +28,14 @@ export const buildHighlightConfig = (globalConfig, widgetConfig) => { }; const { levels: _levels, ...fields } = widgetConfig || {}; - - Object.keys(fields).forEach((key) => { - if (fields[key] === null || fields[key] === undefined) { - delete fields[key]; - } - }); + const normalizedFields = normalizeFieldKeys(fields, widgetType); const hasLevels = Object.values(levels).some(Boolean); - const hasFields = Object.keys(fields).length > 0; + const hasFields = Object.keys(normalizedFields).length > 0; if (!hasLevels && !hasFields) return null; - return { levels, fields }; + return { levels, fields: normalizedFields }; }; const NUMERIC_OPERATORS = {