diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index 1c69fe4b5..e075c7e0e 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -1433,8 +1433,7 @@ }, "reviewLabels": { "summary": "{{count}} labels selected", - "empty": "No labels available", - "allNonAlertDetections": "All non-alert activity will be included as detections." + "empty": "No labels available" }, "filters": { "objectFieldLabel": "{{field}} for {{label}}" @@ -1610,7 +1609,8 @@ "configMessages": { "review": { "recordDisabled": "Recording is disabled, review items will not be generated.", - "detectDisabled": "Object detection is disabled. Review items require detected objects to categorize alerts and detections." + "detectDisabled": "Object detection is disabled. Review items require detected objects to categorize alerts and detections.", + "allNonAlertDetections": "All non-alert activity will be included as detections." }, "audio": { "noAudioRole": "No streams have the audio role defined. You must enable the audio role for audio detection to function." diff --git a/web/src/components/config-form/section-configs/review.ts b/web/src/components/config-form/section-configs/review.ts index 8b8fdc5af..6f769179d 100644 --- a/web/src/components/config-form/section-configs/review.ts +++ b/web/src/components/config-form/section-configs/review.ts @@ -27,6 +27,21 @@ const review: SectionConfigOverrides = { }, }, ], + fieldMessages: [ + { + key: "detections-all-non-alert", + field: "detections.labels", + messageKey: "configMessages.review.allNonAlertDetections", + severity: "info", + position: "after", + condition: (ctx) => { + const labels = ( + ctx.formData?.detections as Record | undefined + )?.labels; + return !Array.isArray(labels) || labels.length === 0; + }, + }, + ], fieldDocs: { "alerts.labels": "/configuration/review/#alerts-and-detections", "detections.labels": "/configuration/review/#alerts-and-detections", @@ -59,8 +74,6 @@ const review: SectionConfigOverrides = { "ui:widget": "reviewLabels", "ui:options": { suppressMultiSchema: true, - emptySelectionHintKey: - "configForm.reviewLabels.allNonAlertDetections", }, }, required_zones: { diff --git a/web/src/components/config-form/sections/BaseSection.tsx b/web/src/components/config-form/sections/BaseSection.tsx index 3dbf1ee50..96bd3efa9 100644 --- a/web/src/components/config-form/sections/BaseSection.tsx +++ b/web/src/components/config-form/sections/BaseSection.tsx @@ -573,8 +573,16 @@ export function ConfigSection({ if (activeFieldMessages.length === 0) return sectionConfig.uiSchema; const merged = { ...(sectionConfig.uiSchema ?? {}) }; for (const msg of activeFieldMessages) { - const fieldKey = msg.field; - const existing = merged[fieldKey] as Record | undefined; + const segments = msg.field.split("."); + // Navigate to the nested uiSchema node, shallow-cloning along the way + let node = merged; + for (let i = 0; i < segments.length - 1; i++) { + const seg = segments[i]; + node[seg] = { ...(node[seg] as Record) }; + node = node[seg] as Record; + } + const leafKey = segments[segments.length - 1]; + const existing = node[leafKey] as Record | undefined; const existingMessages = ((existing?.["ui:messages"] as unknown[]) ?? []) as Array<{ key: string; @@ -582,7 +590,7 @@ export function ConfigSection({ severity: string; position?: string; }>; - merged[fieldKey] = { + node[leafKey] = { ...existing, "ui:messages": [ ...existingMessages, diff --git a/web/src/components/config-form/theme/widgets/SwitchesWidget.tsx b/web/src/components/config-form/theme/widgets/SwitchesWidget.tsx index a7351c8b7..46fb5345e 100644 --- a/web/src/components/config-form/theme/widgets/SwitchesWidget.tsx +++ b/web/src/components/config-form/theme/widgets/SwitchesWidget.tsx @@ -45,8 +45,6 @@ export type SwitchesWidgetOptions = { enableSearch?: boolean; /** Allow users to add custom entries not in the predefined list */ allowCustomEntries?: boolean; - /** i18n key for a hint shown when no entities are selected */ - emptySelectionHintKey?: string; }; function normalizeValue(value: unknown): string[] { @@ -131,11 +129,6 @@ export function SwitchesWidget(props: WidgetProps) { [props.options], ); - const emptySelectionHintKey = useMemo( - () => props.options?.emptySelectionHintKey as string | undefined, - [props.options], - ); - const selectedEntities = useMemo(() => normalizeValue(value), [value]); const [isOpen, setIsOpen] = useState(selectedEntities.length > 0); const [searchTerm, setSearchTerm] = useState(""); @@ -215,12 +208,6 @@ export function SwitchesWidget(props: WidgetProps) { - {emptySelectionHintKey && selectedEntities.length === 0 && t && ( -
- {t(emptySelectionHintKey, { ns: namespace })} -
- )} - {allEntities.length === 0 && !allowCustomEntries ? (
{emptyMessage}