@kenyaemr/esm-patient-registration-app 8.0.1-pre.95 → 8.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/.turbo/turbo-build.log +23 -22
- package/dist/108.js +1 -0
- package/dist/108.js.map +1 -0
- package/dist/130.js +1 -1
- package/dist/130.js.LICENSE.txt +2 -0
- package/dist/130.js.map +1 -1
- package/dist/2.js +1 -0
- package/dist/2.js.map +1 -0
- package/dist/250.js +1 -0
- package/dist/250.js.map +1 -0
- package/dist/271.js +1 -1
- package/dist/319.js +1 -1
- package/dist/325.js +1 -0
- package/dist/325.js.map +1 -0
- package/dist/372.js +2 -0
- package/dist/372.js.map +1 -0
- package/dist/460.js +1 -1
- package/dist/574.js +1 -1
- package/dist/644.js +1 -1
- package/dist/66.js +1 -0
- package/dist/66.js.map +1 -0
- package/dist/662.js +1 -0
- package/dist/662.js.map +1 -0
- package/dist/757.js +1 -1
- package/dist/{59.js → 76.js} +1 -1
- package/dist/{59.js.map → 76.js.map} +1 -1
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/833.js +1 -1
- package/dist/895.js +2 -0
- package/dist/895.js.LICENSE.txt +34 -0
- package/dist/895.js.map +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +161 -188
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +10 -0
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package-lock.json +6047 -0
- package/package.json +3 -4
- package/src/client-registry/client-registry.component.tsx +22 -0
- package/src/client-registry/hie-client-registry/hie-client-registry.component.tsx +134 -0
- package/src/client-registry/hie-client-registry/hie-client-registry.scss +53 -0
- package/src/client-registry/hie-client-registry/hie-resource.ts +162 -0
- package/src/client-registry/hie-client-registry/hie-types.ts +29 -0
- package/src/client-registry/hie-client-registry/modal/confirm-hie.modal.tsx +82 -0
- package/src/client-registry/hie-client-registry/modal/confirm-hie.scss +10 -0
- package/src/{patient-verification → client-registry/patient-verification}/patient-verification-hook.tsx +2 -2
- package/src/{patient-verification → client-registry/patient-verification}/patient-verification-utils.ts +1 -1
- package/src/{patient-verification → client-registry/patient-verification}/patient-verification.component.tsx +4 -1
- package/src/{patient-verification → client-registry/patient-verification}/patient-verification.scss +17 -1
- package/src/{patient-verification → client-registry/patient-verification}/verification-modal/empty-prompt.component.tsx +9 -6
- package/src/config-schema.ts +72 -2
- package/src/index.ts +6 -6
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +98 -0
- 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 +21 -7
- package/src/patient-registration/field/field.component.tsx +11 -5
- package/src/patient-registration/field/field.resource.ts +11 -4
- package/src/patient-registration/field/field.scss +44 -5
- package/src/patient-registration/field/gender/gender-field.component.tsx +2 -1
- package/src/patient-registration/field/gender/gender-field.test.tsx +1 -0
- package/src/patient-registration/field/id/id-field.component.tsx +8 -6
- package/src/patient-registration/field/id/id-field.test.tsx +27 -8
- package/src/patient-registration/field/name/name-field.component.tsx +5 -1
- package/src/patient-registration/field/obs/obs-field.component.tsx +1 -1
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +1 -0
- package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +76 -27
- 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 +12 -1
- package/src/patient-registration/field/person-attributes/useUpdateIdentifierRequirement.tsx +83 -0
- package/src/patient-registration/form-manager.test.ts +21 -0
- package/src/patient-registration/form-manager.ts +40 -20
- package/src/patient-registration/input/basic-input/input/input.component.tsx +5 -1
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +18 -10
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +166 -67
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +3 -0
- package/src/patient-registration/input/input.scss +5 -0
- package/src/patient-registration/patient-registration-context.ts +4 -3
- package/src/patient-registration/patient-registration-hooks.ts +67 -9
- package/src/patient-registration/patient-registration-utils.ts +3 -7
- package/src/patient-registration/patient-registration.component.tsx +44 -30
- package/src/patient-registration/patient-registration.resource.ts +8 -0
- package/src/patient-registration/patient-registration.test.tsx +9 -3
- package/src/patient-registration/patient-registration.types.ts +4 -1
- 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 +4 -14
- package/src/patient-registration/section/section.component.tsx +1 -1
- package/src/patient-registration/section/section.scss +5 -0
- package/src/patient-registration/validation/{patient-registration-validation.test.tsx → patient-registration-validation.test.ts} +26 -4
- package/src/patient-registration/validation/patient-registration-validation.ts +126 -0
- package/src/routes.json +14 -17
- package/src/widgets/cancel-patient-edit.modal.tsx +33 -0
- package/src/widgets/cancel-patient-edit.test.tsx +2 -3
- package/src/widgets/delete-identifier-confirmation.modal.tsx +22 -15
- package/src/widgets/delete-identifier-confirmation.test.tsx +2 -1
- package/translations/am.json +36 -25
- package/translations/ar.json +37 -26
- package/translations/en.json +51 -20
- package/translations/es.json +38 -26
- package/translations/fr.json +47 -35
- package/translations/he.json +37 -30
- package/translations/km.json +37 -30
- package/translations/zh.json +37 -20
- package/translations/zh_CN.json +37 -20
- package/dist/152.js +0 -1
- package/dist/152.js.map +0 -1
- package/dist/255.js +0 -2
- package/dist/255.js.map +0 -1
- package/dist/303.js +0 -1
- package/dist/303.js.map +0 -1
- package/dist/330.js +0 -1
- package/dist/330.js.map +0 -1
- package/dist/564.js +0 -1
- package/dist/564.js.map +0 -1
- package/dist/623.js +0 -1
- package/dist/623.js.map +0 -1
- package/dist/729.js +0 -1
- package/dist/729.js.map +0 -1
- package/dist/735.js +0 -1
- package/dist/735.js.map +0 -1
- package/dist/831.js +0 -2
- package/dist/831.js.LICENSE.txt +0 -14
- package/dist/831.js.map +0 -1
- package/src/patient-registration/validation/patient-registration-validation.tsx +0 -60
- package/src/widgets/cancel-patient-edit.component.tsx +0 -37
- package/src/widgets/delete-identifier-confirmation.scss +0 -34
- /package/dist/{255.js.LICENSE.txt → 372.js.LICENSE.txt} +0 -0
- /package/src/{patient-verification → client-registry/patient-verification}/assets/counties.json +0 -0
- /package/src/{patient-verification → client-registry/patient-verification}/assets/verification-assets.ts +0 -0
- /package/src/{patient-verification → client-registry/patient-verification}/verification-modal/confirm-prompt.component.tsx +0 -0
- /package/src/{patient-verification → client-registry/patient-verification}/verification-types.ts +0 -0
|
@@ -66,7 +66,7 @@ export interface TextInputProps
|
|
|
66
66
|
* `true` to use the light version. For use on $ui-01 backgrounds only.
|
|
67
67
|
* Don't use this to make tile background color same as container background color.
|
|
68
68
|
* 'The `light` prop for `TextInput` has ' +
|
|
69
|
-
|
|
69
|
+
'been deprecated in favor of the new `Layer` component. It will be removed in the next major release.'
|
|
70
70
|
*/
|
|
71
71
|
light?: boolean;
|
|
72
72
|
|
|
@@ -145,6 +145,10 @@ export const Input: React.FC<InputProps> = ({ checkWarning, ...props }) => {
|
|
|
145
145
|
t('invalidEmail')
|
|
146
146
|
t('numberInNameDubious')
|
|
147
147
|
t('yearsEstimateRequired')
|
|
148
|
+
t('deathdayIsRequired', 'Death date is required when the patient is marked as deceased.')
|
|
149
|
+
t('deathdayInvalidDate', 'Date of death is invalid')
|
|
150
|
+
t('deathCauseRequired', 'Cause of death is required')
|
|
151
|
+
t('nonCodedCauseOfDeathRequired', 'Non-coded cause of death is required')
|
|
148
152
|
*/
|
|
149
153
|
|
|
150
154
|
const value = field.value || '';
|
package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx
CHANGED
|
@@ -26,7 +26,8 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
26
26
|
() => identifierTypes.find((identifierType) => identifierType.uuid === patientIdentifier.identifierTypeUuid),
|
|
27
27
|
[patientIdentifier, identifierTypes],
|
|
28
28
|
);
|
|
29
|
-
const { autoGeneration, initialValue, identifierValue, identifierName, required } = patientIdentifier;
|
|
29
|
+
const { autoGeneration, initialValue, identifierValue, identifierName, required, selectedSource } = patientIdentifier;
|
|
30
|
+
const manualEntryEnabled = selectedSource?.autoGenerationOption?.manualEntryEnabled;
|
|
30
31
|
const [hideInputField, setHideInputField] = useState(autoGeneration || initialValue === identifierValue);
|
|
31
32
|
const name = `identifiers.${fieldName}.identifierValue`;
|
|
32
33
|
const [identifierField, identifierFieldMeta] = useField(name);
|
|
@@ -46,8 +47,8 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
46
47
|
setFieldValue(`identifiers.${fieldName}`, {
|
|
47
48
|
...patientIdentifier,
|
|
48
49
|
identifierValue: initialValue,
|
|
49
|
-
selectedSource
|
|
50
|
-
autoGeneration
|
|
50
|
+
selectedSource,
|
|
51
|
+
autoGeneration,
|
|
51
52
|
} as PatientIdentifierValue);
|
|
52
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
54
|
}, [initialValue, setHideInputField]);
|
|
@@ -57,6 +58,7 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
57
58
|
setFieldValue(`identifiers.${fieldName}`, {
|
|
58
59
|
...patientIdentifier,
|
|
59
60
|
...setIdentifierSource(identifierType?.identifierSources?.[0], initialValue, initialValue),
|
|
61
|
+
...(autoGeneration && manualEntryEnabled && { identifierValue: initialValue ?? '' }),
|
|
60
62
|
});
|
|
61
63
|
};
|
|
62
64
|
|
|
@@ -83,9 +85,12 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
83
85
|
}
|
|
84
86
|
};
|
|
85
87
|
|
|
88
|
+
const showEditButton = !required && hideInputField && (!!initialValue || manualEntryEnabled);
|
|
89
|
+
const showResetButton =
|
|
90
|
+
(!!initialValue && initialValue !== identifierValue) || (!hideInputField && manualEntryEnabled);
|
|
86
91
|
return (
|
|
87
92
|
<div className={styles.IDInput}>
|
|
88
|
-
{!
|
|
93
|
+
{!hideInputField ? (
|
|
89
94
|
<Input
|
|
90
95
|
id={name}
|
|
91
96
|
labelText={identifierName}
|
|
@@ -99,21 +104,24 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
99
104
|
/>
|
|
100
105
|
) : (
|
|
101
106
|
<div className={styles.textID}>
|
|
102
|
-
<p className={styles.label}>
|
|
103
|
-
|
|
107
|
+
<p data-testid="identifier-label" className={styles.label}>
|
|
108
|
+
{required ? identifierName : `${t('optionalIdentifierLabel', { identifierName })}`}
|
|
109
|
+
</p>
|
|
110
|
+
<p data-testid="identifier-placeholder" className={styles.bodyShort02}>
|
|
104
111
|
{autoGeneration ? t('autoGeneratedPlaceholderText', 'Auto-generated') : identifierValue}
|
|
105
112
|
</p>
|
|
106
|
-
<input type="hidden" {...identifierField} disabled />
|
|
113
|
+
<input data-testid="identifier-input" type="hidden" {...identifierField} disabled />
|
|
107
114
|
{/* This is added for any error descriptions */}
|
|
108
115
|
{!!(identifierFieldMeta.touched && identifierFieldMeta.error) && (
|
|
109
116
|
<span className={styles.dangerLabel01}>{identifierFieldMeta.error && t(identifierFieldMeta.error)}</span>
|
|
110
117
|
)}
|
|
111
118
|
</div>
|
|
112
119
|
)}
|
|
113
|
-
<div
|
|
114
|
-
{
|
|
120
|
+
<div className={styles.actionButtonContainer}>
|
|
121
|
+
{showEditButton && (
|
|
115
122
|
<UserHasAccess privilege="Edit Patient Identifiers">
|
|
116
123
|
<Button
|
|
124
|
+
data-testid="edit-button"
|
|
117
125
|
size="md"
|
|
118
126
|
kind="ghost"
|
|
119
127
|
onClick={handleEdit}
|
|
@@ -124,7 +132,7 @@ const IdentifierInput: React.FC<IdentifierInputProps> = ({ patientIdentifier, fi
|
|
|
124
132
|
</Button>
|
|
125
133
|
</UserHasAccess>
|
|
126
134
|
)}
|
|
127
|
-
{
|
|
135
|
+
{showResetButton && (
|
|
128
136
|
<UserHasAccess privilege="Edit Patient Identifiers">
|
|
129
137
|
<Button
|
|
130
138
|
size="md"
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
/* eslint-disable testing-library/no-node-access */
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { Form, Formik } from 'formik';
|
|
5
|
+
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
6
|
+
import { esmPatientRegistrationSchema, type RegistrationConfig } from '../../../../config-schema';
|
|
7
|
+
import { ResourcesContext, type Resources } from '../../../../offline.resources';
|
|
8
|
+
import {
|
|
9
|
+
PatientRegistrationContext,
|
|
10
|
+
type PatientRegistrationContextProps,
|
|
11
|
+
} from '../../../patient-registration-context';
|
|
12
|
+
import type {
|
|
13
|
+
AddressTemplate,
|
|
14
|
+
FormValues,
|
|
15
|
+
IdentifierSource,
|
|
16
|
+
PatientIdentifierValue,
|
|
17
|
+
} from '../../../patient-registration.types';
|
|
7
18
|
import IdentifierInput from './identifier-input.component';
|
|
19
|
+
import userEvent from '@testing-library/user-event';
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const openmrsID = {
|
|
12
|
-
name: 'OpenMRS ID',
|
|
21
|
+
const mockIdentifierTypes = [
|
|
22
|
+
{
|
|
13
23
|
fieldName: 'openMrsId',
|
|
14
|
-
|
|
15
|
-
uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
|
|
16
|
-
format: null,
|
|
17
|
-
isPrimary: true,
|
|
24
|
+
format: '',
|
|
18
25
|
identifierSources: [
|
|
19
|
-
{
|
|
20
|
-
uuid: '691eed12-c0f1-11e2-94be-8c13b969e334',
|
|
21
|
-
name: 'Generator 1 for OpenMRS ID',
|
|
22
|
-
autoGenerationOption: {
|
|
23
|
-
manualEntryEnabled: false,
|
|
24
|
-
automaticGenerationEnabled: true,
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
26
|
{
|
|
28
27
|
uuid: '01af8526-cea4-4175-aa90-340acb411771',
|
|
29
28
|
name: 'Generator 2 for OpenMRS ID',
|
|
@@ -33,72 +32,172 @@ xdescribe('identifier input', () => {
|
|
|
33
32
|
},
|
|
34
33
|
},
|
|
35
34
|
],
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
isPrimary: true,
|
|
36
|
+
name: 'OpenMRS ID',
|
|
37
|
+
required: true,
|
|
38
|
+
uniquenessBehavior: 'UNIQUE' as const,
|
|
39
|
+
uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
|
|
40
|
+
},
|
|
41
|
+
];
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
const mockResourcesContextValue: Resources = {
|
|
44
|
+
addressTemplate: {} as AddressTemplate,
|
|
45
|
+
currentSession: {
|
|
46
|
+
authenticated: true,
|
|
47
|
+
sessionId: 'JSESSION',
|
|
48
|
+
currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
|
|
49
|
+
},
|
|
50
|
+
relationshipTypes: [],
|
|
51
|
+
identifierTypes: [...mockIdentifierTypes],
|
|
52
|
+
};
|
|
41
53
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
};
|
|
54
|
+
const mockContextValues: PatientRegistrationContextProps = {
|
|
55
|
+
currentPhoto: '',
|
|
56
|
+
inEditMode: false,
|
|
57
|
+
identifierTypes: [],
|
|
58
|
+
initialFormValues: {} as FormValues,
|
|
59
|
+
isOffline: false,
|
|
60
|
+
setCapturePhotoProps: jest.fn(),
|
|
61
|
+
setFieldValue: jest.fn(),
|
|
62
|
+
setInitialFormValues: jest.fn(),
|
|
63
|
+
setFieldTouched: jest.fn(),
|
|
64
|
+
validationSchema: null,
|
|
65
|
+
values: {} as FormValues,
|
|
66
|
+
};
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
const { identifierInput, identifierSourceSelectInput } = await setupIdentifierInput(openmrsID);
|
|
68
|
+
const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
describe('identifier input', () => {
|
|
71
|
+
mockUseConfig.mockReturnValue({
|
|
72
|
+
...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
|
|
62
73
|
});
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
const fieldName = 'openMrsId';
|
|
76
|
+
const openmrsID = {
|
|
77
|
+
identifierTypeUuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
|
|
78
|
+
initialValue: '',
|
|
79
|
+
identifierName: 'OpenMRS ID',
|
|
80
|
+
selectedSource: {
|
|
81
|
+
uuid: '01af8526-cea4-4175-aa90-340acb411771',
|
|
82
|
+
name: 'Generator 2 for OpenMRS ID',
|
|
83
|
+
autoGenerationOption: {
|
|
84
|
+
manualEntryEnabled: false,
|
|
85
|
+
automaticGenerationEnabled: true,
|
|
86
|
+
},
|
|
87
|
+
} as IdentifierSource,
|
|
88
|
+
autoGeneration: false,
|
|
89
|
+
preferred: true,
|
|
90
|
+
required: true,
|
|
91
|
+
} as PatientIdentifierValue;
|
|
92
|
+
|
|
93
|
+
const setupIdentifierInput = (patientIdentifier: PatientIdentifierValue, initialValues = {}) => {
|
|
94
|
+
render(
|
|
95
|
+
<ResourcesContext.Provider value={mockResourcesContextValue}>
|
|
96
|
+
<Formik initialValues={initialValues} onSubmit={jest.fn()}>
|
|
97
|
+
<Form>
|
|
98
|
+
<PatientRegistrationContext.Provider value={mockContextValues}>
|
|
99
|
+
<IdentifierInput patientIdentifier={patientIdentifier} fieldName={fieldName} />
|
|
100
|
+
</PatientRegistrationContext.Provider>
|
|
101
|
+
</Form>
|
|
102
|
+
</Formik>
|
|
103
|
+
</ResourcesContext.Provider>,
|
|
104
|
+
);
|
|
105
|
+
};
|
|
66
106
|
|
|
67
|
-
|
|
68
|
-
|
|
107
|
+
it('shows the identifier input', () => {
|
|
108
|
+
openmrsID.autoGeneration = false;
|
|
109
|
+
setupIdentifierInput(openmrsID as PatientIdentifierValue);
|
|
110
|
+
expect(screen.getByLabelText(openmrsID.identifierName)).toBeInTheDocument();
|
|
69
111
|
});
|
|
70
112
|
|
|
71
|
-
it('
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
113
|
+
it('displays an edit button when there is an initial value', async () => {
|
|
114
|
+
// setup
|
|
115
|
+
openmrsID.autoGeneration = false;
|
|
116
|
+
openmrsID.required = false;
|
|
117
|
+
openmrsID.initialValue = '1002UU9';
|
|
118
|
+
openmrsID.identifierValue = '1002UU9';
|
|
119
|
+
// replay
|
|
120
|
+
setupIdentifierInput(openmrsID as PatientIdentifierValue);
|
|
121
|
+
expect(screen.getByText('Edit')).toBeInTheDocument();
|
|
75
122
|
});
|
|
76
123
|
|
|
77
|
-
it('
|
|
124
|
+
it('hides the edit button when the identifier is required', async () => {
|
|
78
125
|
// setup
|
|
79
|
-
openmrsID.
|
|
126
|
+
openmrsID.autoGeneration = false;
|
|
127
|
+
openmrsID.required = true;
|
|
128
|
+
openmrsID.initialValue = '1002UU9';
|
|
129
|
+
openmrsID.identifierValue = '1002UU9';
|
|
80
130
|
// replay
|
|
81
|
-
|
|
82
|
-
expect(
|
|
83
|
-
expect(identifierInput.disabled).toBe(false);
|
|
131
|
+
setupIdentifierInput(openmrsID);
|
|
132
|
+
expect(screen.queryByText('Edit')).not.toBeInTheDocument();
|
|
84
133
|
});
|
|
85
134
|
|
|
86
|
-
it('
|
|
135
|
+
it('displays a delete button when the identifier is not a default type', () => {
|
|
87
136
|
// setup
|
|
88
|
-
openmrsID.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
137
|
+
openmrsID.required = false;
|
|
138
|
+
// replay
|
|
139
|
+
setupIdentifierInput(openmrsID);
|
|
140
|
+
expect(screen.getByText('Delete')).toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('auto-generated identifier', () => {
|
|
144
|
+
it('hides the input when the identifier is auto-generated', () => {
|
|
145
|
+
openmrsID.autoGeneration = true;
|
|
146
|
+
setupIdentifierInput(openmrsID);
|
|
147
|
+
expect(screen.getByTestId('identifier-input')).toHaveAttribute('type', 'hidden');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("displays 'Auto-Generated' when the indentifier has auto generation", () => {
|
|
151
|
+
openmrsID.autoGeneration = true;
|
|
152
|
+
setupIdentifierInput(openmrsID);
|
|
153
|
+
expect(screen.getByTestId('identifier-placeholder').innerHTML).toBe('Auto-generated');
|
|
154
|
+
expect(screen.getByTestId('identifier-input')).toBeDisabled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('manual entry allowed', () => {
|
|
158
|
+
openmrsID.selectedSource = {
|
|
92
159
|
autoGenerationOption: {
|
|
93
160
|
manualEntryEnabled: true,
|
|
94
|
-
automaticGenerationEnabled: false,
|
|
95
161
|
},
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
} as IdentifierSource;
|
|
163
|
+
|
|
164
|
+
it('shows the edit button', () => {
|
|
165
|
+
openmrsID.autoGeneration = true;
|
|
166
|
+
setupIdentifierInput(openmrsID);
|
|
167
|
+
expect(screen.getByText('Edit')).toBeInTheDocument();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('edit button clicked', () => {
|
|
171
|
+
it('displays an empty input field', async () => {
|
|
172
|
+
const user = userEvent.setup();
|
|
173
|
+
openmrsID.autoGeneration = true;
|
|
174
|
+
openmrsID.required = false;
|
|
175
|
+
openmrsID.selectedSource = {
|
|
176
|
+
autoGenerationOption: {
|
|
177
|
+
manualEntryEnabled: true,
|
|
178
|
+
},
|
|
179
|
+
} as IdentifierSource;
|
|
180
|
+
setupIdentifierInput(openmrsID);
|
|
181
|
+
const editButton = screen.getByTestId('edit-button');
|
|
182
|
+
await user.click(editButton);
|
|
183
|
+
expect(screen.getByLabelText(new RegExp(`${openmrsID.identifierName}`))).toHaveValue('');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('displays an input field with the identifier value if it exists', async () => {
|
|
187
|
+
const user = userEvent.setup();
|
|
188
|
+
openmrsID.autoGeneration = true;
|
|
189
|
+
openmrsID.required = false;
|
|
190
|
+
openmrsID.selectedSource = {
|
|
191
|
+
autoGenerationOption: {
|
|
192
|
+
manualEntryEnabled: true,
|
|
193
|
+
},
|
|
194
|
+
} as IdentifierSource;
|
|
195
|
+
setupIdentifierInput(openmrsID, { identifiers: { [fieldName]: { identifierValue: '10001V' } } });
|
|
196
|
+
const editButton = screen.getByTestId('edit-button');
|
|
197
|
+
await user.click(editButton);
|
|
198
|
+
expect(screen.getByLabelText(new RegExp(`${openmrsID.identifierName}`))).toHaveValue('10001V');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
103
202
|
});
|
|
104
203
|
});
|
|
@@ -25,7 +25,10 @@ export const dummyFormValues: FormValues = {
|
|
|
25
25
|
telephoneNumber: '0800001066',
|
|
26
26
|
isDead: false,
|
|
27
27
|
deathDate: '',
|
|
28
|
+
deathTime: '',
|
|
29
|
+
deathTimeFormat: 'AM',
|
|
28
30
|
deathCause: '',
|
|
31
|
+
nonCodedCauseOfDeath: '',
|
|
29
32
|
relationships: [],
|
|
30
33
|
address: {
|
|
31
34
|
address1: 'Bom Jesus Street',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useConfig } from '@openmrs/esm-framework';
|
|
2
2
|
import { createContext, type SetStateAction } from 'react';
|
|
3
3
|
import { type RegistrationConfig } from '../config-schema';
|
|
4
|
-
import { type
|
|
4
|
+
import { type CapturePhotoProps, type FormValues } from './patient-registration.types';
|
|
5
5
|
|
|
6
6
|
export interface PatientRegistrationContextProps {
|
|
7
7
|
currentPhoto: string;
|
|
@@ -9,11 +9,12 @@ export interface PatientRegistrationContextProps {
|
|
|
9
9
|
inEditMode: boolean;
|
|
10
10
|
initialFormValues: FormValues;
|
|
11
11
|
isOffline: boolean;
|
|
12
|
-
setCapturePhotoProps(value: SetStateAction<CapturePhotoProps>): void;
|
|
13
|
-
setFieldValue(field: string, value: any, shouldValidate?: boolean): void;
|
|
14
12
|
setInitialFormValues?: React.Dispatch<SetStateAction<FormValues>>;
|
|
15
13
|
validationSchema: any;
|
|
16
14
|
values: FormValues;
|
|
15
|
+
setCapturePhotoProps(value: SetStateAction<CapturePhotoProps>): void;
|
|
16
|
+
setFieldValue(field: string, value: any, shouldValidate?: boolean): void;
|
|
17
|
+
setFieldTouched(field: string, isTouched?: any, shouldValidate?: boolean): void;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export const PatientRegistrationContext = createContext<PatientRegistrationContextProps | undefined>(undefined);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type FetchResponse,
|
|
3
|
-
type OpenmrsResource,
|
|
4
3
|
getSynchronizationItems,
|
|
5
4
|
openmrsFetch,
|
|
5
|
+
type OpenmrsResource,
|
|
6
|
+
restBaseUrl,
|
|
6
7
|
useConfig,
|
|
7
8
|
usePatient,
|
|
8
|
-
restBaseUrl,
|
|
9
9
|
} from '@openmrs/esm-framework';
|
|
10
10
|
import last from 'lodash-es/last';
|
|
11
11
|
import camelCase from 'lodash-es/camelCase';
|
|
@@ -14,14 +14,17 @@ import useSWR from 'swr';
|
|
|
14
14
|
import { v4 } from 'uuid';
|
|
15
15
|
import { type RegistrationConfig } from '../config-schema';
|
|
16
16
|
import { patientRegistration } from '../constants';
|
|
17
|
-
import { useConceptAnswers, useGlobalProperties } from '../patient-verification/patient-verification-hook';
|
|
18
17
|
import {
|
|
18
|
+
useConceptAnswers,
|
|
19
|
+
useGlobalProperties,
|
|
20
|
+
} from '../client-registry/patient-verification/patient-verification-hook';
|
|
21
|
+
import {
|
|
22
|
+
type Encounter,
|
|
19
23
|
type FormValues,
|
|
24
|
+
type PatientIdentifierResponse,
|
|
20
25
|
type PatientRegistration,
|
|
21
26
|
type PatientUuidMapType,
|
|
22
27
|
type PersonAttributeResponse,
|
|
23
|
-
type PatientIdentifierResponse,
|
|
24
|
-
type Encounter,
|
|
25
28
|
type ConceptAnswers,
|
|
26
29
|
type ObsResponse,
|
|
27
30
|
} from './patient-registration.types';
|
|
@@ -36,8 +39,10 @@ import { useInitialPatientRelationships } from './section/patient-relationships/
|
|
|
36
39
|
import dayjs from 'dayjs';
|
|
37
40
|
|
|
38
41
|
export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
|
|
42
|
+
const { freeTextFieldConceptUuid } = useConfig<RegistrationConfig>();
|
|
39
43
|
const { martialStatus, education, occupation, educationLoad } = useConcepts();
|
|
40
44
|
const { isLoading: isLoadingPatientToEdit, patient: patientToEdit } = usePatient(patientUuid);
|
|
45
|
+
const { data: deathInfo, isLoading: isLoadingDeathInfo } = useInitialPersonDeathInfo(patientUuid);
|
|
41
46
|
const { data: attributes, isLoading: isLoadingAttributes } = useInitialPersonAttributes(patientUuid);
|
|
42
47
|
const { data: identifiers, isLoading: isLoadingIdentifiers } = useInitialPatientIdentifiers(patientUuid);
|
|
43
48
|
const { data: relationships, isLoading: isLoadingRelationships } = useInitialPatientRelationships(patientUuid);
|
|
@@ -59,8 +64,11 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
59
64
|
birthdateEstimated: false,
|
|
60
65
|
telephoneNumber: '',
|
|
61
66
|
isDead: false,
|
|
62
|
-
deathDate:
|
|
67
|
+
deathDate: undefined,
|
|
68
|
+
deathTime: undefined,
|
|
69
|
+
deathTimeFormat: 'AM',
|
|
63
70
|
deathCause: '',
|
|
71
|
+
nonCodedCauseOfDeath: '',
|
|
64
72
|
relationships: [],
|
|
65
73
|
identifiers: {},
|
|
66
74
|
address: {},
|
|
@@ -101,6 +109,24 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
101
109
|
})();
|
|
102
110
|
}, [isLoadingPatientToEdit, patientToEdit, patientUuid]);
|
|
103
111
|
|
|
112
|
+
// Set initial patient death info
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
if (!isLoadingDeathInfo && deathInfo?.dead) {
|
|
115
|
+
const deathDatetime = deathInfo.deathDate || null;
|
|
116
|
+
const deathDate = deathDatetime ? new Date(deathDatetime) : undefined;
|
|
117
|
+
const time = deathDate ? dayjs(deathDate).format('hh:mm') : undefined;
|
|
118
|
+
const timeFormat = deathDate ? (dayjs(deathDate).hour() >= 12 ? 'PM' : 'AM') : 'AM';
|
|
119
|
+
setInitialFormValues((initialFormValues) => ({
|
|
120
|
+
...initialFormValues,
|
|
121
|
+
isDead: deathInfo.dead || false,
|
|
122
|
+
deathDate: deathDate,
|
|
123
|
+
deathTime: time,
|
|
124
|
+
deathTimeFormat: timeFormat,
|
|
125
|
+
deathCause: deathInfo.causeOfDeathNonCoded ? freeTextFieldConceptUuid : deathInfo.causeOfDeath?.uuid,
|
|
126
|
+
nonCodedCauseOfDeath: deathInfo.causeOfDeathNonCoded,
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
}, [isLoadingDeathInfo, deathInfo, setInitialFormValues]);
|
|
104
130
|
// Setting authentication token
|
|
105
131
|
|
|
106
132
|
useEffect(() => {
|
|
@@ -301,6 +327,32 @@ function useInitialPersonAttributes(personUuid: string) {
|
|
|
301
327
|
return result;
|
|
302
328
|
}
|
|
303
329
|
|
|
330
|
+
interface DeathInfoResults {
|
|
331
|
+
uuid: string;
|
|
332
|
+
display: string;
|
|
333
|
+
causeOfDeath: OpenmrsResource | null;
|
|
334
|
+
dead: boolean;
|
|
335
|
+
deathDate: string;
|
|
336
|
+
causeOfDeathNonCoded: string | null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function useInitialPersonDeathInfo(personUuid: string) {
|
|
340
|
+
const { data, error, isLoading } = useSWR<FetchResponse<DeathInfoResults>, Error>(
|
|
341
|
+
!!personUuid
|
|
342
|
+
? `${restBaseUrl}/person/${personUuid}?v=custom:(uuid,display,causeOfDeath,dead,deathDate,causeOfDeathNonCoded)`
|
|
343
|
+
: null,
|
|
344
|
+
openmrsFetch,
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const result = useMemo(() => {
|
|
348
|
+
return {
|
|
349
|
+
data: data?.data,
|
|
350
|
+
isLoading,
|
|
351
|
+
};
|
|
352
|
+
}, [data, error]);
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
|
|
304
356
|
function getPatientAttributeUuidMapForPatient(attributes: Array<PersonAttributeResponse>) {
|
|
305
357
|
const attributeUuidMap = {};
|
|
306
358
|
attributes.forEach((attribute) => {
|
|
@@ -356,9 +408,15 @@ function useConcepts() {
|
|
|
356
408
|
},
|
|
357
409
|
];
|
|
358
410
|
|
|
359
|
-
const
|
|
360
|
-
.find((fieldDefinition) => fieldDefinition.id === 'maritalStatus')
|
|
361
|
-
|
|
411
|
+
const maritalStatusCustomConceptAnswers =
|
|
412
|
+
config.fieldDefinitions.find((fieldDefinition) => fieldDefinition.id === 'maritalStatus')?.customConceptAnswers ??
|
|
413
|
+
[];
|
|
414
|
+
|
|
415
|
+
const martialStatus: Array<ConceptAnswers> =
|
|
416
|
+
maritalStatusCustomConceptAnswers.map((concept) => ({
|
|
417
|
+
uuid: concept?.uuid,
|
|
418
|
+
display: concept?.label,
|
|
419
|
+
})) ?? [];
|
|
362
420
|
|
|
363
421
|
return { martialStatus, education, occupation, educationLoad };
|
|
364
422
|
}
|
|
@@ -3,11 +3,11 @@ import camelCase from 'lodash-es/camelCase';
|
|
|
3
3
|
import { parseDate } from '@openmrs/esm-framework';
|
|
4
4
|
import {
|
|
5
5
|
type AddressValidationSchemaType,
|
|
6
|
+
type Encounter,
|
|
6
7
|
type FormValues,
|
|
7
8
|
type PatientIdentifier,
|
|
8
|
-
type PatientUuidMapType,
|
|
9
9
|
type PatientIdentifierValue,
|
|
10
|
-
type
|
|
10
|
+
type PatientUuidMapType,
|
|
11
11
|
} from './patient-registration.types';
|
|
12
12
|
|
|
13
13
|
export function parseAddressTemplateXml(addressTemplate: string) {
|
|
@@ -47,6 +47,7 @@ export function parseAddressTemplateXml(addressTemplate: string) {
|
|
|
47
47
|
addressValidationSchema,
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
|
|
50
51
|
export function parseAddressTemplateXmlOld(addressTemplate: string) {
|
|
51
52
|
const templateXmlDoc = new DOMParser().parseFromString(addressTemplate, 'text/xml');
|
|
52
53
|
const nameMappings = templateXmlDoc.querySelector('nameMappings').querySelectorAll('property');
|
|
@@ -123,11 +124,6 @@ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
|
|
|
123
124
|
result.birthdate = patient.birthDate ? parseDate(patient.birthDate) : undefined;
|
|
124
125
|
result.telephoneNumber = patient.telecom ? patient.telecom[0].value : '';
|
|
125
126
|
|
|
126
|
-
if (patient.deceasedBoolean || patient.deceasedDateTime) {
|
|
127
|
-
result.isDead = true;
|
|
128
|
-
result.deathDate = patient.deceasedDateTime ? patient.deceasedDateTime.split('T')[0] : '';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
127
|
return {
|
|
132
128
|
...result,
|
|
133
129
|
...patient.identifier.map((identifier) => {
|