diff --git a/src/frontend/lib/types/Forms.tsx b/src/frontend/lib/types/Forms.tsx index 1bc3dc62f35f..42ba36acfdd3 100644 --- a/src/frontend/lib/types/Forms.tsx +++ b/src/frontend/lib/types/Forms.tsx @@ -180,6 +180,8 @@ export interface ApiFormProps { follow?: boolean; actions?: ApiFormAction[]; timeout?: number; + keepOpenOption?: boolean; + onKeepOpenChange?: (keepOpen: boolean) => void; } /** diff --git a/src/frontend/src/components/forms/ApiForm.tsx b/src/frontend/src/components/forms/ApiForm.tsx index 089ce4f36bf0..245e949d2870 100644 --- a/src/frontend/src/components/forms/ApiForm.tsx +++ b/src/frontend/src/components/forms/ApiForm.tsx @@ -7,6 +7,7 @@ import { LoadingOverlay, Paper, Stack, + Switch, Text } from '@mantine/core'; import { useId } from '@mantine/hooks'; @@ -131,7 +132,8 @@ export function OptionsApiForm({ for (const [k, v] of Object.entries(_props.fields)) { _props.fields[k] = constructField({ field: v, - definition: optionsQuery?.data?.[k] + definition: optionsQuery?.data?.[k], + field_name: k }); // If the user has specified initial data, use that value here @@ -169,6 +171,11 @@ export function ApiForm({ }>) { const api = useApi(); const queryClient = useQueryClient(); + const [keepOpen, setKeepOpen] = useState(false); + + useEffect(() => { + props.onKeepOpenChange?.(keepOpen); + }, [keepOpen]); // Accessor for the navigation function (which is used to redirect the user) let navigate: NavigateFunction | null = null; @@ -459,9 +466,14 @@ export function ApiForm({ props.onFormSuccess(response.data, form); } - if (props.follow && props.modelType && response.data?.pk) { + if ( + props.follow && + props.modelType && + response.data?.pk && + !keepOpen + ) { // If we want to automatically follow the returned data - if (!!navigate) { + if (!!navigate && !keepOpen) { navigate(getDetailUrl(props.modelType, response.data?.pk)); } } else if (props.table) { @@ -588,7 +600,6 @@ export function ApiForm({ ); } - return ( @@ -673,7 +684,19 @@ export function ApiForm({ {/* Footer with Action Buttons */} - + + + {props.keepOpenOption && ( + setKeepOpen(e.currentTarget.checked)} + /> + )} + {props.actions?.map((action, i) => ( - + ); @@ -712,7 +735,8 @@ export function CreateApiForm({ const createProps = useMemo( () => ({ ...props, - method: 'POST' + method: 'POST', + keepOpenOption: true }), [props] ); diff --git a/src/frontend/src/hooks/UseForm.tsx b/src/frontend/src/hooks/UseForm.tsx index 6213633e6b0a..026fbbeed755 100644 --- a/src/frontend/src/hooks/UseForm.tsx +++ b/src/frontend/src/hooks/UseForm.tsx @@ -24,9 +24,12 @@ export function useApiFormModal(props: ApiFormModalProps) { return props.modalId ?? id; }, [props.modalId, id]); + const [keepOpen, setKeepOpen] = useState(false); + const formProps = useMemo( () => ({ ...props, + onKeepOpenChange: setKeepOpen, actions: [ ...(props.actions || []), { @@ -38,7 +41,7 @@ export function useApiFormModal(props: ApiFormModalProps) { } ], onFormSuccess: (data, form) => { - if (props.checkClose?.(data, form) ?? true) { + if (!keepOpen && (props.checkClose?.(data, form) ?? true)) { modalClose.current(); } props.onFormSuccess?.(data, form); @@ -47,7 +50,7 @@ export function useApiFormModal(props: ApiFormModalProps) { props.onFormError?.(error, form); } }), - [props] + [props, keepOpen] ); const [isOpen, setIsOpen] = useState(false); @@ -94,7 +97,8 @@ export function useCreateApiFormModal(props: ApiFormModalProps) { props.successMessage === null ? null : (props.successMessage ?? t`Item Created`), - method: props.method ?? 'POST' + method: props.method ?? 'POST', + keepOpenOption: true }), [props] );