@kenyaemr/esm-patient-registration-app 8.1.1-pre.129 → 8.1.2-pre.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +21 -23
- package/dist/108.js +1 -1
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/173.js +2 -0
- package/dist/{895.js.LICENSE.txt → 173.js.LICENSE.txt} +25 -0
- package/dist/173.js.map +1 -0
- package/dist/236.js +1 -0
- package/dist/240.js +1 -0
- package/dist/261.js +1 -0
- package/dist/271.js +1 -1
- package/dist/272.js +1 -0
- package/dist/319.js +1 -1
- package/dist/336.js +1 -0
- package/dist/371.js +1 -0
- package/dist/371.js.map +1 -0
- package/dist/378.js +1 -0
- package/dist/460.js +1 -1
- package/dist/501.js +1 -1
- package/dist/501.js.map +1 -1
- package/dist/539.js +1 -0
- package/dist/566.js +1 -0
- package/dist/574.js +1 -1
- package/dist/623.js +1 -0
- package/dist/623.js.map +1 -0
- package/dist/644.js +1 -1
- package/dist/652.js +1 -0
- package/dist/657.js +1 -0
- package/dist/657.js.map +1 -0
- package/dist/673.js +1 -0
- package/dist/705.js +1 -0
- package/dist/711.js +1 -0
- package/dist/727.js +1 -0
- package/dist/737.js +1 -0
- package/dist/744.js +1 -0
- package/dist/757.js +1 -1
- package/dist/759.js +1 -0
- package/dist/759.js.map +1 -0
- package/dist/76.js +1 -1
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/899.js +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +445 -93
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +25 -0
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package-lock.json +2052 -1699
- package/package.json +4 -4
- package/src/client-registry/hie-client-registry/dependants/dependants.component.tsx +46 -0
- package/src/client-registry/hie-client-registry/hie-client-registry.component.tsx +38 -8
- package/src/client-registry/hie-client-registry/hie-resource.ts +126 -21
- package/src/client-registry/hie-client-registry/hie-types.ts +102 -0
- package/src/client-registry/hie-client-registry/modal/confirm-hie.modal.tsx +78 -62
- package/src/client-registry/hie-client-registry/modal/confirm-hie.scss +55 -3
- package/src/client-registry/hie-client-registry/modal/hie-otp-verification-form.component.tsx +88 -0
- package/src/client-registry/hie-client-registry/modal/hie-patient-detail-preview.component.tsx +77 -0
- package/src/client-registry/hie-client-registry/patient-info/patient-info.component.tsx +17 -0
- package/src/config-schema.ts +30 -2
- package/src/patient-registration/field/address/address-search.component.tsx +5 -2
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
- package/src/patient-registration/field/dob/dob.component.tsx +1 -1
- package/src/patient-registration/field/gender/gender-field.component.tsx +6 -2
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +3 -3
- package/src/patient-registration/field/name/name-field.component.tsx +2 -2
- package/src/patient-registration/field/obs/obs-field.component.tsx +9 -5
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +22 -11
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +1 -1
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +12 -4
- package/src/patient-registration/form-manager.test.ts +4 -1
- package/src/patient-registration/form-manager.ts +0 -1
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +52 -62
- package/src/patient-registration/mpi/mpi-patient.resource.ts +21 -0
- package/src/patient-registration/patient-registration-hooks.ts +90 -25
- package/src/patient-registration/patient-registration-utils.test.ts +33 -0
- package/src/patient-registration/patient-registration-utils.ts +63 -13
- package/src/patient-registration/patient-registration.component.tsx +17 -2
- package/src/patient-registration/patient-registration.test.tsx +442 -56
- package/src/patient-registration/section/demographics/demographics-section.component.tsx +3 -3
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +1 -1
- package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +28 -28
- package/src/widgets/cancel-patient-edit.modal.tsx +2 -0
- package/src/widgets/cancel-patient-edit.scss +29 -0
- package/src/widgets/delete-identifier-confirmation.modal.tsx +2 -0
- package/src/widgets/delete-identifier-confirmation.scss +29 -0
- package/translations/am.json +1 -0
- package/translations/ar.json +6 -4
- package/translations/de.json +118 -0
- package/translations/en.json +17 -0
- package/translations/es.json +2 -0
- package/translations/fr.json +1 -0
- package/translations/he.json +1 -0
- package/translations/hi.json +118 -0
- package/translations/hi_IN.json +118 -0
- package/translations/id.json +118 -0
- package/translations/it.json +118 -0
- package/translations/km.json +1 -0
- package/translations/ne.json +118 -0
- package/translations/pt.json +118 -0
- package/translations/pt_BR.json +118 -0
- package/translations/qu.json +118 -0
- package/translations/si.json +118 -0
- package/translations/sw.json +118 -0
- package/translations/sw_KE.json +118 -0
- package/translations/tr.json +118 -0
- package/translations/tr_TR.json +118 -0
- package/translations/uk.json +118 -0
- package/translations/vi.json +118 -0
- package/translations/zh.json +3 -1
- package/translations/zh_CN.json +2 -0
- package/dist/250.js +0 -1
- package/dist/250.js.map +0 -1
- package/dist/66.js +0 -1
- package/dist/66.js.map +0 -1
- package/dist/662.js +0 -1
- package/dist/662.js.map +0 -1
- package/dist/753.js +0 -1
- package/dist/753.js.map +0 -1
- package/dist/895.js +0 -2
- package/dist/895.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
4
3
|
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { renderWithRouter } from 'tools';
|
|
5
5
|
import { Autosuggest } from './autosuggest.component';
|
|
6
6
|
|
|
7
7
|
const mockPersons = [
|
|
@@ -33,18 +33,16 @@ const mockHandleSuggestionSelected = jest.fn((field, value) => [field, value]);
|
|
|
33
33
|
|
|
34
34
|
describe('Autosuggest', () => {
|
|
35
35
|
it('renders a search box', () => {
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/>
|
|
47
|
-
</BrowserRouter>,
|
|
36
|
+
renderWithRouter(
|
|
37
|
+
<Autosuggest
|
|
38
|
+
getSearchResults={mockGetSearchResults}
|
|
39
|
+
getDisplayValue={(item) => item.display}
|
|
40
|
+
getFieldValue={(item) => item.uuid}
|
|
41
|
+
id="person"
|
|
42
|
+
labelText=""
|
|
43
|
+
onSuggestionSelected={mockHandleSuggestionSelected}
|
|
44
|
+
placeholder="Find Person"
|
|
45
|
+
/>,
|
|
48
46
|
);
|
|
49
47
|
|
|
50
48
|
expect(screen.getByRole('searchbox')).toBeInTheDocument();
|
|
@@ -54,18 +52,16 @@ describe('Autosuggest', () => {
|
|
|
54
52
|
it('renders matching search results in a list when the user types a query', async () => {
|
|
55
53
|
const user = userEvent.setup();
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/>
|
|
68
|
-
</BrowserRouter>,
|
|
55
|
+
renderWithRouter(
|
|
56
|
+
<Autosuggest
|
|
57
|
+
getSearchResults={mockGetSearchResults}
|
|
58
|
+
getDisplayValue={(item) => item.display}
|
|
59
|
+
getFieldValue={(item) => item.uuid}
|
|
60
|
+
id="person"
|
|
61
|
+
labelText=""
|
|
62
|
+
onSuggestionSelected={mockHandleSuggestionSelected}
|
|
63
|
+
placeholder="Find Person"
|
|
64
|
+
/>,
|
|
69
65
|
);
|
|
70
66
|
|
|
71
67
|
const searchbox = screen.getByRole('searchbox');
|
|
@@ -82,18 +78,16 @@ describe('Autosuggest', () => {
|
|
|
82
78
|
it('clears the list of suggestions when a suggestion is selected', async () => {
|
|
83
79
|
const user = userEvent.setup();
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/>
|
|
96
|
-
</BrowserRouter>,
|
|
81
|
+
renderWithRouter(
|
|
82
|
+
<Autosuggest
|
|
83
|
+
getSearchResults={mockGetSearchResults}
|
|
84
|
+
getDisplayValue={(item) => item.display}
|
|
85
|
+
getFieldValue={(item) => item.uuid}
|
|
86
|
+
id="person"
|
|
87
|
+
labelText=""
|
|
88
|
+
onSuggestionSelected={mockHandleSuggestionSelected}
|
|
89
|
+
placeholder="Find Person"
|
|
90
|
+
/>,
|
|
97
91
|
);
|
|
98
92
|
|
|
99
93
|
let list = screen.queryByRole('list');
|
|
@@ -117,18 +111,16 @@ describe('Autosuggest', () => {
|
|
|
117
111
|
it('changes suggestions when a search input is changed', async () => {
|
|
118
112
|
const user = userEvent.setup();
|
|
119
113
|
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
/>
|
|
131
|
-
</BrowserRouter>,
|
|
114
|
+
renderWithRouter(
|
|
115
|
+
<Autosuggest
|
|
116
|
+
getSearchResults={mockGetSearchResults}
|
|
117
|
+
getDisplayValue={(item) => item.display}
|
|
118
|
+
getFieldValue={(item) => item.uuid}
|
|
119
|
+
id="person"
|
|
120
|
+
labelText=""
|
|
121
|
+
onSuggestionSelected={mockHandleSuggestionSelected}
|
|
122
|
+
placeholder="Find Person"
|
|
123
|
+
/>,
|
|
132
124
|
);
|
|
133
125
|
|
|
134
126
|
let list = screen.queryByRole('list');
|
|
@@ -149,18 +141,16 @@ describe('Autosuggest', () => {
|
|
|
149
141
|
it('hides the list of suggestions when the user clicks outside of the component', async () => {
|
|
150
142
|
const user = userEvent.setup();
|
|
151
143
|
|
|
152
|
-
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
/>
|
|
163
|
-
</BrowserRouter>,
|
|
144
|
+
renderWithRouter(
|
|
145
|
+
<Autosuggest
|
|
146
|
+
getSearchResults={mockGetSearchResults}
|
|
147
|
+
getDisplayValue={(item) => item.display}
|
|
148
|
+
getFieldValue={(item) => item.uuid}
|
|
149
|
+
id="person"
|
|
150
|
+
labelText=""
|
|
151
|
+
onSuggestionSelected={mockHandleSuggestionSelected}
|
|
152
|
+
placeholder="Find Person"
|
|
153
|
+
/>,
|
|
164
154
|
);
|
|
165
155
|
|
|
166
156
|
const input = screen.getByRole('searchbox');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import useSWR from 'swr';
|
|
2
|
+
|
|
3
|
+
const fetcher = (url: string) => {
|
|
4
|
+
const headers = new Headers();
|
|
5
|
+
headers.append('Content-Type', 'application/json');
|
|
6
|
+
headers.append('Authorization', `Basic ${btoa('kemr:password')}`);
|
|
7
|
+
return fetch(url, { headers }).then((res) => res.json());
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function useMpiPatient(patientId: string) {
|
|
11
|
+
const url = `https://hiedhs.intellisoftkenya.com/fhir/Patient?_id=${patientId}`;
|
|
12
|
+
|
|
13
|
+
const { data: patient, error: error, isLoading: isLoading } = useSWR<{ data: fhir.Bundle }, Error>(url, fetcher);
|
|
14
|
+
const patientInfo = patient?.['entry']?.[0]?.resource;
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
isLoading,
|
|
18
|
+
patient: patientInfo,
|
|
19
|
+
error,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { type Dispatch, useEffect, useMemo, useState } from 'react';
|
|
1
2
|
import {
|
|
2
3
|
type FetchResponse,
|
|
4
|
+
type OpenmrsResource,
|
|
3
5
|
getSynchronizationItems,
|
|
4
6
|
openmrsFetch,
|
|
5
|
-
type OpenmrsResource,
|
|
6
7
|
restBaseUrl,
|
|
7
8
|
useConfig,
|
|
8
9
|
usePatient,
|
|
9
10
|
} from '@openmrs/esm-framework';
|
|
10
11
|
import last from 'lodash-es/last';
|
|
11
12
|
import camelCase from 'lodash-es/camelCase';
|
|
12
|
-
import
|
|
13
|
+
import dayjs from 'dayjs';
|
|
13
14
|
import useSWR from 'swr';
|
|
14
15
|
import { v4 } from 'uuid';
|
|
15
16
|
import { type RegistrationConfig } from '../config-schema';
|
|
@@ -31,15 +32,25 @@ import {
|
|
|
31
32
|
import {
|
|
32
33
|
getAddressFieldValuesFromFhirPatient,
|
|
33
34
|
getFormValuesFromFhirPatient,
|
|
35
|
+
getIdentifierFieldValuesFromFhirPatient,
|
|
34
36
|
getPatientUuidMapFromFhirPatient,
|
|
35
37
|
getPhonePersonAttributeValueFromFhirPatient,
|
|
36
38
|
latestFirstEncounter,
|
|
37
39
|
} from './patient-registration-utils';
|
|
38
40
|
import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource';
|
|
39
|
-
import
|
|
41
|
+
import { useMpiPatient } from './mpi/mpi-patient.resource';
|
|
42
|
+
|
|
43
|
+
interface DeathInfoResults {
|
|
44
|
+
uuid: string;
|
|
45
|
+
display: string;
|
|
46
|
+
causeOfDeath: OpenmrsResource | null;
|
|
47
|
+
dead: boolean;
|
|
48
|
+
deathDate: string;
|
|
49
|
+
causeOfDeathNonCoded: string | null;
|
|
50
|
+
}
|
|
40
51
|
|
|
41
|
-
export function
|
|
42
|
-
const { freeTextFieldConceptUuid } = useConfig<RegistrationConfig>()
|
|
52
|
+
export function useInitialFormValuesLocal(patientUuid: string): [FormValues, Dispatch<FormValues>] {
|
|
53
|
+
const { freeTextFieldConceptUuid, fieldConfigurations } = useConfig<RegistrationConfig>()
|
|
43
54
|
const { martialStatus, education, occupation, educationLoad } = useConcepts();
|
|
44
55
|
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
|
|
45
56
|
const { data: deathInfo, isLoading: isLoadingDeathInfo } = useInitialPersonDeathInfo(patientUuid);
|
|
@@ -89,7 +100,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
89
100
|
...initialFormValues,
|
|
90
101
|
...getFormValuesFromFhirPatient(patientToEdit),
|
|
91
102
|
address: getAddressFieldValuesFromFhirPatient(patientToEdit),
|
|
92
|
-
...getPhonePersonAttributeValueFromFhirPatient(patientToEdit),
|
|
103
|
+
...getPhonePersonAttributeValueFromFhirPatient(patientToEdit, fieldConfigurations.phone.personAttributeUuid),
|
|
93
104
|
birthdateEstimated: !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate),
|
|
94
105
|
yearsEstimated,
|
|
95
106
|
monthsEstimated,
|
|
@@ -107,7 +118,13 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
107
118
|
setInitialFormValues(registration._patientRegistrationData.formValues);
|
|
108
119
|
}
|
|
109
120
|
})();
|
|
110
|
-
}, [
|
|
121
|
+
}, [
|
|
122
|
+
initialFormValues,
|
|
123
|
+
isLoadingPatientToEdit,
|
|
124
|
+
patientToEdit,
|
|
125
|
+
patientUuid,
|
|
126
|
+
fieldConfigurations.phone.personAttributeUuid,
|
|
127
|
+
]);
|
|
111
128
|
|
|
112
129
|
// Set initial patient death info
|
|
113
130
|
useEffect(() => {
|
|
@@ -126,9 +143,9 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
126
143
|
nonCodedCauseOfDeath: deathInfo.causeOfDeathNonCoded,
|
|
127
144
|
}));
|
|
128
145
|
}
|
|
129
|
-
}, [isLoadingDeathInfo, deathInfo, setInitialFormValues]);
|
|
130
|
-
// Setting authentication token
|
|
146
|
+
}, [isLoadingDeathInfo, deathInfo, setInitialFormValues, freeTextFieldConceptUuid]);
|
|
131
147
|
|
|
148
|
+
// Setting authentication token
|
|
132
149
|
useEffect(() => {
|
|
133
150
|
if (!isLoadingToken && token) {
|
|
134
151
|
setInitialFormValues((initialFormValues) => ({ ...initialFormValues, token: String(token.access_token) }));
|
|
@@ -179,7 +196,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
179
196
|
if (!isLoadingObs) {
|
|
180
197
|
setInitialFormValues((initialFormValues) => ({ ...initialFormValues, obs: obs, observation: observations }));
|
|
181
198
|
}
|
|
182
|
-
}, [isLoadingObs]);
|
|
199
|
+
}, [isLoadingObs, obs, observations]);
|
|
183
200
|
|
|
184
201
|
// Set Initial encounter
|
|
185
202
|
|
|
@@ -190,11 +207,68 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
190
207
|
concepts: [...occupation, ...martialStatus, ...education],
|
|
191
208
|
}));
|
|
192
209
|
}
|
|
193
|
-
}, [educationLoad]);
|
|
210
|
+
}, [educationLoad, martialStatus, education, occupation]);
|
|
194
211
|
|
|
195
212
|
return [initialFormValues, setInitialFormValues];
|
|
196
213
|
}
|
|
197
214
|
|
|
215
|
+
export function useMpiInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
|
|
216
|
+
const { fieldConfigurations } = useConfig<RegistrationConfig>();
|
|
217
|
+
const { isLoading: isLoadingMpiPatient, patient: mpiPatient } = useMpiPatient(patientUuid);
|
|
218
|
+
|
|
219
|
+
const [initialMPIFormValues, setInitialMPIFormValues] = useState<FormValues>({
|
|
220
|
+
patientUuid: v4(),
|
|
221
|
+
givenName: '',
|
|
222
|
+
middleName: '',
|
|
223
|
+
familyName: '',
|
|
224
|
+
additionalGivenName: '',
|
|
225
|
+
additionalMiddleName: '',
|
|
226
|
+
additionalFamilyName: '',
|
|
227
|
+
addNameInLocalLanguage: false,
|
|
228
|
+
gender: '',
|
|
229
|
+
birthdate: null,
|
|
230
|
+
yearsEstimated: 0,
|
|
231
|
+
monthsEstimated: 0,
|
|
232
|
+
birthdateEstimated: false,
|
|
233
|
+
telephoneNumber: '',
|
|
234
|
+
isDead: false,
|
|
235
|
+
deathDate: undefined,
|
|
236
|
+
deathTime: undefined,
|
|
237
|
+
deathTimeFormat: 'AM',
|
|
238
|
+
deathCause: '',
|
|
239
|
+
nonCodedCauseOfDeath: '',
|
|
240
|
+
relationships: [],
|
|
241
|
+
identifiers: {},
|
|
242
|
+
address: {},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
useEffect(() => {
|
|
246
|
+
(async () => {
|
|
247
|
+
if (mpiPatient) {
|
|
248
|
+
// const identifiers = await getIdentifierFieldValuesFromFhirPatient(
|
|
249
|
+
// mpiPatient.data,
|
|
250
|
+
// fieldConfigurations.identifier,
|
|
251
|
+
// );
|
|
252
|
+
|
|
253
|
+
const values = {
|
|
254
|
+
...initialMPIFormValues,
|
|
255
|
+
...getFormValuesFromFhirPatient(mpiPatient),
|
|
256
|
+
address: getAddressFieldValuesFromFhirPatient(mpiPatient),
|
|
257
|
+
attributes: getPhonePersonAttributeValueFromFhirPatient(
|
|
258
|
+
mpiPatient,
|
|
259
|
+
fieldConfigurations.phone.personAttributeUuid,
|
|
260
|
+
),
|
|
261
|
+
};
|
|
262
|
+
setInitialMPIFormValues(values);
|
|
263
|
+
}
|
|
264
|
+
})();
|
|
265
|
+
|
|
266
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
267
|
+
}, [mpiPatient, isLoadingMpiPatient]);
|
|
268
|
+
|
|
269
|
+
return [initialMPIFormValues, setInitialMPIFormValues];
|
|
270
|
+
}
|
|
271
|
+
|
|
198
272
|
export function useInitialAddressFieldValues(patientUuid: string, fallback = {}): [object, Dispatch<object>] {
|
|
199
273
|
const { isLoading, patient } = usePatient(patientUuid);
|
|
200
274
|
const [initialAddressFieldValues, setInitialAddressFieldValues] = useState<object>(fallback);
|
|
@@ -211,7 +285,7 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {})
|
|
|
211
285
|
setInitialAddressFieldValues(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback);
|
|
212
286
|
}
|
|
213
287
|
})();
|
|
214
|
-
}, [isLoading, patient, patientUuid]);
|
|
288
|
+
}, [fallback, initialAddressFieldValues, isLoading, patient, patientUuid]);
|
|
215
289
|
|
|
216
290
|
return [initialAddressFieldValues, setInitialAddressFieldValues];
|
|
217
291
|
}
|
|
@@ -232,7 +306,7 @@ export function usePatientUuidMap(
|
|
|
232
306
|
setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback),
|
|
233
307
|
);
|
|
234
308
|
}
|
|
235
|
-
}, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
|
|
309
|
+
}, [fallback, isLoadingPatientToEdit, patientToEdit, patientUuid, patientUuidMap]);
|
|
236
310
|
|
|
237
311
|
useEffect(() => {
|
|
238
312
|
if (attributes) {
|
|
@@ -287,7 +361,7 @@ export function useInitialPatientIdentifiers(patientUuid: string): {
|
|
|
287
361
|
data: identifiers,
|
|
288
362
|
isLoading,
|
|
289
363
|
};
|
|
290
|
-
}, [data,
|
|
364
|
+
}, [data?.data?.results, isLoading]);
|
|
291
365
|
|
|
292
366
|
return result;
|
|
293
367
|
}
|
|
@@ -323,19 +397,10 @@ function useInitialPersonAttributes(personUuid: string) {
|
|
|
323
397
|
data: data?.data?.results,
|
|
324
398
|
isLoading,
|
|
325
399
|
};
|
|
326
|
-
}, [data,
|
|
400
|
+
}, [data?.data?.results, isLoading]);
|
|
327
401
|
return result;
|
|
328
402
|
}
|
|
329
403
|
|
|
330
|
-
interface DeathInfoResults {
|
|
331
|
-
uuid: string;
|
|
332
|
-
display: string;
|
|
333
|
-
causeOfDeath: OpenmrsResource | null;
|
|
334
|
-
dead: boolean;
|
|
335
|
-
deathDate: string;
|
|
336
|
-
causeOfDeathNonCoded: string | null;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
404
|
function useInitialPersonDeathInfo(personUuid: string) {
|
|
340
405
|
const { data, error, isLoading } = useSWR<FetchResponse<DeathInfoResults>, Error>(
|
|
341
406
|
!!personUuid
|
|
@@ -349,7 +414,7 @@ function useInitialPersonDeathInfo(personUuid: string) {
|
|
|
349
414
|
data: data?.data,
|
|
350
415
|
isLoading,
|
|
351
416
|
};
|
|
352
|
-
}, [data,
|
|
417
|
+
}, [data?.data, isLoading]);
|
|
353
418
|
return result;
|
|
354
419
|
}
|
|
355
420
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { filterOutUndefinedPatientIdentifiers } from './patient-registration-utils';
|
|
2
|
+
|
|
3
|
+
describe('filterOutUndefinedPatientIdentifiers', () => {
|
|
4
|
+
const getIdentifiers = (autoGeneration = true, manualEntryEnabled = false) => ({
|
|
5
|
+
OpenMRSId: {
|
|
6
|
+
autoGeneration: autoGeneration,
|
|
7
|
+
identifierName: 'OpenMRS ID',
|
|
8
|
+
identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
|
|
9
|
+
identifierValue: undefined,
|
|
10
|
+
initialValue: '100GEJ',
|
|
11
|
+
preferred: true,
|
|
12
|
+
required: true,
|
|
13
|
+
selectedSource: {
|
|
14
|
+
uuid: '01af8526-cea4-4175-aa90-340acb411771',
|
|
15
|
+
name: 'Generator for OpenMRS ID',
|
|
16
|
+
autoGenerationOption: {
|
|
17
|
+
manualEntryEnabled: manualEntryEnabled,
|
|
18
|
+
automaticGenerationEnabled: autoGeneration,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should fitler out undefined identifiers', () => {
|
|
25
|
+
const filteredIdentifiers = filterOutUndefinedPatientIdentifiers(getIdentifiers());
|
|
26
|
+
expect(filteredIdentifiers.OpenMRSId).not.toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should retain auto-generated identifiers with manual entry', () => {
|
|
30
|
+
const filteredIdentifiers = filterOutUndefinedPatientIdentifiers(getIdentifiers(true, true));
|
|
31
|
+
expect(filteredIdentifiers.OpenMRSId).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Yup from 'yup';
|
|
2
2
|
import camelCase from 'lodash-es/camelCase';
|
|
3
|
-
import { parseDate } from '@openmrs/esm-framework';
|
|
3
|
+
import { openmrsFetch, restBaseUrl, parseDate } from '@openmrs/esm-framework';
|
|
4
4
|
import {
|
|
5
5
|
type AddressValidationSchemaType,
|
|
6
6
|
type Encounter,
|
|
@@ -110,15 +110,14 @@ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
|
|
|
110
110
|
const result = {} as FormValues;
|
|
111
111
|
const patientName = patient.name[0];
|
|
112
112
|
const additionalPatientName = patient.name[1];
|
|
113
|
-
|
|
114
113
|
result.patientUuid = patient.id;
|
|
115
|
-
result.givenName = patientName?.given[0];
|
|
116
|
-
result.middleName = patientName?.given[1];
|
|
114
|
+
result.givenName = patientName?.given?.[0];
|
|
115
|
+
result.middleName = patientName?.given?.[1];
|
|
117
116
|
result.familyName = patientName?.family;
|
|
118
117
|
result.addNameInLocalLanguage = !!additionalPatientName ? true : undefined;
|
|
119
|
-
result.additionalGivenName = additionalPatientName?.given[0];
|
|
120
|
-
result.additionalMiddleName = additionalPatientName?.given[1];
|
|
121
|
-
result.additionalFamilyName = additionalPatientName?.family;
|
|
118
|
+
result.additionalGivenName = additionalPatientName?.given?.[0] ?? undefined;
|
|
119
|
+
result.additionalMiddleName = additionalPatientName?.given?.[1] ?? undefined;
|
|
120
|
+
result.additionalFamilyName = additionalPatientName?.family ?? undefined;
|
|
122
121
|
|
|
123
122
|
result.gender = patient.gender;
|
|
124
123
|
result.birthdate = patient.birthDate ? parseDate(patient.birthDate) : undefined;
|
|
@@ -192,18 +191,69 @@ export function getPatientIdentifiersFromFhirPatient(patient: fhir.Patient): Arr
|
|
|
192
191
|
});
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
export function
|
|
194
|
+
export async function getIdentifierFieldValuesFromFhirPatient(
|
|
195
|
+
patient: fhir.Patient,
|
|
196
|
+
identifierConfig,
|
|
197
|
+
): Promise<{ [identifierFieldName: string]: PatientIdentifierValue }> {
|
|
198
|
+
const identifiers: FormValues['identifiers'] = {};
|
|
199
|
+
const promises: Promise<void>[] = [];
|
|
200
|
+
|
|
201
|
+
for (const identifier of patient.identifier) {
|
|
202
|
+
for (const config of identifierConfig) {
|
|
203
|
+
if (config.fhirIdentifierSystem !== identifier.system) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const url = `${restBaseUrl}/patientidentifiertype/${config.openmrsIdentifierTypeUuid}`;
|
|
208
|
+
|
|
209
|
+
promises.push(
|
|
210
|
+
openmrsFetch(url)
|
|
211
|
+
.then((response) => {
|
|
212
|
+
if (!response.data?.name) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
identifiers[response.data.name] = {
|
|
216
|
+
identifierUuid: null,
|
|
217
|
+
preferred: false,
|
|
218
|
+
initialValue: identifier.value,
|
|
219
|
+
identifierValue: identifier.value,
|
|
220
|
+
identifierTypeUuid: config.identifierTypeUuid,
|
|
221
|
+
identifierName: response.data.name,
|
|
222
|
+
required: false,
|
|
223
|
+
selectedSource: null,
|
|
224
|
+
autoGeneration: false,
|
|
225
|
+
};
|
|
226
|
+
})
|
|
227
|
+
.catch((error) => {
|
|
228
|
+
console.error(`Error fetching identifier type for ${config.identifierTypeUuid}:`, error);
|
|
229
|
+
}),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
await Promise.all(promises);
|
|
234
|
+
return identifiers;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient, phoneUuid) {
|
|
196
238
|
const result = {};
|
|
197
|
-
|
|
198
|
-
|
|
239
|
+
|
|
240
|
+
if (patient.telecom && Array.isArray(patient.telecom)) {
|
|
241
|
+
const phoneEntry = patient.telecom.find((entry) => entry.system === 'phone');
|
|
242
|
+
if (phoneEntry) {
|
|
243
|
+
result[phoneUuid] = phoneEntry.value;
|
|
244
|
+
}
|
|
199
245
|
}
|
|
246
|
+
|
|
200
247
|
return result;
|
|
201
248
|
}
|
|
202
249
|
|
|
203
|
-
|
|
250
|
+
type IdentifierMap = { [identifierFieldName: string]: PatientIdentifierValue };
|
|
251
|
+
export const filterOutUndefinedPatientIdentifiers = (patientIdentifiers: IdentifierMap): IdentifierMap =>
|
|
204
252
|
Object.fromEntries(
|
|
205
|
-
Object.entries
|
|
206
|
-
([key, value]) =>
|
|
253
|
+
Object.entries(patientIdentifiers).filter(
|
|
254
|
+
([key, value]) =>
|
|
255
|
+
(value.autoGeneration && value.selectedSource.autoGenerationOption.manualEntryEnabled) ||
|
|
256
|
+
value.identifierValue !== undefined,
|
|
207
257
|
),
|
|
208
258
|
);
|
|
209
259
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
createErrorHandler,
|
|
10
10
|
interpolateUrl,
|
|
11
11
|
showSnackbar,
|
|
12
|
+
useAppContext,
|
|
12
13
|
useConfig,
|
|
13
14
|
usePatient,
|
|
14
15
|
usePatientPhoto,
|
|
@@ -20,7 +21,12 @@ import { PatientRegistrationContext } from './patient-registration-context';
|
|
|
20
21
|
import { type SavePatientForm, SavePatientTransactionManager } from './form-manager';
|
|
21
22
|
import { DummyDataInput } from './input/dummy-data/dummy-data-input.component';
|
|
22
23
|
import { cancelRegistration, filterOutUndefinedPatientIdentifiers, scrollIntoView } from './patient-registration-utils';
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
useInitialAddressFieldValues,
|
|
26
|
+
useMpiInitialFormValues,
|
|
27
|
+
useInitialFormValuesLocal,
|
|
28
|
+
usePatientUuidMap,
|
|
29
|
+
} from './patient-registration-hooks';
|
|
24
30
|
import { ResourcesContext } from '../offline.resources';
|
|
25
31
|
import { builtInSections, type RegistrationConfig, type SectionDefinition } from '../config-schema';
|
|
26
32
|
import { SectionWrapper } from './section/section-wrapper.component';
|
|
@@ -44,10 +50,12 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
44
50
|
const config = useConfig() as RegistrationConfig;
|
|
45
51
|
const [target, setTarget] = useState<undefined | string>();
|
|
46
52
|
const { patientUuid: uuidOfPatientToEdit } = useParams();
|
|
53
|
+
const sourcePatientId = new URLSearchParams(search).get('sourceRecord');
|
|
47
54
|
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(uuidOfPatientToEdit);
|
|
48
55
|
const { t } = useTranslation();
|
|
49
56
|
const [capturePhotoProps, setCapturePhotoProps] = useState<CapturePhotoProps | null>(null);
|
|
50
|
-
const [initialFormValues, setInitialFormValues] =
|
|
57
|
+
const [initialFormValues, setInitialFormValues] = useInitialFormValuesLocal(uuidOfPatientToEdit);
|
|
58
|
+
const [initialMPIFormValues, setInitialMPIFormValues] = useMpiInitialFormValues(sourcePatientId);
|
|
51
59
|
const [initialAddressFieldValues] = useInitialAddressFieldValues(uuidOfPatientToEdit);
|
|
52
60
|
const [patientUuidMap] = usePatientUuidMap(uuidOfPatientToEdit);
|
|
53
61
|
const location = currentSession?.sessionLocation?.uuid;
|
|
@@ -61,6 +69,13 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
61
69
|
inEditMode ? initialFormValues.identifiers['nationalUniquePatientIdentifier']?.identifierValue : false,
|
|
62
70
|
);
|
|
63
71
|
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (initialMPIFormValues) {
|
|
74
|
+
setInitialFormValues(initialMPIFormValues);
|
|
75
|
+
}
|
|
76
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
77
|
+
}, [initialMPIFormValues, setInitialMPIFormValues]);
|
|
78
|
+
|
|
64
79
|
useEffect(() => {
|
|
65
80
|
exportedInitialFormValuesForTesting = initialFormValues;
|
|
66
81
|
}, [initialFormValues]);
|