diff --git a/src/form-engine.component.tsx b/src/form-engine.component.tsx index 7ad4def6..8bf4e576 100644 --- a/src/form-engine.component.tsx +++ b/src/form-engine.component.tsx @@ -32,6 +32,7 @@ interface FormEngineProps { onCancel?: () => void; handleClose?: () => void; handleConfirmQuestionDeletion?: (question: Readonly) => Promise; + handleEmptyFormSubmission?: () => Promise; markFormAsDirty?: (isDirty: boolean) => void; } @@ -47,6 +48,7 @@ const FormEngine = ({ onCancel, handleClose, handleConfirmQuestionDeletion, + handleEmptyFormSubmission, markFormAsDirty, }: FormEngineProps) => { const { t } = useTranslation(); @@ -127,6 +129,8 @@ const FormEngine = ({ provider={session?.currentProvider} visit={visit} handleConfirmQuestionDeletion={handleConfirmQuestionDeletion} + handleEmptyFormSubmission={handleEmptyFormSubmission} + handleDiscardForm={handleClose} isFormExpanded={isFormExpanded} formSubmissionProps={{ isSubmitting, diff --git a/src/provider/form-factory-helper.ts b/src/provider/form-factory-helper.ts index 7af8c60d..c6733b90 100644 --- a/src/provider/form-factory-helper.ts +++ b/src/provider/form-factory-helper.ts @@ -46,6 +46,24 @@ export function validateForm(context: FormContextProps) { return errors.length === 0; } +export function validateEmptyForm(context: FormContextProps){ + const { + formFields, + formFieldValidators, + patient, + sessionMode, + addInvalidField, + updateFormField, + methods: { getValues, trigger }, + } = context; + const values = getValues(); + return Object.values(values).every(value => + value === null || + value === undefined || + (Array.isArray(value) && value.length === 0) + ); +} + export async function processPostSubmissionActions( postSubmissionHandlers: PostSubmissionActionMeta[], submissionResults: OpenmrsResource[], diff --git a/src/provider/form-factory-provider.tsx b/src/provider/form-factory-provider.tsx index 019387f8..6aeb0ac9 100644 --- a/src/provider/form-factory-provider.tsx +++ b/src/provider/form-factory-provider.tsx @@ -11,7 +11,7 @@ import { } from '@openmrs/esm-framework'; import { type FormProcessorConstructor } from '../processors/form-processor'; import { type FormContextProps } from './form-provider'; -import { processPostSubmissionActions, validateForm } from './form-factory-helper'; +import { processPostSubmissionActions, validateForm, validateEmptyForm } from './form-factory-helper'; import { useTranslation } from 'react-i18next'; import { usePostSubmissionActions } from '../hooks/usePostSubmissionActions'; @@ -51,7 +51,9 @@ interface FormFactoryProviderProps { handleClose: () => void; }; hideFormCollapseToggle: () => void; + handleDiscardForm: () => void; handleConfirmQuestionDeletion?: (question: Readonly) => Promise; + handleEmptyFormSubmission?: () => Promise; setIsFormDirty: (isFormDirty: boolean) => void; } @@ -70,6 +72,8 @@ export const FormFactoryProvider: React.FC = ({ children, formSubmissionProps, hideFormCollapseToggle, + handleDiscardForm, + handleEmptyFormSubmission, handleConfirmQuestionDeletion, setIsFormDirty, }) => { @@ -96,14 +100,31 @@ export const FormFactoryProvider: React.FC = ({ }); useEffect(() => { - if (isSubmitting) { - // TODO: find a dynamic way of managing the form processing order - const forms = [rootForm.current, ...Object.values(subForms.current)]; - // validate all forms - const isValid = forms.every((formContext) => validateForm(formContext)); - if (isValid) { - Promise.all(forms.map((formContext) => formContext.processor.processSubmission(formContext, abortController))) - .then(async (results) => { + const handleFormSubmission = async () => { + if (isSubmitting) { + const forms = [rootForm.current, ...Object.values(subForms.current)]; + // Validate all forms + const isValid = forms.every((formContext) => validateForm(formContext)); + // Check if the form is empty + const isEmpty = forms.every((formContext) => validateEmptyForm(formContext)); + + if (isEmpty && isValid) { + if (handleEmptyFormSubmission && typeof handleEmptyFormSubmission === 'function') { + try { + await handleEmptyFormSubmission(); + handleDiscardForm() + return setIsSubmitting(false) + } catch (error) { + return setIsSubmitting(false); + } + } + } + + if (isValid) { + try { + const results = await Promise.all( + forms.map((formContext) => formContext.processor.processSubmission(formContext, abortController)) + ); formSubmissionProps.setIsSubmitting(false); if (sessionMode === 'edit') { showSnackbar({ @@ -129,8 +150,7 @@ export const FormFactoryProvider: React.FC = ({ } else { handleClose(); } - }) - .catch((errorObject: Error | ToastDescriptor) => { + } catch (errorObject) { setIsSubmitting(false); if (errorObject instanceof Error) { showToast({ @@ -142,11 +162,13 @@ export const FormFactoryProvider: React.FC = ({ } else { showToast(errorObject); } - }); - } else { - setIsSubmitting(false); + } + } else { + setIsSubmitting(false); + } } - } + }; + handleFormSubmission(); return () => { abortController.abort(); }; @@ -181,4 +203,4 @@ export const useFormFactory = () => { throw new Error('useFormFactoryContext must be used within a FormFactoryProvider'); } return context; -}; +}; \ No newline at end of file