@kenyaemr/esm-patient-registration-app 4.4.0 → 4.4.2
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/dist/144.js +1 -1
- package/dist/144.js.map +1 -1
- package/dist/207.js +1 -1
- package/dist/207.js.map +1 -1
- package/dist/317.js +1 -1
- package/dist/317.js.map +1 -1
- package/dist/330.js +1 -1
- package/dist/330.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/59.js +1 -1
- package/dist/59.js.map +1 -1
- package/dist/591.js +1 -1
- package/dist/591.js.map +1 -1
- package/dist/62.js +1 -1
- package/dist/62.js.map +1 -1
- package/dist/635.js +1 -1
- package/dist/635.js.map +1 -1
- package/dist/68.js +1 -1
- package/dist/68.js.map +1 -1
- package/dist/735.js +1 -1
- package/dist/735.js.map +1 -1
- package/dist/757.js +1 -1
- package/dist/784.js +1 -1
- package/dist/784.js.map +1 -1
- package/dist/805.js +1 -1
- package/dist/805.js.map +1 -1
- package/dist/807.js +1 -1
- package/dist/821.js +1 -1
- package/dist/821.js.map +1 -1
- package/dist/822.js +1 -1
- package/dist/822.js.map +1 -1
- package/dist/858.js +1 -1
- package/dist/858.js.map +1 -1
- package/dist/887.js +1 -1
- package/dist/887.js.map +1 -1
- package/dist/9.js +1 -1
- package/dist/9.js.map +1 -1
- package/dist/975.js +1 -1
- package/dist/975.js.map +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -0
- package/dist/{openmrs-esm-patient-registration-app.js.buildmanifest.json → kenyaemr-esm-patient-registration-app.js.buildmanifest.json} +79 -79
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.old +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +1 -1
- package/src/index.ts +18 -0
- package/src/offline.resources.ts +1 -8
- package/src/patient-registration/field/address/address-field.component.tsx +1 -0
- package/src/patient-registration/patient-registration-hooks.ts +77 -6
- package/src/patient-registration/patient-registration-types.tsx +7 -1
- package/src/patient-registration/patient-registration.component.tsx +26 -3
- package/src/patient-verification/assets/counties.json +236 -0
- package/src/patient-verification/assets/verification-assets.ts +11 -0
- package/src/patient-verification/patient-verification-hook.tsx +156 -0
- package/src/patient-verification/patient-verification-utils.ts +173 -0
- package/src/patient-verification/patient-verification.component.tsx +110 -0
- package/src/patient-verification/patient-verification.scss +30 -0
- package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +69 -0
- package/src/patient-verification/verification-modal/empty-prompt.component.tsx +35 -0
- package/src/patient-verification/verification-types.ts +50 -0
- package/translations/en.json +17 -1
- package/dist/openmrs-esm-patient-registration-app.js +0 -1
- package/dist/openmrs-esm-patient-registration-app.js.map +0 -1
- package/dist/openmrs-esm-patient-registration-app.old +0 -1
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { FetchResponse, getSynchronizationItems, openmrsFetch, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
2
|
+
import last from 'lodash-es/last';
|
|
2
3
|
import camelCase from 'lodash-es/camelCase';
|
|
3
4
|
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
|
4
5
|
import useSWR from 'swr';
|
|
5
6
|
import { v4 } from 'uuid';
|
|
6
7
|
import { RegistrationConfig } from '../config-schema';
|
|
7
8
|
import { patientRegistration } from '../constants';
|
|
9
|
+
import { useConceptAnswers, useGlobalProperties } from '../patient-verification/patient-verification-hook';
|
|
8
10
|
import {
|
|
9
11
|
FormValues,
|
|
10
12
|
PatientRegistration,
|
|
11
13
|
PatientUuidMapType,
|
|
12
14
|
PersonAttributeResponse,
|
|
13
15
|
PatientIdentifierResponse,
|
|
16
|
+
ObsResponse,
|
|
17
|
+
ConceptAnswers,
|
|
14
18
|
Encounter,
|
|
15
19
|
} from './patient-registration-types';
|
|
16
20
|
import {
|
|
@@ -23,12 +27,13 @@ import {
|
|
|
23
27
|
import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource';
|
|
24
28
|
|
|
25
29
|
export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
|
|
30
|
+
const { martialStatus, education, occupation, educationLoad, loadingStatus } = useConcepts();
|
|
26
31
|
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
|
|
27
32
|
const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid);
|
|
28
33
|
const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid);
|
|
29
34
|
const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(patientUuid);
|
|
30
|
-
const { data:
|
|
31
|
-
|
|
35
|
+
const { data: obs, isLoading: isLoadingObs, obs: observations } = usePatientObs(patientUuid);
|
|
36
|
+
const { data: token, isLoading: isLoadingToken } = useGlobalProperties();
|
|
32
37
|
const [initialFormValues, setInitialFormValues] = useState<FormValues>({
|
|
33
38
|
patientUuid: v4(),
|
|
34
39
|
givenName: '',
|
|
@@ -77,6 +82,14 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
77
82
|
})();
|
|
78
83
|
}, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
|
|
79
84
|
|
|
85
|
+
// Setting authentication token
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!isLoadingToken && token) {
|
|
89
|
+
setInitialFormValues((initialFormValues) => ({ ...initialFormValues, token: String(token.access_token) }));
|
|
90
|
+
}
|
|
91
|
+
}, [isLoadingToken, token]);
|
|
92
|
+
|
|
80
93
|
// Set initial patient relationships
|
|
81
94
|
useEffect(() => {
|
|
82
95
|
if (!isLoadingRelationships && relationships) {
|
|
@@ -114,15 +127,24 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
114
127
|
}
|
|
115
128
|
}, [attributes, setInitialFormValues, isLoadingAttributes]);
|
|
116
129
|
|
|
117
|
-
// Set
|
|
130
|
+
// Set initial patient obs
|
|
131
|
+
|
|
118
132
|
useEffect(() => {
|
|
119
|
-
if (
|
|
133
|
+
if (!isLoadingObs) {
|
|
134
|
+
setInitialFormValues((initialFormValues) => ({ ...initialFormValues, obs: obs, observation: observations }));
|
|
135
|
+
}
|
|
136
|
+
}, [isLoadingObs]);
|
|
137
|
+
|
|
138
|
+
// Set Initial encounter
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
if (!educationLoad || !loadingStatus) {
|
|
120
142
|
setInitialFormValues((initialFormValues) => ({
|
|
121
143
|
...initialFormValues,
|
|
122
|
-
|
|
144
|
+
concepts: [...occupation, ...martialStatus, ...education],
|
|
123
145
|
}));
|
|
124
146
|
}
|
|
125
|
-
}, [
|
|
147
|
+
}, [educationLoad, loadingStatus]);
|
|
126
148
|
|
|
127
149
|
return [initialFormValues, setInitialFormValues];
|
|
128
150
|
}
|
|
@@ -264,3 +286,52 @@ function getPatientAttributeUuidMapForPatient(attributes: Array<PersonAttributeR
|
|
|
264
286
|
});
|
|
265
287
|
return attributeUuidMap;
|
|
266
288
|
}
|
|
289
|
+
export function usePatientObs(patientUuid: string) {
|
|
290
|
+
const {
|
|
291
|
+
registrationObs: { encounterTypeUuid },
|
|
292
|
+
} = useConfig() as RegistrationConfig;
|
|
293
|
+
const url = `/ws/rest/v1/encounter?patient=${patientUuid}&encounterType=${encounterTypeUuid}&v=custom:(obs:(uuid,display,concept:(uuid,display),value:(uuid,display)))`;
|
|
294
|
+
const { data, isLoading, error } = useSWR<{ data: ObsResponse }>(patientUuid ? url : null, openmrsFetch);
|
|
295
|
+
let obsObject = {};
|
|
296
|
+
const patientObs = last(data?.data?.results)?.obs?.forEach((ob) => {
|
|
297
|
+
Object.assign(obsObject, { [ob.concept.uuid]: ob.value.uuid });
|
|
298
|
+
});
|
|
299
|
+
return { data: obsObject, isLoading, error, obs: data?.data };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function useConcepts() {
|
|
303
|
+
const { data: martialStatus, isLoading: loadingStatus } = useConceptAnswers('1054AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
|
|
304
|
+
const { data: education, isLoading: educationLoad } = useConceptAnswers('1712AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
|
|
305
|
+
const occupation: Array<ConceptAnswers> = [
|
|
306
|
+
{
|
|
307
|
+
uuid: '1538AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
308
|
+
display: 'Farmer',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
uuid: '1540AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
312
|
+
display: 'Employee',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
uuid: '1539AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
316
|
+
display: 'Trader',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
uuid: '159465AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
320
|
+
display: 'Student',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
uuid: '159466AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
324
|
+
display: 'Driver',
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
uuid: '1107AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
328
|
+
display: 'None',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
uuid: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
332
|
+
display: 'Other',
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
return { martialStatus, education, occupation, loadingStatus, educationLoad };
|
|
337
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Session } from '@openmrs/esm-framework';
|
|
1
|
+
import { OpenmrsResource, Session } from '@openmrs/esm-framework';
|
|
2
2
|
import { RegistrationConfig } from '../config-schema';
|
|
3
3
|
import { SavePatientTransactionManager } from './form-manager';
|
|
4
4
|
|
|
@@ -185,6 +185,9 @@ export interface FormValues {
|
|
|
185
185
|
address: {
|
|
186
186
|
[addressField: string]: string;
|
|
187
187
|
};
|
|
188
|
+
observation?: ObsResponse;
|
|
189
|
+
concepts?: Array<ConceptAnswers>;
|
|
190
|
+
token?: string;
|
|
188
191
|
}
|
|
189
192
|
|
|
190
193
|
export interface PatientUuidMapType {
|
|
@@ -263,3 +266,6 @@ export interface ConceptAnswers {
|
|
|
263
266
|
display: string;
|
|
264
267
|
uuid: string;
|
|
265
268
|
}
|
|
269
|
+
export interface ObsResponse {
|
|
270
|
+
results: Array<{ obs: Array<{ uuid: string; display: string; value: OpenmrsResource; concept: OpenmrsResource }> }>;
|
|
271
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useContext, useMemo, useRef } from 'react';
|
|
2
2
|
import { Button, Link } from '@carbon/react';
|
|
3
|
-
import { XAxis } from '@carbon/react/icons';
|
|
3
|
+
import { XAxis, ShareKnowledge } from '@carbon/react/icons';
|
|
4
4
|
import { Router, useLocation, useParams } from 'react-router-dom';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Formik, Form, FormikHelpers } from 'formik';
|
|
@@ -17,12 +17,19 @@ import {
|
|
|
17
17
|
parseAddressTemplateXml,
|
|
18
18
|
scrollIntoView,
|
|
19
19
|
} from './patient-registration-utils';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
useInitialAddressFieldValues,
|
|
22
|
+
useInitialFormValues,
|
|
23
|
+
usePatientObs,
|
|
24
|
+
usePatientUuidMap,
|
|
25
|
+
} from './patient-registration-hooks';
|
|
21
26
|
import { ResourcesContext } from '../offline.resources';
|
|
22
27
|
import { builtInSections, RegistrationConfig, SectionDefinition } from '../config-schema';
|
|
23
28
|
import { SectionWrapper } from './section/section-wrapper.component';
|
|
24
29
|
import BeforeSavePrompt from './before-save-prompt';
|
|
25
30
|
import styles from './patient-registration.scss';
|
|
31
|
+
import PatientVerification from '../patient-verification/patient-verification.component';
|
|
32
|
+
import { handleSavePatientToClientRegistry } from '../patient-verification/patient-verification-hook';
|
|
26
33
|
|
|
27
34
|
let exportedInitialFormValuesForTesting = {} as FormValues;
|
|
28
35
|
|
|
@@ -50,6 +57,9 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
50
57
|
const { data: photo } = usePatientPhoto(patientToEdit?.id);
|
|
51
58
|
const savePatientTransactionManager = useRef(new SavePatientTransactionManager());
|
|
52
59
|
const fieldDefinition = config?.fieldDefinitions?.filter((def) => def.type === 'address');
|
|
60
|
+
const [enableClientRegistry, setEnableClientRegistry] = useState(
|
|
61
|
+
inEditMode ? initialFormValues.identifiers['nationalUniquePatientIdentifier']?.identifierValue : false,
|
|
62
|
+
);
|
|
53
63
|
|
|
54
64
|
useEffect(() => {
|
|
55
65
|
exportedInitialFormValuesForTesting = initialFormValues;
|
|
@@ -175,6 +185,18 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
175
185
|
</Link>
|
|
176
186
|
</div>
|
|
177
187
|
))}
|
|
188
|
+
<Button
|
|
189
|
+
renderIcon={ShareKnowledge}
|
|
190
|
+
disabled={!currentSession || !identifierTypes}
|
|
191
|
+
onClick={() => {
|
|
192
|
+
setEnableClientRegistry(true);
|
|
193
|
+
props.isValid
|
|
194
|
+
? handleSavePatientToClientRegistry(props.values, props.setValues, inEditMode)
|
|
195
|
+
: props.validateForm().then((errors) => displayErrors(errors));
|
|
196
|
+
}}
|
|
197
|
+
className={styles.submitButton}>
|
|
198
|
+
{t('postToRegistry', 'Post to registry')}
|
|
199
|
+
</Button>
|
|
178
200
|
<Button
|
|
179
201
|
className={styles.submitButton}
|
|
180
202
|
type="submit"
|
|
@@ -182,7 +204,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
182
204
|
// Current session and identifiers are required for patient registration.
|
|
183
205
|
// If currentSession or identifierTypes are not available, then the
|
|
184
206
|
// user should be blocked to register the patient.
|
|
185
|
-
disabled={!
|
|
207
|
+
disabled={!enableClientRegistry}>
|
|
186
208
|
{inEditMode ? t('updatePatient', 'Update Patient') : t('registerPatient', 'Register Patient')}
|
|
187
209
|
</Button>
|
|
188
210
|
<Button className={styles.cancelButton} kind="tertiary" onClick={cancelRegistration}>
|
|
@@ -204,6 +226,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
204
226
|
isOffline,
|
|
205
227
|
initialFormValues: props.initialValues,
|
|
206
228
|
}}>
|
|
229
|
+
<PatientVerification props={props} />
|
|
207
230
|
{sections.map((section, index) => (
|
|
208
231
|
<SectionWrapper
|
|
209
232
|
key={`registration-section-${section.id}`}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Mombasa",
|
|
4
|
+
"code": 1,
|
|
5
|
+
"capital": "Mombasa City"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "Kwale",
|
|
9
|
+
"code": 2,
|
|
10
|
+
"capital": "Kwale"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Kilifi",
|
|
14
|
+
"code": 3,
|
|
15
|
+
"capital": "Kilifi"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "Tana River",
|
|
19
|
+
"code": 4,
|
|
20
|
+
"capital": "Hola"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Lamu",
|
|
24
|
+
"code": 5,
|
|
25
|
+
"capital": "Lamu"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "Taita taveta",
|
|
29
|
+
"code": 6,
|
|
30
|
+
"capital": "Voi"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Garissa",
|
|
34
|
+
"code": 7,
|
|
35
|
+
"capital": "Garissa"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "Wajir",
|
|
39
|
+
"code": 8,
|
|
40
|
+
"capital": "Wajir"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Mandera",
|
|
44
|
+
"code": 9,
|
|
45
|
+
"capital": "Mandera"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "Marsabit",
|
|
49
|
+
"code": 10,
|
|
50
|
+
"capital": "Marsabit"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "Isiolo",
|
|
54
|
+
"code": 11,
|
|
55
|
+
"capital": "Isiolo"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "Meru",
|
|
59
|
+
"code": 12,
|
|
60
|
+
"capital": "Meru"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "Tharaka nithi",
|
|
64
|
+
"code": 13,
|
|
65
|
+
"capital": "Chuka"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "Embu",
|
|
69
|
+
"code": 14,
|
|
70
|
+
"capital": "Embu"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "Kitui",
|
|
74
|
+
"code": 15,
|
|
75
|
+
"capital": "Kitui"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "Machakos",
|
|
79
|
+
"code": 16,
|
|
80
|
+
"capital": "Machakos"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "Makueni",
|
|
84
|
+
"code": 17,
|
|
85
|
+
"capital": "Wote"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "Nyandarua",
|
|
89
|
+
"code": 18,
|
|
90
|
+
"capital": "Ol Kalou"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "Nyeri",
|
|
94
|
+
"code": 19,
|
|
95
|
+
"capital": "Nyeri"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "Kirinyaga",
|
|
99
|
+
"code": 20,
|
|
100
|
+
"capital": "Kerugoya/Kutus"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "Murang'a",
|
|
104
|
+
"code": 21,
|
|
105
|
+
"capital": "Murang'a"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "Kiambu",
|
|
109
|
+
"code": 22,
|
|
110
|
+
"capital": "Kiambu"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "Turkana",
|
|
114
|
+
"code": 23,
|
|
115
|
+
"capital": "Lodwar"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "West pokot",
|
|
119
|
+
"code": 24,
|
|
120
|
+
"capital": "Kapenguria"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "Samburu",
|
|
124
|
+
"code": 25,
|
|
125
|
+
"capital": "Maralal"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "Trans nzoia",
|
|
129
|
+
"code": 26,
|
|
130
|
+
"capital": "Kitale"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "Uasin gishu",
|
|
134
|
+
"code": 27,
|
|
135
|
+
"capital": "Eldoret"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "Elgeyo marakwet",
|
|
139
|
+
"code": 28,
|
|
140
|
+
"capital": "Iten"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "Nandi",
|
|
144
|
+
"code": 29,
|
|
145
|
+
"capital": "Kapsabet"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "Baringo",
|
|
149
|
+
"code": 30,
|
|
150
|
+
"capital": "Kabarnet"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "Laikipia",
|
|
154
|
+
"code": 31,
|
|
155
|
+
"capital": "Rumuruti"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "Nakuru",
|
|
159
|
+
"code": 32,
|
|
160
|
+
"capital": "Nakuru"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "Narok",
|
|
164
|
+
"code": 33,
|
|
165
|
+
"capital": "Narok"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "Kajiado",
|
|
169
|
+
"code": 34
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"name": "Kericho",
|
|
173
|
+
"code": 35,
|
|
174
|
+
"capital": "Kericho"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"name": "Bomet",
|
|
178
|
+
"code": 36,
|
|
179
|
+
"capital": "Bomet"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "Kakamega",
|
|
183
|
+
"code": 37,
|
|
184
|
+
"capital": "Kakamega"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "Vihiga",
|
|
188
|
+
"code": 38,
|
|
189
|
+
"capital": "Vihiga"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "Bungoma",
|
|
193
|
+
"code": 39,
|
|
194
|
+
"capital": "Bungoma"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "Busia",
|
|
198
|
+
"code": 40,
|
|
199
|
+
"capital": "Busia"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "Siaya",
|
|
203
|
+
"code": 41,
|
|
204
|
+
"capital": "Siaya"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "Kisumu",
|
|
208
|
+
"code": 42,
|
|
209
|
+
"capital": "Kisumu"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"name": "Homa bay",
|
|
213
|
+
"code": 43,
|
|
214
|
+
"capital": "Homa Bay"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "Migori",
|
|
218
|
+
"code": 44,
|
|
219
|
+
"capital": "Migori"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"name": "Kisii",
|
|
223
|
+
"code": 45,
|
|
224
|
+
"capital": "Kisii"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "Nyamira",
|
|
228
|
+
"code": 46,
|
|
229
|
+
"capital": "Nyamira"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"name": "Nairobi",
|
|
233
|
+
"code": 47,
|
|
234
|
+
"capital": "Nairobi City"
|
|
235
|
+
}
|
|
236
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const countries = [
|
|
2
|
+
{ name: 'Kenya', initials: 'KE' },
|
|
3
|
+
{ name: 'Uganda', initials: 'UG' },
|
|
4
|
+
{ name: 'Tanzania', initials: 'TZ' },
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
export const verificationIdentifierTypes = [
|
|
8
|
+
{ name: 'National ID', value: 'national-id' },
|
|
9
|
+
{ name: 'Passport', value: 'passport' },
|
|
10
|
+
{ name: 'Birth certificate number', value: 'birth-certificate-number' },
|
|
11
|
+
];
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { FetchResponse, openmrsFetch, showNotification, showToast } from '@openmrs/esm-framework';
|
|
2
|
+
import { ConceptAnswers, ConceptResponse, FormValues } from '../patient-registration/patient-registration-types';
|
|
3
|
+
import { generateNUPIPayload, handleClientRegistryResponse } from './patient-verification-utils';
|
|
4
|
+
import useSWR from 'swr';
|
|
5
|
+
import useSWRImmutable from 'swr/immutable';
|
|
6
|
+
|
|
7
|
+
export function searchClientRegistry(identifierType: string, searchTerm: string, token: string) {
|
|
8
|
+
const url = `https://afyakenyaapi.health.go.ke/partners/registry/search/KE/${identifierType}/${searchTerm}`;
|
|
9
|
+
return fetch(url, { headers: { Authorization: `Bearer ${token}` } }).then((r) => r.json());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function savePatientToClientRegistry(formValues: FormValues) {
|
|
13
|
+
const createdRegistryPatient = generateNUPIPayload(formValues);
|
|
14
|
+
return fetch(`https://afyakenyaapi.health.go.ke/partners/registry`, {
|
|
15
|
+
headers: { Authorization: `Bearer ${formValues.token}`, 'Content-Type': 'application/json' },
|
|
16
|
+
method: 'POST',
|
|
17
|
+
body: JSON.stringify(createdRegistryPatient),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function handleSavePatientToClientRegistry(
|
|
22
|
+
formValues: FormValues,
|
|
23
|
+
setValues: (values: FormValues, shouldValidate?: boolean) => void,
|
|
24
|
+
inEditMode: boolean,
|
|
25
|
+
) {
|
|
26
|
+
const mode = inEditMode ? 'edit' : 'new';
|
|
27
|
+
switch (mode) {
|
|
28
|
+
case 'edit': {
|
|
29
|
+
try {
|
|
30
|
+
const searchResponse = await searchClientRegistry(
|
|
31
|
+
'national-id',
|
|
32
|
+
formValues.identifiers['nationalId'].identifierValue,
|
|
33
|
+
formValues.token,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// if client does not exists post client to registry
|
|
37
|
+
if (searchResponse?.clientExists === false) {
|
|
38
|
+
postToRegistry(formValues, setValues);
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
showToast({
|
|
42
|
+
title: 'Client registry error',
|
|
43
|
+
description: `${error}`,
|
|
44
|
+
millis: 10000,
|
|
45
|
+
kind: 'error',
|
|
46
|
+
critical: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
case 'new': {
|
|
52
|
+
postToRegistry(formValues, setValues);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useConceptAnswers(conceptUuid: string): { data: Array<ConceptAnswers>; isLoading: boolean } {
|
|
58
|
+
const { data, error, isLoading } = useSWR<FetchResponse<ConceptResponse>, Error>(
|
|
59
|
+
`/ws/rest/v1/concept/${conceptUuid}`,
|
|
60
|
+
openmrsFetch,
|
|
61
|
+
);
|
|
62
|
+
if (error) {
|
|
63
|
+
showToast({
|
|
64
|
+
title: error.name,
|
|
65
|
+
description: error.message,
|
|
66
|
+
kind: 'error',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return { data: data?.data?.answers ?? [], isLoading };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const urlencoded = new URLSearchParams();
|
|
73
|
+
urlencoded.append('client_id', 'palladium.partner.client');
|
|
74
|
+
urlencoded.append('client_secret', '28f95b2a');
|
|
75
|
+
urlencoded.append('grant_type', 'client_credentials');
|
|
76
|
+
urlencoded.append('scope', 'DHP.Gateway DHP.Partners');
|
|
77
|
+
|
|
78
|
+
const swrFetcher = async (url) => {
|
|
79
|
+
const res = await fetch(url, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
body: urlencoded,
|
|
82
|
+
redirect: 'follow',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// If the status code is not in the range 200-299,
|
|
86
|
+
// we still try to parse and throw it.
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
const error = new Error('An error occurred while fetching the data.') as any;
|
|
89
|
+
// Attach extra info to the error object.
|
|
90
|
+
error.info = await res.json();
|
|
91
|
+
error.status = res.status;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return res.json();
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export function useGlobalProperties() {
|
|
99
|
+
const { data, isLoading, error } = useSWRImmutable(
|
|
100
|
+
`https://afyakenyaidentityapi.health.go.ke/connect/token`,
|
|
101
|
+
swrFetcher,
|
|
102
|
+
{ refreshInterval: 864000 },
|
|
103
|
+
);
|
|
104
|
+
return { data: data, isLoading, error };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function postToRegistry(
|
|
108
|
+
formValues: FormValues,
|
|
109
|
+
setValues: (values: FormValues, shouldValidate?: boolean) => void,
|
|
110
|
+
) {
|
|
111
|
+
try {
|
|
112
|
+
const clientRegistryResponse = await savePatientToClientRegistry(formValues);
|
|
113
|
+
if (clientRegistryResponse.ok) {
|
|
114
|
+
const savedValues = await clientRegistryResponse.json();
|
|
115
|
+
const nupiIdentifier = {
|
|
116
|
+
['nationalUniquePatientIdentifier']: {
|
|
117
|
+
identifierTypeUuid: 'f85081e2-b4be-4e48-b3a4-7994b69bb101',
|
|
118
|
+
identifierName: 'National Unique patient identifier',
|
|
119
|
+
identifierValue: savedValues['clientNumber'],
|
|
120
|
+
initialValue: savedValues['clientNumber'],
|
|
121
|
+
identifierUuid: undefined,
|
|
122
|
+
selectedSource: { uuid: '', name: '' },
|
|
123
|
+
preferred: false,
|
|
124
|
+
required: false,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
setValues({ ...formValues, identifiers: { ...formValues.identifiers, ...nupiIdentifier } });
|
|
128
|
+
showToast({
|
|
129
|
+
title: 'Posted patient to client registry successfully',
|
|
130
|
+
description: `The patient has been saved to client registry`,
|
|
131
|
+
kind: 'success',
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
const responseError = await clientRegistryResponse.json();
|
|
135
|
+
const errorMessage = Object.values(responseError.errors ?? {})
|
|
136
|
+
.map((error: any) => error.join())
|
|
137
|
+
.toString();
|
|
138
|
+
setValues({
|
|
139
|
+
...formValues,
|
|
140
|
+
attributes: {
|
|
141
|
+
...formValues.attributes,
|
|
142
|
+
['869f623a-f78e-4ace-9202-0bed481822f5']: 'Failed validation',
|
|
143
|
+
['752a0331-5293-4aa5-bf46-4d51aaf2cdc5']: 'Failed',
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
showNotification({
|
|
147
|
+
title: responseError.title,
|
|
148
|
+
description: errorMessage,
|
|
149
|
+
kind: 'warning',
|
|
150
|
+
millis: 150000,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
showNotification({ kind: 'error', title: 'NUPI Post failed', description: JSON.stringify(error) });
|
|
155
|
+
}
|
|
156
|
+
}
|