From e7394d0dc13abc90e762b2843c22be49f2ec2b5a Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Tue, 4 Nov 2025 08:57:47 -0600 Subject: [PATCH] Form validation tweaks (#20790) * ensure id field is expanded on form errors * only validate id field when name field has no errors * use ref instead * all numeric is an invalid name --- web/src/components/input/NameAndIdFields.tsx | 14 ++++++++++++-- web/src/utils/stringUtil.ts | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/web/src/components/input/NameAndIdFields.tsx b/web/src/components/input/NameAndIdFields.tsx index ad4ebcfcc..7b988edc7 100644 --- a/web/src/components/input/NameAndIdFields.tsx +++ b/web/src/components/input/NameAndIdFields.tsx @@ -8,7 +8,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { useFormContext } from "react-hook-form"; import { generateFixedHash, isValidId } from "@/utils/stringUtil"; import { useTranslation } from "react-i18next"; @@ -41,8 +41,9 @@ export default function NameAndIdFields({ placeholderId, }: NameAndIdFieldsProps) { const { t } = useTranslation(["common"]); - const { watch, setValue, trigger } = useFormContext(); + const { watch, setValue, trigger, formState } = useFormContext(); const [isIdVisible, setIsIdVisible] = useState(false); + const hasUserTypedRef = useRef(false); const defaultProcessId = (name: string) => { const normalized = name.replace(/\s+/g, "_").toLowerCase(); @@ -58,6 +59,7 @@ export default function NameAndIdFields({ useEffect(() => { const subscription = watch((value, { name }) => { if (name === nameField) { + hasUserTypedRef.current = true; const processedId = effectiveProcessId(value[nameField] || ""); setValue(idField, processedId as PathValue>); trigger(idField); @@ -66,6 +68,14 @@ export default function NameAndIdFields({ return () => subscription.unsubscribe(); }, [watch, setValue, trigger, nameField, idField, effectiveProcessId]); + // Auto-expand if there's an error on the ID field after user has typed + useEffect(() => { + const idError = formState.errors[idField]; + if (idError && hasUserTypedRef.current && !isIdVisible) { + setIsIdVisible(true); + } + }, [formState.errors, idField, isIdVisible]); + return ( <>