-
Notifications
You must be signed in to change notification settings - Fork 234
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
(feat) HIE-9: Add MPI workflows to OpenMRS frontend #1397
base: main
Are you sure you want to change the base?
Changes from 24 commits
f77c65c
f66ce2b
b11c671
686c252
70cbe50
cfeabb8
f571888
0a6610d
47ad201
5c7d78f
dae3d8d
0b18cee
a0691d9
2fa10df
75b09e4
5f2929b
9b4007f
10a800c
46b43d3
7db0c90
9573231
b25b124
70f3827
27b81f3
2487439
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { fhirBaseUrl, openmrsFetch } from '@openmrs/esm-framework'; | ||
import useSWR from 'swr'; | ||
|
||
export function useMpiPatient(patientId: string) { | ||
const url = `${fhirBaseUrl}/Patient/${patientId}/$cr`; | ||
|
||
const { | ||
data: patient, | ||
error: error, | ||
isLoading: isLoading, | ||
} = useSWR<{ data: fhir.Patient }, Error>(url, openmrsFetch); | ||
|
||
return { | ||
isLoading, | ||
patient, | ||
error: error, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,11 +25,13 @@ import { | |
import { | ||
getAddressFieldValuesFromFhirPatient, | ||
getFormValuesFromFhirPatient, | ||
getIdentifierFieldValuesFromFhirPatient, | ||
getPatientUuidMapFromFhirPatient, | ||
getPhonePersonAttributeValueFromFhirPatient, | ||
latestFirstEncounter, | ||
} from './patient-registration-utils'; | ||
import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource'; | ||
import { useMpiPatient } from './mpi/mpi-patient.resource'; | ||
|
||
interface DeathInfoResults { | ||
uuid: string; | ||
|
@@ -40,8 +42,8 @@ interface DeathInfoResults { | |
causeOfDeathNonCoded: string | null; | ||
} | ||
|
||
export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] { | ||
const { freeTextFieldConceptUuid } = useConfig<RegistrationConfig>(); | ||
export function useInitialFormValuesLocal(patientUuid: string): [FormValues, Dispatch<FormValues>] { | ||
const { freeTextFieldConceptUuid, fieldConfigurations } = useConfig<RegistrationConfig>(); | ||
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid); | ||
const { data: deathInfo, isLoading: isLoadingDeathInfo } = useInitialPersonDeathInfo(patientUuid); | ||
const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid); | ||
|
@@ -90,7 +92,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch | |
...initialFormValues, | ||
...getFormValuesFromFhirPatient(patientToEdit), | ||
address: getAddressFieldValuesFromFhirPatient(patientToEdit), | ||
...getPhonePersonAttributeValueFromFhirPatient(patientToEdit), | ||
...getPhonePersonAttributeValueFromFhirPatient(patientToEdit, fieldConfigurations.phone.personAttributeUuid), | ||
birthdateEstimated: !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate), | ||
yearsEstimated, | ||
monthsEstimated, | ||
|
@@ -108,7 +110,13 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch | |
setInitialFormValues(registration._patientRegistrationData.formValues); | ||
} | ||
})(); | ||
}, [initialFormValues, isLoadingPatientToEdit, patientToEdit, patientUuid]); | ||
}, [ | ||
initialFormValues, | ||
isLoadingPatientToEdit, | ||
patientToEdit, | ||
patientUuid, | ||
fieldConfigurations.phone.personAttributeUuid, | ||
]); | ||
|
||
// Set initial patient death info | ||
useEffect(() => { | ||
|
@@ -180,6 +188,64 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch | |
return [initialFormValues, setInitialFormValues]; | ||
} | ||
|
||
export function useMpiInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] { | ||
const { fieldConfigurations } = useConfig<RegistrationConfig>(); | ||
const { isLoading: isLoadingMpiPatient, patient: mpiPatient } = useMpiPatient(patientUuid); | ||
|
||
const [initialMPIFormValues, setInitialMPIFormValues] = useState<FormValues>({ | ||
patientUuid: v4(), | ||
givenName: '', | ||
middleName: '', | ||
familyName: '', | ||
additionalGivenName: '', | ||
additionalMiddleName: '', | ||
additionalFamilyName: '', | ||
addNameInLocalLanguage: false, | ||
gender: '', | ||
birthdate: null, | ||
yearsEstimated: 0, | ||
monthsEstimated: 0, | ||
birthdateEstimated: false, | ||
telephoneNumber: '', | ||
isDead: false, | ||
deathDate: undefined, | ||
deathTime: undefined, | ||
deathTimeFormat: 'AM', | ||
deathCause: '', | ||
nonCodedCauseOfDeath: '', | ||
relationships: [], | ||
identifiers: {}, | ||
address: {}, | ||
}); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
if (mpiPatient?.data?.identifier) { | ||
const identifiers = await getIdentifierFieldValuesFromFhirPatient( | ||
mpiPatient.data, | ||
fieldConfigurations.identifierMappings, | ||
); | ||
|
||
const values = { | ||
...initialMPIFormValues, | ||
...getFormValuesFromFhirPatient(mpiPatient.data), | ||
address: getAddressFieldValuesFromFhirPatient(mpiPatient.data), | ||
identifiers, | ||
attributes: getPhonePersonAttributeValueFromFhirPatient( | ||
mpiPatient.data, | ||
fieldConfigurations.phone.personAttributeUuid, | ||
), | ||
}; | ||
setInitialMPIFormValues(values); | ||
} | ||
})(); | ||
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [mpiPatient, isLoadingMpiPatient]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need |
||
|
||
return [initialMPIFormValues, setInitialMPIFormValues]; | ||
} | ||
|
||
export function useInitialAddressFieldValues(patientUuid: string, fallback = {}): [object, Dispatch<object>] { | ||
const { isLoading, patient } = usePatient(patientUuid); | ||
const [initialAddressFieldValues, setInitialAddressFieldValues] = useState<object>(fallback); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import { | |
createErrorHandler, | ||
interpolateUrl, | ||
showSnackbar, | ||
useAppContext, | ||
useConfig, | ||
usePatient, | ||
usePatientPhoto, | ||
|
@@ -19,7 +20,12 @@ import { PatientRegistrationContext } from './patient-registration-context'; | |
import { type SavePatientForm, SavePatientTransactionManager } from './form-manager'; | ||
import { DummyDataInput } from './input/dummy-data/dummy-data-input.component'; | ||
import { cancelRegistration, filterOutUndefinedPatientIdentifiers, scrollIntoView } from './patient-registration-utils'; | ||
import { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks'; | ||
import { | ||
useInitialAddressFieldValues, | ||
useMpiInitialFormValues, | ||
useInitialFormValuesLocal, | ||
usePatientUuidMap, | ||
} from './patient-registration-hooks'; | ||
import { ResourcesContext } from '../offline.resources'; | ||
import { builtInSections, type RegistrationConfig, type SectionDefinition } from '../config-schema'; | ||
import { SectionWrapper } from './section/section-wrapper.component'; | ||
|
@@ -39,10 +45,12 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa | |
const config = useConfig() as RegistrationConfig; | ||
const [target, setTarget] = useState<undefined | string>(); | ||
const { patientUuid: uuidOfPatientToEdit } = useParams(); | ||
const sourcePatientId = new URLSearchParams(search).get('sourceRecord'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this kind of data should be passed through the URL. It's a SPA, we can pass it through some kind of state variable (including the AppContext or something if necessary). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ibacher While we could use the AppContext API, wouldn’t a query parameter be a reasonable fit for passing a patient ID? Or is the concern about exposing identifiers, particularly those from external registries? If that’s not the case, creating a context for this might feel overkill. What do you think? |
||
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(uuidOfPatientToEdit); | ||
const { t } = useTranslation(); | ||
const [capturePhotoProps, setCapturePhotoProps] = useState<CapturePhotoProps | null>(null); | ||
const [initialFormValues, setInitialFormValues] = useInitialFormValues(uuidOfPatientToEdit); | ||
const [initialFormValues, setInitialFormValues] = useInitialFormValuesLocal(uuidOfPatientToEdit); | ||
const [initialMPIFormValues, setInitialMPIFormValues] = useMpiInitialFormValues(sourcePatientId); | ||
const [initialAddressFieldValues] = useInitialAddressFieldValues(uuidOfPatientToEdit); | ||
const [patientUuidMap] = usePatientUuidMap(uuidOfPatientToEdit); | ||
const location = currentSession?.sessionLocation?.uuid; | ||
|
@@ -53,6 +61,13 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa | |
const fieldDefinition = config?.fieldDefinitions?.filter((def) => def.type === 'address'); | ||
const validationSchema = getValidationSchema(config); | ||
|
||
useEffect(() => { | ||
if (initialMPIFormValues) { | ||
setInitialFormValues(initialMPIFormValues); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [initialMPIFormValues, setInitialMPIFormValues]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is |
||
|
||
useEffect(() => { | ||
exportedInitialFormValuesForTesting = initialFormValues; | ||
}, [initialFormValues]); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would providing all the dependencies cause unnecessary rerenders?