@ampath/esm-patient-registration-app 6.0.1-pre.98 → 9.2.0-next.12
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/1119.js +1 -0
- package/dist/1197.js +1 -0
- package/dist/21.js +1 -0
- package/dist/21.js.map +1 -0
- package/dist/2146.js +1 -0
- package/dist/2372.js +1 -0
- package/dist/2372.js.map +1 -0
- package/dist/2470.js +1 -0
- package/dist/2470.js.map +1 -0
- package/dist/2690.js +1 -0
- package/dist/2913.js +2 -0
- package/dist/{913.js.LICENSE.txt → 2913.js.LICENSE.txt} +3 -23
- package/dist/2913.js.map +1 -0
- package/dist/3093.js +1 -0
- package/dist/3093.js.map +1 -0
- package/dist/3099.js +1 -0
- package/dist/3144.js +2 -0
- package/dist/3144.js.LICENSE.txt +19 -0
- package/dist/3144.js.map +1 -0
- package/dist/320.js +2 -0
- package/dist/{876.js.LICENSE.txt → 320.js.LICENSE.txt} +2 -3
- package/dist/320.js.map +1 -0
- package/dist/3464.js +1 -0
- package/dist/3464.js.map +1 -0
- package/dist/3474.js +2 -0
- package/dist/3474.js.LICENSE.txt +8 -0
- package/dist/3474.js.map +1 -0
- package/dist/3584.js +1 -0
- package/dist/4041.js +2 -0
- package/dist/4041.js.map +1 -0
- package/dist/4055.js +1 -0
- package/dist/4132.js +1 -0
- package/dist/4300.js +1 -0
- package/dist/4335.js +1 -0
- package/dist/4463.js +1 -0
- package/dist/4463.js.map +1 -0
- package/dist/4618.js +1 -0
- package/dist/4652.js +1 -0
- package/dist/4944.js +1 -0
- package/dist/5173.js +1 -0
- package/dist/5220.js +2 -0
- package/dist/5220.js.LICENSE.txt +29 -0
- package/dist/5220.js.map +1 -0
- package/dist/5241.js +1 -0
- package/dist/5442.js +1 -0
- package/dist/5661.js +1 -0
- package/dist/6022.js +1 -0
- package/dist/6078.js +2 -0
- package/dist/6078.js.LICENSE.txt +9 -0
- package/dist/6078.js.map +1 -0
- package/dist/627.js +1 -0
- package/dist/627.js.map +1 -0
- package/dist/6276.js +1 -0
- package/dist/6276.js.map +1 -0
- package/dist/6468.js +1 -0
- package/dist/6679.js +1 -0
- package/dist/6737.js +2 -0
- package/dist/6737.js.LICENSE.txt +9 -0
- package/dist/6737.js.map +1 -0
- package/dist/6840.js +1 -0
- package/dist/6859.js +1 -0
- package/dist/7092.js +1 -0
- package/dist/7092.js.map +1 -0
- package/dist/7097.js +1 -0
- package/dist/7159.js +1 -0
- package/dist/723.js +1 -0
- package/dist/7495.js +2 -0
- package/dist/7495.js.LICENSE.txt +9 -0
- package/dist/7495.js.map +1 -0
- package/dist/7617.js +1 -0
- package/dist/795.js +1 -0
- package/dist/8163.js +1 -0
- package/dist/8349.js +1 -0
- package/dist/8404.js +2 -0
- package/dist/{629.js.LICENSE.txt → 8404.js.LICENSE.txt} +9 -3
- package/dist/8404.js.map +1 -0
- package/dist/8434.js +1 -0
- package/dist/8434.js.map +1 -0
- package/dist/8618.js +1 -0
- package/dist/89.js +2 -0
- package/dist/89.js.LICENSE.txt +9 -0
- package/dist/89.js.map +1 -0
- package/dist/890.js +1 -0
- package/dist/9214.js +1 -0
- package/dist/9538.js +1 -0
- package/dist/9569.js +1 -0
- package/dist/986.js +1 -0
- package/dist/9876.js +1 -0
- package/dist/9876.js.map +1 -0
- package/dist/9879.js +1 -0
- package/dist/9895.js +1 -0
- package/dist/9900.js +1 -0
- package/dist/9913.js +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +36 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-registration-app.js +1 -0
- package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +1576 -0
- package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
- package/dist/routes.json +1 -1
- package/package.json +16 -15
- package/src/{add-patient-link.tsx → add-patient-link.extension.tsx} +4 -2
- package/src/add-patient-link.test.tsx +6 -10
- package/src/config-schema.ts +109 -55
- package/src/constants.ts +1 -1
- package/src/declarations.d.ts +5 -4
- package/src/index.ts +10 -29
- package/src/nav-link.test.tsx +3 -3
- package/src/offline.resources.ts +26 -18
- package/src/patient-photo.extension.tsx +3 -1
- package/src/patient-registration/field/address/address-field.component.tsx +58 -37
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +16 -18
- package/src/patient-registration/field/address/address-hierarchy.resource.tsx +3 -3
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +290 -0
- package/src/patient-registration/field/address/address-search.component.tsx +7 -5
- package/src/patient-registration/field/address/address-search.scss +5 -5
- package/src/patient-registration/field/address/address-search.test.tsx +140 -0
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +98 -0
- package/src/patient-registration/field/custom-field.component.tsx +3 -9
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +84 -0
- package/src/patient-registration/field/dob/dob.component.tsx +55 -50
- package/src/patient-registration/field/dob/dob.test.tsx +90 -0
- package/src/patient-registration/field/field.component.tsx +12 -6
- package/src/patient-registration/field/field.resource.ts +11 -4
- package/src/patient-registration/field/field.scss +69 -25
- package/src/patient-registration/field/field.test.tsx +329 -0
- package/src/patient-registration/field/gender/gender-field.component.tsx +14 -9
- package/src/patient-registration/field/gender/gender-field.test.tsx +73 -33
- package/src/patient-registration/field/id/id-field.component.tsx +24 -23
- package/src/patient-registration/field/id/id-field.test.tsx +147 -0
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +12 -10
- package/src/patient-registration/field/id/identifier-selection.scss +12 -8
- package/src/patient-registration/field/name/name-field.component.tsx +10 -5
- package/src/patient-registration/field/obs/obs-field.component.tsx +59 -2
- package/src/patient-registration/field/obs/obs-field.test.tsx +133 -39
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +141 -0
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +105 -0
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +48 -0
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +19 -22
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +193 -0
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +90 -0
- package/src/patient-registration/form-manager.test.ts +91 -0
- package/src/patient-registration/form-manager.ts +49 -23
- package/src/patient-registration/input/basic-input/input/input.component.tsx +6 -2
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +49 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +5 -5
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +164 -0
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +73 -36
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +335 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +3 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +2 -11
- package/src/patient-registration/input/input.scss +17 -13
- package/src/patient-registration/patient-registration-context.ts +22 -11
- package/src/patient-registration/patient-registration-hooks.ts +158 -193
- package/src/patient-registration/patient-registration-utils.test.ts +33 -0
- package/src/patient-registration/patient-registration-utils.ts +11 -13
- package/src/patient-registration/patient-registration.component.tsx +87 -103
- package/src/patient-registration/{patient-registration.resource.testt.tsx → patient-registration.resource.test.tsx} +0 -4
- package/src/patient-registration/patient-registration.resource.ts +27 -3
- package/src/patient-registration/patient-registration.scss +27 -38
- package/src/patient-registration/patient-registration.test.tsx +579 -0
- package/src/patient-registration/patient-registration.types.ts +23 -25
- package/src/patient-registration/section/death-info/death-info-section.component.tsx +22 -17
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +47 -0
- package/src/patient-registration/section/demographics/demographics-section.component.tsx +5 -5
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +98 -0
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +8 -7
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +113 -0
- package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +28 -28
- package/src/patient-registration/section/patient-relationships/relationships.scss +4 -4
- package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
- package/src/patient-registration/section/section.component.tsx +1 -1
- package/src/patient-registration/section/section.scss +21 -1
- package/src/patient-registration/ui-components/overlay/overlay.scss +8 -8
- package/src/patient-registration/validation/{patient-registration-validation.test.tsx → patient-registration-validation.test.ts} +71 -23
- package/src/patient-registration/validation/patient-registration-validation.ts +123 -0
- package/src/resources-context.ts +14 -0
- package/src/root.component.tsx +3 -3
- package/src/routes.json +10 -24
- package/src/widgets/cancel-patient-edit.modal.tsx +33 -0
- package/src/widgets/cancel-patient-edit.test.tsx +22 -0
- package/src/widgets/delete-identifier-confirmation.modal.tsx +48 -0
- package/src/widgets/{delete-identifier-confirmation-modal.testt.tsx → delete-identifier-confirmation.test.tsx} +5 -7
- package/src/widgets/edit-patient-details-button.component.tsx +0 -1
- package/src/widgets/edit-patient-details-button.test.tsx +35 -0
- package/translations/am.json +43 -35
- package/translations/ar.json +41 -33
- package/translations/ar_SY.json +119 -0
- package/translations/bn.json +119 -0
- package/translations/de.json +119 -0
- package/translations/en.json +44 -42
- package/translations/en_US.json +119 -0
- package/translations/es.json +69 -57
- package/translations/es_MX.json +119 -0
- package/translations/fr.json +74 -58
- package/translations/he.json +44 -40
- package/translations/hi.json +119 -0
- package/translations/hi_IN.json +119 -0
- package/translations/id.json +119 -0
- package/translations/it.json +119 -0
- package/translations/ka.json +119 -0
- package/translations/km.json +44 -40
- package/translations/ku.json +119 -0
- package/translations/ky.json +119 -0
- package/translations/lg.json +119 -0
- package/translations/ne.json +119 -0
- package/translations/pl.json +119 -0
- package/translations/pt.json +119 -0
- package/translations/pt_BR.json +119 -0
- package/translations/qu.json +119 -0
- package/translations/ro_RO.json +119 -0
- package/translations/ru_RU.json +119 -0
- package/translations/si.json +119 -0
- package/translations/sw.json +119 -0
- package/translations/sw_KE.json +119 -0
- package/translations/tr.json +119 -0
- package/translations/tr_TR.json +119 -0
- package/translations/uk.json +119 -0
- package/translations/uz.json +119 -0
- package/translations/uz@Latn.json +119 -0
- package/translations/uz_UZ.json +119 -0
- package/translations/vi.json +119 -0
- package/translations/zh.json +45 -23
- package/translations/zh_CN.json +39 -17
- package/.turbo/turbo-build.log +0 -40
- package/dist/132.js +0 -1
- package/dist/197.js +0 -1
- package/dist/236.js +0 -1
- package/dist/236.js.map +0 -1
- package/dist/300.js +0 -1
- package/dist/335.js +0 -1
- package/dist/372.js +0 -1
- package/dist/372.js.map +0 -1
- package/dist/41.js +0 -2
- package/dist/41.js.map +0 -1
- package/dist/449.js +0 -1
- package/dist/449.js.map +0 -1
- package/dist/464.js +0 -1
- package/dist/464.js.map +0 -1
- package/dist/495.js +0 -1
- package/dist/495.js.map +0 -1
- package/dist/55.js +0 -1
- package/dist/56.js +0 -1
- package/dist/56.js.map +0 -1
- package/dist/621.js +0 -1
- package/dist/621.js.map +0 -1
- package/dist/629.js +0 -2
- package/dist/629.js.map +0 -1
- package/dist/652.js +0 -1
- package/dist/661.js +0 -1
- package/dist/757.js +0 -1
- package/dist/757.js.map +0 -1
- package/dist/828.js +0 -1
- package/dist/828.js.map +0 -1
- package/dist/830.js +0 -1
- package/dist/830.js.map +0 -1
- package/dist/831.js +0 -2
- package/dist/831.js.LICENSE.txt +0 -3
- package/dist/831.js.map +0 -1
- package/dist/876.js +0 -2
- package/dist/876.js.map +0 -1
- package/dist/879.js +0 -1
- package/dist/913.js +0 -2
- package/dist/913.js.map +0 -1
- package/dist/927.js +0 -1
- package/dist/927.js.map +0 -1
- package/dist/99.js +0 -1
- package/dist/ampath-esm-patient-registration-app.js +0 -1
- package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +0 -694
- package/dist/ampath-esm-patient-registration-app.js.map +0 -1
- package/src/patient-registration/date-util.ts +0 -52
- package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +0 -56
- package/src/patient-registration/validation/patient-registration-validation.tsx +0 -60
- package/src/patient-verification/assets/counties.json +0 -236
- package/src/patient-verification/assets/verification-assets.ts +0 -11
- package/src/patient-verification/patient-verification-hook.tsx +0 -176
- package/src/patient-verification/patient-verification-utils.ts +0 -179
- package/src/patient-verification/patient-verification.component.tsx +0 -124
- package/src/patient-verification/patient-verification.scss +0 -25
- package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +0 -72
- package/src/patient-verification/verification-modal/empty-prompt.component.tsx +0 -35
- package/src/patient-verification/verification-types.ts +0 -50
- package/src/widgets/cancel-patient-edit.component.tsx +0 -37
- package/src/widgets/delete-identifier-confirmation-modal.tsx +0 -41
- package/src/widgets/delete-identifier-modal.scss +0 -34
- /package/dist/{41.js.LICENSE.txt → 4041.js.LICENSE.txt} +0 -0
- /package/src/patient-registration/input/custom-input/identifier/{utils.testt.ts → utils.test.ts} +0 -0
|
@@ -1,21 +1,48 @@
|
|
|
1
|
-
import
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { getConfig } from '@openmrs/esm-framework';
|
|
3
|
+
import { type RegistrationConfig } from '../../config-schema';
|
|
2
4
|
import { getValidationSchema } from './patient-registration-validation';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
const mockGetConfig = jest.mocked(getConfig);
|
|
7
|
+
|
|
8
|
+
describe('Patient registration validation', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockGetConfig.mockResolvedValue({
|
|
11
|
+
fieldConfigurations: {
|
|
12
|
+
gender: [
|
|
13
|
+
{
|
|
14
|
+
label: 'M',
|
|
15
|
+
value: 'male',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: 'F',
|
|
19
|
+
value: 'female',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: 'O',
|
|
23
|
+
value: 'other',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'U',
|
|
27
|
+
value: 'unknown',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
});
|
|
7
32
|
});
|
|
8
33
|
|
|
9
34
|
const validFormValues = {
|
|
10
|
-
givenName: 'John',
|
|
11
|
-
familyName: 'Doe',
|
|
12
|
-
additionalGivenName: '',
|
|
13
35
|
additionalFamilyName: '',
|
|
14
|
-
|
|
36
|
+
additionalGivenName: '',
|
|
15
37
|
birthdate: new Date('1990-01-01'),
|
|
16
38
|
birthdateEstimated: false,
|
|
39
|
+
isDead: false,
|
|
40
|
+
causeOfDeath: null,
|
|
17
41
|
deathDate: null,
|
|
18
42
|
email: 'john.doe@example.com',
|
|
43
|
+
familyName: 'Doe',
|
|
44
|
+
gender: 'male',
|
|
45
|
+
givenName: 'John',
|
|
19
46
|
identifiers: {
|
|
20
47
|
nationalId: {
|
|
21
48
|
required: true,
|
|
@@ -29,8 +56,10 @@ describe('Patient Registration Validation', () => {
|
|
|
29
56
|
};
|
|
30
57
|
|
|
31
58
|
const validateFormValues = async (formValues) => {
|
|
32
|
-
const config = (await getConfig('@openmrs/esm-patient-registration-app')) as
|
|
33
|
-
const
|
|
59
|
+
const config = (await getConfig('@openmrs/esm-patient-registration-app')) as unknown as RegistrationConfig;
|
|
60
|
+
const mockT = (key: string, defaultValue: string) => defaultValue;
|
|
61
|
+
|
|
62
|
+
const validationSchema = getValidationSchema(config, mockT);
|
|
34
63
|
try {
|
|
35
64
|
await validationSchema.validate(formValues, { abortEarly: false });
|
|
36
65
|
} catch (err) {
|
|
@@ -49,7 +78,7 @@ describe('Patient Registration Validation', () => {
|
|
|
49
78
|
givenName: '',
|
|
50
79
|
};
|
|
51
80
|
const validationError = await validateFormValues(invalidFormValues);
|
|
52
|
-
expect(validationError.errors).toContain('
|
|
81
|
+
expect(validationError.errors).toContain('Given name is required');
|
|
53
82
|
});
|
|
54
83
|
|
|
55
84
|
it('should require familyName', async () => {
|
|
@@ -58,7 +87,7 @@ describe('Patient Registration Validation', () => {
|
|
|
58
87
|
familyName: '',
|
|
59
88
|
};
|
|
60
89
|
const validationError = await validateFormValues(invalidFormValues);
|
|
61
|
-
expect(validationError.errors).toContain('
|
|
90
|
+
expect(validationError.errors).toContain('Family name is required');
|
|
62
91
|
});
|
|
63
92
|
|
|
64
93
|
it('should require additionalGivenName when addNameInLocalLanguage is true', async () => {
|
|
@@ -68,7 +97,7 @@ describe('Patient Registration Validation', () => {
|
|
|
68
97
|
additionalGivenName: '',
|
|
69
98
|
};
|
|
70
99
|
const validationError = await validateFormValues(invalidFormValues);
|
|
71
|
-
expect(validationError.errors).toContain('
|
|
100
|
+
expect(validationError.errors).toContain('Given name is required');
|
|
72
101
|
});
|
|
73
102
|
|
|
74
103
|
it('should require additionalFamilyName when addNameInLocalLanguage is true', async () => {
|
|
@@ -78,7 +107,7 @@ describe('Patient Registration Validation', () => {
|
|
|
78
107
|
additionalFamilyName: '',
|
|
79
108
|
};
|
|
80
109
|
const validationError = await validateFormValues(invalidFormValues);
|
|
81
|
-
expect(validationError.errors).toContain('
|
|
110
|
+
expect(validationError.errors).toContain('Family name is required');
|
|
82
111
|
});
|
|
83
112
|
|
|
84
113
|
it('should require gender', async () => {
|
|
@@ -87,7 +116,7 @@ describe('Patient Registration Validation', () => {
|
|
|
87
116
|
gender: '',
|
|
88
117
|
};
|
|
89
118
|
const validationError = await validateFormValues(invalidFormValues);
|
|
90
|
-
expect(validationError.errors).toContain('
|
|
119
|
+
expect(validationError.errors).toContain('Gender unspecified');
|
|
91
120
|
});
|
|
92
121
|
|
|
93
122
|
it('should allow female as a valid gender', async () => {
|
|
@@ -117,13 +146,22 @@ describe('Patient Registration Validation', () => {
|
|
|
117
146
|
expect(validationError).toBeFalsy();
|
|
118
147
|
});
|
|
119
148
|
|
|
120
|
-
it('should throw error when date of birth is a future date', async () => {
|
|
149
|
+
it('should throw an error when date of birth is a future date', async () => {
|
|
121
150
|
const invalidFormValues = {
|
|
122
151
|
...validFormValues,
|
|
123
152
|
birthdate: new Date('2100-01-01'),
|
|
124
153
|
};
|
|
125
154
|
const validationError = await validateFormValues(invalidFormValues);
|
|
126
|
-
expect(validationError.errors).toContain('
|
|
155
|
+
expect(validationError.errors).toContain('Birthday cannot be in future');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should throw an error when date of birth is more than 140 years ago', async () => {
|
|
159
|
+
const invalidFormValues = {
|
|
160
|
+
...validFormValues,
|
|
161
|
+
birthdate: dayjs().subtract(141, 'years').toDate(),
|
|
162
|
+
};
|
|
163
|
+
const validationError = await validateFormValues(invalidFormValues);
|
|
164
|
+
expect(validationError.errors).toContain('Birthday cannot be more than 140 years ago');
|
|
127
165
|
});
|
|
128
166
|
|
|
129
167
|
it('should require yearsEstimated when birthdateEstimated is true', async () => {
|
|
@@ -132,10 +170,10 @@ describe('Patient Registration Validation', () => {
|
|
|
132
170
|
birthdateEstimated: true,
|
|
133
171
|
};
|
|
134
172
|
const validationError = await validateFormValues(invalidFormValues);
|
|
135
|
-
expect(validationError.errors).toContain('
|
|
173
|
+
expect(validationError.errors).toContain('Estimated years required');
|
|
136
174
|
});
|
|
137
175
|
|
|
138
|
-
it('should throw error when monthEstimated is negative', async () => {
|
|
176
|
+
it('should throw an error when monthEstimated is negative', async () => {
|
|
139
177
|
const invalidFormValues = {
|
|
140
178
|
...validFormValues,
|
|
141
179
|
birthdateEstimated: true,
|
|
@@ -143,15 +181,25 @@ describe('Patient Registration Validation', () => {
|
|
|
143
181
|
monthsEstimated: -1,
|
|
144
182
|
};
|
|
145
183
|
const validationError = await validateFormValues(invalidFormValues);
|
|
146
|
-
expect(validationError.errors).toContain('
|
|
184
|
+
expect(validationError.errors).toContain('Estimated months cannot be negative');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should throw an error when yearsEstimated is more than 140', async () => {
|
|
188
|
+
const invalidFormValues = {
|
|
189
|
+
...validFormValues,
|
|
190
|
+
birthdateEstimated: true,
|
|
191
|
+
yearsEstimated: 141,
|
|
192
|
+
};
|
|
193
|
+
const validationError = await validateFormValues(invalidFormValues);
|
|
194
|
+
expect(validationError.errors).toContain('Estimated years cannot be more than 140');
|
|
147
195
|
});
|
|
148
196
|
|
|
149
|
-
it('should throw error when deathDate is in future', async () => {
|
|
197
|
+
it('should throw an error when deathDate is in future', async () => {
|
|
150
198
|
const invalidFormValues = {
|
|
151
199
|
...validFormValues,
|
|
152
200
|
deathDate: new Date('2100-01-01'),
|
|
153
201
|
};
|
|
154
202
|
const validationError = await validateFormValues(invalidFormValues);
|
|
155
|
-
expect(validationError.errors).toContain('
|
|
203
|
+
expect(validationError.errors).toContain('Death date cannot be in future');
|
|
156
204
|
});
|
|
157
205
|
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import * as Yup from 'yup';
|
|
3
|
+
import mapValues from 'lodash/mapValues';
|
|
4
|
+
import { type RegistrationConfig } from '../../config-schema';
|
|
5
|
+
import { type FormValues } from '../patient-registration.types';
|
|
6
|
+
import { getDatetime } from '../patient-registration.resource';
|
|
7
|
+
|
|
8
|
+
export function getValidationSchema(config: RegistrationConfig, t: (key: string, defaultValue: string) => string) {
|
|
9
|
+
return Yup.object({
|
|
10
|
+
givenName: Yup.string().required(t('givenNameRequired', 'Given name is required')),
|
|
11
|
+
familyName: Yup.string().required(t('familyNameRequired', 'Family name is required')),
|
|
12
|
+
additionalGivenName: Yup.string().when('addNameInLocalLanguage', {
|
|
13
|
+
is: true,
|
|
14
|
+
then: Yup.string().required(t('givenNameRequired', 'Given name is required')),
|
|
15
|
+
otherwise: Yup.string().notRequired(),
|
|
16
|
+
}),
|
|
17
|
+
additionalFamilyName: Yup.string().when('addNameInLocalLanguage', {
|
|
18
|
+
is: true,
|
|
19
|
+
then: Yup.string().required(t('familyNameRequired', 'Family name is required')),
|
|
20
|
+
otherwise: Yup.string().notRequired(),
|
|
21
|
+
}),
|
|
22
|
+
gender: Yup.string()
|
|
23
|
+
.oneOf(
|
|
24
|
+
config.fieldConfigurations.gender.map((g) => g.value),
|
|
25
|
+
t('genderUnspecified', 'Gender unspecified'),
|
|
26
|
+
)
|
|
27
|
+
.required(t('genderRequired', 'Gender is required')),
|
|
28
|
+
birthdate: Yup.date().when('birthdateEstimated', {
|
|
29
|
+
is: false,
|
|
30
|
+
then: Yup.date()
|
|
31
|
+
.required(t('birthdayRequired', 'Birthday is required'))
|
|
32
|
+
.max(Date(), t('birthdayNotInTheFuture', 'Birthday cannot be in future'))
|
|
33
|
+
.min(
|
|
34
|
+
dayjs().subtract(140, 'years').toDate(),
|
|
35
|
+
t('birthdayNotOver140YearsAgo', 'Birthday cannot be more than 140 years ago'),
|
|
36
|
+
)
|
|
37
|
+
.nullable(),
|
|
38
|
+
otherwise: Yup.date().nullable(),
|
|
39
|
+
}),
|
|
40
|
+
yearsEstimated: Yup.number().when('birthdateEstimated', {
|
|
41
|
+
is: true,
|
|
42
|
+
then: Yup.number()
|
|
43
|
+
.required(t('yearsEstimateRequired', 'Estimated years required'))
|
|
44
|
+
.min(0, t('negativeYears', 'Estimated years cannot be negative'))
|
|
45
|
+
.max(140, t('nonsensicalYears', 'Estimated years cannot be more than 140')),
|
|
46
|
+
otherwise: Yup.number().nullable(),
|
|
47
|
+
}),
|
|
48
|
+
monthsEstimated: Yup.number().min(0, t('negativeMonths', 'Estimated months cannot be negative')),
|
|
49
|
+
isDead: Yup.boolean(),
|
|
50
|
+
deathDate: Yup.date()
|
|
51
|
+
.when('isDead', {
|
|
52
|
+
is: true,
|
|
53
|
+
then: Yup.date().required(t('deathDateRequired', 'Death date is required')),
|
|
54
|
+
otherwise: Yup.date().nullable(),
|
|
55
|
+
})
|
|
56
|
+
.max(new Date(), t('deathDateInFuture', 'Death date cannot be in future'))
|
|
57
|
+
.test(
|
|
58
|
+
'deathDate-after-birthdate',
|
|
59
|
+
t('deathdayInvalidDate', 'Death date and time cannot be before the birthday'),
|
|
60
|
+
function (value) {
|
|
61
|
+
const { birthdate } = this.parent;
|
|
62
|
+
if (birthdate && value) {
|
|
63
|
+
return dayjs(value).isAfter(birthdate);
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
.test('deathDate-before-today', t('deathDateInFuture', 'Death date cannot be in future'), function (value) {
|
|
69
|
+
const { deathTime, deathTimeFormat } = this.parent;
|
|
70
|
+
if (value && deathTime && deathTimeFormat && /^(1[0-2]|0?[1-9]):([0-5]?[0-9])$/.test(deathTime)) {
|
|
71
|
+
return dayjs(getDatetime(value, deathTime, deathTimeFormat)).isBefore(dayjs());
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}),
|
|
75
|
+
deathTime: Yup.string()
|
|
76
|
+
.when('isDead', {
|
|
77
|
+
is: true,
|
|
78
|
+
then: Yup.string().required(t('deathTimeRequired', 'Death time is required')),
|
|
79
|
+
otherwise: Yup.string().nullable(),
|
|
80
|
+
})
|
|
81
|
+
.matches(/^(1[0-2]|0?[1-9]):([0-5]?[0-9])$/, t('deathTimeInvalid', "Time doesn't match the format 'hh:mm'")),
|
|
82
|
+
|
|
83
|
+
deathTimeFormat: Yup.string()
|
|
84
|
+
.when('isDead', {
|
|
85
|
+
is: true,
|
|
86
|
+
then: Yup.string().required(t('deathTimeFormatRequired', 'Time format is required')),
|
|
87
|
+
otherwise: Yup.string().nullable(),
|
|
88
|
+
})
|
|
89
|
+
.oneOf(['AM', 'PM'], t('deathTimeFormatInvalid', 'Time format is invalid')),
|
|
90
|
+
|
|
91
|
+
deathCause: Yup.string().when('isDead', {
|
|
92
|
+
is: true,
|
|
93
|
+
then: Yup.string().required(t('deathCauseRequired', 'Cause of death is required')),
|
|
94
|
+
otherwise: Yup.string().nullable(),
|
|
95
|
+
}),
|
|
96
|
+
nonCodedCauseOfDeath: Yup.string().when(['isDead', 'deathCause'], {
|
|
97
|
+
is: (isDead, deathCause) => isDead && deathCause === config.freeTextFieldConceptUuid,
|
|
98
|
+
then: Yup.string().required(t('nonCodedCauseOfDeathRequired', 'Cause of death is required')),
|
|
99
|
+
otherwise: Yup.string().nullable(),
|
|
100
|
+
}),
|
|
101
|
+
email: Yup.string().optional().email(t('invalidEmail', 'Invalid email')),
|
|
102
|
+
identifiers: Yup.lazy((obj: FormValues['identifiers']) =>
|
|
103
|
+
Yup.object(
|
|
104
|
+
mapValues(obj, () =>
|
|
105
|
+
Yup.object({
|
|
106
|
+
required: Yup.bool(),
|
|
107
|
+
identifierValue: Yup.string().when('required', {
|
|
108
|
+
is: true,
|
|
109
|
+
then: Yup.string().required(t('identifierValueRequired', 'Identifier value is required')),
|
|
110
|
+
otherwise: Yup.string().notRequired(),
|
|
111
|
+
}),
|
|
112
|
+
}),
|
|
113
|
+
),
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
relationships: Yup.array().of(
|
|
117
|
+
Yup.object().shape({
|
|
118
|
+
relatedPersonUuid: Yup.string().required(),
|
|
119
|
+
relationshipType: Yup.string().required(),
|
|
120
|
+
}),
|
|
121
|
+
),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { type Resources } from './offline.resources';
|
|
3
|
+
|
|
4
|
+
export const ResourcesContext = createContext<Resources>(null);
|
|
5
|
+
|
|
6
|
+
export const ResourcesContextProvider = ResourcesContext.Provider;
|
|
7
|
+
|
|
8
|
+
export const useResourcesContext = () => {
|
|
9
|
+
const context = useContext(ResourcesContext);
|
|
10
|
+
if (!context) {
|
|
11
|
+
throw new Error('useResourcesContext must be used within a ResourcesContextProvider');
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
};
|
package/src/root.component.tsx
CHANGED
|
@@ -5,11 +5,11 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
|
5
5
|
import { Grid, Row } from '@carbon/react';
|
|
6
6
|
import { ExtensionSlot, useConnectivity, useSession } from '@openmrs/esm-framework';
|
|
7
7
|
import {
|
|
8
|
-
ResourcesContext,
|
|
9
8
|
fetchAddressTemplate,
|
|
10
9
|
fetchAllRelationshipTypes,
|
|
11
10
|
fetchPatientIdentifierTypesWithSources,
|
|
12
11
|
} from './offline.resources';
|
|
12
|
+
import { ResourcesContextProvider } from './resources-context';
|
|
13
13
|
import { FormManager } from './patient-registration/form-manager';
|
|
14
14
|
import { PatientRegistration } from './patient-registration/patient-registration.component';
|
|
15
15
|
import styles from './root.scss';
|
|
@@ -37,7 +37,7 @@ export default function Root() {
|
|
|
37
37
|
<Row>
|
|
38
38
|
<ExtensionSlot name="breadcrumbs-slot" />
|
|
39
39
|
</Row>
|
|
40
|
-
<
|
|
40
|
+
<ResourcesContextProvider
|
|
41
41
|
value={{
|
|
42
42
|
addressTemplate,
|
|
43
43
|
relationshipTypes,
|
|
@@ -56,7 +56,7 @@ export default function Root() {
|
|
|
56
56
|
/>
|
|
57
57
|
</Routes>
|
|
58
58
|
</BrowserRouter>
|
|
59
|
-
</
|
|
59
|
+
</ResourcesContextProvider>
|
|
60
60
|
</Grid>
|
|
61
61
|
</main>
|
|
62
62
|
);
|
package/src/routes.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
3
|
"backendDependencies": {
|
|
4
|
-
"webservices.rest": "
|
|
4
|
+
"webservices.rest": ">=2.2.0"
|
|
5
5
|
},
|
|
6
6
|
"pages": [
|
|
7
7
|
{
|
|
@@ -23,13 +23,8 @@
|
|
|
23
23
|
"name": "add-patient-action",
|
|
24
24
|
"slot": "top-nav-actions-slot",
|
|
25
25
|
"online": true,
|
|
26
|
-
"offline": true
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
"component": "cancelPatientEditModal",
|
|
30
|
-
"name": "cancel-patient-edit-modal",
|
|
31
|
-
"online": true,
|
|
32
|
-
"offline": true
|
|
26
|
+
"offline": true,
|
|
27
|
+
"order": 30
|
|
33
28
|
},
|
|
34
29
|
{
|
|
35
30
|
"component": "patientPhotoExtension",
|
|
@@ -51,25 +46,16 @@
|
|
|
51
46
|
"slot": "patient-search-actions-slot",
|
|
52
47
|
"online": true,
|
|
53
48
|
"offline": true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"name": "delete-identifier-confirmation-modal",
|
|
58
|
-
"online": true,
|
|
59
|
-
"offline": true
|
|
60
|
-
},
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"modals": [
|
|
61
52
|
{
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"online": true,
|
|
65
|
-
"offline": true
|
|
53
|
+
"name": "cancel-patient-edit-modal",
|
|
54
|
+
"component": "cancelPatientEditModal"
|
|
66
55
|
},
|
|
67
56
|
{
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"online": true,
|
|
71
|
-
"offline": true
|
|
57
|
+
"name": "delete-identifier-confirmation-modal",
|
|
58
|
+
"component": "deleteIdentifierConfirmationModal"
|
|
72
59
|
}
|
|
73
60
|
]
|
|
74
61
|
}
|
|
75
|
-
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
interface CancelPatientEditPropsModal {
|
|
6
|
+
close(): void;
|
|
7
|
+
onConfirm(): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const CancelPatientEditModal: React.FC<CancelPatientEditPropsModal> = ({ close, onConfirm }) => {
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<ModalHeader
|
|
15
|
+
closeModal={close}
|
|
16
|
+
title={t('confirmDiscardChangesTitle', 'Are you sure you want to discard these changes?')}
|
|
17
|
+
/>
|
|
18
|
+
<ModalBody>
|
|
19
|
+
<p>{t('confirmDiscardChangesBody', 'Your unsaved changes will be lost if you proceed to discard the form')}.</p>
|
|
20
|
+
</ModalBody>
|
|
21
|
+
<ModalFooter>
|
|
22
|
+
<Button kind="secondary" onClick={close}>
|
|
23
|
+
{t('cancel', 'Cancel')}
|
|
24
|
+
</Button>
|
|
25
|
+
<Button kind="danger" onClick={onConfirm}>
|
|
26
|
+
{t('discard', 'Discard')}
|
|
27
|
+
</Button>
|
|
28
|
+
</ModalFooter>
|
|
29
|
+
</>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default CancelPatientEditModal;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { screen, render } from '@testing-library/react';
|
|
4
|
+
import CancelPatientEdit from './cancel-patient-edit.modal';
|
|
5
|
+
|
|
6
|
+
describe('CancelPatientEdit modal', () => {
|
|
7
|
+
const mockClose = jest.fn();
|
|
8
|
+
const mockOnConfirm = jest.fn();
|
|
9
|
+
|
|
10
|
+
it('renders the modal and triggers close and onConfirm functions', async () => {
|
|
11
|
+
const user = userEvent.setup();
|
|
12
|
+
render(<CancelPatientEdit close={mockClose} onConfirm={mockOnConfirm} />);
|
|
13
|
+
|
|
14
|
+
const cancelButton = screen.getByRole('button', { name: /Cancel/i });
|
|
15
|
+
await user.click(cancelButton);
|
|
16
|
+
expect(mockClose).toHaveBeenCalledTimes(1);
|
|
17
|
+
|
|
18
|
+
const discardButton = screen.getByRole('button', { name: /discard/i });
|
|
19
|
+
await user.click(discardButton);
|
|
20
|
+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, ModalBody, ModalHeader, ModalFooter } from '@carbon/react';
|
|
4
|
+
|
|
5
|
+
interface DeleteIdentifierConfirmationModalProps {
|
|
6
|
+
closeModal: () => void;
|
|
7
|
+
deleteIdentifier: (x: boolean) => void;
|
|
8
|
+
identifierName: string;
|
|
9
|
+
identifierValue: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DeleteIdentifierConfirmationModal: React.FC<DeleteIdentifierConfirmationModalProps> = ({
|
|
13
|
+
closeModal,
|
|
14
|
+
deleteIdentifier,
|
|
15
|
+
identifierName,
|
|
16
|
+
identifierValue,
|
|
17
|
+
}) => {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<ModalHeader
|
|
23
|
+
closeModal={closeModal}
|
|
24
|
+
title={t('deleteIdentifierModalHeading', 'Delete identifier?')}></ModalHeader>
|
|
25
|
+
<ModalBody>
|
|
26
|
+
<p>
|
|
27
|
+
{identifierName && identifierValue && (
|
|
28
|
+
<span>
|
|
29
|
+
<strong>{identifierName}</strong>
|
|
30
|
+
{t('deleteIdentifierModalText', ' has a value of ')} <strong>{identifierValue}</strong>.{' '}
|
|
31
|
+
</span>
|
|
32
|
+
)}
|
|
33
|
+
{t('confirmIdentifierDeletionText', 'Are you sure you want to delete this identifier?')}
|
|
34
|
+
</p>
|
|
35
|
+
</ModalBody>
|
|
36
|
+
<ModalFooter>
|
|
37
|
+
<Button kind="secondary" size="lg" onClick={closeModal}>
|
|
38
|
+
{t('cancel', 'Cancel')}
|
|
39
|
+
</Button>
|
|
40
|
+
<Button kind="danger" size="lg" onClick={() => deleteIdentifier(true)}>
|
|
41
|
+
{t('removeIdentifierButton', 'Remove identifier')}
|
|
42
|
+
</Button>
|
|
43
|
+
</ModalFooter>
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default DeleteIdentifierConfirmationModal;
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import DeleteIdentifierConfirmationModal from './delete-identifier-confirmation
|
|
4
|
+
import DeleteIdentifierConfirmationModal from './delete-identifier-confirmation.modal';
|
|
5
5
|
|
|
6
|
-
describe('DeleteIdentifierConfirmationModal
|
|
6
|
+
describe('DeleteIdentifierConfirmationModal', () => {
|
|
7
7
|
const mockDeleteIdentifier = jest.fn();
|
|
8
|
+
const closeModal = jest.fn();
|
|
8
9
|
const mockIdentifierName = 'Identifier Name';
|
|
9
10
|
const mockIdentifierValue = 'Identifier Value';
|
|
10
11
|
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
jest.clearAllMocks();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
12
|
it('renders the modal and triggers deleteIdentifier function', async () => {
|
|
16
13
|
const user = userEvent.setup();
|
|
17
14
|
|
|
18
15
|
render(
|
|
19
16
|
<DeleteIdentifierConfirmationModal
|
|
17
|
+
closeModal={closeModal}
|
|
20
18
|
deleteIdentifier={mockDeleteIdentifier}
|
|
21
19
|
identifierName={mockIdentifierName}
|
|
22
20
|
identifierValue={mockIdentifierValue}
|
|
@@ -25,7 +23,7 @@ describe('DeleteIdentifierConfirmationModal component', () => {
|
|
|
25
23
|
|
|
26
24
|
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
27
25
|
await user.click(cancelButton);
|
|
28
|
-
expect(
|
|
26
|
+
expect(closeModal).toHaveBeenCalledTimes(1);
|
|
29
27
|
|
|
30
28
|
const removeButton = screen.getByRole('button', { name: /remove identifier/i });
|
|
31
29
|
await user.click(removeButton);
|
|
@@ -20,7 +20,6 @@ const EditPatientDetailsButton: React.FC<EditPatientDetailsButtonProps> = ({ pat
|
|
|
20
20
|
<button
|
|
21
21
|
className="cds--overflow-menu-options__btn"
|
|
22
22
|
role="menuitem"
|
|
23
|
-
title={t('editPatientDetails', 'Edit patient details')}
|
|
24
23
|
data-floating-menu-primary-focus
|
|
25
24
|
onClick={handleClick}>
|
|
26
25
|
<span className="cds--overflow-menu-options__option-content">
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import { navigate } from '@openmrs/esm-framework';
|
|
5
|
+
import { mockPatient } from '__mocks__';
|
|
6
|
+
import EditPatientDetailsButton from './edit-patient-details-button.component';
|
|
7
|
+
|
|
8
|
+
const mockNavigate = jest.mocked(navigate);
|
|
9
|
+
|
|
10
|
+
describe('EditPatientDetailsButton', () => {
|
|
11
|
+
const patientUuid = mockPatient.uuid;
|
|
12
|
+
|
|
13
|
+
it('should navigate to the edit page when clicked', async () => {
|
|
14
|
+
const user = userEvent.setup();
|
|
15
|
+
|
|
16
|
+
render(<EditPatientDetailsButton patientUuid={patientUuid} />);
|
|
17
|
+
|
|
18
|
+
const button = screen.getByRole('menuitem');
|
|
19
|
+
await user.click(button);
|
|
20
|
+
|
|
21
|
+
expect(mockNavigate).toHaveBeenCalledWith({ to: expect.stringContaining(`/patient/${patientUuid}/edit`) });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should call the onTransition function when provided', async () => {
|
|
25
|
+
const user = userEvent.setup();
|
|
26
|
+
|
|
27
|
+
const onTransitionMock = jest.fn();
|
|
28
|
+
render(<EditPatientDetailsButton patientUuid={patientUuid} onTransition={onTransitionMock} />);
|
|
29
|
+
|
|
30
|
+
const button = screen.getByRole('menuitem');
|
|
31
|
+
await user.click(button);
|
|
32
|
+
|
|
33
|
+
expect(onTransitionMock).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
});
|