@kenyaemr/esm-patient-registration-app 4.4.1 → 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.
Files changed (43) hide show
  1. package/dist/144.js +2 -0
  2. package/dist/144.js.map +1 -0
  3. package/dist/207.js +1 -1
  4. package/dist/330.js +1 -0
  5. package/dist/330.js.map +1 -0
  6. package/dist/574.js +1 -1
  7. package/dist/59.js +1 -0
  8. package/dist/59.js.map +1 -0
  9. package/dist/68.js +1 -1
  10. package/dist/68.js.map +1 -1
  11. package/dist/735.js +1 -1
  12. package/dist/{909.js → 821.js} +1 -1
  13. package/dist/{909.js.map → 821.js.map} +1 -1
  14. package/dist/{698.js → 822.js} +1 -1
  15. package/dist/822.js.map +1 -0
  16. package/dist/887.js +1 -1
  17. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  18. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +122 -74
  19. package/dist/kenyaemr-esm-patient-registration-app.old +1 -1
  20. package/dist/main.js +1 -1
  21. package/dist/main.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/constants.ts +1 -1
  24. package/src/index.ts +18 -0
  25. package/src/offline.resources.ts +1 -8
  26. package/src/patient-registration/field/address/address-field.component.tsx +1 -0
  27. package/src/patient-registration/patient-registration-hooks.ts +77 -6
  28. package/src/patient-registration/patient-registration-types.tsx +7 -1
  29. package/src/patient-registration/patient-registration.component.tsx +26 -3
  30. package/src/patient-verification/assets/counties.json +236 -0
  31. package/src/patient-verification/assets/verification-assets.ts +11 -0
  32. package/src/patient-verification/patient-verification-hook.tsx +156 -0
  33. package/src/patient-verification/patient-verification-utils.ts +173 -0
  34. package/src/patient-verification/patient-verification.component.tsx +110 -0
  35. package/src/patient-verification/patient-verification.scss +30 -0
  36. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +69 -0
  37. package/src/patient-verification/verification-modal/empty-prompt.component.tsx +35 -0
  38. package/src/patient-verification/verification-types.ts +50 -0
  39. package/translations/en.json +17 -1
  40. package/dist/417.js +0 -2
  41. package/dist/417.js.map +0 -1
  42. package/dist/698.js.map +0 -1
  43. /package/dist/{417.js.LICENSE.txt → 144.js.LICENSE.txt} +0 -0
@@ -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: encounters } = useInitialEncounters(patientUuid, patientToEdit);
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 Initial registration encounters
130
+ // Set initial patient obs
131
+
118
132
  useEffect(() => {
119
- if (patientToEdit && encounters) {
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
- obs: encounters as Record<string, string>,
144
+ concepts: [...occupation, ...martialStatus, ...education],
123
145
  }));
124
146
  }
125
- }, [encounters, patientToEdit]);
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 { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks';
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={!currentSession || !identifierTypes}>
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
+ }