@kenyaemr/esm-patient-registration-app 8.0.1-pre.99 → 8.0.3-pre.131
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 -3
- 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 +1 -1
- 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 +1 -1
- package/src/{patient-verification → client-registry/patient-verification}/patient-verification.scss +1 -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/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
package/src/config-schema.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface FieldDefinition {
|
|
|
18
18
|
required: boolean;
|
|
19
19
|
matches?: string;
|
|
20
20
|
};
|
|
21
|
+
locationTag?: string;
|
|
21
22
|
answerConceptSetUuid?: string;
|
|
22
23
|
customConceptAnswers?: Array<CustomConceptAnswer>;
|
|
23
24
|
showWhenExpression?: {
|
|
@@ -42,6 +43,10 @@ export interface RegistrationConfig {
|
|
|
42
43
|
sectionDefinitions: Array<SectionDefinition>;
|
|
43
44
|
fieldDefinitions: Array<FieldDefinition>;
|
|
44
45
|
fieldConfigurations: {
|
|
46
|
+
causeOfDeath: {
|
|
47
|
+
conceptUuid: string;
|
|
48
|
+
required?: boolean;
|
|
49
|
+
};
|
|
45
50
|
name: {
|
|
46
51
|
displayMiddleName: boolean;
|
|
47
52
|
allowUnidentifiedPatients: boolean;
|
|
@@ -83,6 +88,12 @@ export interface RegistrationConfig {
|
|
|
83
88
|
encounterProviderRoleUuid: string;
|
|
84
89
|
registrationFormUuid: string | null;
|
|
85
90
|
};
|
|
91
|
+
freeTextFieldConceptUuid: string;
|
|
92
|
+
hieClientRegistry: {
|
|
93
|
+
identifierTypes: Array<{ identifierType: string; identifierValue: string }>;
|
|
94
|
+
baseUrl: string;
|
|
95
|
+
encodedCredentials: string;
|
|
96
|
+
};
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
export const builtInSections: Array<SectionDefinition> = [
|
|
@@ -92,12 +103,21 @@ export const builtInSections: Array<SectionDefinition> = [
|
|
|
92
103
|
fields: ['name', 'gender', 'dob', 'id'],
|
|
93
104
|
},
|
|
94
105
|
{ id: 'contact', name: 'Contact Details', fields: ['address', 'phone'] },
|
|
95
|
-
{ id: 'death', name: 'Death Info', fields: [] },
|
|
106
|
+
{ id: 'death', name: 'Death Info', fields: ['dateAndTimeOfDeath', 'causeOfDeath'] },
|
|
96
107
|
{ id: 'relationships', name: 'Relationships', fields: [] },
|
|
97
108
|
];
|
|
98
109
|
|
|
99
110
|
// These fields are handled specially in field.component.tsx
|
|
100
|
-
export const builtInFields = [
|
|
111
|
+
export const builtInFields = [
|
|
112
|
+
'name',
|
|
113
|
+
'gender',
|
|
114
|
+
'dob',
|
|
115
|
+
'id',
|
|
116
|
+
'address',
|
|
117
|
+
'phone',
|
|
118
|
+
'causeOfDeath',
|
|
119
|
+
'dateAndTimeOfDeath',
|
|
120
|
+
] as const;
|
|
101
121
|
|
|
102
122
|
export const esmPatientRegistrationSchema = {
|
|
103
123
|
sections: {
|
|
@@ -174,6 +194,12 @@ export const esmPatientRegistrationSchema = {
|
|
|
174
194
|
_description: 'Optional RegEx for testing the validity of the input.',
|
|
175
195
|
},
|
|
176
196
|
},
|
|
197
|
+
locationTag: {
|
|
198
|
+
_type: Type.String,
|
|
199
|
+
_default: null,
|
|
200
|
+
_description:
|
|
201
|
+
'Only for fields with "person attribute" type `org.openmrs.Location`. This filters the list of location options in the dropdown based on their location tag. By default, all locations are shown.',
|
|
202
|
+
},
|
|
177
203
|
answerConceptSetUuid: {
|
|
178
204
|
_type: Type.ConceptUuid,
|
|
179
205
|
_default: null,
|
|
@@ -204,6 +230,14 @@ export const esmPatientRegistrationSchema = {
|
|
|
204
230
|
'Definitions for custom fields that can be used in sectionDefinitions. Can also be used to override built-in fields.',
|
|
205
231
|
},
|
|
206
232
|
fieldConfigurations: {
|
|
233
|
+
causeOfDeath: {
|
|
234
|
+
conceptUuid: {
|
|
235
|
+
_type: Type.ConceptUuid,
|
|
236
|
+
_description: 'The concept UUID to get cause of death answers',
|
|
237
|
+
_default: '9272a14b-7260-4353-9e5b-5787b5dead9d',
|
|
238
|
+
},
|
|
239
|
+
required: { _type: Type.Boolean, _default: false },
|
|
240
|
+
},
|
|
207
241
|
name: {
|
|
208
242
|
displayMiddleName: { _type: Type.Boolean, _default: true },
|
|
209
243
|
allowUnidentifiedPatients: {
|
|
@@ -364,6 +398,42 @@ export const esmPatientRegistrationSchema = {
|
|
|
364
398
|
'The form UUID to associate with the registration encounter. By default no form will be associated.',
|
|
365
399
|
},
|
|
366
400
|
},
|
|
401
|
+
freeTextFieldConceptUuid: {
|
|
402
|
+
_default: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
403
|
+
_type: Type.ConceptUuid,
|
|
404
|
+
},
|
|
405
|
+
hieClientRegistry: {
|
|
406
|
+
identifierTypes: {
|
|
407
|
+
_type: Type.Array,
|
|
408
|
+
_elements: {
|
|
409
|
+
identifierType: {
|
|
410
|
+
_type: Type.String,
|
|
411
|
+
_description: 'The label of the identifier type',
|
|
412
|
+
},
|
|
413
|
+
identifierValue: {
|
|
414
|
+
_type: Type.String,
|
|
415
|
+
_description: 'The value of the identifier type',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
_default: [
|
|
419
|
+
{ identifierType: 'National ID', identifierValue: 'national-id' },
|
|
420
|
+
{ identifierType: 'Passport Number', identifierValue: 'passport-number' },
|
|
421
|
+
{ identifierType: 'Birth Certificate Number', identifierValue: 'birth-certificate-number' },
|
|
422
|
+
{ identifierType: 'Alien ID Number', identifierValue: 'alien-id-number' },
|
|
423
|
+
{ identifierType: 'Refugee ID Number', identifierValue: 'refugee-number' },
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
baseUrl: {
|
|
427
|
+
_type: Type.String,
|
|
428
|
+
_default: 'https://hie.paperless.co.ke/v4/custom/',
|
|
429
|
+
_description: 'The base URL for the HIE API',
|
|
430
|
+
},
|
|
431
|
+
encodedCredentials: {
|
|
432
|
+
_type: Type.String,
|
|
433
|
+
_default: 'a2VueWFfZW1yOkFsZG5iJmtmayZKc2whMjM0',
|
|
434
|
+
_description: 'The base64 encoded credentials for the HIE API',
|
|
435
|
+
},
|
|
436
|
+
},
|
|
367
437
|
_validators: [
|
|
368
438
|
validator(
|
|
369
439
|
(config: RegistrationConfig) =>
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import rootComponent from './root.component';
|
|
|
6
6
|
import addPatientLinkComponent from './add-patient-link';
|
|
7
7
|
import editPatientDetailsButtonComponent from './widgets/edit-patient-details-button.component';
|
|
8
8
|
import { PatientPhotoExtension } from './patient-photo.extension';
|
|
9
|
+
import HIEConfirmationModal from './client-registry/hie-client-registry/modal/confirm-hie.modal';
|
|
9
10
|
|
|
10
11
|
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
11
12
|
|
|
@@ -50,10 +51,7 @@ export const editPatient = getSyncLifecycle(rootComponent, {
|
|
|
50
51
|
|
|
51
52
|
export const addPatientLink = getSyncLifecycle(addPatientLinkComponent, options);
|
|
52
53
|
|
|
53
|
-
export const cancelPatientEditModal = getAsyncLifecycle(
|
|
54
|
-
() => import('./widgets/cancel-patient-edit.component'),
|
|
55
|
-
options,
|
|
56
|
-
);
|
|
54
|
+
export const cancelPatientEditModal = getAsyncLifecycle(() => import('./widgets/cancel-patient-edit.modal'), options);
|
|
57
55
|
|
|
58
56
|
export const patientPhotoExtension = getSyncLifecycle(PatientPhotoExtension, options);
|
|
59
57
|
|
|
@@ -68,11 +66,13 @@ export const deleteIdentifierConfirmationModal = getAsyncLifecycle(
|
|
|
68
66
|
);
|
|
69
67
|
|
|
70
68
|
export const confirmClientRegistryModal = getAsyncLifecycle(
|
|
71
|
-
() => import('./patient-verification/verification-modal/confirm-prompt.component'),
|
|
69
|
+
() => import('./client-registry/patient-verification/verification-modal/confirm-prompt.component'),
|
|
72
70
|
options,
|
|
73
71
|
);
|
|
74
72
|
|
|
75
73
|
export const emptyClientRegistryModal = getAsyncLifecycle(
|
|
76
|
-
() => import('./patient-verification/verification-modal/empty-prompt.component'),
|
|
74
|
+
() => import('./client-registry/patient-verification/verification-modal/empty-prompt.component'),
|
|
77
75
|
options,
|
|
78
76
|
);
|
|
77
|
+
|
|
78
|
+
export const hieConfirmationModal = getSyncLifecycle(HIEConfirmationModal, options);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Field, useField } from 'formik';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { InlineNotification, Layer, Select, SelectItem, SelectSkeleton, TextInput } from '@carbon/react';
|
|
6
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
7
|
+
import { type RegistrationConfig } from '../../../config-schema';
|
|
8
|
+
import { useConceptAnswers } from '../field.resource';
|
|
9
|
+
import styles from '../field.scss';
|
|
10
|
+
|
|
11
|
+
export const CauseOfDeathField: React.FC = () => {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const { fieldConfigurations, freeTextFieldConceptUuid } = useConfig<RegistrationConfig>();
|
|
14
|
+
const [deathCause, deathCauseMeta] = useField('deathCause');
|
|
15
|
+
|
|
16
|
+
const conceptUuid = fieldConfigurations?.causeOfDeath?.conceptUuid;
|
|
17
|
+
const required = fieldConfigurations?.causeOfDeath?.required;
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
data: conceptAnswers,
|
|
21
|
+
isLoading: isLoadingConceptAnswers,
|
|
22
|
+
error: errorLoadingConceptAnswers,
|
|
23
|
+
} = useConceptAnswers(conceptUuid);
|
|
24
|
+
|
|
25
|
+
const answers = useMemo(() => {
|
|
26
|
+
if (!isLoadingConceptAnswers && conceptAnswers) {
|
|
27
|
+
return conceptAnswers.map((answer) => ({ ...answer, label: answer.display }));
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
}, [conceptAnswers, isLoadingConceptAnswers]);
|
|
31
|
+
|
|
32
|
+
if (isLoadingConceptAnswers) {
|
|
33
|
+
return (
|
|
34
|
+
<div className={classNames(styles.customField, styles.halfWidthInDesktopView)}>
|
|
35
|
+
<h4 className={styles.productiveHeading02Light}>{t('causeOfDeathInputLabel', 'Cause of death')}</h4>
|
|
36
|
+
<SelectSkeleton />
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={classNames(styles.customField, styles.halfWidthInDesktopView)}>
|
|
43
|
+
<h4 className={styles.productiveHeading02Light}>{t('causeOfDeathInputLabel', 'Cause of death')}</h4>
|
|
44
|
+
{errorLoadingConceptAnswers || !conceptUuid ? (
|
|
45
|
+
<InlineNotification
|
|
46
|
+
hideCloseButton
|
|
47
|
+
kind="error"
|
|
48
|
+
title={t('errorFetchingCodedCausesOfDeath', 'Error fetching coded causes of death')}
|
|
49
|
+
subtitle={t('refreshOrContactAdmin', 'Try refreshing the page or contact your system administrator')}
|
|
50
|
+
/>
|
|
51
|
+
) : (
|
|
52
|
+
<>
|
|
53
|
+
<Field name="deathCause">
|
|
54
|
+
{({ field, form: { touched, errors }, meta }) => {
|
|
55
|
+
return (
|
|
56
|
+
<Layer>
|
|
57
|
+
<Select
|
|
58
|
+
{...field}
|
|
59
|
+
id="deathCause"
|
|
60
|
+
invalid={errors.deathCause && touched.deathCause}
|
|
61
|
+
invalidText={errors.deathCause?.message}
|
|
62
|
+
labelText={t('causeOfDeathInputLabel', 'Cause of Death')}
|
|
63
|
+
name="deathCause"
|
|
64
|
+
required={required}>
|
|
65
|
+
<SelectItem id="empty-default-option" value={null} text={t('selectAnOption', 'Select an option')} />
|
|
66
|
+
{answers.map((answer) => (
|
|
67
|
+
<SelectItem id={answer.uuid} key={answer.uuid} text={answer.label} value={answer.uuid} />
|
|
68
|
+
))}
|
|
69
|
+
</Select>
|
|
70
|
+
</Layer>
|
|
71
|
+
);
|
|
72
|
+
}}
|
|
73
|
+
</Field>
|
|
74
|
+
{deathCause.value === freeTextFieldConceptUuid && (
|
|
75
|
+
<div className={styles.nonCodedCauseOfDeath}>
|
|
76
|
+
<Field name="nonCodedCauseOfDeath">
|
|
77
|
+
{({ field, form: { touched, errors }, meta }) => {
|
|
78
|
+
return (
|
|
79
|
+
<Layer>
|
|
80
|
+
<TextInput
|
|
81
|
+
{...field}
|
|
82
|
+
id="nonCodedCauseOfDeath"
|
|
83
|
+
invalid={errors?.nonCodedCauseOfDeath && touched.nonCodedCauseOfDeath}
|
|
84
|
+
invalidText={errors?.nonCodedCauseOfDeath?.message}
|
|
85
|
+
labelText={t('nonCodedCauseOfDeath', 'Non-coded cause of death')}
|
|
86
|
+
placeholder={t('enterNonCodedCauseOfDeath', 'Enter non-coded cause of death')}
|
|
87
|
+
/>
|
|
88
|
+
</Layer>
|
|
89
|
+
);
|
|
90
|
+
}}
|
|
91
|
+
</Field>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
};
|
package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useCallback, useContext } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import { Layer, SelectItem, TimePicker, TimePickerSelect } from '@carbon/react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { useField } from 'formik';
|
|
7
|
+
import { OpenmrsDatePicker } from '@openmrs/esm-framework';
|
|
8
|
+
import { PatientRegistrationContext } from '../../patient-registration-context';
|
|
9
|
+
import type { FormValues } from '../../patient-registration.types';
|
|
10
|
+
import styles from '../field.scss';
|
|
11
|
+
|
|
12
|
+
export const DateAndTimeOfDeathField: React.FC = () => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className={classNames(styles.dodField, styles.halfWidthInDesktopView)}>
|
|
17
|
+
<h4 className={styles.productiveHeading02Light}>{t('deathDateInputLabel', 'Date of Death')}</h4>
|
|
18
|
+
<span>
|
|
19
|
+
<DeathDateField />
|
|
20
|
+
<DeathTimeField />
|
|
21
|
+
</span>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function DeathDateField() {
|
|
27
|
+
const { values, setFieldValue } = useContext(PatientRegistrationContext);
|
|
28
|
+
const [deathDate, deathDateMeta] = useField<keyof FormValues>('deathDate');
|
|
29
|
+
const { t } = useTranslation();
|
|
30
|
+
const today = dayjs().hour(23).minute(59).second(59).toDate();
|
|
31
|
+
const onDateChange = useCallback(
|
|
32
|
+
(selectedDate: Date) => {
|
|
33
|
+
setFieldValue(
|
|
34
|
+
'deathDate',
|
|
35
|
+
selectedDate ? dayjs(selectedDate).hour(0).minute(0).second(0).millisecond(0).toDate() : undefined,
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
[deathDate],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Layer>
|
|
43
|
+
<OpenmrsDatePicker
|
|
44
|
+
{...deathDate}
|
|
45
|
+
id="deathDate"
|
|
46
|
+
invalidText={t(deathDateMeta.error)}
|
|
47
|
+
invalid={!!(deathDateMeta.touched && deathDateMeta.error)}
|
|
48
|
+
isRequired={values.isDead}
|
|
49
|
+
labelText={t('deathDateInputLabel', 'Date of death')}
|
|
50
|
+
maxDate={today}
|
|
51
|
+
onChange={onDateChange}
|
|
52
|
+
/>
|
|
53
|
+
</Layer>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function DeathTimeField() {
|
|
58
|
+
const { t } = useTranslation();
|
|
59
|
+
const [deathTimeField, deathTimeMeta] = useField<keyof FormValues>('deathTime');
|
|
60
|
+
const [deathTimeFormatField, deathTimeFormatMeta] = useField<keyof FormValues>('deathTimeFormat');
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Layer>
|
|
64
|
+
<TimePicker
|
|
65
|
+
{...deathTimeField}
|
|
66
|
+
id="time-picker"
|
|
67
|
+
labelText={t('timeOfDeathInputLabel', 'Time of death (hh:mm)')}
|
|
68
|
+
className={styles.timeOfDeathField}
|
|
69
|
+
pattern="^(1[0-2]|0?[1-9]):([0-5]?[0-9])$"
|
|
70
|
+
invalid={!!(deathTimeMeta.touched && deathTimeMeta.error)}
|
|
71
|
+
invalidText={t(deathTimeMeta.error)}>
|
|
72
|
+
<TimePickerSelect
|
|
73
|
+
{...deathTimeFormatField}
|
|
74
|
+
id="time-format-picker"
|
|
75
|
+
aria-label={t('timeFormat', 'Time Format')}
|
|
76
|
+
invalid={!!deathTimeFormatMeta.touched && deathTimeFormatMeta.error}
|
|
77
|
+
invalidText={t(deathTimeFormatMeta.error)}>
|
|
78
|
+
<SelectItem value="AM" text="AM" />
|
|
79
|
+
<SelectItem value="PM" text="PM" />
|
|
80
|
+
</TimePickerSelect>
|
|
81
|
+
</TimePicker>
|
|
82
|
+
</Layer>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -30,7 +30,7 @@ export const DobField: React.FC = () => {
|
|
|
30
30
|
const [birthdate, birthdateMeta] = useField('birthdate');
|
|
31
31
|
const [yearsEstimated, yearsEstimateMeta] = useField('yearsEstimated');
|
|
32
32
|
const [monthsEstimated, monthsEstimateMeta] = useField('monthsEstimated');
|
|
33
|
-
const { setFieldValue } = useContext(PatientRegistrationContext);
|
|
33
|
+
const { setFieldValue, setFieldTouched } = useContext(PatientRegistrationContext);
|
|
34
34
|
const today = new Date();
|
|
35
35
|
|
|
36
36
|
const onToggle = useCallback(
|
|
@@ -39,6 +39,7 @@ export const DobField: React.FC = () => {
|
|
|
39
39
|
setFieldValue('birthdate', '');
|
|
40
40
|
setFieldValue('yearsEstimated', 0);
|
|
41
41
|
setFieldValue('monthsEstimated', '');
|
|
42
|
+
setFieldTouched('birthdateEstimated', true, false);
|
|
42
43
|
},
|
|
43
44
|
[setFieldValue],
|
|
44
45
|
);
|
|
@@ -46,8 +47,9 @@ export const DobField: React.FC = () => {
|
|
|
46
47
|
const onDateChange = useCallback(
|
|
47
48
|
(birthdate: Date) => {
|
|
48
49
|
setFieldValue('birthdate', birthdate);
|
|
50
|
+
setFieldTouched('birthdate', true, false);
|
|
49
51
|
},
|
|
50
|
-
[setFieldValue],
|
|
52
|
+
[setFieldValue, setFieldTouched],
|
|
51
53
|
);
|
|
52
54
|
|
|
53
55
|
const onEstimatedYearsChange = useCallback(
|
|
@@ -80,7 +82,10 @@ export const DobField: React.FC = () => {
|
|
|
80
82
|
setFieldValue('yearsEstimated', years);
|
|
81
83
|
setFieldValue('monthsEstimated', months > 0 ? months : '');
|
|
82
84
|
setFieldValue('birthdate', calcBirthdate(years, months, dateOfBirth));
|
|
83
|
-
|
|
85
|
+
setFieldTouched('yearsEstimated', true, false);
|
|
86
|
+
setFieldTouched('monthsEstimated', true, false);
|
|
87
|
+
setFieldTouched('birthdate', true, false);
|
|
88
|
+
}, [setFieldValue, setFieldTouched, monthsEstimateMeta, yearsEstimateMeta, dateOfBirth]);
|
|
84
89
|
|
|
85
90
|
return (
|
|
86
91
|
<div className={styles.halfWidthInDesktopView}>
|
|
@@ -88,7 +93,7 @@ export const DobField: React.FC = () => {
|
|
|
88
93
|
{(allowEstimatedBirthDate || dobUnknown) && (
|
|
89
94
|
<div className={styles.dobField}>
|
|
90
95
|
<div className={styles.dobContentSwitcherLabel}>
|
|
91
|
-
<span className={styles.label01}>{t('dobToggleLabelText', 'Date of
|
|
96
|
+
<span className={styles.label01}>{t('dobToggleLabelText', 'Date of birth known?')}</span>
|
|
92
97
|
</div>
|
|
93
98
|
<ContentSwitcher onChange={onToggle} selectedIndex={dobUnknown ? 1 : 0}>
|
|
94
99
|
<Switch name="known" text={t('yes', 'Yes')} />
|
|
@@ -103,8 +108,9 @@ export const DobField: React.FC = () => {
|
|
|
103
108
|
id="birthdate"
|
|
104
109
|
{...birthdate}
|
|
105
110
|
onChange={onDateChange}
|
|
111
|
+
onBlur={() => setFieldTouched('birthdate', true, false)}
|
|
106
112
|
maxDate={today}
|
|
107
|
-
labelText={t('dateOfBirthLabelText', 'Date of
|
|
113
|
+
labelText={t('dateOfBirthLabelText', 'Date of birth')}
|
|
108
114
|
isInvalid={!!(birthdateMeta.touched && birthdateMeta.error)}
|
|
109
115
|
invalidText={t(birthdateMeta.error)}
|
|
110
116
|
value={birthdate.value}
|
|
@@ -125,7 +131,11 @@ export const DobField: React.FC = () => {
|
|
|
125
131
|
min={0}
|
|
126
132
|
required
|
|
127
133
|
{...yearsEstimated}
|
|
128
|
-
onBlur={
|
|
134
|
+
onBlur={(e) => {
|
|
135
|
+
yearsEstimated.onBlur(e);
|
|
136
|
+
setFieldTouched('yearsEstimated', true, false);
|
|
137
|
+
updateBirthdate();
|
|
138
|
+
}}
|
|
129
139
|
/>
|
|
130
140
|
</div>
|
|
131
141
|
<div className={styles.dobField}>
|
|
@@ -141,7 +151,11 @@ export const DobField: React.FC = () => {
|
|
|
141
151
|
min={0}
|
|
142
152
|
{...monthsEstimated}
|
|
143
153
|
required={!yearsEstimateMeta.value}
|
|
144
|
-
onBlur={
|
|
154
|
+
onBlur={(e) => {
|
|
155
|
+
monthsEstimated.onBlur(e);
|
|
156
|
+
setFieldTouched('monthsEstimated', true, false);
|
|
157
|
+
updateBirthdate();
|
|
158
|
+
}}
|
|
145
159
|
/>
|
|
146
160
|
</div>
|
|
147
161
|
</div>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { NameField } from './name/name-field.component';
|
|
3
|
-
import { GenderField } from './gender/gender-field.component';
|
|
4
|
-
import { Identifiers } from './id/id-field.component';
|
|
5
|
-
import { DobField } from './dob/dob.component';
|
|
6
2
|
import { reportError, useConfig } from '@openmrs/esm-framework';
|
|
7
3
|
import { builtInFields, type RegistrationConfig } from '../../config-schema';
|
|
8
|
-
import { CustomField } from './custom-field.component';
|
|
9
4
|
import { AddressComponent } from './address/address-field.component';
|
|
5
|
+
import { CauseOfDeathField } from './cause-of-death/cause-of-death.component';
|
|
6
|
+
import { CustomField } from './custom-field.component';
|
|
7
|
+
import { DateAndTimeOfDeathField } from './date-and-time-of-death/date-and-time-of-death.component';
|
|
8
|
+
import { DobField } from './dob/dob.component';
|
|
9
|
+
import { GenderField } from './gender/gender-field.component';
|
|
10
|
+
import { Identifiers } from './id/id-field.component';
|
|
11
|
+
import { NameField } from './name/name-field.component';
|
|
10
12
|
import { PhoneField } from './phone/phone-field.component';
|
|
11
13
|
|
|
12
14
|
export interface FieldProps {
|
|
@@ -35,6 +37,10 @@ export function Field({ name }: FieldProps) {
|
|
|
35
37
|
return <GenderField />;
|
|
36
38
|
case 'dob':
|
|
37
39
|
return <DobField />;
|
|
40
|
+
case 'dateAndTimeOfDeath':
|
|
41
|
+
return <DateAndTimeOfDeathField />;
|
|
42
|
+
case 'causeOfDeath':
|
|
43
|
+
return <CauseOfDeathField />;
|
|
38
44
|
case 'address':
|
|
39
45
|
return <AddressComponent />;
|
|
40
46
|
case 'id':
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type FetchResponse, openmrsFetch,
|
|
1
|
+
import { type FetchResponse, openmrsFetch, restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
|
|
2
2
|
import useSWRImmutable from 'swr/immutable';
|
|
3
3
|
import { type ConceptAnswers, type ConceptResponse } from '../patient-registration.types';
|
|
4
|
+
import { useMemo } from 'react';
|
|
4
5
|
|
|
5
6
|
export function useConcept(conceptUuid: string): { data: ConceptResponse; isLoading: boolean } {
|
|
6
7
|
const shouldFetch = typeof conceptUuid === 'string' && conceptUuid !== '';
|
|
@@ -15,10 +16,15 @@ export function useConcept(conceptUuid: string): { data: ConceptResponse; isLoad
|
|
|
15
16
|
kind: 'error',
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
const results = useMemo(() => ({ data: data?.data, isLoading }), [data, isLoading]);
|
|
20
|
+
return results;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
export function useConceptAnswers(conceptUuid: string): {
|
|
23
|
+
export function useConceptAnswers(conceptUuid: string): {
|
|
24
|
+
data: Array<ConceptAnswers>;
|
|
25
|
+
isLoading: boolean;
|
|
26
|
+
error: Error;
|
|
27
|
+
} {
|
|
22
28
|
const shouldFetch = typeof conceptUuid === 'string' && conceptUuid !== '';
|
|
23
29
|
const { data, error, isLoading } = useSWRImmutable<FetchResponse<ConceptResponse>, Error>(
|
|
24
30
|
shouldFetch ? `${restBaseUrl}/concept/${conceptUuid}` : null,
|
|
@@ -31,5 +37,6 @@ export function useConceptAnswers(conceptUuid: string): { data: Array<ConceptAns
|
|
|
31
37
|
kind: 'error',
|
|
32
38
|
});
|
|
33
39
|
}
|
|
34
|
-
|
|
40
|
+
const results = useMemo(() => ({ data: data?.data?.answers, isLoading, error }), [isLoading, error, data]);
|
|
41
|
+
return results;
|
|
35
42
|
}
|
|
@@ -64,8 +64,30 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
.sexField,
|
|
67
|
-
.dobField
|
|
67
|
+
.dobField,
|
|
68
|
+
.dodField {
|
|
68
69
|
margin-bottom: layout.$spacing-05;
|
|
70
|
+
|
|
71
|
+
span {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-flow: row nowrap;
|
|
74
|
+
justify-content: space-between;
|
|
75
|
+
align-items: start;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.nonCodedCauseOfDeath {
|
|
80
|
+
margin-top: layout.$spacing-04;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.timeOfDeathContainer {
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.timeOfDeathField {
|
|
89
|
+
flex: none;
|
|
90
|
+
margin-left: layout.$spacing-02;
|
|
69
91
|
}
|
|
70
92
|
|
|
71
93
|
.dobContentSwitcherLabel {
|
|
@@ -77,22 +99,35 @@
|
|
|
77
99
|
align-items: center;
|
|
78
100
|
}
|
|
79
101
|
|
|
102
|
+
.arrowRightIcon {
|
|
103
|
+
fill: currentColor !important;
|
|
104
|
+
}
|
|
105
|
+
|
|
80
106
|
.configureIdentifiersButton {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
81
109
|
margin: 0 0 layout.$spacing-05 layout.$spacing-05;
|
|
82
110
|
|
|
83
111
|
svg {
|
|
84
112
|
margin-left: layout.$spacing-03;
|
|
85
113
|
}
|
|
86
|
-
|
|
87
|
-
.customField {
|
|
88
|
-
margin-bottom: layout.$spacing-05;
|
|
89
|
-
}
|
|
90
114
|
}
|
|
91
115
|
|
|
92
116
|
.attributeField {
|
|
93
117
|
margin-bottom: layout.$spacing-05;
|
|
94
118
|
}
|
|
95
119
|
|
|
120
|
+
.locationAttributeFieldContainer {
|
|
121
|
+
position: relative;
|
|
122
|
+
|
|
123
|
+
.loadingContainer {
|
|
124
|
+
background-color: colors.$white;
|
|
125
|
+
position: absolute;
|
|
126
|
+
right: layout.$spacing-07;
|
|
127
|
+
bottom: layout.$spacing-02;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
96
131
|
:global(.omrs-breakpoint-lt-desktop) {
|
|
97
132
|
.grid {
|
|
98
133
|
grid-template-columns: 1fr;
|
|
@@ -130,3 +165,7 @@
|
|
|
130
165
|
line-height: 1.34;
|
|
131
166
|
margin: layout.$spacing-02 0 0;
|
|
132
167
|
}
|
|
168
|
+
|
|
169
|
+
.customField {
|
|
170
|
+
margin-bottom: layout.$spacing-05;
|
|
171
|
+
}
|
|
@@ -11,11 +11,12 @@ export const GenderField: React.FC = () => {
|
|
|
11
11
|
const { fieldConfigurations } = useConfig<RegistrationConfig>();
|
|
12
12
|
const { t } = useTranslation();
|
|
13
13
|
const [field, meta] = useField('gender');
|
|
14
|
-
const { setFieldValue } = useContext(PatientRegistrationContext);
|
|
14
|
+
const { setFieldValue, setFieldTouched } = useContext(PatientRegistrationContext);
|
|
15
15
|
const fieldConfigs = fieldConfigurations?.gender;
|
|
16
16
|
|
|
17
17
|
const setGender = (gender: string) => {
|
|
18
18
|
setFieldValue('gender', gender);
|
|
19
|
+
setFieldTouched('gender', true, false);
|
|
19
20
|
};
|
|
20
21
|
/**
|
|
21
22
|
* DO NOT REMOVE THIS COMMENT HERE, ADDS TRANSLATION FOR SEX OPTIONS
|
|
@@ -25,14 +25,16 @@ export function setIdentifierSource(
|
|
|
25
25
|
selectedSource: IdentifierSource;
|
|
26
26
|
} {
|
|
27
27
|
const autoGeneration = identifierSource?.autoGenerationOption?.automaticGenerationEnabled;
|
|
28
|
+
const manualEntryEnabled = identifierSource?.autoGenerationOption?.manualEntryEnabled;
|
|
28
29
|
return {
|
|
29
30
|
selectedSource: identifierSource,
|
|
30
31
|
autoGeneration,
|
|
31
|
-
identifierValue:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
identifierValue:
|
|
33
|
+
autoGeneration && !manualEntryEnabled
|
|
34
|
+
? 'auto-generated'
|
|
35
|
+
: identifierValue !== 'auto-generated'
|
|
36
|
+
? identifierValue
|
|
37
|
+
: initialValue,
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -126,7 +128,7 @@ export const Identifiers: React.FC = () => {
|
|
|
126
128
|
className={styles.configureIdentifiersButton}
|
|
127
129
|
onClick={() => setShowIdentifierOverlay(true)}
|
|
128
130
|
size={isDesktop(layout) ? 'sm' : 'md'}>
|
|
129
|
-
{t('configure', 'Configure')} <ArrowRight size={16} />
|
|
131
|
+
{t('configure', 'Configure')} <ArrowRight className={styles.arrowRightIcon} size={16} />
|
|
130
132
|
</Button>
|
|
131
133
|
</div>
|
|
132
134
|
</UserHasAccess>
|