Skip to content

Commit 035b3e9

Browse files
Add form validation with scroll-to-error functionality in AssistantEditor
Co-authored-by: wenxi <wenxi@onyx.app>
1 parent a5a516f commit 035b3e9

File tree

1 file changed

+53
-1
lines changed

1 file changed

+53
-1
lines changed

web/src/app/admin/assistants/AssistantEditor.tsx

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ export function AssistantEditor({
638638
values,
639639
setFieldValue,
640640
errors,
641+
submitForm,
642+
validateForm,
641643
...formikProps
642644
}: FormikProps<any>) => {
643645
function toggleToolInValues(toolId: number) {
@@ -648,6 +650,55 @@ export function AssistantEditor({
648650
setFieldValue("enabled_tools_map", updatedEnabledToolsMap);
649651
}
650652

653+
// Helper function to scroll to the first error field
654+
const scrollToFirstError = (errors: Record<string, any>) => {
655+
const errorFields = Object.keys(errors);
656+
if (errorFields.length === 0) return;
657+
658+
const firstErrorField = errorFields[0];
659+
// Try to find the field element by name attribute or aria-label
660+
const fieldElement =
661+
document.querySelector(`[name="${firstErrorField}"]`) ||
662+
document.querySelector(`[aria-label*="${firstErrorField}"]`) ||
663+
document.querySelector(`input[name="${firstErrorField}"]`) ||
664+
document.querySelector(`textarea[name="${firstErrorField}"]`) ||
665+
document.querySelector(`select[name="${firstErrorField}"]`) ||
666+
// Fallback to finding by field wrapper with error message
667+
document.querySelector(`.field-${firstErrorField}`) ||
668+
// Look for error message elements that might be near the field
669+
document.querySelector(`[class*="error"][class*="${firstErrorField}"]`);
670+
671+
if (fieldElement) {
672+
fieldElement.scrollIntoView({
673+
behavior: 'smooth',
674+
block: 'center',
675+
inline: 'nearest'
676+
});
677+
678+
// Focus the field if it's focusable
679+
if (fieldElement instanceof HTMLElement &&
680+
(fieldElement.tagName === 'INPUT' ||
681+
fieldElement.tagName === 'TEXTAREA' ||
682+
fieldElement.tagName === 'SELECT')) {
683+
setTimeout(() => fieldElement.focus(), 300);
684+
}
685+
}
686+
};
687+
688+
// Custom submit handler that scrolls to errors
689+
const handleFormSubmit = async () => {
690+
const validationErrors = await validateForm(values);
691+
692+
if (Object.keys(validationErrors).length > 0) {
693+
// There are validation errors, scroll to the first one
694+
scrollToFirstError(validationErrors);
695+
return;
696+
}
697+
698+
// No validation errors, proceed with normal submission
699+
submitForm();
700+
};
701+
651702
// model must support image input for image generation
652703
// to work
653704
const currentLLMSupportsImageOutput = modelSupportsImageInput(
@@ -1620,7 +1671,8 @@ export function AssistantEditor({
16201671
</div>
16211672
<div className="flex gap-x-2">
16221673
<Button
1623-
type="submit"
1674+
type="button"
1675+
onClick={handleFormSubmit}
16241676
disabled={isSubmitting || isRequestSuccessful}
16251677
>
16261678
{isUpdate ? "Update" : "Create"}

0 commit comments

Comments
 (0)