@openmrs/esm-patient-registration-app 3.0.2-pre.310 → 3.0.2-pre.332

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 (63) hide show
  1. package/dist/258.js +2 -0
  2. package/dist/258.js.map +1 -0
  3. package/dist/272.js +1 -1
  4. package/dist/272.js.map +1 -1
  5. package/dist/295.js +1 -1
  6. package/dist/295.js.map +1 -1
  7. package/dist/30.js +1 -1
  8. package/dist/30.js.map +1 -1
  9. package/dist/309.js +3 -0
  10. package/dist/{770.js.LICENSE.txt → 309.js.LICENSE.txt} +0 -0
  11. package/dist/309.js.map +1 -0
  12. package/dist/323.js +1 -1
  13. package/dist/323.js.map +1 -1
  14. package/dist/324.js +1 -1
  15. package/dist/324.js.map +1 -1
  16. package/dist/401.js +2 -0
  17. package/dist/401.js.map +1 -0
  18. package/dist/574.js +1 -1
  19. package/dist/71.js +1 -1
  20. package/dist/71.js.map +1 -1
  21. package/dist/817.js +1 -1
  22. package/dist/817.js.map +1 -1
  23. package/dist/831.js +3 -0
  24. package/dist/{932.js.LICENSE.txt → 831.js.LICENSE.txt} +0 -0
  25. package/dist/831.js.map +1 -0
  26. package/dist/916.js +2 -0
  27. package/dist/916.js.map +1 -0
  28. package/dist/932.js +1 -2
  29. package/dist/932.js.map +1 -1
  30. package/dist/openmrs-esm-patient-registration-app.js +1 -1
  31. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +103 -80
  32. package/dist/openmrs-esm-patient-registration-app.js.map +1 -1
  33. package/package.json +2 -2
  34. package/src/index.ts +7 -1
  35. package/src/offline.resources.ts +34 -30
  36. package/src/offline.ts +9 -9
  37. package/src/patient-registration/field/id/id-field.component.tsx +15 -12
  38. package/src/patient-registration/{ui-components/identifier-selection-overlay/index.tsx → field/id/identifier-selection-overlay.tsx} +29 -30
  39. package/src/patient-registration/{ui-components/identifier-selection-overlay → field/id}/identifier-selection.scss +0 -0
  40. package/src/patient-registration/form-manager.test.ts +5 -14
  41. package/src/patient-registration/form-manager.ts +104 -72
  42. package/src/patient-registration/input/basic-input/input/input.component.tsx +2 -8
  43. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +23 -34
  44. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +2 -0
  45. package/src/patient-registration/patient-registration-hooks.ts +71 -62
  46. package/src/patient-registration/patient-registration-types.tsx +30 -17
  47. package/src/patient-registration/patient-registration-utils.ts +4 -3
  48. package/src/patient-registration/patient-registration.component.tsx +17 -19
  49. package/src/patient-registration/patient-registration.resource.tsx +6 -5
  50. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +6 -6
  51. package/src/root.component.tsx +2 -2
  52. package/src/widgets/delete-identifier-confirmation-modal.tsx +41 -0
  53. package/src/widgets/delete-identifier-modal.scss +32 -0
  54. package/src/widgets/edit-patient-details-button.component.tsx +1 -1
  55. package/translations/en.json +1 -2
  56. package/dist/373.js +0 -2
  57. package/dist/373.js.map +0 -1
  58. package/dist/764.js +0 -2
  59. package/dist/764.js.map +0 -1
  60. package/dist/770.js +0 -3
  61. package/dist/770.js.map +0 -1
  62. package/dist/94.js +0 -2
  63. package/dist/94.js.map +0 -1
@@ -1,8 +1,10 @@
1
1
  import FormManager from './form-manager';
2
+ import { FormValues } from './patient-registration-types';
2
3
 
3
4
  jest.mock('./patient-registration.resource');
4
5
 
5
- const formValues = {
6
+ const formValues: FormValues = {
7
+ patientUuid: '',
6
8
  givenName: '',
7
9
  middleName: '',
8
10
  familyName: '',
@@ -42,20 +44,9 @@ const identifierSource = {
42
44
  describe('FormManager', () => {
43
45
  describe('createIdentifiers', () => {
44
46
  it('uses the uuid of a field name if it exists', async () => {
45
- const result = await FormManager.getPatientIdentifiersToCreate(
47
+ const result = await FormManager.savePatientIdentifiers(
48
+ undefined,
46
49
  formValues.identifiers,
47
- [
48
- {
49
- name: 'foo',
50
- required: false,
51
- isPrimary: true,
52
- fieldName: 'givenName',
53
- uuid: 'identifierType',
54
- format: 'n/a',
55
- autoGenerationSource: identifierSource,
56
- identifierSources: [],
57
- },
58
- ],
59
50
  'Nyc',
60
51
  new AbortController(),
61
52
  );
@@ -10,6 +10,7 @@ import {
10
10
  CapturePhotoProps,
11
11
  PatientIdentifier,
12
12
  PatientIdentifierValue,
13
+ PatientRegistration,
13
14
  } from './patient-registration-types';
14
15
  import {
15
16
  addPatientIdentifier,
@@ -24,7 +25,7 @@ import {
24
25
  import isEqual from 'lodash-es/isEqual';
25
26
 
26
27
  export type SavePatientForm = (
27
- patientUuid: string | undefined,
28
+ isNewPatient: boolean,
28
29
  values: FormValues,
29
30
  patientUuidMap: PatientUuidMapType,
30
31
  initialAddressFieldValues: Record<string, any>,
@@ -38,7 +39,7 @@ export type SavePatientForm = (
38
39
 
39
40
  export default class FormManager {
40
41
  static async savePatientFormOffline(
41
- patientUuid = v4(),
42
+ isNewPatient: boolean,
42
43
  values: FormValues,
43
44
  patientUuidMap: PatientUuidMapType,
44
45
  initialAddressFieldValues: Record<string, any>,
@@ -48,10 +49,12 @@ export default class FormManager {
48
49
  currentLocation: string,
49
50
  personAttributeSections: any,
50
51
  ): Promise<null> {
51
- await queueSynchronizationItem(
52
- patientRegistration,
53
- {
54
- patientUuid,
52
+ const syncItem: PatientRegistration = {
53
+ fhirPatient: FormManager.mapPatientToFhirPatient(
54
+ FormManager.getPatientToCreate(values, personAttributeSections, patientUuidMap, initialAddressFieldValues, []),
55
+ ),
56
+ _patientRegistrationData: {
57
+ isNewPatient,
55
58
  formValues: values,
56
59
  patientUuidMap,
57
60
  initialAddressFieldValues,
@@ -61,18 +64,19 @@ export default class FormManager {
61
64
  currentLocation,
62
65
  personAttributeSections,
63
66
  },
64
- {
65
- id: patientUuid,
66
- displayName: 'Patient registration',
67
- dependencies: [],
68
- },
69
- );
67
+ };
68
+
69
+ await queueSynchronizationItem(patientRegistration, syncItem, {
70
+ id: values.patientUuid,
71
+ displayName: 'Patient registration',
72
+ dependencies: [],
73
+ });
70
74
 
71
75
  return null;
72
76
  }
73
77
 
74
78
  static async savePatientFormOnline(
75
- patientUuid: string | undefined,
79
+ isNewPatient: boolean,
76
80
  values: FormValues,
77
81
  patientUuidMap: PatientUuidMapType,
78
82
  initialAddressFieldValues: Record<string, any>,
@@ -83,19 +87,13 @@ export default class FormManager {
83
87
  personAttributeSections: any,
84
88
  abortController: AbortController,
85
89
  ): Promise<string> {
86
- const patientIdentifiers: PatientIdentifier[] = await FormManager.getPatientIdentifiersToCreate(
90
+ const patientIdentifiers: Array<PatientIdentifier> = await FormManager.savePatientIdentifiers(
91
+ values.patientUuid,
87
92
  values.identifiers,
88
- identifierTypes,
89
93
  currentLocation,
90
94
  abortController,
91
95
  );
92
96
 
93
- if (patientUuid) {
94
- await Promise.all(
95
- FormManager.savePatientIdentifiers(patientUuid, patientIdentifiers, values.identifiers, abortController),
96
- );
97
- }
98
-
99
97
  const createdPatient = FormManager.getPatientToCreate(
100
98
  values,
101
99
  personAttributeSections,
@@ -104,11 +102,15 @@ export default class FormManager {
104
102
  patientIdentifiers,
105
103
  );
106
104
 
107
- FormManager.getDeletedNames(patientUuidMap).forEach(async (name) => {
105
+ FormManager.getDeletedNames(values.patientUuid, patientUuidMap).forEach(async (name) => {
108
106
  await deletePersonName(name.nameUuid, name.personUuid, abortController);
109
107
  });
110
108
 
111
- const savePatientResponse = await savePatient(abortController, createdPatient, patientUuidMap.patientUuid);
109
+ const savePatientResponse = await savePatient(
110
+ abortController,
111
+ createdPatient,
112
+ isNewPatient ? undefined : values.patientUuid,
113
+ );
112
114
 
113
115
  if (savePatientResponse.ok) {
114
116
  await Promise.all(
@@ -144,41 +146,39 @@ export default class FormManager {
144
146
  return savePatientResponse.data.uuid;
145
147
  }
146
148
 
147
- static getPatientIdentifiersToCreate(
148
- patientIdentifiers: PatientIdentifierValue[], // values.identifiers
149
- identifierTypes: Array<PatientIdentifierType>,
149
+ static async savePatientIdentifiers(
150
+ patientUuid: string,
151
+ patientIdentifiers: Array<PatientIdentifierValue>, // values.identifiers
150
152
  location: string,
151
153
  abortController: AbortController,
152
154
  ): Promise<Array<PatientIdentifier>> {
153
- const identifierTypeRequests: Array<Promise<PatientIdentifier>> = patientIdentifiers.map(
154
- async (patientIdentifier) => {
155
- const { identifierType: identifierTypeUuid, identifier, uuid, action, source } = patientIdentifier;
156
- const identifierType = identifierTypes.find((identifierType) => identifierType.uuid === identifierTypeUuid);
157
- if (identifier) {
158
- return {
155
+ let identifierTypeRequests = patientIdentifiers
156
+ .filter((identifier) => identifier.action !== 'DELETE' && identifier.action !== 'NONE')
157
+ .map(async (patientIdentifier) => {
158
+ const { identifierTypeUuid, identifier, uuid, action, source, preferred, autoGeneration } = patientIdentifier;
159
+ if (identifier || (source && autoGeneration)) {
160
+ const identifierValue = identifier
161
+ ? identifier
162
+ : await (
163
+ await generateIdentifier(patientIdentifier.source.uuid, abortController)
164
+ ).data.identifier;
165
+ const identifierToCreate = {
159
166
  uuid,
160
- identifier,
167
+ identifier: identifierValue,
161
168
  identifierType: identifierTypeUuid,
162
- location: location,
163
- preferred: identifierType?.isPrimary,
164
- };
165
- } else if (source && patientIdentifier?.autoGeneration) {
166
- const generateIdentifierResponse = await generateIdentifier(patientIdentifier.source.uuid, abortController);
167
- return {
168
- // is this undefined?
169
- uuid,
170
- identifier: generateIdentifierResponse.data.identifier,
171
- identifierType: identifierTypeUuid,
172
- location: location,
173
- preferred: identifierType?.isPrimary,
174
- };
175
- } else if (action === 'DELETE') {
176
- return {
177
- uuid,
178
- identifier: identifierTypeUuid,
179
169
  location,
180
- preferred: identifierType.isPrimary,
170
+ preferred,
181
171
  };
172
+
173
+ if (patientUuid) {
174
+ if (action === 'ADD') {
175
+ await addPatientIdentifier(patientUuid, identifierToCreate, abortController);
176
+ } else if (action === 'UPDATE') {
177
+ await updatePatientIdentifier(patientUuid, uuid, identifierToCreate.identifier, abortController);
178
+ }
179
+ }
180
+
181
+ return identifierToCreate;
182
182
  } else {
183
183
  // This is a case that should not occur.
184
184
  // If it did, the subsequent network request (when creating the patient) would fail with
@@ -186,18 +186,26 @@ export default class FormManager {
186
186
  // Better stop early.
187
187
  throw new Error('No approach for generating a patient identifier could be found.');
188
188
  }
189
- },
190
- );
189
+ });
190
+
191
+ if (patientUuid) {
192
+ patientIdentifiers
193
+ .filter((patientIdentifier) => patientIdentifier.action === 'DELETE')
194
+ .forEach(
195
+ async (patientIdentifier) =>
196
+ await deletePatientIdentifier(patientUuid, patientIdentifier.uuid, abortController),
197
+ );
198
+ }
191
199
 
192
200
  return Promise.all(identifierTypeRequests);
193
201
  }
194
202
 
195
- static getDeletedNames(patientUuidMap: PatientUuidMapType) {
203
+ static getDeletedNames(patientUuid: string, patientUuidMap: PatientUuidMapType) {
196
204
  if (patientUuidMap?.additionalNameUuid) {
197
205
  return [
198
206
  {
199
207
  nameUuid: patientUuidMap.additionalNameUuid,
200
- personUuid: patientUuidMap.patientUuid,
208
+ personUuid: patientUuid,
201
209
  },
202
210
  ];
203
211
  }
@@ -218,9 +226,9 @@ export default class FormManager {
218
226
  }
219
227
 
220
228
  return {
221
- uuid: patientUuidMap['patientUuid'],
229
+ uuid: values.patientUuid,
222
230
  person: {
223
- uuid: patientUuidMap['patientUuid'],
231
+ uuid: values.patientUuid,
224
232
  names: FormManager.getNames(values, patientUuidMap),
225
233
  gender: values.gender.charAt(0),
226
234
  birthdate: values.birthdate,
@@ -293,20 +301,44 @@ export default class FormManager {
293
301
  };
294
302
  }
295
303
 
296
- static savePatientIdentifiers(
297
- patientUuid: string,
298
- patientIdentifiers: PatientIdentifier[],
299
- identifiers: PatientIdentifierValue[],
300
- abortController: AbortController,
301
- ) {
302
- return identifiers.map((identifier, index) => {
303
- if (identifier.action === 'ADD') {
304
- return addPatientIdentifier(patientUuid, patientIdentifiers[index], abortController);
305
- } else if (identifier.action === 'UPDATE') {
306
- return updatePatientIdentifier(patientUuid, patientIdentifiers[index], abortController);
307
- } else if (identifier.action === 'DELETE') {
308
- deletePatientIdentifier(patientUuid, patientIdentifiers[index]?.uuid, abortController);
309
- }
310
- });
304
+ static mapPatientToFhirPatient(patient: Partial<Patient>): fhir.Patient {
305
+ // Important:
306
+ // When changing this code, ideally assume that `patient` can be missing any attribute.
307
+ // The `fhir.Patient` provides us with the benefit that all properties are nullable and thus
308
+ // not required (technically, at least). -> Even if we cannot map some props here, we still
309
+ // provide a valid fhir.Patient object. The various patient chart modules should be able to handle
310
+ // such missing props correctly (and should be updated if they don't).
311
+
312
+ // Gender in the original object only uses a single letter. fhir.Patient expects a full string.
313
+ const genderMap = {
314
+ M: 'male',
315
+ F: 'female',
316
+ O: 'other',
317
+ U: 'unknown',
318
+ };
319
+
320
+ // Mapping inspired by:
321
+ // https://github.com/openmrs/openmrs-module-fhir/blob/669b3c52220bb9abc622f815f4dc0d8523687a57/api/src/main/java/org/openmrs/module/fhir/api/util/FHIRPatientUtil.java#L36
322
+ // https://github.com/openmrs/openmrs-esm-patient-management/blob/94e6f637fb37cf4984163c355c5981ea6b8ca38c/packages/esm-patient-search-app/src/patient-search-result/patient-search-result.component.tsx#L21
323
+ // Update as required.
324
+ return {
325
+ id: patient.uuid,
326
+ gender: genderMap[patient.person?.gender],
327
+ birthDate: patient.person?.birthdate,
328
+ deceasedBoolean: patient.person.dead,
329
+ deceasedDateTime: patient.person.deathDate,
330
+ name: patient.person?.names?.map((name) => ({
331
+ given: [name.givenName, name.middleName].filter(Boolean),
332
+ family: name.familyName,
333
+ })),
334
+ address: patient.person?.addresses.map((address) => ({
335
+ city: address.cityVillage,
336
+ country: address.country,
337
+ postalCode: address.postalCode,
338
+ state: address.stateProvince,
339
+ use: 'home',
340
+ })),
341
+ telecom: patient.person.attributes?.filter((attribute) => attribute.attributeType === 'Telephone Number'),
342
+ };
311
343
  }
312
344
  }
@@ -1,15 +1,9 @@
1
1
  import React, { useMemo } from 'react';
2
- import { TextInput } from 'carbon-components-react';
2
+ import { TextInput, TextInputProps } from 'carbon-components-react';
3
3
  import { useField } from 'formik';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- interface InputProps {
7
- id: string;
8
- name: string;
9
- labelText: string;
10
- light: boolean;
11
- disabled?: boolean;
12
- placeholder?: string;
6
+ interface InputProps extends TextInputProps {
13
7
  checkWarning?(value: string): string;
14
8
  }
15
9
 
@@ -1,13 +1,13 @@
1
- import * as Yup from 'yup';
2
1
  import React, { useState, useEffect, useCallback, useContext, useMemo } from 'react';
3
2
  import styles from '../../input.scss';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import { Input } from '../../basic-input/input/input.component';
6
5
  import { PatientIdentifierValue } from '../../../patient-registration-types';
7
6
  import { PatientRegistrationContext } from '../../../patient-registration-context';
8
- import { TrashCan16, Edit16, Undo16 } from '@carbon/icons-react';
7
+ import { TrashCan16, Edit16 } from '@carbon/icons-react';
9
8
  import { Button } from 'carbon-components-react';
10
9
  import { ResourcesContext } from '../../../../offline.resources';
10
+ import { showModal } from '@openmrs/esm-framework';
11
11
 
12
12
  interface IdentifierInputProps {
13
13
  patientIdentifier: PatientIdentifierValue;
@@ -16,23 +16,21 @@ interface IdentifierInputProps {
16
16
  }
17
17
 
18
18
  export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, index, remove }) => {
19
- const { patientIdentifiers: identifierTypes } = useContext(ResourcesContext);
19
+ const { identifierTypes } = useContext(ResourcesContext);
20
20
  const identifierType = useMemo(
21
- () => identifierTypes.find((identifierType) => identifierType.uuid === patientIdentifier.identifierType),
22
- [patientIdentifier],
21
+ () => identifierTypes.find((identifierType) => identifierType.uuid === patientIdentifier.identifierTypeUuid),
22
+ [patientIdentifier, identifierTypes],
23
23
  );
24
- const { validationSchema, setValidationSchema, setFieldValue, values } = React.useContext(PatientRegistrationContext);
24
+
25
+ const { setFieldValue } = React.useContext(PatientRegistrationContext);
25
26
  const { source, action, identifier } = patientIdentifier;
26
- const identifierName = identifierType.name;
27
+ const identifierName = identifierType?.name;
27
28
  const fieldName = `identifiers[${index}].identifier`;
28
29
  const { t } = useTranslation();
29
30
  const [option, setAutoGenerationOption] = useState({
30
31
  manualEntryEnabled: source ? true : undefined,
31
32
  automaticGenerationEnabled: undefined,
32
33
  });
33
- const [identifierValidationSchema, setIdentifierValidationSchema] = useState(Yup.object({}));
34
- // This will save the value, if value is to be restored is needed
35
- const [restoredValue, setRestoredValue] = useState(patientIdentifier.identifier);
36
34
 
37
35
  useEffect(() => {
38
36
  if (source && source.autoGenerationOption && source.autoGenerationOption.automaticGenerationEnabled) {
@@ -61,16 +59,15 @@ export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentif
61
59
  manualEntryEnabled: true,
62
60
  automaticGenerationEnabled: false,
63
61
  });
64
- setFieldValue(`identifiers[${index}].autoGeneration`, true);
62
+ setFieldValue(`identifiers[${index}].autoGeneration`, false);
65
63
  }
66
64
  }, [source]);
67
65
 
68
66
  const handleEdit = useCallback(() => {
69
- setRestoredValue(patientIdentifier.identifier);
70
67
  setFieldValue(`identifiers[${index}]`, {
71
68
  ...patientIdentifier,
72
69
  action: 'UPDATE',
73
- source: identifierType.identifierSources.length > 0 ? identifierType.identifierSources[0] : null,
70
+ source: identifierType?.identifierSources?.[0],
74
71
  } as PatientIdentifierValue);
75
72
  }, [patientIdentifier]);
76
73
 
@@ -78,18 +75,18 @@ export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentif
78
75
  if (action === 'ADD') {
79
76
  remove(index);
80
77
  } else {
81
- setFieldValue(`identifiers[${index}].action`, 'DELETE');
78
+ const confirmDeleteIdentifierModal = showModal('delete-identifier-confirmation-modal', {
79
+ deleteIdentifier: (deleteIdentifier) => {
80
+ if (deleteIdentifier) {
81
+ setFieldValue(`identifiers[${index}].action`, 'DELETE');
82
+ }
83
+ confirmDeleteIdentifierModal();
84
+ },
85
+ identifierName,
86
+ identifierValue: identifier,
87
+ });
82
88
  }
83
- }, [patientIdentifier]);
84
-
85
- const handleUndo = useCallback(() => {
86
- setFieldValue(`identifiers[${index}]`, {
87
- ...patientIdentifier,
88
- action: 'NONE',
89
- source: null,
90
- identifier: restoredValue,
91
- } as PatientIdentifierValue);
92
- }, [patientIdentifier, restoredValue]);
89
+ }, [action, patientIdentifier, identifierName, identifier]);
93
90
 
94
91
  const autoGenerated = !option.manualEntryEnabled || (option.manualEntryEnabled && option.automaticGenerationEnabled);
95
92
 
@@ -107,6 +104,7 @@ export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentif
107
104
  labelText={identifierName}
108
105
  name={fieldName}
109
106
  disabled={!option.manualEntryEnabled}
107
+ required
110
108
  />
111
109
  ) : (
112
110
  <div className={styles.textID}>
@@ -117,16 +115,7 @@ export const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentif
117
115
  </div>
118
116
  )}
119
117
  <div>
120
- {patientIdentifier.action === 'UPDATE' && (
121
- <Button
122
- kind="ghost"
123
- onClick={handleUndo}
124
- iconDescription={t('restoreIdentifierTooltip', 'Restore')}
125
- hasIconOnly>
126
- <Undo16 />
127
- </Button>
128
- )}
129
- {patientIdentifier.action === 'NONE' && (
118
+ {!(identifierType.isPrimary || identifierType.required) && patientIdentifier.action === 'NONE' && (
130
119
  <Button kind="ghost" onClick={handleEdit} iconDescription={t('editIdentifierTooltip', 'Edit')} hasIconOnly>
131
120
  <Edit16 />
132
121
  </Button>
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { v4 } from 'uuid';
2
3
  import { FormValues } from '../../patient-registration-types';
3
4
  import styles from './../input.scss';
4
5
 
@@ -7,6 +8,7 @@ interface DummyDataInputProps {
7
8
  }
8
9
 
9
10
  export const dummyFormValues: FormValues = {
11
+ patientUuid: v4(),
10
12
  givenName: 'John',
11
13
  middleName: '',
12
14
  familyName: 'Smith',
@@ -1,71 +1,81 @@
1
1
  import { getSynchronizationItems, usePatient } from '@openmrs/esm-framework';
2
2
  import { Dispatch, useEffect, useState } from 'react';
3
+ import { v4 } from 'uuid';
3
4
  import { patientRegistration } from '../constants';
4
- import {
5
- FormValues,
6
- PatientIdentifierValue,
7
- PatientRegistration,
8
- PatientUuidMapType,
9
- } from './patient-registration-types';
5
+ import { FormValues, PatientRegistration, PatientUuidMapType } from './patient-registration-types';
10
6
  import {
11
7
  getAddressFieldValuesFromFhirPatient,
12
8
  getFormValuesFromFhirPatient,
13
- getPatientIdentifiers,
9
+ getInitialPatientIdentifiers,
14
10
  getPatientUuidMapFromFhirPatient,
15
11
  getPhonePersonAttributeValueFromFhirPatient,
16
12
  } from './patient-registration-utils';
17
13
 
18
- const blankFormValues: FormValues = {
19
- givenName: '',
20
- middleName: '',
21
- familyName: '',
22
- unidentifiedPatient: false,
23
- additionalGivenName: '',
24
- additionalMiddleName: '',
25
- additionalFamilyName: '',
26
- addNameInLocalLanguage: false,
27
- gender: '',
28
- birthdate: null,
29
- yearsEstimated: 0,
30
- monthsEstimated: 0,
31
- birthdateEstimated: false,
32
- telephoneNumber: '',
33
- address1: '',
34
- address2: '',
35
- cityVillage: '',
36
- stateProvince: '',
37
- country: '',
38
- postalCode: '',
39
- isDead: false,
40
- deathDate: '',
41
- deathCause: '',
42
- relationships: [{ relatedPerson: '', relationship: '' }],
43
- identifiers: [],
44
- };
45
-
46
- export function useInitialFormValues(
47
- patientUuid: string,
48
- fallback = blankFormValues,
49
- ): [FormValues, Dispatch<FormValues>] {
50
- const { isLoading, patient } = usePatient(patientUuid);
51
- const [initialFormValues, setInitialFormValues] = useState<FormValues>(fallback);
14
+ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
15
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
16
+ const [initialFormValues, setInitialFormValues] = useState<FormValues>({
17
+ patientUuid: v4(),
18
+ givenName: '',
19
+ middleName: '',
20
+ familyName: '',
21
+ unidentifiedPatient: false,
22
+ additionalGivenName: '',
23
+ additionalMiddleName: '',
24
+ additionalFamilyName: '',
25
+ addNameInLocalLanguage: false,
26
+ gender: '',
27
+ birthdate: null,
28
+ yearsEstimated: 0,
29
+ monthsEstimated: 0,
30
+ birthdateEstimated: false,
31
+ telephoneNumber: '',
32
+ address1: '',
33
+ address2: '',
34
+ cityVillage: '',
35
+ stateProvince: '',
36
+ country: '',
37
+ postalCode: '',
38
+ isDead: false,
39
+ deathDate: '',
40
+ deathCause: '',
41
+ relationships: [{ relatedPerson: '', relationship: '' }],
42
+ identifiers: [],
43
+ });
52
44
 
53
45
  useEffect(() => {
54
46
  (async () => {
55
- if (patient) {
47
+ if (patientToEdit) {
56
48
  setInitialFormValues({
57
49
  ...initialFormValues,
58
- ...getFormValuesFromFhirPatient(patient),
59
- ...getAddressFieldValuesFromFhirPatient(patient),
60
- ...getPhonePersonAttributeValueFromFhirPatient(patient),
61
- identifiers: await getPatientIdentifiers(patientUuid),
50
+ ...getFormValuesFromFhirPatient(patientToEdit),
51
+ ...getAddressFieldValuesFromFhirPatient(patientToEdit),
52
+ ...getPhonePersonAttributeValueFromFhirPatient(patientToEdit),
62
53
  });
63
- } else if (!isLoading && patientUuid) {
54
+ } else if (!isLoadingPatientToEdit && patientUuid) {
64
55
  const registration = await getPatientRegistration(patientUuid);
65
- setInitialFormValues(registration?.formValues ?? fallback);
56
+
57
+ if (!registration._patientRegistrationData.formValues) {
58
+ console.error(
59
+ `Found a queued offline patient registration for patient ${patientUuid}, but without form values. Not using these values.`,
60
+ );
61
+ return;
62
+ }
63
+
64
+ setInitialFormValues(registration._patientRegistrationData.formValues);
66
65
  }
67
66
  })();
68
- }, [isLoading, patient, patientUuid]);
67
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
68
+
69
+ useEffect(() => {
70
+ if (patientUuid) {
71
+ getInitialPatientIdentifiers(patientUuid).then((identifiers) =>
72
+ setInitialFormValues((initialFormValues) => ({
73
+ ...initialFormValues,
74
+ identifiers,
75
+ })),
76
+ );
77
+ }
78
+ }, [patientUuid]);
69
79
 
70
80
  return [initialFormValues, setInitialFormValues];
71
81
  }
@@ -83,7 +93,7 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {})
83
93
  });
84
94
  } else if (!isLoading && patientUuid) {
85
95
  const registration = await getPatientRegistration(patientUuid);
86
- setInitialAddressFieldValues(registration?.initialAddressFieldValues ?? fallback);
96
+ setInitialAddressFieldValues(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback);
87
97
  }
88
98
  })();
89
99
  }, [isLoading, patient, patientUuid]);
@@ -93,26 +103,25 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {})
93
103
 
94
104
  export function usePatientUuidMap(
95
105
  patientUuid: string,
96
- fallback = {},
106
+ fallback: PatientUuidMapType = {},
97
107
  ): [PatientUuidMapType, Dispatch<PatientUuidMapType>] {
98
- const { isLoading, patient } = usePatient(patientUuid);
108
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
99
109
  const [patientUuidMap, setPatientUuidMap] = useState(fallback);
100
110
 
101
111
  useEffect(() => {
102
- (async () => {
103
- if (patient) {
104
- setPatientUuidMap({ ...patientUuidMap, ...getPatientUuidMapFromFhirPatient(patient) });
105
- } else if (!isLoading && patientUuid) {
106
- const registration = await getPatientRegistration(patientUuid);
107
- setPatientUuidMap(registration?.initialAddressFieldValues ?? fallback);
108
- }
109
- })();
110
- }, [isLoading, patient, patientUuid]);
112
+ if (patientToEdit) {
113
+ setPatientUuidMap({ ...patientUuidMap, ...getPatientUuidMapFromFhirPatient(patientToEdit) });
114
+ } else if (!isLoadingPatientToEdit && patientUuid) {
115
+ getPatientRegistration(patientUuid).then((registration) =>
116
+ setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback),
117
+ );
118
+ }
119
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
111
120
 
112
121
  return [patientUuidMap, setPatientUuidMap];
113
122
  }
114
123
 
115
124
  async function getPatientRegistration(patientUuid: string) {
116
125
  const items = await getSynchronizationItems<PatientRegistration>(patientRegistration);
117
- return items.find((item) => item.patientUuid === patientUuid);
126
+ return items.find((item) => item._patientRegistrationData.formValues.patientUuid === patientUuid);
118
127
  }