fix genai additional_concerns validation error with textarea array widget

The additional_concerns field is list[str] in the backend but was using the textarea widget which produces a string value, causing validation errors.
Created a TextareaArrayWidget that converts between array (one item per line) and textarea display, and switched additional_concerns to use it
This commit is contained in:
Josh Hawkins 2026-03-26 10:36:58 -05:00
parent 5f61079f81
commit b74dcf43b2
3 changed files with 53 additions and 1 deletions

View File

@ -29,7 +29,7 @@ const review: SectionConfigOverrides = {
}, },
genai: { genai: {
additional_concerns: { additional_concerns: {
"ui:widget": "textarea", "ui:widget": "textareaArray",
"ui:options": { "ui:options": {
size: "full", size: "full",
}, },

View File

@ -30,6 +30,7 @@ import { CameraPathWidget } from "./widgets/CameraPathWidget";
import { OptionalFieldWidget } from "./widgets/OptionalFieldWidget"; import { OptionalFieldWidget } from "./widgets/OptionalFieldWidget";
import { SemanticSearchModelWidget } from "./widgets/SemanticSearchModelWidget"; import { SemanticSearchModelWidget } from "./widgets/SemanticSearchModelWidget";
import { OnvifProfileWidget } from "./widgets/OnvifProfileWidget"; import { OnvifProfileWidget } from "./widgets/OnvifProfileWidget";
import { TextareaArrayWidget } from "./widgets/TextareaArrayWidget";
import { FieldTemplate } from "./templates/FieldTemplate"; import { FieldTemplate } from "./templates/FieldTemplate";
import { ObjectFieldTemplate } from "./templates/ObjectFieldTemplate"; import { ObjectFieldTemplate } from "./templates/ObjectFieldTemplate";
@ -81,6 +82,7 @@ export const frigateTheme: FrigateTheme = {
optionalField: OptionalFieldWidget, optionalField: OptionalFieldWidget,
semanticSearchModel: SemanticSearchModelWidget, semanticSearchModel: SemanticSearchModelWidget,
onvifProfile: OnvifProfileWidget, onvifProfile: OnvifProfileWidget,
textareaArray: TextareaArrayWidget,
}, },
templates: { templates: {
FieldTemplate: FieldTemplate as React.ComponentType<FieldTemplateProps>, FieldTemplate: FieldTemplate as React.ComponentType<FieldTemplateProps>,

View File

@ -0,0 +1,50 @@
// Textarea Array Widget - displays an array of strings as a multiline textarea
// Each line in the textarea corresponds to one item in the array.
import type { WidgetProps } from "@rjsf/utils";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { getSizedFieldClassName } from "../utils";
import { useCallback } from "react";
export function TextareaArrayWidget(props: WidgetProps) {
const { id, value, disabled, readonly, onChange, onBlur, onFocus, schema, options } =
props;
// Convert array to newline-separated text for display
let textValue = "";
if (Array.isArray(value) && value.length > 0) {
textValue = value.join("\n");
} else if (typeof value === "string") {
textValue = value;
}
const fieldClassName = getSizedFieldClassName(options, "md");
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
const text = e.target.value;
if (text === "") {
onChange([]);
return;
}
// Split by newlines and filter out empty lines
const items = text.split("\n").filter((line) => line.trim() !== "");
onChange(items);
},
[onChange],
);
return (
<Textarea
id={id}
className={cn("text-md", fieldClassName)}
value={textValue}
disabled={disabled || readonly}
rows={(options.rows as number) || 3}
onChange={handleChange}
onBlur={(e) => onBlur(id, e.target.value)}
onFocus={(e) => onFocus(id, e.target.value)}
aria-label={schema.title}
/>
);
}