@ampath/esm-patient-registration-app 6.0.1-pre.6

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 (176) hide show
  1. package/.turbo/turbo-build.log +41 -0
  2. package/README.md +7 -0
  3. package/dist/130.js +2 -0
  4. package/dist/130.js.LICENSE.txt +3 -0
  5. package/dist/130.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/249.js +2 -0
  9. package/dist/249.js.LICENSE.txt +46 -0
  10. package/dist/249.js.map +1 -0
  11. package/dist/255.js +2 -0
  12. package/dist/255.js.LICENSE.txt +9 -0
  13. package/dist/255.js.map +1 -0
  14. package/dist/271.js +1 -0
  15. package/dist/303.js +1 -0
  16. package/dist/303.js.map +1 -0
  17. package/dist/319.js +1 -0
  18. package/dist/365.js +1 -0
  19. package/dist/365.js.map +1 -0
  20. package/dist/460.js +1 -0
  21. package/dist/525.js +1 -0
  22. package/dist/525.js.map +1 -0
  23. package/dist/537.js +1 -0
  24. package/dist/537.js.map +1 -0
  25. package/dist/574.js +1 -0
  26. package/dist/591.js +2 -0
  27. package/dist/591.js.LICENSE.txt +32 -0
  28. package/dist/591.js.map +1 -0
  29. package/dist/621.js +1 -0
  30. package/dist/621.js.map +1 -0
  31. package/dist/644.js +1 -0
  32. package/dist/729.js +1 -0
  33. package/dist/729.js.map +1 -0
  34. package/dist/735.js +1 -0
  35. package/dist/735.js.map +1 -0
  36. package/dist/757.js +1 -0
  37. package/dist/784.js +2 -0
  38. package/dist/784.js.LICENSE.txt +9 -0
  39. package/dist/784.js.map +1 -0
  40. package/dist/788.js +1 -0
  41. package/dist/807.js +1 -0
  42. package/dist/833.js +1 -0
  43. package/dist/879.js +1 -0
  44. package/dist/879.js.map +1 -0
  45. package/dist/ampath-esm-patient-registration-app.js +1 -0
  46. package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +649 -0
  47. package/dist/ampath-esm-patient-registration-app.js.map +1 -0
  48. package/dist/main.js +2 -0
  49. package/dist/main.js.LICENSE.txt +56 -0
  50. package/dist/main.js.map +1 -0
  51. package/dist/routes.json +1 -0
  52. package/docs/images/patient-registration-hierarchy.png +0 -0
  53. package/jest.config.js +3 -0
  54. package/package.json +61 -0
  55. package/src/add-patient-link.scss +3 -0
  56. package/src/add-patient-link.test.tsx +20 -0
  57. package/src/add-patient-link.tsx +21 -0
  58. package/src/config-schema.ts +410 -0
  59. package/src/constants.ts +14 -0
  60. package/src/declarations.d.ts +6 -0
  61. package/src/index.ts +71 -0
  62. package/src/nav-link.test.tsx +13 -0
  63. package/src/nav-link.tsx +10 -0
  64. package/src/offline.resources.ts +155 -0
  65. package/src/offline.ts +91 -0
  66. package/src/patient-registration/before-save-prompt.tsx +73 -0
  67. package/src/patient-registration/date-util.ts +52 -0
  68. package/src/patient-registration/field/__mocks__/field.resource.ts +60 -0
  69. package/src/patient-registration/field/address/address-field.component.tsx +153 -0
  70. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +73 -0
  71. package/src/patient-registration/field/address/address-hierarchy.resource.tsx +157 -0
  72. package/src/patient-registration/field/address/address-search.component.tsx +85 -0
  73. package/src/patient-registration/field/address/address-search.scss +53 -0
  74. package/src/patient-registration/field/address/custom-address-field.component.tsx +31 -0
  75. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +214 -0
  76. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +135 -0
  77. package/src/patient-registration/field/custom-field.component.tsx +25 -0
  78. package/src/patient-registration/field/dob/dob.component.tsx +159 -0
  79. package/src/patient-registration/field/dob/dob.test.tsx +75 -0
  80. package/src/patient-registration/field/field.component.tsx +47 -0
  81. package/src/patient-registration/field/field.resource.ts +35 -0
  82. package/src/patient-registration/field/field.scss +127 -0
  83. package/src/patient-registration/field/field.test.tsx +294 -0
  84. package/src/patient-registration/field/gender/gender-field.component.tsx +49 -0
  85. package/src/patient-registration/field/gender/gender-field.test.tsx +59 -0
  86. package/src/patient-registration/field/id/id-field.component.tsx +144 -0
  87. package/src/patient-registration/field/id/id-field.test.tsx +107 -0
  88. package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +198 -0
  89. package/src/patient-registration/field/id/identifier-selection.scss +37 -0
  90. package/src/patient-registration/field/name/name-field.component.tsx +142 -0
  91. package/src/patient-registration/field/obs/obs-field.component.tsx +204 -0
  92. package/src/patient-registration/field/obs/obs-field.test.tsx +205 -0
  93. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +60 -0
  94. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +116 -0
  95. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +127 -0
  96. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +88 -0
  97. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +187 -0
  98. package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +20 -0
  99. package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +58 -0
  100. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +88 -0
  101. package/src/patient-registration/field/phone/phone-field.component.tsx +16 -0
  102. package/src/patient-registration/form-manager.test.ts +67 -0
  103. package/src/patient-registration/form-manager.ts +414 -0
  104. package/src/patient-registration/input/basic-input/input/input.component.tsx +179 -0
  105. package/src/patient-registration/input/basic-input/input/input.test.tsx +72 -0
  106. package/src/patient-registration/input/basic-input/select/select-input.component.tsx +32 -0
  107. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +49 -0
  108. package/src/patient-registration/input/combo-input/combo-input.component.tsx +128 -0
  109. package/src/patient-registration/input/combo-input/selection-tick.component.tsx +20 -0
  110. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +187 -0
  111. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +62 -0
  112. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +132 -0
  113. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +156 -0
  114. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +107 -0
  115. package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
  116. package/src/patient-registration/input/custom-input/identifier/utils.ts +19 -0
  117. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +53 -0
  118. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +43 -0
  119. package/src/patient-registration/input/input.scss +118 -0
  120. package/src/patient-registration/patient-registration-context.ts +24 -0
  121. package/src/patient-registration/patient-registration-hooks.ts +287 -0
  122. package/src/patient-registration/patient-registration-utils.ts +216 -0
  123. package/src/patient-registration/patient-registration.component.tsx +240 -0
  124. package/src/patient-registration/patient-registration.resource.test.tsx +26 -0
  125. package/src/patient-registration/patient-registration.resource.ts +250 -0
  126. package/src/patient-registration/patient-registration.scss +122 -0
  127. package/src/patient-registration/patient-registration.test.tsx +471 -0
  128. package/src/patient-registration/patient-registration.types.ts +318 -0
  129. package/src/patient-registration/section/death-info/death-info-section.component.tsx +31 -0
  130. package/src/patient-registration/section/death-info/death-info-section.test.tsx +64 -0
  131. package/src/patient-registration/section/demographics/demographics-section.component.tsx +30 -0
  132. package/src/patient-registration/section/demographics/demographics-section.test.tsx +83 -0
  133. package/src/patient-registration/section/generic-section.component.tsx +17 -0
  134. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +235 -0
  135. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +100 -0
  136. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +78 -0
  137. package/src/patient-registration/section/patient-relationships/relationships.scss +35 -0
  138. package/src/patient-registration/section/section-wrapper.component.tsx +40 -0
  139. package/src/patient-registration/section/section.component.tsx +23 -0
  140. package/src/patient-registration/section/section.scss +1 -0
  141. package/src/patient-registration/ui-components/overlay/overlay.component.tsx +51 -0
  142. package/src/patient-registration/ui-components/overlay/overlay.scss +63 -0
  143. package/src/patient-registration/validation/patient-registration-validation.test.tsx +157 -0
  144. package/src/patient-registration/validation/patient-registration-validation.tsx +60 -0
  145. package/src/patient-verification/client-registry-constants.ts +13 -0
  146. package/src/patient-verification/client-registry.component.tsx +66 -0
  147. package/src/patient-verification/client-registry.scss +1 -0
  148. package/src/patient-verification/utils.tsx +56 -0
  149. package/src/patient-verification/verification-modal.scss +20 -0
  150. package/src/patient-verification/verification.component.tsx +48 -0
  151. package/src/resource.ts +12 -0
  152. package/src/root.component.tsx +63 -0
  153. package/src/root.scss +7 -0
  154. package/src/root.test.tsx +32 -0
  155. package/src/routes.json +66 -0
  156. package/src/widgets/cancel-patient-edit.component.tsx +37 -0
  157. package/src/widgets/cancel-patient-edit.test.tsx +27 -0
  158. package/src/widgets/delete-identifier-confirmation-modal.test.tsx +34 -0
  159. package/src/widgets/delete-identifier-confirmation-modal.tsx +41 -0
  160. package/src/widgets/delete-identifier-modal.scss +34 -0
  161. package/src/widgets/display-photo.component.tsx +30 -0
  162. package/src/widgets/display-photo.test.tsx +37 -0
  163. package/src/widgets/edit-patient-details-button.component.tsx +34 -0
  164. package/src/widgets/edit-patient-details-button.scss +3 -0
  165. package/src/widgets/edit-patient-details-button.test.tsx +41 -0
  166. package/translations/am.json +97 -0
  167. package/translations/ar.json +97 -0
  168. package/translations/en.json +103 -0
  169. package/translations/es.json +97 -0
  170. package/translations/fr.json +97 -0
  171. package/translations/he.json +97 -0
  172. package/translations/km.json +97 -0
  173. package/translations/zh.json +89 -0
  174. package/translations/zh_CN.json +89 -0
  175. package/tsconfig.json +5 -0
  176. package/webpack.config.js +1 -0
@@ -0,0 +1,287 @@
1
+ import {
2
+ type FetchResponse,
3
+ type OpenmrsResource,
4
+ getSynchronizationItems,
5
+ openmrsFetch,
6
+ useConfig,
7
+ usePatient,
8
+ } from '@openmrs/esm-framework';
9
+ import camelCase from 'lodash-es/camelCase';
10
+ import { type Dispatch, useEffect, useMemo, useState } from 'react';
11
+ import useSWR from 'swr';
12
+ import { v4 } from 'uuid';
13
+ import { type RegistrationConfig } from '../config-schema';
14
+ import { patientRegistration } from '../constants';
15
+ import {
16
+ type FormValues,
17
+ type PatientRegistration,
18
+ type PatientUuidMapType,
19
+ type PersonAttributeResponse,
20
+ type PatientIdentifierResponse,
21
+ type Encounter,
22
+ } from './patient-registration.types';
23
+ import {
24
+ getAddressFieldValuesFromFhirPatient,
25
+ getFormValuesFromFhirPatient,
26
+ getPatientUuidMapFromFhirPatient,
27
+ getPhonePersonAttributeValueFromFhirPatient,
28
+ latestFirstEncounter,
29
+ } from './patient-registration-utils';
30
+ import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource';
31
+ import dayjs from 'dayjs';
32
+
33
+ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
34
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
35
+ const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid);
36
+ const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid);
37
+ const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(patientUuid);
38
+ const { data: encounters } = useInitialEncounters(patientUuid, patientToEdit);
39
+
40
+ const [initialFormValues, setInitialFormValues] = useState<FormValues>({
41
+ patientUuid: v4(),
42
+ givenName: '',
43
+ middleName: '',
44
+ familyName: '',
45
+ additionalGivenName: '',
46
+ additionalMiddleName: '',
47
+ additionalFamilyName: '',
48
+ addNameInLocalLanguage: false,
49
+ gender: '',
50
+ birthdate: null,
51
+ yearsEstimated: 0,
52
+ monthsEstimated: 0,
53
+ birthdateEstimated: false,
54
+ telephoneNumber: '',
55
+ isDead: false,
56
+ deathDate: '',
57
+ deathCause: '',
58
+ relationships: [],
59
+ identifiers: {},
60
+ address: {},
61
+ });
62
+
63
+ useEffect(() => {
64
+ (async () => {
65
+ if (patientToEdit) {
66
+ const birthdateEstimated = !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate);
67
+ const [years = 0, months = 0] = patientToEdit.birthDate.split('-').map((val) => parseInt(val));
68
+ // Please refer: https://github.com/openmrs/openmrs-esm-patient-management/pull/697#issuecomment-1562706118
69
+ const estimatedMonthsAvailable = patientToEdit.birthDate.split('-').length > 1;
70
+ const yearsEstimated = birthdateEstimated ? Math.floor(dayjs().diff(patientToEdit.birthDate, 'month') / 12) : 0;
71
+ const monthsEstimated =
72
+ birthdateEstimated && estimatedMonthsAvailable ? dayjs().diff(patientToEdit.birthDate, 'month') % 12 : 0;
73
+
74
+ setInitialFormValues({
75
+ ...initialFormValues,
76
+ ...getFormValuesFromFhirPatient(patientToEdit),
77
+ address: getAddressFieldValuesFromFhirPatient(patientToEdit),
78
+ ...getPhonePersonAttributeValueFromFhirPatient(patientToEdit),
79
+ birthdateEstimated: !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate),
80
+ yearsEstimated,
81
+ monthsEstimated,
82
+ });
83
+ } else if (!isLoadingPatientToEdit && patientUuid) {
84
+ const registration = await getPatientRegistration(patientUuid);
85
+
86
+ if (!registration._patientRegistrationData.formValues) {
87
+ console.error(
88
+ `Found a queued offline patient registration for patient ${patientUuid}, but without form values. Not using these values.`,
89
+ );
90
+ return;
91
+ }
92
+
93
+ setInitialFormValues(registration._patientRegistrationData.formValues);
94
+ }
95
+ })();
96
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
97
+
98
+ // Set initial patient relationships
99
+ useEffect(() => {
100
+ if (!isLoadingRelationships && relationships) {
101
+ setInitialFormValues((initialFormValues) => ({
102
+ ...initialFormValues,
103
+ relationships,
104
+ }));
105
+ }
106
+ }, [isLoadingRelationships, relationships, setInitialFormValues]);
107
+
108
+ // Set Initial patient identifiers
109
+ useEffect(() => {
110
+ if (!isLoadingIdentifiers && identifiers) {
111
+ setInitialFormValues((initialFormValues) => ({
112
+ ...initialFormValues,
113
+ identifiers,
114
+ }));
115
+ }
116
+ }, [isLoadingIdentifiers, identifiers, setInitialFormValues]);
117
+
118
+ // Set Initial person attributes
119
+ useEffect(() => {
120
+ if (!isLoadingAttributes && attributes) {
121
+ let personAttributes = {};
122
+ attributes.forEach((attribute) => {
123
+ personAttributes[attribute.attributeType.uuid] =
124
+ attribute.attributeType.format === 'org.openmrs.Concept' && typeof attribute.value === 'object'
125
+ ? attribute.value?.uuid
126
+ : attribute.value;
127
+ });
128
+
129
+ setInitialFormValues((initialFormValues) => ({
130
+ ...initialFormValues,
131
+ attributes: personAttributes,
132
+ }));
133
+ }
134
+ }, [attributes, setInitialFormValues, isLoadingAttributes]);
135
+
136
+ // Set Initial registration encounters
137
+ useEffect(() => {
138
+ if (patientToEdit && encounters) {
139
+ setInitialFormValues((initialFormValues) => ({
140
+ ...initialFormValues,
141
+ obs: encounters as Record<string, string>,
142
+ }));
143
+ }
144
+ }, [encounters, patientToEdit]);
145
+
146
+ return [initialFormValues, setInitialFormValues];
147
+ }
148
+
149
+ export function useInitialAddressFieldValues(patientUuid: string, fallback = {}): [object, Dispatch<object>] {
150
+ const { isLoading, patient } = usePatient(patientUuid);
151
+ const [initialAddressFieldValues, setInitialAddressFieldValues] = useState<object>(fallback);
152
+
153
+ useEffect(() => {
154
+ (async () => {
155
+ if (patient) {
156
+ setInitialAddressFieldValues({
157
+ ...initialAddressFieldValues,
158
+ address: getAddressFieldValuesFromFhirPatient(patient),
159
+ });
160
+ } else if (!isLoading && patientUuid) {
161
+ const registration = await getPatientRegistration(patientUuid);
162
+ setInitialAddressFieldValues(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback);
163
+ }
164
+ })();
165
+ }, [isLoading, patient, patientUuid]);
166
+
167
+ return [initialAddressFieldValues, setInitialAddressFieldValues];
168
+ }
169
+
170
+ export function usePatientUuidMap(
171
+ patientUuid: string,
172
+ fallback: PatientUuidMapType = {},
173
+ ): [PatientUuidMapType, Dispatch<PatientUuidMapType>] {
174
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
175
+ const { data: attributes } = useInitialPersonAttributes(patientUuid);
176
+ const [patientUuidMap, setPatientUuidMap] = useState(fallback);
177
+
178
+ useEffect(() => {
179
+ if (patientToEdit) {
180
+ setPatientUuidMap({ ...patientUuidMap, ...getPatientUuidMapFromFhirPatient(patientToEdit) });
181
+ } else if (!isLoadingPatientToEdit && patientUuid) {
182
+ getPatientRegistration(patientUuid).then((registration) =>
183
+ setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback),
184
+ );
185
+ }
186
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
187
+
188
+ useEffect(() => {
189
+ if (attributes) {
190
+ setPatientUuidMap((prevPatientUuidMap) => ({
191
+ ...prevPatientUuidMap,
192
+ ...getPatientAttributeUuidMapForPatient(attributes),
193
+ }));
194
+ }
195
+ }, [attributes]);
196
+
197
+ return [patientUuidMap, setPatientUuidMap];
198
+ }
199
+
200
+ async function getPatientRegistration(patientUuid: string) {
201
+ const items = await getSynchronizationItems<PatientRegistration>(patientRegistration);
202
+ return items.find((item) => item._patientRegistrationData.formValues.patientUuid === patientUuid);
203
+ }
204
+
205
+ export function useInitialPatientIdentifiers(patientUuid: string): {
206
+ data: FormValues['identifiers'];
207
+ isLoading: boolean;
208
+ } {
209
+ const shouldFetch = !!patientUuid;
210
+
211
+ const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<PatientIdentifierResponse> }>, Error>(
212
+ shouldFetch
213
+ ? `/ws/rest/v1/patient/${patientUuid}/identifier?v=custom:(uuid,identifier,identifierType:(uuid,required,name),preferred)`
214
+ : null,
215
+ openmrsFetch,
216
+ );
217
+
218
+ const result: {
219
+ data: FormValues['identifiers'];
220
+ isLoading: boolean;
221
+ } = useMemo(() => {
222
+ const identifiers: FormValues['identifiers'] = {};
223
+
224
+ data?.data?.results?.forEach((patientIdentifier) => {
225
+ identifiers[camelCase(patientIdentifier.identifierType.name)] = {
226
+ identifierUuid: patientIdentifier.uuid,
227
+ preferred: patientIdentifier.preferred,
228
+ initialValue: patientIdentifier.identifier,
229
+ identifierValue: patientIdentifier.identifier,
230
+ identifierTypeUuid: patientIdentifier.identifierType.uuid,
231
+ identifierName: patientIdentifier.identifierType.name,
232
+ required: patientIdentifier.identifierType.required,
233
+ selectedSource: null,
234
+ autoGeneration: false,
235
+ };
236
+ });
237
+ return {
238
+ data: identifiers,
239
+ isLoading,
240
+ };
241
+ }, [data, error]);
242
+
243
+ return result;
244
+ }
245
+
246
+ function useInitialEncounters(patientUuid: string, patientToEdit: fhir.Patient) {
247
+ const { registrationObs } = useConfig() as RegistrationConfig;
248
+ const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<Encounter> }>>(
249
+ patientToEdit && registrationObs.encounterTypeUuid
250
+ ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=custom:(encounterDatetime,obs:(concept:ref,value:ref))&encounterType=${registrationObs.encounterTypeUuid}`
251
+ : null,
252
+ openmrsFetch,
253
+ );
254
+ const obs = data?.data.results.sort(latestFirstEncounter)?.at(0)?.obs;
255
+ const encounters = obs
256
+ ?.map(({ concept, value }) => ({
257
+ [(concept as OpenmrsResource).uuid]: typeof value === 'object' ? value?.uuid : value,
258
+ }))
259
+ .reduce((accu, curr) => Object.assign(accu, curr), {});
260
+
261
+ return { data: encounters, isLoading, error };
262
+ }
263
+
264
+ function useInitialPersonAttributes(personUuid: string) {
265
+ const shouldFetch = !!personUuid;
266
+ const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<PersonAttributeResponse> }>, Error>(
267
+ shouldFetch
268
+ ? `/ws/rest/v1/person/${personUuid}/attribute?v=custom:(uuid,display,attributeType:(uuid,display,format),value)`
269
+ : null,
270
+ openmrsFetch,
271
+ );
272
+ const result = useMemo(() => {
273
+ return {
274
+ data: data?.data?.results,
275
+ isLoading,
276
+ };
277
+ }, [data, error]);
278
+ return result;
279
+ }
280
+
281
+ function getPatientAttributeUuidMapForPatient(attributes: Array<PersonAttributeResponse>) {
282
+ const attributeUuidMap = {};
283
+ attributes.forEach((attribute) => {
284
+ attributeUuidMap[`attribute.${attribute?.attributeType?.uuid}`] = attribute?.uuid;
285
+ });
286
+ return attributeUuidMap;
287
+ }
@@ -0,0 +1,216 @@
1
+ import * as Yup from 'yup';
2
+ import {
3
+ type AddressValidationSchemaType,
4
+ type FormValues,
5
+ type PatientIdentifier,
6
+ type PatientUuidMapType,
7
+ type PatientIdentifierValue,
8
+ type Encounter,
9
+ } from './patient-registration.types';
10
+ import { parseDate } from '@openmrs/esm-framework';
11
+ import camelCase from 'lodash-es/camelCase';
12
+ import capitalize from 'lodash-es/capitalize';
13
+
14
+ export function parseAddressTemplateXml(addressTemplate: string) {
15
+ const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
16
+ const nameMappings = templateXmlDoc.querySelector('nameMappings');
17
+ const properties = nameMappings.getElementsByTagName('entry');
18
+ const validationSchemaObjs = Array.prototype.map.call(properties, (property: Element) => {
19
+ const name = property.getElementsByTagName('string')[0].innerHTML;
20
+ const label = property.getElementsByTagName('string')[1].innerHTML;
21
+ const regex = findElementValueInXmlDoc(name, 'elementRegex', templateXmlDoc) || '.*';
22
+ const regexFormat = findElementValueInXmlDoc(name, 'elementRegexFormats', templateXmlDoc) || '';
23
+
24
+ return {
25
+ name,
26
+ label,
27
+ regex,
28
+ regexFormat,
29
+ };
30
+ });
31
+
32
+ const addressValidationSchema = Yup.object(
33
+ validationSchemaObjs.reduce((final, current) => {
34
+ final[current.name] = Yup.string().matches(current.regex, current.regexFormat);
35
+ return final;
36
+ }, {}),
37
+ );
38
+
39
+ const addressFieldValues = Array.prototype.map.call(properties, (property: Element) => {
40
+ const name = property.getElementsByTagName('string')[0].innerHTML;
41
+ return {
42
+ name,
43
+ defaultValue: '',
44
+ };
45
+ });
46
+ return {
47
+ addressFieldValues,
48
+ addressValidationSchema,
49
+ };
50
+ }
51
+ export function parseAddressTemplateXmlOld(addressTemplate: string) {
52
+ const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
53
+ const nameMappings = templateXmlDoc.querySelector('nameMappings').querySelectorAll('property');
54
+ const validationSchemaObjs: AddressValidationSchemaType[] = Array.prototype.map.call(
55
+ nameMappings,
56
+ (nameMapping: Element) => {
57
+ const name = nameMapping.getAttribute('name');
58
+ const label = nameMapping.getAttribute('value');
59
+ const regex = findElementValueInXmlDoc(name, 'elementRegex', templateXmlDoc) || '.*';
60
+ const regexFormat = findElementValueInXmlDoc(name, 'elementRegexFormats', templateXmlDoc) || '';
61
+
62
+ return {
63
+ name,
64
+ label,
65
+ regex,
66
+ regexFormat,
67
+ };
68
+ },
69
+ );
70
+
71
+ const addressValidationSchema = Yup.object(
72
+ validationSchemaObjs.reduce((final, current) => {
73
+ final[current.name] = Yup.string().matches(current.regex, current.regexFormat);
74
+ return final;
75
+ }, {}),
76
+ );
77
+
78
+ const addressFieldValues: Array<{ name: string; defaultValue: string }> = Array.prototype.map.call(
79
+ nameMappings,
80
+ (nameMapping: Element) => {
81
+ const name = nameMapping.getAttribute('name');
82
+ const defaultValue = findElementValueInXmlDoc(name, 'elementDefaults', templateXmlDoc) ?? '';
83
+ return { name, defaultValue };
84
+ },
85
+ );
86
+
87
+ return {
88
+ addressFieldValues,
89
+ addressValidationSchema,
90
+ };
91
+ }
92
+
93
+ function findElementValueInXmlDoc(fieldName: string, elementSelector: string, doc: XMLDocument) {
94
+ return doc.querySelector(elementSelector)?.querySelector(`[name=${fieldName}]`)?.getAttribute('value') ?? null;
95
+ }
96
+
97
+ export function scrollIntoView(viewId: string) {
98
+ document.getElementById(viewId).scrollIntoView({
99
+ behavior: 'smooth',
100
+ block: 'center',
101
+ inline: 'center',
102
+ });
103
+ }
104
+
105
+ export function cancelRegistration() {
106
+ window.history.back();
107
+ }
108
+
109
+ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
110
+ const result = {} as FormValues;
111
+ const patientName = patient.name[0];
112
+ const additionalPatientName = patient.name[1];
113
+
114
+ result.patientUuid = patient.id;
115
+ result.givenName = patientName?.given[0];
116
+ result.middleName = patientName?.given[1];
117
+ result.familyName = patientName?.family;
118
+ result.addNameInLocalLanguage = !!additionalPatientName ? true : undefined;
119
+ result.additionalGivenName = additionalPatientName?.given[0];
120
+ result.additionalMiddleName = additionalPatientName?.given[1];
121
+ result.additionalFamilyName = additionalPatientName?.family;
122
+
123
+ result.gender = patient.gender;
124
+ result.birthdate = patient.birthDate ? parseDate(patient.birthDate) : undefined;
125
+ result.telephoneNumber = patient.telecom ? patient.telecom[0].value : '';
126
+
127
+ if (patient.deceasedBoolean || patient.deceasedDateTime) {
128
+ result.isDead = true;
129
+ result.deathDate = patient.deceasedDateTime ? patient.deceasedDateTime.split('T')[0] : '';
130
+ }
131
+
132
+ return {
133
+ ...result,
134
+ ...patient.identifier.map((identifier) => {
135
+ const key = camelCase(identifier.system || identifier.type.text);
136
+ return { [key]: identifier.value };
137
+ }),
138
+ };
139
+ }
140
+
141
+ export function getAddressFieldValuesFromFhirPatient(patient: fhir.Patient) {
142
+ const result = {};
143
+ const address = patient.address?.[0];
144
+
145
+ if (address) {
146
+ for (const key of Object.keys(address)) {
147
+ switch (key) {
148
+ case 'city':
149
+ result['cityVillage'] = address[key];
150
+ break;
151
+ case 'state':
152
+ result['stateProvince'] = address[key];
153
+ break;
154
+ case 'district':
155
+ result['countyDistrict'] = address[key];
156
+ break;
157
+ case 'extension':
158
+ address[key].forEach((ext) => {
159
+ ext.extension.forEach((extension) => {
160
+ result[extension.url.split('#')[1]] = extension.valueString;
161
+ });
162
+ });
163
+ break;
164
+ default:
165
+ if (key === 'country' || key === 'postalCode') {
166
+ result[key] = address[key];
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ return result;
173
+ }
174
+
175
+ export function getPatientUuidMapFromFhirPatient(patient: fhir.Patient): PatientUuidMapType {
176
+ const patientName = patient.name[0];
177
+ const additionalPatientName = patient.name[1];
178
+ const address = patient.address?.[0];
179
+
180
+ return {
181
+ preferredNameUuid: patientName?.id,
182
+ additionalNameUuid: additionalPatientName?.id,
183
+ preferredAddressUuid: address?.id,
184
+ ...patient.identifier.map((identifier) => {
185
+ const key = camelCase(identifier.system || identifier.type.text);
186
+ return { [key]: { uuid: identifier.id, value: identifier.value } };
187
+ }),
188
+ };
189
+ }
190
+
191
+ export function getPatientIdentifiersFromFhirPatient(patient: fhir.Patient): Array<PatientIdentifier> {
192
+ return patient.identifier.map((identifier) => {
193
+ return {
194
+ uuid: identifier.id,
195
+ identifier: identifier.value,
196
+ };
197
+ });
198
+ }
199
+
200
+ export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient) {
201
+ const result = {};
202
+ if (patient.telecom) {
203
+ result['phone'] = patient.telecom[0].value;
204
+ }
205
+ return result;
206
+ }
207
+
208
+ export const filterUndefinedPatientIdenfier = (patientIdenfiers) =>
209
+ Object.fromEntries(
210
+ Object.entries<PatientIdentifierValue>(patientIdenfiers).filter(
211
+ ([key, value]) => value.identifierValue !== undefined,
212
+ ),
213
+ );
214
+
215
+ export const latestFirstEncounter = (a: Encounter, b: Encounter) =>
216
+ new Date(b.encounterDatetime).getTime() - new Date(a.encounterDatetime).getTime();