@olaboot/esm-patient-registration-app 9.2.0 → 10.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/1339.js +1 -0
- package/dist/1339.js.map +1 -0
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1789.js +1 -0
- package/dist/1789.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2747.js +1 -0
- package/dist/2747.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3720.js +1 -0
- package/dist/3720.js.map +1 -0
- package/dist/3906.js +1 -0
- package/dist/3906.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/3989.js +1 -0
- package/dist/3989.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5208.js +43 -0
- package/dist/5208.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/5280.js +1 -0
- package/dist/5280.js.map +1 -0
- package/dist/5338.js +6 -0
- package/dist/5338.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/555.js +1 -0
- package/dist/555.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5697.js +1 -0
- package/dist/{4024.js.map → 5697.js.map} +1 -1
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6388.js +1 -0
- package/dist/6388.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7649.js +1 -0
- package/dist/7649.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9061.js +1 -0
- package/dist/9061.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9397.js +1 -0
- package/dist/9397.js.map +1 -0
- package/dist/9712.js +1 -0
- package/dist/9712.js.map +1 -0
- package/dist/9771.js +1 -0
- package/dist/9771.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/9816.js +1 -0
- package/dist/9816.js.map +1 -0
- package/dist/main.js +7 -6
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-registration-app.js +6 -0
- package/dist/{olaboot-esm-patient-registration-app.js.buildmanifest.json → openmrs-esm-patient-registration-app.js.buildmanifest.json} +540 -455
- package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
- package/dist/routes.json +1 -1
- package/package.json +8 -9
- package/src/add-patient-link.extension.tsx +3 -2
- package/src/add-patient-link.test.tsx +2 -1
- package/src/config-schema.ts +1 -1
- package/src/index.ts +2 -24
- package/src/nav-link.test.tsx +1 -0
- package/src/offline.resources.ts +97 -31
- package/src/patient-registration/before-save-prompt.test.tsx +199 -0
- package/src/patient-registration/field/__mocks__/field.resource.ts +8 -7
- package/src/patient-registration/field/address/address-field.component.tsx +10 -13
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +6 -1
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +191 -198
- package/src/patient-registration/field/address/address-search.component.tsx +20 -8
- package/src/patient-registration/field/address/address-search.scss +19 -2
- package/src/patient-registration/field/address/address-search.test.tsx +249 -57
- package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.test.tsx +251 -0
- package/src/patient-registration/field/custom-field.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.test.tsx +144 -0
- package/src/patient-registration/field/dob/dob.component.tsx +2 -2
- package/src/patient-registration/field/dob/dob.test.tsx +370 -54
- package/src/patient-registration/field/field.component.tsx +1 -1
- package/src/patient-registration/field/field.resource.ts +2 -2
- package/src/patient-registration/field/field.test.tsx +25 -22
- package/src/patient-registration/field/gender/gender-field.test.tsx +240 -54
- package/src/patient-registration/field/id/id-field.component.tsx +15 -5
- package/src/patient-registration/field/id/id-field.test.tsx +103 -47
- package/src/patient-registration/field/id/identifier-selection-overlay.test.tsx +346 -0
- package/src/patient-registration/field/name/name-field.component.tsx +2 -2
- package/src/patient-registration/field/name/name-field.test.tsx +282 -0
- package/src/patient-registration/field/obs/obs-field.test.tsx +294 -118
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +172 -108
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +2 -5
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +2 -2
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +249 -131
- package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +1 -1
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +98 -70
- package/src/patient-registration/field/phone/phone-field.test.tsx +100 -0
- package/src/patient-registration/form-manager.test.ts +6 -5
- package/src/patient-registration/form-manager.ts +5 -2
- package/src/patient-registration/input/basic-input/input/input.component.tsx +3 -121
- package/src/patient-registration/input/basic-input/input/input.test.tsx +151 -51
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +113 -33
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +60 -24
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +10 -101
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +144 -108
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +241 -177
- package/src/patient-registration/input/custom-input/identifier/utils.test.ts +47 -8
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +12 -12
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +52 -20
- package/src/patient-registration/input/input.scss +1 -2
- package/src/patient-registration/patient-registration-context.ts +5 -3
- package/src/patient-registration/patient-registration-hooks.ts +4 -12
- package/src/patient-registration/patient-registration-utils.test.ts +2 -1
- package/src/patient-registration/patient-registration-utils.ts +2 -98
- package/src/patient-registration/patient-registration.component.tsx +50 -46
- package/src/patient-registration/patient-registration.resource.test.tsx +4 -7
- package/src/patient-registration/patient-registration.resource.ts +1 -4
- package/src/patient-registration/patient-registration.scss +16 -3
- package/src/patient-registration/patient-registration.test.tsx +99 -65
- package/src/patient-registration/patient-registration.types.ts +17 -28
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +130 -34
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +122 -68
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +15 -15
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +278 -84
- package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
- package/src/patient-registration/ui-components/overlay/overlay.test.tsx +104 -0
- package/src/patient-registration/validation/patient-registration-validation.test.ts +2 -1
- package/src/patient-registration/validation/patient-registration-validation.ts +9 -3
- package/src/root.component.tsx +2 -5
- package/src/widgets/cancel-patient-edit.test.tsx +48 -11
- package/src/widgets/delete-identifier-confirmation.test.tsx +77 -24
- package/src/widgets/edit-patient-details-button.component.tsx +14 -18
- package/src/widgets/edit-patient-details-button.scss +2 -2
- package/src/widgets/edit-patient-details-button.test.tsx +11 -13
- package/translations/am.json +9 -4
- package/translations/ar.json +9 -4
- package/translations/ar_SY.json +9 -4
- package/translations/bn.json +9 -4
- package/translations/cs.json +9 -4
- package/translations/de.json +120 -115
- package/translations/en.json +9 -4
- package/translations/en_US.json +9 -4
- package/translations/es.json +9 -4
- package/translations/es_MX.json +9 -4
- package/translations/fr.json +9 -4
- package/translations/he.json +9 -4
- package/translations/hi.json +9 -4
- package/translations/hi_IN.json +9 -4
- package/translations/id.json +9 -4
- package/translations/it.json +9 -4
- package/translations/ka.json +9 -4
- package/translations/km.json +9 -4
- package/translations/ku.json +9 -4
- package/translations/ky.json +9 -4
- package/translations/lg.json +9 -4
- package/translations/ne.json +9 -4
- package/translations/pl.json +9 -4
- package/translations/pt.json +9 -4
- package/translations/pt_BR.json +10 -5
- package/translations/qu.json +9 -4
- package/translations/ro_RO.json +9 -4
- package/translations/ru_RU.json +9 -4
- package/translations/si.json +9 -4
- package/translations/sq.json +9 -4
- package/translations/sw.json +9 -4
- package/translations/sw_KE.json +9 -4
- package/translations/tr.json +9 -4
- package/translations/tr_TR.json +9 -4
- package/translations/uk.json +9 -4
- package/translations/uz.json +9 -4
- package/translations/uz@Latn.json +9 -4
- package/translations/uz_UZ.json +9 -4
- package/translations/vi.json +9 -4
- package/translations/zh.json +50 -45
- package/translations/zh_CN.json +9 -4
- package/translations/zh_TW.json +9 -4
- package/vitest.config.ts +4 -0
- package/ADDRESS_CONFIGURATION.md +0 -152
- package/IDENTIFIER_CONFIGURATION.md +0 -142
- package/IMPLEMENTATION_SUMMARY.md +0 -111
- package/QUICK_START.md +0 -95
- package/address-required-fields-config.json +0 -26
- package/dist/126.js +0 -1
- package/dist/15.js +0 -1
- package/dist/1564.js +0 -1
- package/dist/1567.js +0 -1
- package/dist/1845.js +0 -1
- package/dist/1953.js +0 -1
- package/dist/200.js +0 -1
- package/dist/200.js.map +0 -1
- package/dist/215.js +0 -1
- package/dist/2178.js +0 -1
- package/dist/250.js +0 -1
- package/dist/250.js.map +0 -1
- package/dist/2523.js +0 -1
- package/dist/2523.js.map +0 -1
- package/dist/2566.js +0 -1
- package/dist/2586.js +0 -1
- package/dist/2586.js.map +0 -1
- package/dist/2716.js +0 -1
- package/dist/2716.js.map +0 -1
- package/dist/2759.js +0 -1
- package/dist/2821.js +0 -6
- package/dist/2821.js.map +0 -1
- package/dist/3089.js +0 -1
- package/dist/3089.js.map +0 -1
- package/dist/3230.js +0 -1
- package/dist/3441.js +0 -1
- package/dist/3565.js +0 -1
- package/dist/3571.js +0 -1
- package/dist/3571.js.map +0 -1
- package/dist/3746.js +0 -1
- package/dist/3925.js +0 -1
- package/dist/3946.js +0 -1
- package/dist/4024.js +0 -1
- package/dist/4744.js +0 -1
- package/dist/4744.js.map +0 -1
- package/dist/4809.js +0 -1
- package/dist/4894.js +0 -1
- package/dist/4970.js +0 -1
- package/dist/4970.js.map +0 -1
- package/dist/5130.js +0 -1
- package/dist/5187.js +0 -1
- package/dist/5491.js +0 -1
- package/dist/5491.js.map +0 -1
- package/dist/5595.js +0 -1
- package/dist/5961.js +0 -1
- package/dist/6133.js +0 -1
- package/dist/634.js +0 -1
- package/dist/634.js.map +0 -1
- package/dist/6456.js +0 -1
- package/dist/6466.js +0 -1
- package/dist/6613.js +0 -1
- package/dist/6783.js +0 -1
- package/dist/7073.js +0 -38
- package/dist/7073.js.map +0 -1
- package/dist/7154.js +0 -1
- package/dist/7154.js.map +0 -1
- package/dist/7348.js +0 -1
- package/dist/7439.js +0 -1
- package/dist/7439.js.map +0 -1
- package/dist/7543.js +0 -1
- package/dist/7607.js +0 -1
- package/dist/772.js +0 -1
- package/dist/7984.js +0 -1
- package/dist/7984.js.map +0 -1
- package/dist/8538.js +0 -1
- package/dist/8538.js.map +0 -1
- package/dist/8599.js +0 -1
- package/dist/8727.js +0 -1
- package/dist/8847.js +0 -1
- package/dist/9015.js +0 -1
- package/dist/906.js +0 -1
- package/dist/9065.js +0 -1
- package/dist/9182.js +0 -1
- package/dist/9339.js +0 -1
- package/dist/9453.js +0 -1
- package/dist/9833.js +0 -1
- package/dist/9833.js.map +0 -1
- package/dist/9856.js +0 -1
- package/dist/9856.js.map +0 -1
- package/dist/9920.js +0 -1
- package/dist/9938.js +0 -1
- package/dist/9943.js +0 -1
- package/dist/9943.js.map +0 -1
- package/dist/olaboot-esm-patient-registration-app.js +0 -5
- package/dist/olaboot-esm-patient-registration-app.js.map +0 -1
- package/example-config.json +0 -14
- package/jest.config.js +0 -3
- package/src/resource.ts +0 -12
|
@@ -1,34 +1,66 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
2
3
|
import { render, screen } from '@testing-library/react';
|
|
3
4
|
import userEvent from '@testing-library/user-event';
|
|
4
5
|
import { DummyDataInput, dummyFormValues } from './dummy-data-input.component';
|
|
5
6
|
import { initialFormValues } from '../../patient-registration.component';
|
|
6
7
|
import { type FormValues } from '../../patient-registration.types';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Helper to render DummyDataInput component.
|
|
11
|
+
*/
|
|
12
|
+
function renderDummyDataInput(onSetValues?: (values: FormValues) => void) {
|
|
13
|
+
let capturedValues: FormValues = initialFormValues;
|
|
14
|
+
const handleSetValues =
|
|
15
|
+
onSetValues ||
|
|
16
|
+
((values: FormValues) => {
|
|
17
|
+
capturedValues = values;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const utils = render(<DummyDataInput setValues={handleSetValues} />);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
...utils,
|
|
24
|
+
getCapturedValues: () => capturedValues,
|
|
20
25
|
};
|
|
26
|
+
}
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
describe('DummyDataInput component', () => {
|
|
29
|
+
describe('Rendering', () => {
|
|
30
|
+
it('renders the dummy data input button', () => {
|
|
31
|
+
renderDummyDataInput();
|
|
32
|
+
|
|
33
|
+
const button = screen.getByLabelText('Input Dummy Data') as HTMLButtonElement;
|
|
34
|
+
expect(button).toBeInTheDocument();
|
|
35
|
+
expect(button.type).toBe('button');
|
|
36
|
+
});
|
|
25
37
|
});
|
|
26
38
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
describe('User interaction', () => {
|
|
40
|
+
it('populates form values with dummy data when button is clicked', async () => {
|
|
41
|
+
const user = userEvent.setup();
|
|
42
|
+
const mockSetValues = vi.fn();
|
|
43
|
+
renderDummyDataInput(mockSetValues);
|
|
44
|
+
|
|
45
|
+
const button = screen.getByLabelText('Input Dummy Data');
|
|
46
|
+
await user.click(button);
|
|
47
|
+
|
|
48
|
+
expect(mockSetValues).toHaveBeenCalledTimes(1);
|
|
49
|
+
expect(mockSetValues).toHaveBeenCalledWith(dummyFormValues);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('can be clicked multiple times', async () => {
|
|
53
|
+
const user = userEvent.setup();
|
|
54
|
+
const mockSetValues = vi.fn();
|
|
55
|
+
renderDummyDataInput(mockSetValues);
|
|
56
|
+
|
|
57
|
+
const button = screen.getByLabelText('Input Dummy Data');
|
|
58
|
+
await user.click(button);
|
|
59
|
+
await user.click(button);
|
|
60
|
+
await user.click(button);
|
|
30
61
|
|
|
31
|
-
|
|
32
|
-
|
|
62
|
+
expect(mockSetValues).toHaveBeenCalledTimes(3);
|
|
63
|
+
expect(mockSetValues).toHaveBeenCalledWith(dummyFormValues);
|
|
64
|
+
});
|
|
33
65
|
});
|
|
34
66
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createContext, type SetStateAction, useContext } from 'react';
|
|
2
|
+
import type { FormikHelpers } from 'formik';
|
|
3
|
+
import type { ObjectSchema } from 'yup';
|
|
2
4
|
import { type PatientIdentifierType, useConfig } from '@openmrs/esm-framework';
|
|
3
5
|
import { type CapturePhotoProps, type FormValues } from './patient-registration.types';
|
|
4
6
|
import { type RegistrationConfig } from '../config-schema';
|
|
@@ -10,10 +12,10 @@ export interface PatientRegistrationContextProps {
|
|
|
10
12
|
initialFormValues: FormValues;
|
|
11
13
|
isOffline: boolean;
|
|
12
14
|
setCapturePhotoProps(value: SetStateAction<CapturePhotoProps>): void;
|
|
13
|
-
setFieldTouched
|
|
14
|
-
setFieldValue
|
|
15
|
+
setFieldTouched: FormikHelpers<FormValues>['setFieldTouched'];
|
|
16
|
+
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
|
15
17
|
setInitialFormValues?: React.Dispatch<SetStateAction<FormValues>>;
|
|
16
|
-
validationSchema: any;
|
|
18
|
+
validationSchema: ObjectSchema<any> | null;
|
|
17
19
|
values: FormValues;
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type PatientRegistration,
|
|
22
22
|
type PatientUuidMapType,
|
|
23
23
|
type PersonAttributeResponse,
|
|
24
|
+
type AddressProperties,
|
|
24
25
|
} from './patient-registration.types';
|
|
25
26
|
import {
|
|
26
27
|
getAddressFieldValuesFromFhirPatient,
|
|
@@ -30,17 +31,6 @@ import {
|
|
|
30
31
|
latestFirstEncounter,
|
|
31
32
|
} from './patient-registration-utils';
|
|
32
33
|
|
|
33
|
-
interface AddressFieldValues {
|
|
34
|
-
address?: {
|
|
35
|
-
cityVillage?: string;
|
|
36
|
-
country?: string;
|
|
37
|
-
countyDistrict?: string;
|
|
38
|
-
postalCode?: string;
|
|
39
|
-
stateProvince?: string;
|
|
40
|
-
[key: string]: string | undefined;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
34
|
interface DeathInfoResults {
|
|
45
35
|
causeOfDeath: OpenmrsResource | null;
|
|
46
36
|
causeOfDeathNonCoded: string | null;
|
|
@@ -192,6 +182,8 @@ export function useInitialFormValues(
|
|
|
192
182
|
return [initialFormValues, setInitialFormValues];
|
|
193
183
|
}
|
|
194
184
|
|
|
185
|
+
type AddressFieldValues = Partial<Record<AddressProperties, string>>;
|
|
186
|
+
|
|
195
187
|
export function useInitialAddressFieldValues(
|
|
196
188
|
fallback: AddressFieldValues = {},
|
|
197
189
|
isLoadingPatientToEdit: boolean,
|
|
@@ -243,7 +235,7 @@ export function usePatientUuidMap(
|
|
|
243
235
|
try {
|
|
244
236
|
const registration = await getPatientRegistration(patientUuid);
|
|
245
237
|
if (!abortController.signal.aborted) {
|
|
246
|
-
setPatientUuidMap(registration?._patientRegistrationData.
|
|
238
|
+
setPatientUuidMap(registration?._patientRegistrationData.patientUuidMap ?? fallbackRef.current);
|
|
247
239
|
}
|
|
248
240
|
} catch (error) {
|
|
249
241
|
if (!abortController.signal.aborted) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { filterOutUndefinedPatientIdentifiers } from './patient-registration-utils';
|
|
2
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
2
3
|
|
|
3
4
|
describe('filterOutUndefinedPatientIdentifiers', () => {
|
|
4
5
|
const getIdentifiers = (autoGeneration = true, manualEntryEnabled = false) => ({
|
|
@@ -21,7 +22,7 @@ describe('filterOutUndefinedPatientIdentifiers', () => {
|
|
|
21
22
|
},
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
it('should
|
|
25
|
+
it('should filter out undefined identifiers', () => {
|
|
25
26
|
const filteredIdentifiers = filterOutUndefinedPatientIdentifiers(getIdentifiers());
|
|
26
27
|
expect(filteredIdentifiers.OpenMRSId).not.toBeDefined();
|
|
27
28
|
});
|
|
@@ -1,103 +1,16 @@
|
|
|
1
|
-
import * as Yup from 'yup';
|
|
2
1
|
import camelCase from 'lodash-es/camelCase';
|
|
3
2
|
import { parseDate } from '@openmrs/esm-framework';
|
|
4
3
|
import {
|
|
5
|
-
type AddressValidationSchemaType,
|
|
6
4
|
type Encounter,
|
|
7
5
|
type FormValues,
|
|
8
|
-
type PatientIdentifier,
|
|
9
6
|
type PatientIdentifierValue,
|
|
10
7
|
type PatientUuidMapType,
|
|
11
8
|
} from './patient-registration.types';
|
|
12
9
|
|
|
13
|
-
export function parseAddressTemplateXml(addressTemplate: string) {
|
|
14
|
-
const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
|
|
15
|
-
const nameMappings = templateXmlDoc.querySelector('nameMappings');
|
|
16
|
-
const properties = nameMappings.getElementsByTagName('entry');
|
|
17
|
-
const validationSchemaObjs = Array.prototype.map.call(properties, (property: Element) => {
|
|
18
|
-
const name = property.getElementsByTagName('string')[0].innerHTML;
|
|
19
|
-
const label = property.getElementsByTagName('string')[1].innerHTML;
|
|
20
|
-
const regex = findElementValueInXmlDoc(name, 'elementRegex', templateXmlDoc) || '.*';
|
|
21
|
-
const regexFormat = findElementValueInXmlDoc(name, 'elementRegexFormats', templateXmlDoc) || '';
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
name,
|
|
25
|
-
label,
|
|
26
|
-
regex,
|
|
27
|
-
regexFormat,
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const addressValidationSchema = Yup.object(
|
|
32
|
-
validationSchemaObjs.reduce((final, current) => {
|
|
33
|
-
final[current.name] = Yup.string().matches(current.regex, current.regexFormat);
|
|
34
|
-
return final;
|
|
35
|
-
}, {}),
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const addressFieldValues = Array.prototype.map.call(properties, (property: Element) => {
|
|
39
|
-
const name = property.getElementsByTagName('string')[0].innerHTML;
|
|
40
|
-
return {
|
|
41
|
-
name,
|
|
42
|
-
defaultValue: '',
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
return {
|
|
46
|
-
addressFieldValues,
|
|
47
|
-
addressValidationSchema,
|
|
48
|
-
};
|
|
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
10
|
export function scrollIntoView(viewId: string) {
|
|
98
|
-
document.getElementById(viewId)
|
|
11
|
+
document.getElementById(viewId)?.scrollIntoView({
|
|
99
12
|
behavior: 'smooth',
|
|
100
|
-
block: '
|
|
13
|
+
block: 'start',
|
|
101
14
|
inline: 'center',
|
|
102
15
|
});
|
|
103
16
|
}
|
|
@@ -183,15 +96,6 @@ export function getPatientUuidMapFromFhirPatient(patient: fhir.Patient): Patient
|
|
|
183
96
|
};
|
|
184
97
|
}
|
|
185
98
|
|
|
186
|
-
export function getPatientIdentifiersFromFhirPatient(patient: fhir.Patient): Array<PatientIdentifier> {
|
|
187
|
-
return patient.identifier.map((identifier) => {
|
|
188
|
-
return {
|
|
189
|
-
uuid: identifier.id,
|
|
190
|
-
identifier: identifier.value,
|
|
191
|
-
};
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
99
|
export function getPhonePersonAttributeValueFromFhirPatient(patient: fhir.Patient) {
|
|
196
100
|
const result = {};
|
|
197
101
|
if (patient.telecom) {
|
|
@@ -4,7 +4,7 @@ import { Button, InlineLoading, Link } from '@carbon/react';
|
|
|
4
4
|
import { XAxis } from '@carbon/react/icons';
|
|
5
5
|
import { useLocation, useParams } from 'react-router-dom';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
-
import { Form, Formik, type FormikHelpers } from 'formik';
|
|
7
|
+
import { Form, Formik, type FormikHelpers, type FormikErrors } from 'formik';
|
|
8
8
|
import {
|
|
9
9
|
createErrorHandler,
|
|
10
10
|
interpolateUrl,
|
|
@@ -60,7 +60,10 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
60
60
|
|
|
61
61
|
const location = currentSession?.sessionLocation?.uuid;
|
|
62
62
|
const inEditMode = isLoadingPatientToEdit ? undefined : !!(uuidOfPatientToEdit && patientToEdit);
|
|
63
|
-
const
|
|
63
|
+
const showDummyDataInput = useMemo(
|
|
64
|
+
() => localStorage.getItem('openmrs:devtools') === 'true' && !inEditMode,
|
|
65
|
+
[inEditMode],
|
|
66
|
+
);
|
|
64
67
|
const { data: photo } = usePatientPhoto(patientToEdit?.id);
|
|
65
68
|
const savePatientTransactionManager = useRef(new SavePatientTransactionManager());
|
|
66
69
|
const validationSchema = getValidationSchema(config, t);
|
|
@@ -73,8 +76,8 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
73
76
|
return config.sections
|
|
74
77
|
.map(
|
|
75
78
|
(sectionName) =>
|
|
76
|
-
config.sectionDefinitions.filter((s) => s.id
|
|
77
|
-
builtInSections.filter((s) => s.id
|
|
79
|
+
config.sectionDefinitions.filter((s) => s.id === sectionName)[0] ??
|
|
80
|
+
builtInSections.filter((s) => s.id === sectionName)[0],
|
|
78
81
|
)
|
|
79
82
|
.filter((s) => s);
|
|
80
83
|
}, [config.sections, config.sectionDefinitions]);
|
|
@@ -144,7 +147,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
144
147
|
}
|
|
145
148
|
};
|
|
146
149
|
|
|
147
|
-
const getDescription = (errors) => {
|
|
150
|
+
const getDescription = (errors: FormikErrors<FormValues>): JSX.Element => {
|
|
148
151
|
return (
|
|
149
152
|
<ul style={{ listStyle: 'inside' }}>
|
|
150
153
|
{Object.keys(errors).map((error, index) => {
|
|
@@ -154,7 +157,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
154
157
|
);
|
|
155
158
|
};
|
|
156
159
|
|
|
157
|
-
const displayErrors = (errors) => {
|
|
160
|
+
const displayErrors = (errors: FormikErrors<FormValues>): void => {
|
|
158
161
|
if (errors && typeof errors === 'object' && !!Object.keys(errors).length) {
|
|
159
162
|
showSnackbar({
|
|
160
163
|
isLowContrast: true,
|
|
@@ -200,47 +203,48 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
|
|
|
200
203
|
<Form className={styles.form}>
|
|
201
204
|
<BeforeSavePrompt when={Object.keys(props.touched).length > 0} redirect={target} />
|
|
202
205
|
<div className={styles.formContainer}>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
</
|
|
206
|
+
{/* Navigation Sidebar */}
|
|
207
|
+
<div className={styles.stickyColumn}>
|
|
208
|
+
<h4>
|
|
209
|
+
{inEditMode
|
|
210
|
+
? t('editPatientDetails', 'Edit patient details')
|
|
211
|
+
: t('createNewPatient', 'Create new patient')}
|
|
212
|
+
</h4>
|
|
213
|
+
{showDummyDataInput && <DummyDataInput setValues={props.setValues} />}
|
|
214
|
+
<p className={styles.label01}>{t('jumpTo', 'Jump to')}</p>
|
|
215
|
+
{sections.map((section) => (
|
|
216
|
+
<div className={classNames(styles.space05, styles.touchTarget)} key={section.name}>
|
|
217
|
+
<Link className={styles.linkName} onClick={() => scrollIntoView(section.id)}>
|
|
218
|
+
<XAxis size={16} /> {t(`${section.id}Section`, section.name)}
|
|
219
|
+
</Link>
|
|
220
|
+
</div>
|
|
221
|
+
))}
|
|
222
|
+
<hr className={styles.divider} />
|
|
223
|
+
<Button
|
|
224
|
+
className={styles.submitButton}
|
|
225
|
+
type="submit"
|
|
226
|
+
onClick={() => props.validateForm().then((errors) => displayErrors(errors))}
|
|
227
|
+
// Current session and identifiers are required for patient registration.
|
|
228
|
+
// If currentSession or identifierTypes are not available, then the
|
|
229
|
+
// user should be blocked to register the patient.
|
|
230
|
+
disabled={!currentSession || !identifierTypes || props.isSubmitting}>
|
|
231
|
+
{props.isSubmitting ? (
|
|
232
|
+
<InlineLoading
|
|
233
|
+
className={styles.spinner}
|
|
234
|
+
description={`${t('submitting', 'Submitting')} ...`}
|
|
235
|
+
iconDescription="submitting"
|
|
236
|
+
/>
|
|
237
|
+
) : inEditMode ? (
|
|
238
|
+
t('updatePatient', 'Update patient')
|
|
239
|
+
) : (
|
|
240
|
+
t('registerPatient', 'Register patient')
|
|
241
|
+
)}
|
|
242
|
+
</Button>
|
|
243
|
+
<Button className={styles.cancelButton} kind="secondary" onClick={cancelRegistration}>
|
|
244
|
+
{t('cancel', 'Cancel')}
|
|
245
|
+
</Button>
|
|
243
246
|
</div>
|
|
247
|
+
{/* Registration Form */}
|
|
244
248
|
<div className={styles.infoGrid}>
|
|
245
249
|
<PatientRegistrationContextProvider value={createContextValue(props)}>
|
|
246
250
|
{sections.map((section, index) => (
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import { vi, describe, it, expect } from 'vitest';
|
|
2
3
|
import { savePatient } from './patient-registration.resource';
|
|
3
4
|
|
|
4
|
-
const mockOpenmrsFetch = openmrsFetch
|
|
5
|
-
|
|
6
|
-
jest.mock('@openmrs/esm-framework', () => ({
|
|
7
|
-
openmrsFetch: jest.fn(),
|
|
8
|
-
}));
|
|
5
|
+
const mockOpenmrsFetch = vi.mocked(openmrsFetch);
|
|
9
6
|
|
|
10
7
|
describe('savePatient', () => {
|
|
11
8
|
it('appends patient uuid in url if provided', () => {
|
|
12
|
-
mockOpenmrsFetch.mockImplementationOnce((
|
|
9
|
+
mockOpenmrsFetch.mockImplementationOnce(() => Promise.resolve({} as any));
|
|
13
10
|
savePatient(null, '1234');
|
|
14
11
|
expect(mockOpenmrsFetch.mock.calls[0][0]).toEqual(`${restBaseUrl}/patient/1234`);
|
|
15
12
|
});
|
|
16
13
|
|
|
17
14
|
it('does not append patient uuid in url', () => {
|
|
18
|
-
mockOpenmrsFetch.mockImplementationOnce(() => {});
|
|
15
|
+
mockOpenmrsFetch.mockImplementationOnce(() => Promise.resolve({} as any));
|
|
19
16
|
savePatient(null);
|
|
20
17
|
expect(mockOpenmrsFetch.mock.calls[0][0]).toEqual(`${restBaseUrl}/patient/`);
|
|
21
18
|
});
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
1
2
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
3
|
import { type Patient, type Relationship, type PatientIdentifier, type Encounter } from './patient-registration.types';
|
|
3
|
-
import dayjs from 'dayjs';
|
|
4
|
-
|
|
5
|
-
export const uuidIdentifier = '05a29f94-c0ed-11e2-94be-8c13b969e334';
|
|
6
|
-
export const uuidTelephoneNumber = '14d4f066-15f5-102d-96e4-000c29c2a5d7';
|
|
7
4
|
|
|
8
5
|
function dataURItoFile(dataURI: string) {
|
|
9
6
|
const byteString = window.atob(dataURI.split(',')[1]);
|
|
@@ -45,16 +45,29 @@
|
|
|
45
45
|
margin: layout.$spacing-05 0;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
.divider {
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 1px;
|
|
51
|
+
margin: layout.$spacing-07 0;
|
|
52
|
+
border: none;
|
|
53
|
+
background-color: $ui-03;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
.formContainer {
|
|
49
57
|
display: flex;
|
|
50
58
|
width: 100%;
|
|
59
|
+
align-items: flex-start;
|
|
60
|
+
height: calc(100dvh - var(--omrs-topnav-height, 3rem)); // subtract nav height
|
|
61
|
+
overflow-y: auto;
|
|
62
|
+
scrollbar-width: none;
|
|
63
|
+
&::-webkit-scrollbar {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
51
66
|
}
|
|
52
67
|
|
|
53
68
|
.stickyColumn {
|
|
54
69
|
position: sticky;
|
|
55
|
-
|
|
56
|
-
// layout.$spacing-09 for the nav height and layout.$spacing-05 for top margin
|
|
57
|
-
top: layout.$spacing-10;
|
|
70
|
+
top: layout.$spacing-05;
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
.touchTarget a:active {
|