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

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 (44) hide show
  1. package/dist/272.js +1 -1
  2. package/dist/272.js.map +1 -1
  3. package/dist/295.js +1 -1
  4. package/dist/295.js.map +1 -1
  5. package/dist/30.js +1 -1
  6. package/dist/30.js.map +1 -1
  7. package/dist/309.js +3 -0
  8. package/dist/{770.js.LICENSE.txt → 309.js.LICENSE.txt} +0 -0
  9. package/dist/309.js.map +1 -0
  10. package/dist/323.js +1 -1
  11. package/dist/323.js.map +1 -1
  12. package/dist/401.js +2 -0
  13. package/dist/401.js.map +1 -0
  14. package/dist/71.js +1 -1
  15. package/dist/71.js.map +1 -1
  16. package/dist/817.js +1 -1
  17. package/dist/817.js.map +1 -1
  18. package/dist/831.js +3 -0
  19. package/dist/{932.js.LICENSE.txt → 831.js.LICENSE.txt} +0 -0
  20. package/dist/831.js.map +1 -0
  21. package/dist/932.js +1 -2
  22. package/dist/932.js.map +1 -1
  23. package/dist/94.js +1 -1
  24. package/dist/94.js.map +1 -1
  25. package/dist/openmrs-esm-patient-registration-app.js +1 -1
  26. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +76 -76
  27. package/dist/openmrs-esm-patient-registration-app.js.map +1 -1
  28. package/package.json +2 -2
  29. package/src/offline.resources.ts +33 -29
  30. package/src/offline.ts +9 -9
  31. package/src/patient-registration/form-manager.ts +71 -22
  32. package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +2 -0
  33. package/src/patient-registration/patient-registration-hooks.ts +59 -60
  34. package/src/patient-registration/patient-registration-types.tsx +20 -10
  35. package/src/patient-registration/patient-registration-utils.ts +1 -1
  36. package/src/patient-registration/patient-registration.component.tsx +14 -16
  37. package/src/patient-registration/patient-registration.resource.tsx +2 -2
  38. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +6 -6
  39. package/dist/373.js +0 -2
  40. package/dist/373.js.map +0 -1
  41. package/dist/764.js +0 -2
  42. package/dist/764.js.map +0 -1
  43. package/dist/770.js +0 -3
  44. package/dist/770.js.map +0 -1
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import find from 'lodash-es/find';
3
3
  import camelCase from 'lodash-es/camelCase';
4
- import { FetchResponse, openmrsFetch, SessionUser } from '@openmrs/esm-framework';
4
+ import escapeRegExp from 'lodash-es/escapeRegExp';
5
+ import { FetchResponse, messageOmrsServiceWorker, openmrsFetch, SessionUser } from '@openmrs/esm-framework';
5
6
  import { PatientIdentifierType, FetchedPatientIdentifierType } from './patient-registration/patient-registration-types';
6
7
  import { mockAutoGenerationOptionsResult } from '../__mocks__/autogenerationoptions.mock';
7
8
  import { cacheForOfflineHeaders } from './constants';
@@ -16,26 +17,20 @@ export interface Resources {
16
17
  export const ResourcesContext = React.createContext<Resources>(null);
17
18
 
18
19
  export async function fetchCurrentSession(abortController?: AbortController): Promise<FetchResponse<SessionUser>> {
19
- const { data } = await openmrsFetch('/ws/rest/v1/session', {
20
- signal: abortController?.signal,
21
- headers: cacheForOfflineHeaders,
22
- });
20
+ const { data } = await cacheAndFetch('/ws/rest/v1/session', abortController);
23
21
  return data;
24
22
  }
25
23
 
26
24
  export async function fetchAddressTemplate(abortController?: AbortController) {
27
- const { data } = await openmrsFetch('/ws/rest/v1/systemsetting?q=layout.address.format&v=custom:(value)', {
28
- signal: abortController?.signal,
29
- headers: cacheForOfflineHeaders,
30
- });
25
+ const { data } = await cacheAndFetch(
26
+ '/ws/rest/v1/systemsetting?q=layout.address.format&v=custom:(value)',
27
+ abortController,
28
+ );
31
29
  return data;
32
30
  }
33
31
 
34
32
  export async function fetchAllRelationshipTypes(abortController?: AbortController) {
35
- const { data } = await openmrsFetch('/ws/rest/v1/relationshiptype?v=default', {
36
- signal: abortController?.signal,
37
- headers: cacheForOfflineHeaders,
38
- });
33
+ const { data } = await cacheAndFetch('/ws/rest/v1/relationshiptype?v=default', abortController);
39
34
  return data;
40
35
  }
41
36
 
@@ -69,14 +64,14 @@ export async function fetchPatientIdentifierTypesWithSources(
69
64
  }
70
65
 
71
66
  async function fetchPrimaryIdentifierType(abortController: AbortController): Promise<FetchedPatientIdentifierType> {
72
- const primaryIdentifierTypeResponse = await openmrsFetch(
67
+ const primaryIdentifierTypeResponse = await cacheAndFetch(
73
68
  '/ws/rest/v1/metadatamapping/termmapping?v=full&code=emr.primaryIdentifierType',
74
- { signal: abortController?.signal, headers: cacheForOfflineHeaders },
69
+ abortController,
75
70
  );
76
71
 
77
- const { data: metadata } = await openmrsFetch(
72
+ const { data: metadata } = await cacheAndFetch(
78
73
  `/ws/rest/v1/patientidentifiertype/${primaryIdentifierTypeResponse.data.results[0].metadataUuid}`,
79
- { signal: abortController?.signal, headers: cacheForOfflineHeaders },
74
+ abortController,
80
75
  );
81
76
 
82
77
  return {
@@ -92,25 +87,25 @@ async function fetchPrimaryIdentifierType(abortController: AbortController): Pro
92
87
  async function fetchSecondaryIdentifierTypes(
93
88
  abortController?: AbortController,
94
89
  ): Promise<Array<FetchedPatientIdentifierType>> {
95
- const secondaryIdentifierTypeResponse = await openmrsFetch(
90
+ const secondaryIdentifierTypeResponse = await cacheAndFetch(
96
91
  '/ws/rest/v1/metadatamapping/termmapping?v=full&code=emr.extraPatientIdentifierTypes',
97
- { signal: abortController?.signal, headers: cacheForOfflineHeaders },
92
+ abortController,
98
93
  );
99
94
 
100
95
  if (secondaryIdentifierTypeResponse.data.results) {
101
96
  const extraIdentifierTypesSetUuid = secondaryIdentifierTypeResponse.data.results[0].metadataUuid;
102
- const metadataResponse = await openmrsFetch(
97
+ const metadataResponse = await cacheAndFetch(
103
98
  `/ws/rest/v1/metadatamapping/metadataset/${extraIdentifierTypesSetUuid}/members`,
104
- { signal: abortController?.signal, headers: cacheForOfflineHeaders },
99
+ abortController,
105
100
  );
106
101
 
107
102
  if (metadataResponse.data.results) {
108
103
  return await Promise.all(
109
104
  metadataResponse.data.results.map(async (setMember) => {
110
- const type = await openmrsFetch(`/ws/rest/v1/patientidentifiertype/${setMember.metadataUuid}`, {
111
- signal: abortController?.signal,
112
- headers: cacheForOfflineHeaders,
113
- });
105
+ const type = await cacheAndFetch(
106
+ `/ws/rest/v1/patientidentifiertype/${setMember.metadataUuid}`,
107
+ abortController,
108
+ );
114
109
 
115
110
  return {
116
111
  name: type.data.name,
@@ -129,10 +124,10 @@ async function fetchSecondaryIdentifierTypes(
129
124
  }
130
125
 
131
126
  async function fetchIdentifierSources(identifierType: string, abortController?: AbortController) {
132
- return await openmrsFetch(`/ws/rest/v1/idgen/identifiersource?v=full&identifierType=${identifierType}`, {
133
- signal: abortController?.signal,
134
- headers: cacheForOfflineHeaders,
135
- });
127
+ return await cacheAndFetch(
128
+ `/ws/rest/v1/idgen/identifiersource?v=full&identifierType=${identifierType}`,
129
+ abortController,
130
+ );
136
131
  }
137
132
 
138
133
  function fetchAutoGenerationOptions(identifierType: string, abortController?: AbortController) {
@@ -142,3 +137,12 @@ function fetchAutoGenerationOptions(identifierType: string, abortController?: Ab
142
137
  // });‚
143
138
  return Promise.resolve(mockAutoGenerationOptionsResult);
144
139
  }
140
+
141
+ async function cacheAndFetch(url: string, abortController?: AbortController) {
142
+ await messageOmrsServiceWorker({
143
+ type: 'registerDynamicRoute',
144
+ pattern: escapeRegExp(url),
145
+ });
146
+
147
+ return await openmrsFetch(url, { headers: cacheForOfflineHeaders, signal: abortController?.signal });
148
+ }
package/src/offline.ts CHANGED
@@ -41,15 +41,15 @@ export async function syncPatientRegistration(
41
41
  options: SyncProcessOptions<PatientRegistration>,
42
42
  ) {
43
43
  await FormManager.savePatientFormOnline(
44
- undefined,
45
- queuedPatient.formValues,
46
- queuedPatient.patientUuidMap,
47
- queuedPatient.initialAddressFieldValues,
48
- queuedPatient.identifierTypes,
49
- queuedPatient.capturePhotoProps,
50
- queuedPatient.patientPhotoConceptUuid,
51
- queuedPatient.currentLocation,
52
- queuedPatient.personAttributeSections,
44
+ queuedPatient._patientRegistrationData.isNewPatient,
45
+ queuedPatient._patientRegistrationData.formValues,
46
+ queuedPatient._patientRegistrationData.patientUuidMap,
47
+ queuedPatient._patientRegistrationData.initialAddressFieldValues,
48
+ queuedPatient._patientRegistrationData.identifierTypes,
49
+ queuedPatient._patientRegistrationData.capturePhotoProps,
50
+ queuedPatient._patientRegistrationData.patientPhotoConceptUuid,
51
+ queuedPatient._patientRegistrationData.currentLocation,
52
+ queuedPatient._patientRegistrationData.personAttributeSections,
53
53
  options.abort,
54
54
  );
55
55
  }
@@ -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>,
@@ -90,9 +94,9 @@ export default class FormManager {
90
94
  abortController,
91
95
  );
92
96
 
93
- if (patientUuid) {
97
+ if (!isNewPatient) {
94
98
  await Promise.all(
95
- FormManager.savePatientIdentifiers(patientUuid, patientIdentifiers, values.identifiers, abortController),
99
+ FormManager.savePatientIdentifiers(values.patientUuid, patientIdentifiers, values.identifiers, abortController),
96
100
  );
97
101
  }
98
102
 
@@ -104,11 +108,15 @@ export default class FormManager {
104
108
  patientIdentifiers,
105
109
  );
106
110
 
107
- FormManager.getDeletedNames(patientUuidMap).forEach(async (name) => {
111
+ FormManager.getDeletedNames(values.patientUuid, patientUuidMap).forEach(async (name) => {
108
112
  await deletePersonName(name.nameUuid, name.personUuid, abortController);
109
113
  });
110
114
 
111
- const savePatientResponse = await savePatient(abortController, createdPatient, patientUuidMap.patientUuid);
115
+ const savePatientResponse = await savePatient(
116
+ abortController,
117
+ createdPatient,
118
+ isNewPatient ? undefined : values.patientUuid,
119
+ );
112
120
 
113
121
  if (savePatientResponse.ok) {
114
122
  await Promise.all(
@@ -145,7 +153,7 @@ export default class FormManager {
145
153
  }
146
154
 
147
155
  static getPatientIdentifiersToCreate(
148
- patientIdentifiers: PatientIdentifierValue[], // values.identifiers
156
+ patientIdentifiers: PatientIdentifierValue[],
149
157
  identifierTypes: Array<PatientIdentifierType>,
150
158
  location: string,
151
159
  abortController: AbortController,
@@ -192,12 +200,12 @@ export default class FormManager {
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,
@@ -309,4 +317,45 @@ export default class FormManager {
309
317
  }
310
318
  });
311
319
  }
320
+
321
+ static mapPatientToFhirPatient(patient: Partial<Patient>): fhir.Patient {
322
+ // Important:
323
+ // When changing this code, ideally assume that `patient` can be missing any attribute.
324
+ // The `fhir.Patient` provides us with the benefit that all properties are nullable and thus
325
+ // not required (technically, at least). -> Even if we cannot map some props here, we still
326
+ // provide a valid fhir.Patient object. The various patient chart modules should be able to handle
327
+ // such missing props correctly (and should be updated if they don't).
328
+
329
+ // Gender in the original object only uses a single letter. fhir.Patient expects a full string.
330
+ const genderMap = {
331
+ M: 'male',
332
+ F: 'female',
333
+ O: 'other',
334
+ U: 'unknown',
335
+ };
336
+
337
+ // Mapping inspired by:
338
+ // https://github.com/openmrs/openmrs-module-fhir/blob/669b3c52220bb9abc622f815f4dc0d8523687a57/api/src/main/java/org/openmrs/module/fhir/api/util/FHIRPatientUtil.java#L36
339
+ // 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
340
+ // Update as required.
341
+ return {
342
+ id: patient.uuid,
343
+ gender: genderMap[patient.person?.gender],
344
+ birthDate: patient.person?.birthdate,
345
+ deceasedBoolean: patient.person.dead,
346
+ deceasedDateTime: patient.person.deathDate,
347
+ name: patient.person?.names?.map((name) => ({
348
+ given: [name.givenName, name.middleName].filter(Boolean),
349
+ family: name.familyName,
350
+ })),
351
+ address: patient.person?.addresses.map((address) => ({
352
+ city: address.cityVillage,
353
+ country: address.country,
354
+ postalCode: address.postalCode,
355
+ state: address.stateProvince,
356
+ use: 'home',
357
+ })),
358
+ telecom: patient.person.attributes?.filter((attribute) => attribute.attributeType === 'Telephone Number'),
359
+ };
360
+ }
312
361
  }
@@ -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,12 +1,8 @@
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,
@@ -15,57 +11,61 @@ import {
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),
50
+ ...getFormValuesFromFhirPatient(patientToEdit),
51
+ ...getAddressFieldValuesFromFhirPatient(patientToEdit),
52
+ ...getPhonePersonAttributeValueFromFhirPatient(patientToEdit),
61
53
  identifiers: await getPatientIdentifiers(patientUuid),
62
54
  });
63
- } else if (!isLoading && patientUuid) {
55
+ } else if (!isLoadingPatientToEdit && patientUuid) {
64
56
  const registration = await getPatientRegistration(patientUuid);
65
- setInitialFormValues(registration?.formValues ?? fallback);
57
+
58
+ if (!registration._patientRegistrationData.formValues) {
59
+ console.error(
60
+ `Found a queued offline patient registration for patient ${patientUuid}, but without form values. Not using these values.`,
61
+ );
62
+ return;
63
+ }
64
+
65
+ setInitialFormValues(registration._patientRegistrationData.formValues);
66
66
  }
67
67
  })();
68
- }, [isLoading, patient, patientUuid]);
68
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
69
69
 
70
70
  return [initialFormValues, setInitialFormValues];
71
71
  }
@@ -83,7 +83,7 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {})
83
83
  });
84
84
  } else if (!isLoading && patientUuid) {
85
85
  const registration = await getPatientRegistration(patientUuid);
86
- setInitialAddressFieldValues(registration?.initialAddressFieldValues ?? fallback);
86
+ setInitialAddressFieldValues(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback);
87
87
  }
88
88
  })();
89
89
  }, [isLoading, patient, patientUuid]);
@@ -93,26 +93,25 @@ export function useInitialAddressFieldValues(patientUuid: string, fallback = {})
93
93
 
94
94
  export function usePatientUuidMap(
95
95
  patientUuid: string,
96
- fallback = {},
96
+ fallback: PatientUuidMapType = {},
97
97
  ): [PatientUuidMapType, Dispatch<PatientUuidMapType>] {
98
- const { isLoading, patient } = usePatient(patientUuid);
98
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
99
99
  const [patientUuidMap, setPatientUuidMap] = useState(fallback);
100
100
 
101
101
  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]);
102
+ if (patientToEdit) {
103
+ setPatientUuidMap({ ...patientUuidMap, ...getPatientUuidMapFromFhirPatient(patientToEdit) });
104
+ } else if (!isLoadingPatientToEdit && patientUuid) {
105
+ getPatientRegistration(patientUuid).then((registration) =>
106
+ setPatientUuidMap(registration?._patientRegistrationData.initialAddressFieldValues ?? fallback),
107
+ );
108
+ }
109
+ }, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
111
110
 
112
111
  return [patientUuidMap, setPatientUuidMap];
113
112
  }
114
113
 
115
114
  async function getPatientRegistration(patientUuid: string) {
116
115
  const items = await getSynchronizationItems<PatientRegistration>(patientRegistration);
117
- return items.find((item) => item.patientUuid === patientUuid);
116
+ return items.find((item) => item._patientRegistrationData.formValues.patientUuid === patientUuid);
118
117
  }
@@ -38,15 +38,25 @@ export interface PatientIdentifierValue {
38
38
 
39
39
  export interface PatientRegistration {
40
40
  id?: number;
41
- patientUuid: string;
42
- formValues: FormValues;
43
- patientUuidMap: PatientUuidMapType;
44
- initialAddressFieldValues: Record<string, any>;
45
- identifierTypes: Array<PatientIdentifierType>;
46
- capturePhotoProps: CapturePhotoProps;
47
- patientPhotoConceptUuid: string;
48
- currentLocation: string;
49
- personAttributeSections: any;
41
+ /**
42
+ * The preliminary patient in the FHIR format.
43
+ */
44
+ fhirPatient: fhir.Patient;
45
+ /**
46
+ * Internal data collected by patient-registration. Required for later syncing and editing.
47
+ * Not supposed to be used outside of this module.
48
+ */
49
+ _patientRegistrationData: {
50
+ isNewPatient: boolean;
51
+ formValues: FormValues;
52
+ patientUuidMap: PatientUuidMapType;
53
+ initialAddressFieldValues: Record<string, any>;
54
+ identifierTypes: Array<PatientIdentifierType>;
55
+ capturePhotoProps: CapturePhotoProps;
56
+ patientPhotoConceptUuid: string;
57
+ currentLocation: string;
58
+ personAttributeSections: any;
59
+ };
50
60
  }
51
61
 
52
62
  /**
@@ -100,6 +110,7 @@ export interface IdentifierSource {
100
110
  }
101
111
 
102
112
  export interface FormValues {
113
+ patientUuid: string;
103
114
  givenName: string;
104
115
  middleName: string;
105
116
  familyName: string;
@@ -129,7 +140,6 @@ export interface FormValues {
129
140
 
130
141
  export interface PatientUuidMapType {
131
142
  additionalNameUuid?: string;
132
- patientUuid?: string;
133
143
  preferredNameUuid?: string;
134
144
  preferredAddressUuid?: string;
135
145
  }
@@ -73,6 +73,7 @@ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
73
73
  const patientName = patient.name[0];
74
74
  const additionalPatientName = patient.name[1];
75
75
 
76
+ result.patientUuid = patient.id;
76
77
  result.givenName = patientName?.given[0];
77
78
  result.middleName = patientName?.given[1];
78
79
  result.familyName = patientName?.family;
@@ -142,7 +143,6 @@ export function getPatientUuidMapFromFhirPatient(patient: fhir.Patient): Patient
142
143
  const address = patient.address?.[0];
143
144
 
144
145
  return {
145
- patientUuid: patient.id,
146
146
  preferredNameUuid: patientName?.id,
147
147
  additionalNameUuid: additionalPatientName?.id,
148
148
  preferredAddressUuid: address?.id,
@@ -36,21 +36,21 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
36
36
  const { currentSession, addressTemplate, patientIdentifiers } = useContext(ResourcesContext);
37
37
  const { search } = useLocation();
38
38
  const config = useConfig();
39
- const { patientUuid } = match.params;
40
39
  const [sections, setSections] = useState([]);
41
40
  const [target, setTarget] = useState<undefined | string>();
42
41
  const [validationSchema, setValidationSchema] = useState(initialSchema);
43
- const { isLoading, patient } = usePatient(patientUuid);
42
+ const { patientUuid: uuidOfPatientToEdit } = match.params;
43
+ const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(uuidOfPatientToEdit);
44
44
  const { t } = useTranslation();
45
45
  const [capturePhotoProps, setCapturePhotoProps] = useState<CapturePhotoProps | null>(null);
46
46
  const [fieldConfigs, setFieldConfigs] = useState({});
47
- const [initialFormValues, setInitialFormValues] = useInitialFormValues(patientUuid);
48
- const [initialAddressFieldValues] = useInitialAddressFieldValues(patientUuid);
49
- const [patientUuidMap] = usePatientUuidMap(patientUuid);
47
+ const [initialFormValues, setInitialFormValues] = useInitialFormValues(uuidOfPatientToEdit);
48
+ const [initialAddressFieldValues] = useInitialAddressFieldValues(uuidOfPatientToEdit);
49
+ const [patientUuidMap] = usePatientUuidMap(uuidOfPatientToEdit);
50
50
  const location = currentSession.sessionLocation?.uuid;
51
- const inEditMode = isLoading ? undefined : !!(patientUuid && patient);
51
+ const inEditMode = isLoadingPatientToEdit ? undefined : !!(uuidOfPatientToEdit && patientToEdit);
52
52
  const showDummyData = useMemo(() => localStorage.getItem('openmrs:devtools') === 'true' && !inEditMode, [inEditMode]);
53
- const { data: photo } = usePatientPhoto(patient?.id);
53
+ const { data: photo } = usePatientPhoto(patientToEdit?.id);
54
54
 
55
55
  useEffect(() => {
56
56
  exportedInitialFormValuesForTesting = initialFormValues;
@@ -98,8 +98,8 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
98
98
  helpers.setSubmitting(true);
99
99
 
100
100
  try {
101
- const createdPatientUuid = await savePatientForm(
102
- patientUuid,
101
+ await savePatientForm(
102
+ !inEditMode,
103
103
  values,
104
104
  patientUuidMap,
105
105
  initialAddressFieldValues,
@@ -124,14 +124,12 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
124
124
  kind: 'success',
125
125
  });
126
126
 
127
- if (createdPatientUuid) {
128
- const redirectUrl = interpolateUrl(
129
- new URLSearchParams(search).get('afterUrl') ||
130
- interpolateString(config.links.submitButton, { patientUuid: createdPatientUuid }),
131
- );
127
+ const redirectUrl = interpolateUrl(
128
+ new URLSearchParams(search).get('afterUrl') ||
129
+ interpolateString(config.links.submitButton, { patientUuid: values.patientUuid }),
130
+ );
132
131
 
133
- setTarget(redirectUrl);
134
- }
132
+ setTarget(redirectUrl);
135
133
  } catch (error) {
136
134
  if (error.responseBody?.error?.globalErrors) {
137
135
  error.responseBody.error.globalErrors.forEach((error) => {
@@ -19,8 +19,8 @@ function dataURItoFile(dataURI: string) {
19
19
  return new File([blob], 'patient-photo.png');
20
20
  }
21
21
 
22
- export function savePatient(abortController: AbortController, patient: Patient, patientUuid: string) {
23
- return openmrsFetch(`/ws/rest/v1/patient/${patientUuid || ''}`, {
22
+ export function savePatient(abortController: AbortController, patient: Patient, updatePatientUuid?: string) {
23
+ return openmrsFetch(`/ws/rest/v1/patient/${updatePatientUuid || ''}`, {
24
24
  headers: {
25
25
  'Content-Type': 'application/json',
26
26
  },