@kenyaemr/esm-patient-registration-app 4.3.0

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 (162) hide show
  1. package/README.md +7 -0
  2. package/__mocks__/autogenerationoptions.mock.ts +34 -0
  3. package/__mocks__/react-i18next.js +49 -0
  4. package/dist/144.js +2 -0
  5. package/dist/144.js.LICENSE.txt +27 -0
  6. package/dist/144.js.map +1 -0
  7. package/dist/207.js +1 -0
  8. package/dist/207.js.map +1 -0
  9. package/dist/317.js +2 -0
  10. package/dist/317.js.LICENSE.txt +6 -0
  11. package/dist/317.js.map +1 -0
  12. package/dist/330.js +1 -0
  13. package/dist/330.js.map +1 -0
  14. package/dist/574.js +1 -0
  15. package/dist/59.js +1 -0
  16. package/dist/59.js.map +1 -0
  17. package/dist/591.js +2 -0
  18. package/dist/591.js.LICENSE.txt +32 -0
  19. package/dist/591.js.map +1 -0
  20. package/dist/62.js +1 -0
  21. package/dist/62.js.map +1 -0
  22. package/dist/635.js +1 -0
  23. package/dist/635.js.map +1 -0
  24. package/dist/68.js +1 -0
  25. package/dist/68.js.map +1 -0
  26. package/dist/735.js +1 -0
  27. package/dist/735.js.map +1 -0
  28. package/dist/757.js +1 -0
  29. package/dist/784.js +2 -0
  30. package/dist/784.js.LICENSE.txt +9 -0
  31. package/dist/784.js.map +1 -0
  32. package/dist/805.js +1 -0
  33. package/dist/805.js.map +1 -0
  34. package/dist/807.js +1 -0
  35. package/dist/821.js +1 -0
  36. package/dist/821.js.map +1 -0
  37. package/dist/822.js +1 -0
  38. package/dist/822.js.map +1 -0
  39. package/dist/858.js +2 -0
  40. package/dist/858.js.LICENSE.txt +3 -0
  41. package/dist/858.js.map +1 -0
  42. package/dist/887.js +1 -0
  43. package/dist/887.js.map +1 -0
  44. package/dist/9.js +2 -0
  45. package/dist/9.js.LICENSE.txt +9 -0
  46. package/dist/9.js.map +1 -0
  47. package/dist/975.js +1 -0
  48. package/dist/975.js.map +1 -0
  49. package/dist/main.js +2 -0
  50. package/dist/main.js.LICENSE.txt +9 -0
  51. package/dist/main.js.map +1 -0
  52. package/dist/openmrs-esm-patient-registration-app.js +1 -0
  53. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +623 -0
  54. package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
  55. package/dist/openmrs-esm-patient-registration-app.old +1 -0
  56. package/docs/images/patient-registration-hierarchy.png +0 -0
  57. package/package.json +55 -0
  58. package/src/add-patient-link.scss +3 -0
  59. package/src/add-patient-link.tsx +21 -0
  60. package/src/config-schema.ts +405 -0
  61. package/src/constants.ts +14 -0
  62. package/src/declarations.d.tsx +4 -0
  63. package/src/index.ts +131 -0
  64. package/src/nav-link.tsx +10 -0
  65. package/src/offline.resources.ts +109 -0
  66. package/src/offline.ts +90 -0
  67. package/src/patient-registration/before-save-prompt.tsx +72 -0
  68. package/src/patient-registration/date-util.ts +52 -0
  69. package/src/patient-registration/field/__mocks__/field.resource.ts +60 -0
  70. package/src/patient-registration/field/address/address-field.component.tsx +31 -0
  71. package/src/patient-registration/field/address/address-hierarchy.component.tsx +143 -0
  72. package/src/patient-registration/field/address/address-hierarchy.test.tsx +181 -0
  73. package/src/patient-registration/field/address/address-search.component.tsx +98 -0
  74. package/src/patient-registration/field/address/address-search.scss +53 -0
  75. package/src/patient-registration/field/custom-field.component.tsx +25 -0
  76. package/src/patient-registration/field/dob/dob.component.tsx +143 -0
  77. package/src/patient-registration/field/dob/dob.test.tsx +73 -0
  78. package/src/patient-registration/field/field.component.tsx +44 -0
  79. package/src/patient-registration/field/field.resource.ts +35 -0
  80. package/src/patient-registration/field/field.scss +127 -0
  81. package/src/patient-registration/field/gender/gender-field.component.tsx +49 -0
  82. package/src/patient-registration/field/gender/gender-field.test.tsx +66 -0
  83. package/src/patient-registration/field/id/id-field.component.tsx +142 -0
  84. package/src/patient-registration/field/id/identifier-selection-overlay.tsx +194 -0
  85. package/src/patient-registration/field/id/identifier-selection.scss +37 -0
  86. package/src/patient-registration/field/name/name-field.component.tsx +109 -0
  87. package/src/patient-registration/field/obs/obs-field.component.tsx +185 -0
  88. package/src/patient-registration/field/obs/obs-field.test.tsx +127 -0
  89. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +59 -0
  90. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +68 -0
  91. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +81 -0
  92. package/src/patient-registration/field/person-attributes/person-attributes.resource.tsx +20 -0
  93. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +57 -0
  94. package/src/patient-registration/form-manager.test.ts +68 -0
  95. package/src/patient-registration/form-manager.ts +413 -0
  96. package/src/patient-registration/input/basic-input/input/input.component.tsx +59 -0
  97. package/src/patient-registration/input/basic-input/input/input.test.tsx +170 -0
  98. package/src/patient-registration/input/basic-input/select/select-input.component.tsx +32 -0
  99. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +32 -0
  100. package/src/patient-registration/input/combo-input/combo-input.component.tsx +76 -0
  101. package/src/patient-registration/input/combo-input/combo-input.test.tsx +43 -0
  102. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +84 -0
  103. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +53 -0
  104. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +109 -0
  105. package/src/patient-registration/input/custom-input/estimated-age/estimated-age-input.component.tsx +32 -0
  106. package/src/patient-registration/input/custom-input/estimated-age/estimated-age-input.test.tsx +36 -0
  107. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +156 -0
  108. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +110 -0
  109. package/src/patient-registration/input/custom-input/identifier/utils.ts +19 -0
  110. package/src/patient-registration/input/custom-input/unidentified-patient/unidentified-patient-input.component.tsx +24 -0
  111. package/src/patient-registration/input/custom-input/unidentified-patient/unidentified-patient-input.test.tsx +39 -0
  112. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +53 -0
  113. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +43 -0
  114. package/src/patient-registration/input/input.scss +108 -0
  115. package/src/patient-registration/patient-registration-context.ts +24 -0
  116. package/src/patient-registration/patient-registration-hooks.ts +320 -0
  117. package/src/patient-registration/patient-registration-types.tsx +271 -0
  118. package/src/patient-registration/patient-registration-utils.ts +219 -0
  119. package/src/patient-registration/patient-registration.component.tsx +250 -0
  120. package/src/patient-registration/patient-registration.resource.test.tsx +26 -0
  121. package/src/patient-registration/patient-registration.resource.tsx +296 -0
  122. package/src/patient-registration/patient-registration.scss +94 -0
  123. package/src/patient-registration/patient-registration.test.tsx +436 -0
  124. package/src/patient-registration/section/death-info/death-info-section.component.tsx +30 -0
  125. package/src/patient-registration/section/death-info/death-info-section.test.tsx +73 -0
  126. package/src/patient-registration/section/demographics/demographics-section.component.tsx +30 -0
  127. package/src/patient-registration/section/demographics/demographics-section.test.tsx +84 -0
  128. package/src/patient-registration/section/generic-section.component.tsx +17 -0
  129. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +226 -0
  130. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +78 -0
  131. package/src/patient-registration/section/patient-relationships/relationships.scss +35 -0
  132. package/src/patient-registration/section/section-wrapper.component.tsx +40 -0
  133. package/src/patient-registration/section/section.component.tsx +23 -0
  134. package/src/patient-registration/section/section.scss +1 -0
  135. package/src/patient-registration/ui-components/overlay/index.tsx +51 -0
  136. package/src/patient-registration/ui-components/overlay/overlay.scss +63 -0
  137. package/src/patient-registration/validation/patient-registration-validation.test.tsx +129 -0
  138. package/src/patient-registration/validation/patient-registration-validation.tsx +46 -0
  139. package/src/patient-verification/assets/counties.json +236 -0
  140. package/src/patient-verification/assets/verification-assets.ts +11 -0
  141. package/src/patient-verification/patient-verification-hook.tsx +156 -0
  142. package/src/patient-verification/patient-verification-utils.ts +173 -0
  143. package/src/patient-verification/patient-verification.component.tsx +118 -0
  144. package/src/patient-verification/patient-verification.scss +30 -0
  145. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +69 -0
  146. package/src/patient-verification/verification-modal/empty-prompt.component.tsx +35 -0
  147. package/src/patient-verification/verification-types.ts +50 -0
  148. package/src/resource.ts +12 -0
  149. package/src/root.component.tsx +66 -0
  150. package/src/root.scss +7 -0
  151. package/src/root.test.tsx +32 -0
  152. package/src/widgets/cancel-patient-edit.component.tsx +37 -0
  153. package/src/widgets/delete-identifier-confirmation-modal.tsx +41 -0
  154. package/src/widgets/delete-identifier-modal.scss +34 -0
  155. package/src/widgets/display-photo.component.tsx +30 -0
  156. package/src/widgets/edit-patient-details-button.component.tsx +34 -0
  157. package/src/widgets/edit-patient-details-button.scss +3 -0
  158. package/translations/en.json +108 -0
  159. package/translations/fr.json +89 -0
  160. package/translations/km.json +89 -0
  161. package/tsconfig.json +5 -0
  162. package/webpack.config.js +1 -0
@@ -0,0 +1,46 @@
1
+ import * as Yup from 'yup';
2
+ import mapValues from 'lodash/mapValues';
3
+ import { FormValues } from '../patient-registration-types';
4
+
5
+ export const validationSchema = Yup.object({
6
+ givenName: Yup.string().required('givenNameRequired'),
7
+ familyName: Yup.string().required('familyNameRequired'),
8
+ additionalGivenName: Yup.string().when('addNameInLocalLanguage', {
9
+ is: true,
10
+ then: Yup.string().required('givenNameRequired'),
11
+ otherwise: Yup.string().notRequired(),
12
+ }),
13
+ additionalFamilyName: Yup.string().when('addNameInLocalLanguage', {
14
+ is: true,
15
+ then: Yup.string().required('familyNameRequired'),
16
+ otherwise: Yup.string().notRequired(),
17
+ }),
18
+ gender: Yup.string().oneOf(['Male', 'Female', 'Other', 'Unknown'], 'genderUnspecified').required('genderRequired'),
19
+ birthdate: Yup.date().when('birthdateEstimated', {
20
+ is: false,
21
+ then: Yup.date().required('birthdayRequired').max(Date(), 'birthdayNotInTheFuture').nullable(),
22
+ otherwise: Yup.date().nullable(),
23
+ }),
24
+ yearsEstimated: Yup.number().when('birthdateEstimated', {
25
+ is: true,
26
+ then: Yup.number().required('yearsEstimateRequired').min(0, 'negativeYears'),
27
+ otherwise: Yup.number().nullable(),
28
+ }),
29
+ monthsEstimated: Yup.number().min(0, 'negativeMonths'),
30
+ deathDate: Yup.date().max(Date(), 'deathdayNotInTheFuture').nullable(),
31
+ email: Yup.string().optional().email('invalidEmail'),
32
+ identifiers: Yup.lazy((obj: FormValues['identifiers']) =>
33
+ Yup.object(
34
+ mapValues(obj, () =>
35
+ Yup.object({
36
+ required: Yup.bool(),
37
+ identifierValue: Yup.string().when('required', {
38
+ is: true,
39
+ then: Yup.string().required('identifierValueRequired'),
40
+ otherwise: Yup.string().notRequired(),
41
+ }),
42
+ }),
43
+ ),
44
+ ),
45
+ ),
46
+ });
@@ -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
+ }
@@ -0,0 +1,173 @@
1
+ import { showModal } from '@openmrs/esm-framework';
2
+ import { FormikProps } from 'formik';
3
+ import { FormValues } from '../patient-registration/patient-registration-types';
4
+ import { ClientRegistryPatient, RegistryPatient } from './verification-types';
5
+ import counties from './assets/counties.json';
6
+
7
+ export function handleClientRegistryResponse(
8
+ clientResponse: ClientRegistryPatient,
9
+ props: FormikProps<FormValues>,
10
+ searchTerm: string,
11
+ ) {
12
+ if (clientResponse?.clientExists === false) {
13
+ const nupiIdentifiers = {
14
+ ['nationalId']: {
15
+ initialValue: searchTerm,
16
+ identifierUuid: undefined,
17
+ selectedSource: { uuid: '', name: '' },
18
+ preferred: false,
19
+ required: false,
20
+ identifierTypeUuid: '49af6cdc-7968-4abb-bf46-de10d7f4859f',
21
+ identifierName: 'National ID',
22
+ identifierValue: searchTerm,
23
+ },
24
+ };
25
+ const dispose = showModal('empty-client-registry-modal', {
26
+ onConfirm: () => {
27
+ props.setValues({ ...props.values, identifiers: { ...props.values.identifiers, ...nupiIdentifiers } });
28
+ dispose();
29
+ },
30
+ close: () => dispose(),
31
+ });
32
+ }
33
+
34
+ if (clientResponse?.clientExists) {
35
+ const {
36
+ client: {
37
+ middleName,
38
+ lastName,
39
+ firstName,
40
+ contact,
41
+ country,
42
+ countyOfBirth,
43
+ residence,
44
+ identifications,
45
+ gender,
46
+ dateOfBirth,
47
+ isAlive,
48
+ clientNumber,
49
+ educationLevel,
50
+ occupation,
51
+ maritalStatus,
52
+ },
53
+ } = clientResponse;
54
+
55
+ const nupiIdentifiers = {
56
+ ['nationalId']: {
57
+ initialValue: identifications !== undefined && identifications[0]?.identificationNumber,
58
+ identifierUuid: undefined,
59
+ selectedSource: { uuid: '', name: '' },
60
+ preferred: false,
61
+ required: false,
62
+ identifierTypeUuid: '49af6cdc-7968-4abb-bf46-de10d7f4859f',
63
+ identifierName: 'National ID',
64
+ identifierValue: identifications !== undefined && identifications[0]?.identificationNumber,
65
+ },
66
+
67
+ ['nationalUniquePatientIdentifier']: {
68
+ identifierTypeUuid: 'f85081e2-b4be-4e48-b3a4-7994b69bb101',
69
+ identifierName: 'National Unique patient identifier',
70
+ identifierValue: clientNumber,
71
+ initialValue: clientNumber,
72
+ identifierUuid: undefined,
73
+ selectedSource: { uuid: '', name: '' },
74
+ preferred: false,
75
+ required: false,
76
+ },
77
+ };
78
+
79
+ const dispose = showModal('confirm-client-registry-modal', {
80
+ onConfirm: () => {
81
+ props.setValues({
82
+ ...props.values,
83
+ familyName: lastName,
84
+ middleName: middleName,
85
+ givenName: firstName,
86
+ gender: gender === 'male' ? 'Male' : 'Female',
87
+ birthdate: new Date(dateOfBirth),
88
+ isDead: !isAlive,
89
+ attributes: {
90
+ 'b2c38640-2603-4629-aebd-3b54f33f1e3a': contact?.primaryPhone,
91
+ '94614350-84c8-41e0-ac29-86bc107069be': contact?.secondaryPhone,
92
+ 'b8d0b331-1d2d-4a9a-b741-1816f498bdb6': contact?.emailAddress ?? '',
93
+ },
94
+ address: {
95
+ address1: residence?.address,
96
+ address2: '',
97
+ address4: residence?.ward,
98
+ cityVillage: residence?.village,
99
+ stateProvince: residence?.subCounty,
100
+ countyDistrict: counties.find((county) => county.code === parseInt(residence?.county))?.name,
101
+ country: 'Kenya',
102
+ postalCode: residence?.address,
103
+ },
104
+ identifiers: { ...props.values.identifiers, ...nupiIdentifiers },
105
+ obs: {
106
+ '1054AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA':
107
+ props.values.concepts.find((concept) => concept.display?.toLowerCase() === maritalStatus?.toLowerCase())
108
+ ?.uuid ?? '',
109
+ '1712AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA':
110
+ props.values.concepts.find((concept) => concept.display?.toLowerCase() === educationLevel?.toLowerCase())
111
+ ?.uuid ?? '',
112
+ '1542AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA':
113
+ props.values.concepts.find((concept) => concept.display?.toLowerCase() === occupation?.toLowerCase())
114
+ ?.uuid ?? '',
115
+ },
116
+ });
117
+ dispose();
118
+ },
119
+ close: () => dispose(),
120
+ patient: clientResponse.client,
121
+ });
122
+ }
123
+ }
124
+
125
+ export function generateNUPIPayload(formValues: FormValues): RegistryPatient {
126
+ const educationLevel = formValues.concepts.find(
127
+ (concept) => concept.uuid === formValues.obs['1712AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
128
+ );
129
+ const occupation = formValues.concepts.find(
130
+ (concept) => concept.uuid === formValues.obs['1542AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
131
+ );
132
+ const maritalStatus = formValues.concepts.find(
133
+ (concept) => concept.uuid === formValues.obs['1054AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'],
134
+ );
135
+
136
+ let createRegistryPatient: RegistryPatient = {
137
+ firstName: formValues?.givenName,
138
+ middleName: formValues?.middleName,
139
+ lastName: formValues?.familyName,
140
+ gender: formValues?.gender === 'Male' ? 'male' : 'female',
141
+ dateOfBirth: new Date(formValues.birthdate).toISOString(),
142
+ isAlive: !formValues.isDead,
143
+ residence: {
144
+ county: `0${counties.find((county) => county.name.includes(formValues.address['countyDistrict']))?.code}`,
145
+ subCounty: formValues.address['stateProvince']?.toLocaleLowerCase(),
146
+ ward: formValues.address['address4']?.toLocaleLowerCase(),
147
+ village: formValues.address['cityVillage'],
148
+ landmark: formValues.address['address2'],
149
+ address: formValues.address['postalCode'],
150
+ },
151
+ nextOfKins: [],
152
+ contact: {
153
+ primaryPhone: formValues.attributes['b2c38640-2603-4629-aebd-3b54f33f1e3a'],
154
+ secondaryPhone: formValues.attributes['94614350-84c8-41e0-ac29-86bc107069be'],
155
+ emailAddress: formValues.attributes['b8d0b331-1d2d-4a9a-b741-1816f498bdb6'],
156
+ },
157
+ country: 'KE',
158
+ countyOfBirth: `0${counties.find((county) => county.name.includes(formValues.address['countyDistrict']))?.code}`,
159
+ educationLevel: educationLevel?.display?.toLowerCase() ?? '',
160
+ religion: '',
161
+ occupation: occupation?.display?.toLowerCase() ?? '',
162
+ maritalStatus: maritalStatus?.display?.toLowerCase() ?? '',
163
+ originFacilityKmflCode: '',
164
+ nascopCCCNumber: '',
165
+ identifications: [
166
+ {
167
+ identificationType: 'national-id',
168
+ identificationNumber: formValues.identifiers['nationalId']?.identifierValue,
169
+ },
170
+ ],
171
+ };
172
+ return createRegistryPatient;
173
+ }