// ConfigForm - Main RJSF form wrapper component import Form from "@rjsf/core"; import validator from "@rjsf/validator-ajv8"; import type { RJSFSchema, UiSchema } from "@rjsf/utils"; import type { IChangeEvent } from "@rjsf/core"; import { frigateTheme } from "./theme"; import { transformSchema } from "@/lib/config-schema"; import { createErrorTransformer } from "@/lib/config-schema/errorMessages"; import { useMemo, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { cn } from "@/lib/utils"; export interface ConfigFormProps { /** JSON Schema for the form */ schema: RJSFSchema; /** Current form data */ formData?: unknown; /** Called when form data changes */ onChange?: (data: unknown) => void; /** Called when form is submitted */ onSubmit?: (data: unknown) => void; /** Called when form has errors on submit */ onError?: (errors: unknown[]) => void; /** Additional uiSchema overrides */ uiSchema?: UiSchema; /** Field ordering */ fieldOrder?: string[]; /** Field groups for layout */ fieldGroups?: Record; /** Fields to hide */ hiddenFields?: string[]; /** Fields marked as advanced (collapsed by default) */ advancedFields?: string[]; /** Whether form is disabled */ disabled?: boolean; /** Whether form is read-only */ readonly?: boolean; /** Whether to show submit button */ showSubmit?: boolean; /** Custom class name */ className?: string; /** Live validation mode */ liveValidate?: boolean; /** Form context passed to all widgets */ formContext?: Record; /** i18n namespace for field labels */ i18nNamespace?: string; } export function ConfigForm({ schema, formData, onChange, onSubmit, onError, uiSchema: customUiSchema, fieldOrder, fieldGroups, hiddenFields, advancedFields, disabled = false, readonly = false, showSubmit = false, className, liveValidate = true, formContext, i18nNamespace, }: ConfigFormProps) { const { t, i18n } = useTranslation([ i18nNamespace || "common", "views/settings", "config/validation", ]); // Determine which fields to hide based on advanced toggle const effectiveHiddenFields = useMemo(() => { return hiddenFields; }, [hiddenFields]); // Transform schema and generate uiSchema const { schema: transformedSchema, uiSchema: generatedUiSchema } = useMemo( () => transformSchema(schema, { fieldOrder, hiddenFields: effectiveHiddenFields, advancedFields: advancedFields, i18nNamespace, }), [schema, fieldOrder, effectiveHiddenFields, advancedFields, i18nNamespace], ); // Merge generated uiSchema with custom overrides const finalUiSchema = useMemo( () => ({ ...generatedUiSchema, "ui:groups": fieldGroups, ...customUiSchema, "ui:submitButtonOptions": showSubmit ? { norender: false } : { norender: true }, }), [generatedUiSchema, customUiSchema, showSubmit, fieldGroups], ); // Create error transformer for user-friendly error messages const errorTransformer = useMemo(() => createErrorTransformer(i18n), [i18n]); const handleChange = useCallback( (e: IChangeEvent) => { onChange?.(e.formData); }, [onChange], ); const handleSubmit = useCallback( (e: IChangeEvent) => { onSubmit?.(e.formData); }, [onSubmit], ); // Extended form context with i18n info const extendedFormContext = useMemo( () => ({ ...formContext, i18nNamespace, t, }), [formContext, i18nNamespace, t], ); return (
); } export default ConfigForm;