@kenyaemr/esm-patient-registration-app 4.5.2 → 4.5.4
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/196.js +1 -1
- package/dist/196.js.map +1 -1
- package/dist/330.js +1 -0
- package/dist/330.js.map +1 -0
- package/dist/59.js +1 -0
- package/dist/59.js.map +1 -0
- package/dist/68.js +1 -1
- package/dist/68.js.map +1 -1
- package/dist/879.js +1 -0
- package/dist/879.js.map +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +88 -16
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/config-schema.ts +11 -5
- package/src/index.ts +3 -0
- package/src/patient-registration/field/dob/dob.component.tsx +14 -12
- package/src/patient-registration/field/dob/dob.test.tsx +6 -1
- package/src/patient-registration/field/name/name-field.component.tsx +38 -21
- package/src/patient-registration/form-manager.test.ts +0 -1
- package/src/patient-registration/form-manager.ts +0 -8
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +0 -1
- package/src/patient-registration/patient-registration-hooks.ts +13 -1
- package/src/patient-registration/patient-registration-types.tsx +0 -1
- package/src/patient-registration/patient-registration-utils.ts +0 -3
- package/src/routes.json +10 -0
- package/src/widgets/cancel-patient-edit.test.tsx +24 -0
- package/src/widgets/delete-identifier-confirmation-modal.test.tsx +31 -0
package/src/index.ts
CHANGED
|
@@ -57,3 +57,6 @@ export const deleteIdentifierConfirmationModal = getAsyncLifecycle(
|
|
|
57
57
|
() => import('./widgets/delete-identifier-confirmation-modal'),
|
|
58
58
|
options,
|
|
59
59
|
);
|
|
60
|
+
|
|
61
|
+
export const emptyVerificationModal = getAsyncLifecycle(() => import('./patient-verification/verification-modal/empty-prompt.component'), options);
|
|
62
|
+
export const confirmationVerificationModal = getAsyncLifecycle(() => import('./patient-verification/verification-modal/confirm-prompt.component'), options);
|
|
@@ -25,9 +25,9 @@ export const DobField: React.FC = () => {
|
|
|
25
25
|
const { t } = useTranslation();
|
|
26
26
|
const {
|
|
27
27
|
fieldConfigurations: { dateOfBirth },
|
|
28
|
-
} = useConfig()
|
|
29
|
-
const
|
|
30
|
-
const
|
|
28
|
+
} = useConfig<RegistrationConfig>();
|
|
29
|
+
const allowEstimatedBirthDate = dateOfBirth?.allowEstimatedDateOfBirth;
|
|
30
|
+
const [{ value: dobUnknown }] = useField('birthdateEstimated');
|
|
31
31
|
const [birthdate, birthdateMeta] = useField('birthdate');
|
|
32
32
|
const [yearsEstimated, yearsEstimateMeta] = useField('yearsEstimated');
|
|
33
33
|
const [monthsEstimated, monthsEstimateMeta] = useField('monthsEstimated');
|
|
@@ -75,17 +75,19 @@ export const DobField: React.FC = () => {
|
|
|
75
75
|
return (
|
|
76
76
|
<div className={styles.halfWidthInDesktopView}>
|
|
77
77
|
<h4 className={styles.productiveHeading02Light}>{t('birthFieldLabelText', 'Birth')}</h4>
|
|
78
|
-
|
|
79
|
-
<div className={styles.
|
|
80
|
-
<
|
|
78
|
+
{(allowEstimatedBirthDate || dobUnknown) && (
|
|
79
|
+
<div className={styles.dobField}>
|
|
80
|
+
<div className={styles.dobContentSwitcherLabel}>
|
|
81
|
+
<span className={styles.label01}>{t('dobToggleLabelText', 'Date of Birth Known?')}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<ContentSwitcher onChange={onToggle} selectedIndex={dobUnknown ? 1 : 0}>
|
|
84
|
+
<Switch name="known" text={t('yes', 'Yes')} />
|
|
85
|
+
<Switch name="unknown" text={t('no', 'No')} />
|
|
86
|
+
</ContentSwitcher>
|
|
81
87
|
</div>
|
|
82
|
-
|
|
83
|
-
<Switch name="known" text={t('yes', 'Yes')} />
|
|
84
|
-
<Switch name="unknown" text={t('no', 'No')} />
|
|
85
|
-
</ContentSwitcher>
|
|
86
|
-
</div>
|
|
88
|
+
)}
|
|
87
89
|
<Layer>
|
|
88
|
-
{
|
|
90
|
+
{!dobUnknown ? (
|
|
89
91
|
<div className={styles.dobField}>
|
|
90
92
|
<DatePicker dateFormat={dateFormat} datePickerType="single" onChange={onDateChange} maxDate={format(today)}>
|
|
91
93
|
<DatePickerInput
|
|
@@ -14,7 +14,12 @@ jest.mock('@openmrs/esm-framework', () => {
|
|
|
14
14
|
return {
|
|
15
15
|
...originalModule,
|
|
16
16
|
useConfig: jest.fn().mockImplementation(() => ({
|
|
17
|
-
fieldConfigurations: {
|
|
17
|
+
fieldConfigurations: {
|
|
18
|
+
dateOfBirth: {
|
|
19
|
+
allowEstimatedDateOfBirth: true,
|
|
20
|
+
useEstimatedDateOfBirth: { enabled: true, dayOfMonth: 0, month: 0 },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
18
23
|
})),
|
|
19
24
|
};
|
|
20
25
|
});
|
|
@@ -8,6 +8,7 @@ import { PatientRegistrationContext } from '../../patient-registration-context';
|
|
|
8
8
|
import styles from '../field.scss';
|
|
9
9
|
import { RegistrationConfig } from '../../../config-schema';
|
|
10
10
|
|
|
11
|
+
export const unidentifiedPatientAttributeTypeUuid = '8b56eac7-5c76-4b9c-8c6f-1deab8d3fc47';
|
|
11
12
|
const containsNoNumbers = /^([^0-9]*)$/;
|
|
12
13
|
|
|
13
14
|
function checkNumber(value: string) {
|
|
@@ -19,17 +20,26 @@ function checkNumber(value: string) {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export const NameField = () => {
|
|
23
|
+
const { t } = useTranslation();
|
|
24
|
+
const { setCapturePhotoProps, currentPhoto, setFieldValue } = useContext(PatientRegistrationContext);
|
|
22
25
|
const {
|
|
23
26
|
fieldConfigurations: {
|
|
24
|
-
name: {
|
|
27
|
+
name: {
|
|
28
|
+
displayCapturePhoto,
|
|
29
|
+
allowUnidentifiedPatients,
|
|
30
|
+
defaultUnknownGivenName,
|
|
31
|
+
defaultUnknownFamilyName,
|
|
32
|
+
displayMiddleName,
|
|
33
|
+
displayReverseFieldOrder,
|
|
34
|
+
},
|
|
25
35
|
},
|
|
26
|
-
} = useConfig()
|
|
27
|
-
|
|
28
|
-
const {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
36
|
+
} = useConfig<RegistrationConfig>();
|
|
37
|
+
|
|
38
|
+
const [{ value: isPatientUnknownValue }, , { setValue: setUnknownPatient }] = useField<string>(
|
|
39
|
+
`attributes.${unidentifiedPatientAttributeTypeUuid}`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const isPatientUnknown = isPatientUnknownValue === 'true';
|
|
33
43
|
|
|
34
44
|
const onCapturePhoto = useCallback(
|
|
35
45
|
(dataUri: string, photoDateTime: string) => {
|
|
@@ -47,11 +57,11 @@ export const NameField = () => {
|
|
|
47
57
|
if (e.name === 'known') {
|
|
48
58
|
setFieldValue('givenName', '');
|
|
49
59
|
setFieldValue('familyName', '');
|
|
50
|
-
|
|
60
|
+
setUnknownPatient('false');
|
|
51
61
|
} else {
|
|
52
|
-
setFieldValue('givenName',
|
|
53
|
-
setFieldValue('familyName',
|
|
54
|
-
|
|
62
|
+
setFieldValue('givenName', defaultUnknownGivenName);
|
|
63
|
+
setFieldValue('familyName', defaultUnknownFamilyName);
|
|
64
|
+
setUnknownPatient('true');
|
|
55
65
|
}
|
|
56
66
|
};
|
|
57
67
|
|
|
@@ -65,7 +75,7 @@ export const NameField = () => {
|
|
|
65
75
|
/>
|
|
66
76
|
);
|
|
67
77
|
|
|
68
|
-
const middleNameField =
|
|
78
|
+
const middleNameField = displayMiddleName && (
|
|
69
79
|
<Input
|
|
70
80
|
id="middleName"
|
|
71
81
|
name="middleName"
|
|
@@ -98,14 +108,21 @@ export const NameField = () => {
|
|
|
98
108
|
)}
|
|
99
109
|
|
|
100
110
|
<div className={styles.nameField}>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
{(allowUnidentifiedPatients || isPatientUnknown) && (
|
|
112
|
+
<>
|
|
113
|
+
<div className={styles.dobContentSwitcherLabel}>
|
|
114
|
+
<span className={styles.label01}>{t('patientNameKnown', "Patient's Name is Known?")}</span>
|
|
115
|
+
</div>
|
|
116
|
+
<ContentSwitcher
|
|
117
|
+
className={styles.contentSwitcher}
|
|
118
|
+
selectedIndex={isPatientUnknown ? 1 : 0}
|
|
119
|
+
onChange={toggleNameKnown}>
|
|
120
|
+
<Switch name="known" text={t('yes', 'Yes')} />
|
|
121
|
+
<Switch name="unknown" text={t('no', 'No')} />
|
|
122
|
+
</ContentSwitcher>
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
{!isPatientUnknown &&
|
|
109
126
|
(!displayReverseFieldOrder ? (
|
|
110
127
|
<>
|
|
111
128
|
{firstNameField}
|
|
@@ -364,14 +364,6 @@ export class FormManager {
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
if (values.unidentifiedPatient) {
|
|
368
|
-
attributes.push({
|
|
369
|
-
// The UUID of the 'Unknown Patient' attribute-type will always be static across all implementations of OpenMRS
|
|
370
|
-
attributeType: '8b56eac7-5c76-4b9c-8c6f-1deab8d3fc47',
|
|
371
|
-
value: 'true',
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
367
|
return attributes;
|
|
376
368
|
}
|
|
377
369
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
latestFirstEncounter,
|
|
26
26
|
} from './patient-registration-utils';
|
|
27
27
|
import { useInitialPatientRelationships } from './section/patient-relationships/relationships.resource';
|
|
28
|
+
import dayjs from 'dayjs';
|
|
28
29
|
|
|
29
30
|
export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch<FormValues>] {
|
|
30
31
|
const { martialStatus, education, occupation, educationLoad, loadingStatus } = useConcepts();
|
|
@@ -39,7 +40,6 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
39
40
|
givenName: '',
|
|
40
41
|
middleName: '',
|
|
41
42
|
familyName: '',
|
|
42
|
-
unidentifiedPatient: false,
|
|
43
43
|
additionalGivenName: '',
|
|
44
44
|
additionalMiddleName: '',
|
|
45
45
|
additionalFamilyName: '',
|
|
@@ -61,11 +61,22 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
(async () => {
|
|
63
63
|
if (patientToEdit) {
|
|
64
|
+
const birthdateEstimated = !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate);
|
|
65
|
+
const [years = 0, months = 0] = patientToEdit.birthDate.split('-').map((val) => parseInt(val));
|
|
66
|
+
// Please refer: https://github.com/openmrs/openmrs-esm-patient-management/pull/697#issuecomment-1562706118
|
|
67
|
+
const estimatedMonthsAvailable = patientToEdit.birthDate.split('-').length > 1;
|
|
68
|
+
const yearsEstimated = birthdateEstimated ? Math.floor(dayjs().diff(patientToEdit.birthDate, 'month') / 12) : 0;
|
|
69
|
+
const monthsEstimated =
|
|
70
|
+
birthdateEstimated && estimatedMonthsAvailable ? dayjs().diff(patientToEdit.birthDate, 'month') % 12 : 0;
|
|
71
|
+
|
|
64
72
|
setInitialFormValues({
|
|
65
73
|
...initialFormValues,
|
|
66
74
|
...getFormValuesFromFhirPatient(patientToEdit),
|
|
67
75
|
address: getAddressFieldValuesFromFhirPatient(patientToEdit),
|
|
68
76
|
...getPhonePersonAttributeValueFromFhirPatient(patientToEdit),
|
|
77
|
+
birthdateEstimated: !/^\d{4}-\d{2}-\d{2}$/.test(patientToEdit.birthDate),
|
|
78
|
+
yearsEstimated,
|
|
79
|
+
monthsEstimated,
|
|
69
80
|
});
|
|
70
81
|
} else if (!isLoadingPatientToEdit && patientUuid) {
|
|
71
82
|
const registration = await getPatientRegistration(patientUuid);
|
|
@@ -120,6 +131,7 @@ export function useInitialFormValues(patientUuid: string): [FormValues, Dispatch
|
|
|
120
131
|
? attribute.value?.uuid
|
|
121
132
|
: attribute.value;
|
|
122
133
|
});
|
|
134
|
+
|
|
123
135
|
setInitialFormValues((initialFormValues) => ({
|
|
124
136
|
...initialFormValues,
|
|
125
137
|
attributes: personAttributes,
|
|
@@ -115,9 +115,6 @@ export function getFormValuesFromFhirPatient(patient: fhir.Patient) {
|
|
|
115
115
|
result.givenName = patientName?.given[0];
|
|
116
116
|
result.middleName = patientName?.given[1];
|
|
117
117
|
result.familyName = patientName?.family;
|
|
118
|
-
result.unidentifiedPatient =
|
|
119
|
-
patientName.given[0] === 'UNKNOWN' && patientName.family === 'unknown' ? true : undefined;
|
|
120
|
-
|
|
121
118
|
result.addNameInLocalLanguage = !!additionalPatientName ? true : undefined;
|
|
122
119
|
result.additionalGivenName = additionalPatientName?.given[0];
|
|
123
120
|
result.additionalMiddleName = additionalPatientName?.given[1];
|
package/src/routes.json
CHANGED
|
@@ -42,5 +42,15 @@
|
|
|
42
42
|
"name": "delete-identifier-confirmation-modal",
|
|
43
43
|
"online": true,
|
|
44
44
|
"offline": true
|
|
45
|
+
}, {
|
|
46
|
+
"component": "emptyVerificationModal",
|
|
47
|
+
"name":"empty-client-registry-modal",
|
|
48
|
+
"online": true,
|
|
49
|
+
"offline": false
|
|
50
|
+
},{
|
|
51
|
+
"component": "confirmationVerificationModal",
|
|
52
|
+
"name": "confirm-client-registry-modal",
|
|
53
|
+
"online": true,
|
|
54
|
+
"offline": false
|
|
45
55
|
}]
|
|
46
56
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen, render, fireEvent } from '@testing-library/react';
|
|
3
|
+
import CancelPatientEdit from './cancel-patient-edit.component';
|
|
4
|
+
|
|
5
|
+
describe('CancelPatientEdit component', () => {
|
|
6
|
+
const mockClose = jest.fn();
|
|
7
|
+
const mockOnConfirm = jest.fn();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('renders the modal and triggers close and onConfirm functions', () => {
|
|
14
|
+
render(<CancelPatientEdit close={mockClose} onConfirm={mockOnConfirm} />);
|
|
15
|
+
|
|
16
|
+
const cancelButton = screen.getByRole('button', { name: /Cancel/i });
|
|
17
|
+
fireEvent.click(cancelButton);
|
|
18
|
+
expect(mockClose).toHaveBeenCalledTimes(1);
|
|
19
|
+
|
|
20
|
+
const discardButton = screen.getByRole('button', { name: /discard/i });
|
|
21
|
+
fireEvent.click(discardButton);
|
|
22
|
+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, screen } from '@testing-library/react';
|
|
3
|
+
import DeleteIdentifierConfirmationModal from './delete-identifier-confirmation-modal';
|
|
4
|
+
|
|
5
|
+
describe('DeleteIdentifierConfirmationModal component', () => {
|
|
6
|
+
const mockDeleteIdentifier = jest.fn();
|
|
7
|
+
const mockIdentifierName = 'Identifier Name';
|
|
8
|
+
const mockIdentifierValue = 'Identifier Value';
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders the modal and triggers deleteIdentifier function', () => {
|
|
15
|
+
render(
|
|
16
|
+
<DeleteIdentifierConfirmationModal
|
|
17
|
+
deleteIdentifier={mockDeleteIdentifier}
|
|
18
|
+
identifierName={mockIdentifierName}
|
|
19
|
+
identifierValue={mockIdentifierValue}
|
|
20
|
+
/>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const cancelButton = screen.getByRole('button', { name: /cancel/i });
|
|
24
|
+
fireEvent.click(cancelButton);
|
|
25
|
+
expect(mockDeleteIdentifier).toHaveBeenCalledWith(false);
|
|
26
|
+
|
|
27
|
+
const removeButton = screen.getByRole('button', { name: /remove identifier/i });
|
|
28
|
+
fireEvent.click(removeButton);
|
|
29
|
+
expect(mockDeleteIdentifier).toHaveBeenCalledWith(true);
|
|
30
|
+
});
|
|
31
|
+
});
|