Skip to content

Commit

Permalink
(feat) HIE-9: Add MPI workflows to OpenMRS frontend (#1313)
Browse files Browse the repository at this point in the history
* (feat) HIE-9: Add MPI workflows to OpenMRS frontend

* Fix tests

* Address comments

* Add unit test

* Fix failing test

* Minor fixes

* Add feature flag

* Move feature flag definition to routes

* Minor fix

* Move logic to patient search work space

* Fix lint issues post-rebase

---------

Co-authored-by: Dennis Kigen <[email protected]>
  • Loading branch information
reagan-meant and denniskigen authored Nov 14, 2024
1 parent fc5c77b commit e009969
Show file tree
Hide file tree
Showing 24 changed files with 587 additions and 52 deletions.
16 changes: 16 additions & 0 deletions packages/esm-patient-registration-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface RegistrationConfig {
month: number;
};
};
identifier: [{ identifierTypeSystem: string; identifierTypeUuid: string }];
phone: {
personAttributeUuid: string;
validation?: {
Expand Down Expand Up @@ -351,6 +352,21 @@ export const esmPatientRegistrationSchema = {
},
},
},
identifier: {
_type: Type.Array,
_elements: {
identifierTypeSystem: {
_type: Type.String,
_description: 'Identifier system from the fhir server',
},
identifierTypeUuid: {
_type: Type.String,
_default: null,
_description: 'Identifier type uuid of OpenMRS to map the identifier system',
},
},
_default: [],
},
phone: {
personAttributeUuid: {
_type: Type.UUID,
Expand Down
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
Expand Up @@ -24,20 +24,27 @@ import {
import {
getAddressFieldValuesFromFhirPatient,
getFormValuesFromFhirPatient,
getIdentifierFieldValuesFromFhirPatient,
getPatientUuidMapFromFhirPatient,
getPhonePersonAttributeValueFromFhirPatient,
latestFirstEncounter,
} from './patient-registration-utils';
import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource';
import dayjs from 'dayjs';

export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
const { freeTextFieldConceptUuid } = useConfig<RegistrationConfig>();
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
const { data: deathInfo, isLoading: isLoadingDeathInfo } = useInitialPersonDeathInfo(patientUuid);
const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid);
const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid);
const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(patientUuid);
import { useMpiPatient } from './mpi/mpi-patient.resource';

export function useInitialFormValues(patientUuid: string, isLocal: boolean): [FormValues, Dispatch<FormValues>] {
const { freeTextFieldConceptUuid, fieldConfigurations } = useConfig<RegistrationConfig>();
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(isLocal ? patientUuid : null);
const { isLoading: isLoadingMpiPatient, patient: mpiPatient } = useMpiPatient(!isLocal ? patientUuid : null);
const { data: deathInfo, isLoading: isLoadingDeathInfo } = useInitialPersonDeathInfo(isLocal ? patientUuid : null);
const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(isLocal ? patientUuid : null);
const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(
isLocal ? patientUuid : null,
);
const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(
isLocal ? patientUuid : null,
);
const { data: encounters } = useInitialEncounters(patientUuid, patientToEdit);

const [initialFormValues, setInitialFormValues] = useState<FormValues>({
Expand Down Expand Up @@ -81,12 +88,12 @@ 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,
});
} else if (!isLoadingPatientToEdit && patientUuid) {
} else if (!isLoadingPatientToEdit && patientUuid && isLocal) {
const registration = await getPatientRegistration(patientUuid);

if (!registration._patientRegistrationData.formValues) {
Expand All @@ -101,6 +108,32 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
})();
}, [isLoadingPatientToEdit, patientToEdit, patientUuid]);

useEffect(() => {
const fetchValues = async () => {
if (mpiPatient?.data?.identifier) {
const identifiers = await getIdentifierFieldValuesFromFhirPatient(
mpiPatient.data,
fieldConfigurations.identifier,
);

const values = {
...initialFormValues,
...getFormValuesFromFhirPatient(mpiPatient.data),
address: getAddressFieldValuesFromFhirPatient(mpiPatient.data),
identifiers,
attributes: getPhonePersonAttributeValueFromFhirPatient(
mpiPatient.data,
fieldConfigurations.phone.personAttributeUuid,
),
};

setInitialFormValues(values);
}
};

fetchValues();
}, [mpiPatient, isLoadingMpiPatient]);

// Set initial patient death info
useEffect(() => {
if (!isLoadingDeathInfo && deathInfo?.dead) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Yup from 'yup';
import camelCase from 'lodash-es/camelCase';
import { parseDate } from '@openmrs/esm-framework';
import { openmrsFetch, parseDate, restBaseUrl } from '@openmrs/esm-framework';
import {
type AddressValidationSchemaType,
type Encounter,
Expand Down Expand Up @@ -192,10 +192,48 @@ export function getPatientIdentifiersFromFhirPatient(patient: fhir.Patient): Arr
});
}

export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient) {
export async function getIdentifierFieldValuesFromFhirPatient(
patient: fhir.Patient,
identifierConfig,
): Promise<{ [identifierFieldName: string]: PatientIdentifierValue }> {
const identifiers: FormValues['identifiers'] = {};

for (const identifier of patient.identifier) {
for (const config of identifierConfig) {
const identifierConfig = config.identifierTypeSystem === identifier.system ? config : null;

if (identifierConfig) {
let identifierTypeName;

const url = `${restBaseUrl}/patientidentifiertype/${identifierConfig.identifierTypeUuid}`;
await openmrsFetch(url).then((response) => {
if (response.status == 200 && response.data) {
identifierTypeName = response.data.name;
}
});

identifiers[identifierTypeName] = {
identifierUuid: null,
preferred: false, // consider identifier.use === 'official' ?? by default autogen is preferred
initialValue: identifier.value,
identifierValue: identifier.value,
identifierTypeUuid: identifierConfig.identifierTypeUuid,
identifierName: identifierTypeName,
required: false,
selectedSource: null,
autoGeneration: false,
};
}
}
}

return identifiers;
}

export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient, phoneUuid) {
const result = {};
if (patient.telecom) {
result['phone'] = patient.telecom[0].value;
result[phoneUuid] = patient.telecom[0].value;
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ 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');
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] = useInitialFormValues(
uuidOfPatientToEdit || sourcePatientId,
!!uuidOfPatientToEdit,
);
const [initialAddressFieldValues] = useInitialAddressFieldValues(uuidOfPatientToEdit);
const [patientUuidMap] = usePatientUuidMap(uuidOfPatientToEdit);
const location = currentSession?.sessionLocation?.uuid;
Expand Down
Loading

0 comments on commit e009969

Please sign in to comment.