Skip to content

(feat)O3-2971: Adding conformation modal for an empty form. #464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/form-engine.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface FormEngineProps {
onCancel?: () => void;
handleClose?: () => void;
handleConfirmQuestionDeletion?: (question: Readonly<FormField>) => Promise<void>;
handleEmptyFormSubmission?: () => Promise<void>;
markFormAsDirty?: (isDirty: boolean) => void;
}

Expand All @@ -47,6 +48,7 @@ const FormEngine = ({
onCancel,
handleClose,
handleConfirmQuestionDeletion,
handleEmptyFormSubmission,
markFormAsDirty,
}: FormEngineProps) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -127,13 +129,13 @@ const FormEngine = ({
provider={session?.currentProvider}
visit={visit}
handleConfirmQuestionDeletion={handleConfirmQuestionDeletion}
handleEmptyFormSubmission={handleEmptyFormSubmission}
isFormExpanded={isFormExpanded}
formSubmissionProps={{
isSubmitting,
setIsSubmitting,
onSubmit,
onError: () => {},
handleClose: () => {},
onSubmit,
handleClose: handleClose,
}}
hideFormCollapseToggle={hideFormCollapseToggle}
setIsFormDirty={setIsFormDirty}>
Expand Down
18 changes: 18 additions & 0 deletions src/provider/form-factory-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[],
Expand Down
55 changes: 37 additions & 18 deletions src/provider/form-factory-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -47,11 +47,11 @@ interface FormFactoryProviderProps {
isSubmitting: boolean;
setIsSubmitting: (isSubmitting: boolean) => void;
onSubmit: (data: any) => void;
onError: (error: any) => void;
handleClose: () => void;
};
hideFormCollapseToggle: () => void;
handleConfirmQuestionDeletion?: (question: Readonly<FormField>) => Promise<void>;
handleEmptyFormSubmission?: () => Promise<void>;
setIsFormDirty: (isFormDirty: boolean) => void;
}

Expand All @@ -70,14 +70,15 @@ export const FormFactoryProvider: React.FC<FormFactoryProviderProps> = ({
children,
formSubmissionProps,
hideFormCollapseToggle,
handleEmptyFormSubmission,
handleConfirmQuestionDeletion,
setIsFormDirty,
}) => {
const { t } = useTranslation();
const rootForm = useRef<FormContextProps>();
const subForms = useRef<Record<string, FormContextProps>>({});
const layoutType = useLayoutType();
const { isSubmitting, setIsSubmitting, onSubmit, onError, handleClose } = formSubmissionProps;
const { isSubmitting, setIsSubmitting, onSubmit, handleClose } = formSubmissionProps;
const postSubmissionHandlers = usePostSubmissionActions(formJson.postSubmissionActions);

const abortController = new AbortController();
Expand All @@ -96,14 +97,31 @@ export const FormFactoryProvider: React.FC<FormFactoryProviderProps> = ({
});

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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to evaluate isEmpty if the form is invalid.


if (isEmpty && isValid) {
if (handleEmptyFormSubmission && typeof handleEmptyFormSubmission === 'function') {
try {
await handleEmptyFormSubmission();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should basically launch a modal prompting the user. Based on the user's decision, we should either save an empty encounter by submitting the form or close the form. Here is an example.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the current scenario the user is not allowed to submit empty forms, thus there is no saving the form.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair! In either case, we should expect handleEmptyFormSubmission to promise some kind of boolean value; true we discard the form otherwise we continue editing. If the promise is rejected -- continue editing the form.

handleClose()
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({
Expand All @@ -129,8 +147,7 @@ export const FormFactoryProvider: React.FC<FormFactoryProviderProps> = ({
} else {
handleClose();
}
})
.catch((errorObject: Error | ToastDescriptor) => {
} catch (errorObject) {
setIsSubmitting(false);
if (errorObject instanceof Error) {
showToast({
Expand All @@ -142,11 +159,13 @@ export const FormFactoryProvider: React.FC<FormFactoryProviderProps> = ({
} else {
showToast(errorObject);
}
});
} else {
setIsSubmitting(false);
}
} else {
setIsSubmitting(false);
}
}
}
};
handleFormSubmission();
return () => {
abortController.abort();
};
Expand Down Expand Up @@ -181,4 +200,4 @@ export const useFormFactory = () => {
throw new Error('useFormFactoryContext must be used within a FormFactoryProvider');
}
return context;
};
};