import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { Button, InlineLoading, Link } from '@carbon/react'; import { XAxis } from '@carbon/react/icons'; import { useLocation, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Form, Formik, type FormikHelpers, type FormikErrors } from 'formik'; import { createErrorHandler, interpolateUrl, showSnackbar, useConfig, usePatient, usePatientPhoto, } from '@openmrs/esm-framework'; import { builtInSections, type RegistrationConfig, type SectionDefinition } from '../config-schema'; import { cancelRegistration, filterOutUndefinedPatientIdentifiers, scrollIntoView } from './patient-registration-utils'; import { getValidationSchema } from './validation/patient-registration-validation'; import { DummyDataInput } from './input/dummy-data/dummy-data-input.component'; import { PatientRegistrationContextProvider } from './patient-registration-context'; import { useResourcesContext } from '../resources-context'; import { SectionWrapper } from './section/section-wrapper.component'; import { type CapturePhotoProps, type FormValues } from './patient-registration.types'; import { type SavePatientForm, SavePatientTransactionManager } from './form-manager'; import { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks'; import BeforeSavePrompt from './before-save-prompt.component'; import styles from './patient-registration.scss'; let exportedInitialFormValuesForTesting = {} as FormValues; export interface PatientRegistrationProps { savePatientForm: SavePatientForm; isOffline: boolean; } export const PatientRegistration: React.FC = ({ savePatientForm, isOffline }) => { const { t } = useTranslation(); const { currentSession, identifierTypes } = useResourcesContext(); const { patientUuid: uuidOfPatientToEdit } = useParams(); const { search } = useLocation(); const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(uuidOfPatientToEdit); const config = useConfig(); const [initialFormValues, setInitialFormValues] = useInitialFormValues( isLoadingPatientToEdit, patientToEdit, uuidOfPatientToEdit, ); const [initialAddressFieldValues] = useInitialAddressFieldValues( {}, isLoadingPatientToEdit, patientToEdit, uuidOfPatientToEdit, ); const [patientUuidMap] = usePatientUuidMap({}, isLoadingPatientToEdit, patientToEdit, uuidOfPatientToEdit); const [target, setTarget] = useState(); const [capturePhotoProps, setCapturePhotoProps] = useState(null); const location = currentSession?.sessionLocation?.uuid; const inEditMode = isLoadingPatientToEdit ? undefined : !!(uuidOfPatientToEdit && patientToEdit); const showDummyDataInput = useMemo( () => localStorage.getItem('openmrs:devtools') === 'true' && !inEditMode, [inEditMode], ); const { data: photo } = usePatientPhoto(patientToEdit?.id); const savePatientTransactionManager = useRef(new SavePatientTransactionManager()); const validationSchema = getValidationSchema(config, t); useEffect(() => { exportedInitialFormValuesForTesting = initialFormValues; }, [initialFormValues]); const sections: Array = useMemo(() => { return config.sections .map( (sectionName) => config.sectionDefinitions.filter((s) => s.id === sectionName)[0] ?? builtInSections.filter((s) => s.id === sectionName)[0], ) .filter((s) => s); }, [config.sections, config.sectionDefinitions]); const onFormSubmit = async (values: FormValues, helpers: FormikHelpers) => { const abortController = new AbortController(); helpers.setSubmitting(true); const updatedFormValues = { ...values, identifiers: filterOutUndefinedPatientIdentifiers(values.identifiers) }; try { await savePatientForm( !inEditMode, updatedFormValues, patientUuidMap, initialAddressFieldValues, capturePhotoProps, location, initialFormValues['identifiers'], currentSession, config, savePatientTransactionManager.current, abortController, ); showSnackbar({ subtitle: inEditMode ? t('updatePatientSuccessSnackbarSubtitle', "The patient's information has been successfully updated") : t( 'registerPatientSuccessSnackbarSubtitle', 'The patient can now be found by searching for them using their name or ID number', ), title: inEditMode ? t('updatePatientSuccessSnackbarTitle', 'Patient Details Updated') : t('registerPatientSuccessSnackbarTitle', 'New Patient Created'), kind: 'success', isLowContrast: true, }); const afterUrl = new URLSearchParams(search).get('afterUrl'); const redirectUrl = interpolateUrl(afterUrl || config.links.submitButton, { patientUuid: values.patientUuid }); setTarget(redirectUrl); } catch (error) { if (error.responseBody?.error?.globalErrors) { error.responseBody.error.globalErrors.forEach((error) => { showSnackbar({ title: inEditMode ? t('updatePatientErrorSnackbarTitle', 'Patient Details Update Failed') : t('registrationErrorSnackbarTitle', 'Patient Registration Failed'), subtitle: error.message, kind: 'error', }); }); } else if (error.responseBody?.error?.message) { showSnackbar({ title: inEditMode ? t('updatePatientErrorSnackbarTitle', 'Patient Details Update Failed') : t('registrationErrorSnackbarTitle', 'Patient Registration Failed'), subtitle: error.responseBody.error.message, kind: 'error', }); } else { createErrorHandler()(error); } helpers.setSubmitting(false); } }; const getDescription = (errors: FormikErrors): JSX.Element => { return (
    {Object.keys(errors).map((error, index) => { return
  • {t(`${error}LabelText`, error)}
  • ; })}
); }; const displayErrors = (errors: FormikErrors): void => { if (errors && typeof errors === 'object' && !!Object.keys(errors).length) { showSnackbar({ isLowContrast: true, kind: 'warning', title: t('fieldsWithErrors', 'The following fields have errors:'), subtitle: <>{getDescription(errors)}, }); } }; const createContextValue = useCallback( (formikProps) => ({ identifierTypes, validationSchema, values: formikProps.values, inEditMode, setFieldValue: formikProps.setFieldValue, setFieldTouched: formikProps.setFieldTouched, setCapturePhotoProps, currentPhoto: photo?.imageSrc, isOffline, initialFormValues: formikProps.initialValues, setInitialFormValues, }), [ identifierTypes, validationSchema, inEditMode, setCapturePhotoProps, photo?.imageSrc, isOffline, setInitialFormValues, ], ); return ( {(props) => (
0} redirect={target} />
{/* Navigation Sidebar */}

{inEditMode ? t('editPatientDetails', 'Edit patient details') : t('createNewPatient', 'Create new patient')}

{showDummyDataInput && }

{t('jumpTo', 'Jump to')}

{sections.map((section) => (
scrollIntoView(section.id)}> {t(`${section.id}Section`, section.name)}
))}
{/* Registration Form */}
{sections.map((section, index) => ( ))}
)}
); }; /** * @internal * Just exported for testing */ export { exportedInitialFormValuesForTesting as initialFormValues };