Skip to content
2 changes: 2 additions & 0 deletions src/frontend/lib/types/Forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ export interface ApiFormProps {
follow?: boolean;
actions?: ApiFormAction[];
timeout?: number;
keepOpenOption?: boolean;
onKeepOpenChange?: (keepOpen: boolean) => void;
}

/**
Expand Down
38 changes: 31 additions & 7 deletions src/frontend/src/components/forms/ApiForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LoadingOverlay,
Paper,
Stack,
Switch,
Text
} from '@mantine/core';
import { useId } from '@mantine/hooks';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -588,7 +600,6 @@ export function ApiForm({
</Paper>
);
}

return (
<Stack>
<Boundary label={`ApiForm-${id}`}>
Expand Down Expand Up @@ -673,7 +684,19 @@ export function ApiForm({

{/* Footer with Action Buttons */}
<Divider />
<div>
<Group justify='space-between'>
<Group justify='left'>
{props.keepOpenOption && (
<Switch
checked={keepOpen}
radius='lg'
size='sm'
label='Keep form open'
description='Keep form open after submitting'
onChange={(e) => setKeepOpen(e.currentTarget.checked)}
/>
)}
</Group>
<Group justify='right'>
{props.actions?.map((action, i) => (
<Button
Expand All @@ -696,7 +719,7 @@ export function ApiForm({
{props.submitText ?? t`Submit`}
</Button>
</Group>
</div>
</Group>
</Boundary>
</Stack>
);
Expand All @@ -712,7 +735,8 @@ export function CreateApiForm({
const createProps = useMemo<ApiFormProps>(
() => ({
...props,
method: 'POST'
method: 'POST',
keepOpenOption: true
}),
[props]
);
Expand Down
10 changes: 7 additions & 3 deletions src/frontend/src/hooks/UseForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ export function useApiFormModal(props: ApiFormModalProps) {
return props.modalId ?? id;
}, [props.modalId, id]);

const [keepOpen, setKeepOpen] = useState<boolean>(false);

const formProps = useMemo<ApiFormModalProps>(
() => ({
...props,
onKeepOpenChange: setKeepOpen,
actions: [
...(props.actions || []),
{
Expand All @@ -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);
Expand All @@ -47,7 +50,7 @@ export function useApiFormModal(props: ApiFormModalProps) {
props.onFormError?.(error, form);
}
}),
[props]
[props, keepOpen]
);

const [isOpen, setIsOpen] = useState<boolean>(false);
Expand Down Expand Up @@ -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]
);
Expand Down
Loading