@kenyaemr/esm-patient-registration-app 4.3.0
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/README.md +7 -0
- package/__mocks__/autogenerationoptions.mock.ts +34 -0
- package/__mocks__/react-i18next.js +49 -0
- package/dist/144.js +2 -0
- package/dist/144.js.LICENSE.txt +27 -0
- package/dist/144.js.map +1 -0
- package/dist/207.js +1 -0
- package/dist/207.js.map +1 -0
- package/dist/317.js +2 -0
- package/dist/317.js.LICENSE.txt +6 -0
- package/dist/317.js.map +1 -0
- package/dist/330.js +1 -0
- package/dist/330.js.map +1 -0
- package/dist/574.js +1 -0
- package/dist/59.js +1 -0
- package/dist/59.js.map +1 -0
- package/dist/591.js +2 -0
- package/dist/591.js.LICENSE.txt +32 -0
- package/dist/591.js.map +1 -0
- package/dist/62.js +1 -0
- package/dist/62.js.map +1 -0
- package/dist/635.js +1 -0
- package/dist/635.js.map +1 -0
- package/dist/68.js +1 -0
- package/dist/68.js.map +1 -0
- package/dist/735.js +1 -0
- package/dist/735.js.map +1 -0
- package/dist/757.js +1 -0
- package/dist/784.js +2 -0
- package/dist/784.js.LICENSE.txt +9 -0
- package/dist/784.js.map +1 -0
- package/dist/805.js +1 -0
- package/dist/805.js.map +1 -0
- package/dist/807.js +1 -0
- package/dist/821.js +1 -0
- package/dist/821.js.map +1 -0
- package/dist/822.js +1 -0
- package/dist/822.js.map +1 -0
- package/dist/858.js +2 -0
- package/dist/858.js.LICENSE.txt +3 -0
- package/dist/858.js.map +1 -0
- package/dist/887.js +1 -0
- package/dist/887.js.map +1 -0
- package/dist/9.js +2 -0
- package/dist/9.js.LICENSE.txt +9 -0
- package/dist/9.js.map +1 -0
- package/dist/975.js +1 -0
- package/dist/975.js.map +1 -0
- package/dist/main.js +2 -0
- package/dist/main.js.LICENSE.txt +9 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-patient-registration-app.js +1 -0
- package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +623 -0
- package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
- package/dist/openmrs-esm-patient-registration-app.old +1 -0
- package/docs/images/patient-registration-hierarchy.png +0 -0
- package/package.json +55 -0
- package/src/add-patient-link.scss +3 -0
- package/src/add-patient-link.tsx +21 -0
- package/src/config-schema.ts +405 -0
- package/src/constants.ts +14 -0
- package/src/declarations.d.tsx +4 -0
- package/src/index.ts +131 -0
- package/src/nav-link.tsx +10 -0
- package/src/offline.resources.ts +109 -0
- package/src/offline.ts +90 -0
- package/src/patient-registration/before-save-prompt.tsx +72 -0
- package/src/patient-registration/date-util.ts +52 -0
- package/src/patient-registration/field/__mocks__/field.resource.ts +60 -0
- package/src/patient-registration/field/address/address-field.component.tsx +31 -0
- package/src/patient-registration/field/address/address-hierarchy.component.tsx +143 -0
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +181 -0
- package/src/patient-registration/field/address/address-search.component.tsx +98 -0
- package/src/patient-registration/field/address/address-search.scss +53 -0
- package/src/patient-registration/field/custom-field.component.tsx +25 -0
- package/src/patient-registration/field/dob/dob.component.tsx +143 -0
- package/src/patient-registration/field/dob/dob.test.tsx +73 -0
- package/src/patient-registration/field/field.component.tsx +44 -0
- package/src/patient-registration/field/field.resource.ts +35 -0
- package/src/patient-registration/field/field.scss +127 -0
- package/src/patient-registration/field/gender/gender-field.component.tsx +49 -0
- package/src/patient-registration/field/gender/gender-field.test.tsx +66 -0
- package/src/patient-registration/field/id/id-field.component.tsx +142 -0
- package/src/patient-registration/field/id/identifier-selection-overlay.tsx +194 -0
- package/src/patient-registration/field/id/identifier-selection.scss +37 -0
- package/src/patient-registration/field/name/name-field.component.tsx +109 -0
- package/src/patient-registration/field/obs/obs-field.component.tsx +185 -0
- package/src/patient-registration/field/obs/obs-field.test.tsx +127 -0
- package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +59 -0
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +68 -0
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +81 -0
- package/src/patient-registration/field/person-attributes/person-attributes.resource.tsx +20 -0
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +57 -0
- package/src/patient-registration/form-manager.test.ts +68 -0
- package/src/patient-registration/form-manager.ts +413 -0
- package/src/patient-registration/input/basic-input/input/input.component.tsx +59 -0
- package/src/patient-registration/input/basic-input/input/input.test.tsx +170 -0
- package/src/patient-registration/input/basic-input/select/select-input.component.tsx +32 -0
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +32 -0
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +76 -0
- package/src/patient-registration/input/combo-input/combo-input.test.tsx +43 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +84 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +53 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +109 -0
- package/src/patient-registration/input/custom-input/estimated-age/estimated-age-input.component.tsx +32 -0
- package/src/patient-registration/input/custom-input/estimated-age/estimated-age-input.test.tsx +36 -0
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +156 -0
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +110 -0
- package/src/patient-registration/input/custom-input/identifier/utils.ts +19 -0
- package/src/patient-registration/input/custom-input/unidentified-patient/unidentified-patient-input.component.tsx +24 -0
- package/src/patient-registration/input/custom-input/unidentified-patient/unidentified-patient-input.test.tsx +39 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +53 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +43 -0
- package/src/patient-registration/input/input.scss +108 -0
- package/src/patient-registration/patient-registration-context.ts +24 -0
- package/src/patient-registration/patient-registration-hooks.ts +320 -0
- package/src/patient-registration/patient-registration-types.tsx +271 -0
- package/src/patient-registration/patient-registration-utils.ts +219 -0
- package/src/patient-registration/patient-registration.component.tsx +250 -0
- package/src/patient-registration/patient-registration.resource.test.tsx +26 -0
- package/src/patient-registration/patient-registration.resource.tsx +296 -0
- package/src/patient-registration/patient-registration.scss +94 -0
- package/src/patient-registration/patient-registration.test.tsx +436 -0
- package/src/patient-registration/section/death-info/death-info-section.component.tsx +30 -0
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +73 -0
- package/src/patient-registration/section/demographics/demographics-section.component.tsx +30 -0
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +84 -0
- package/src/patient-registration/section/generic-section.component.tsx +17 -0
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +226 -0
- package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +78 -0
- package/src/patient-registration/section/patient-relationships/relationships.scss +35 -0
- package/src/patient-registration/section/section-wrapper.component.tsx +40 -0
- package/src/patient-registration/section/section.component.tsx +23 -0
- package/src/patient-registration/section/section.scss +1 -0
- package/src/patient-registration/ui-components/overlay/index.tsx +51 -0
- package/src/patient-registration/ui-components/overlay/overlay.scss +63 -0
- package/src/patient-registration/validation/patient-registration-validation.test.tsx +129 -0
- package/src/patient-registration/validation/patient-registration-validation.tsx +46 -0
- package/src/patient-verification/assets/counties.json +236 -0
- package/src/patient-verification/assets/verification-assets.ts +11 -0
- package/src/patient-verification/patient-verification-hook.tsx +156 -0
- package/src/patient-verification/patient-verification-utils.ts +173 -0
- package/src/patient-verification/patient-verification.component.tsx +118 -0
- package/src/patient-verification/patient-verification.scss +30 -0
- package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +69 -0
- package/src/patient-verification/verification-modal/empty-prompt.component.tsx +35 -0
- package/src/patient-verification/verification-types.ts +50 -0
- package/src/resource.ts +12 -0
- package/src/root.component.tsx +66 -0
- package/src/root.scss +7 -0
- package/src/root.test.tsx +32 -0
- package/src/widgets/cancel-patient-edit.component.tsx +37 -0
- package/src/widgets/delete-identifier-confirmation-modal.tsx +41 -0
- package/src/widgets/delete-identifier-modal.scss +34 -0
- package/src/widgets/display-photo.component.tsx +30 -0
- package/src/widgets/edit-patient-details-button.component.tsx +34 -0
- package/src/widgets/edit-patient-details-button.scss +3 -0
- package/translations/en.json +108 -0
- package/translations/fr.json +89 -0
- package/translations/km.json +89 -0
- package/tsconfig.json +5 -0
- package/webpack.config.js +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kenyaemr/esm-patient-registration-app",
|
|
3
|
+
"version": "4.3.0",
|
|
4
|
+
"description": "Patient registration microfrontend for the OpenMRS SPA",
|
|
5
|
+
"browser": "dist/openmrs-esm-patient-registration-app.js",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"source": true,
|
|
8
|
+
"license": "MPL-2.0",
|
|
9
|
+
"homepage": "https://github.com/openmrs/openmrs-esm-patient-management#readme",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "openmrs develop",
|
|
12
|
+
"serve": "webpack serve --mode=development",
|
|
13
|
+
"debug": "npm run serve",
|
|
14
|
+
"build": "webpack --mode production",
|
|
15
|
+
"analyze": "webpack --mode=production --env analyze=true",
|
|
16
|
+
"lint": "eslint src --ext ts,tsx",
|
|
17
|
+
"typescript": "tsc",
|
|
18
|
+
"extract-translations": "i18next 'src/**/*.component.tsx'"
|
|
19
|
+
},
|
|
20
|
+
"browserslist": [
|
|
21
|
+
"extends browserslist-config-openmrs"
|
|
22
|
+
],
|
|
23
|
+
"keywords": [
|
|
24
|
+
"openmrs"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/openmrs/openmrs-esm-patient-management.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/openmrs/openmrs-esm-patient-management/issues"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@carbon/react": "^1.12.0",
|
|
38
|
+
"formik": "^2.1.5",
|
|
39
|
+
"geopattern": "^1.2.3",
|
|
40
|
+
"lodash-es": "^4.17.15",
|
|
41
|
+
"react-avatar": "^4.0.0",
|
|
42
|
+
"uuid": "^8.3.2",
|
|
43
|
+
"yup": "^0.29.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@openmrs/esm-framework": "4.x",
|
|
47
|
+
"dayjs": "1.x",
|
|
48
|
+
"react": "18.x",
|
|
49
|
+
"react-i18next": "11.x",
|
|
50
|
+
"react-router-dom": "6.x"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"webpack": "^5.74.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HeaderGlobalAction } from '@carbon/react';
|
|
3
|
+
import { UserFollow } from '@carbon/react/icons';
|
|
4
|
+
import { navigate } from '@openmrs/esm-framework';
|
|
5
|
+
import styles from './add-patient-link.scss';
|
|
6
|
+
|
|
7
|
+
export default function Root() {
|
|
8
|
+
const addPatient = React.useCallback(() => navigate({ to: '${openmrsSpaBase}/patient-registration' }), []);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<HeaderGlobalAction
|
|
12
|
+
aria-label="Add Patient"
|
|
13
|
+
aria-labelledby="Add Patient"
|
|
14
|
+
enterDelayMs={500}
|
|
15
|
+
name="AddPatientIcon"
|
|
16
|
+
onClick={addPatient}
|
|
17
|
+
className={styles.slotStyles}>
|
|
18
|
+
<UserFollow size={20} />
|
|
19
|
+
</HeaderGlobalAction>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { Type, validator, validators } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export interface SectionDefinition {
|
|
4
|
+
id: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
fields: Array<string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface FieldDefinition {
|
|
10
|
+
id: string;
|
|
11
|
+
type: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
uuid: string;
|
|
14
|
+
placeholder: string;
|
|
15
|
+
showHeading: boolean;
|
|
16
|
+
validation: {
|
|
17
|
+
required: boolean;
|
|
18
|
+
matches?: string;
|
|
19
|
+
};
|
|
20
|
+
answerConceptSetUuid?: string;
|
|
21
|
+
customConceptAnswers: Array<CustomConceptAnswer>;
|
|
22
|
+
}
|
|
23
|
+
export interface CustomConceptAnswer {
|
|
24
|
+
uuid: string;
|
|
25
|
+
label?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Gender {
|
|
28
|
+
label?: string;
|
|
29
|
+
value: string;
|
|
30
|
+
id: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RegistrationConfig {
|
|
34
|
+
sections: Array<string>;
|
|
35
|
+
sectionDefinitions: Array<SectionDefinition>;
|
|
36
|
+
fieldDefinitions: Array<FieldDefinition>;
|
|
37
|
+
fieldConfigurations: {
|
|
38
|
+
name: {
|
|
39
|
+
displayMiddleName: boolean;
|
|
40
|
+
unidentifiedPatient: boolean;
|
|
41
|
+
defaultUnknownGivenName: string;
|
|
42
|
+
defaultUnknownFamilyName: string;
|
|
43
|
+
displayCapturePhoto: boolean;
|
|
44
|
+
};
|
|
45
|
+
gender: Array<Gender>;
|
|
46
|
+
address: {
|
|
47
|
+
useAddressHierarchy: {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
useQuickSearch: boolean;
|
|
50
|
+
searchAddressByLevel: boolean;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
dateOfBirth: {
|
|
54
|
+
useEstimatedDateOfBirth: {
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
dayOfMonth: number;
|
|
57
|
+
month: number;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
links: {
|
|
62
|
+
submitButton: string;
|
|
63
|
+
};
|
|
64
|
+
concepts: {
|
|
65
|
+
patientPhotoUuid: string;
|
|
66
|
+
};
|
|
67
|
+
defaultPatientIdentifierTypes: Array<string>;
|
|
68
|
+
registrationObs: {
|
|
69
|
+
encounterTypeUuid: string | null;
|
|
70
|
+
encounterProviderRoleUuid: string;
|
|
71
|
+
registrationFormUuid: string | null;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const builtInSections: Array<SectionDefinition> = [
|
|
76
|
+
{
|
|
77
|
+
id: 'demographics',
|
|
78
|
+
name: 'Basic Info',
|
|
79
|
+
fields: ['name', 'gender', 'dob', 'id'],
|
|
80
|
+
},
|
|
81
|
+
{ id: 'contact', name: 'Contact Details', fields: ['address', 'phone'] },
|
|
82
|
+
{ id: 'death', name: 'Death Info', fields: [] },
|
|
83
|
+
{ id: 'relationships', name: 'Relationships', fields: [] },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
export const builtInFields = ['name', 'gender', 'dob', 'address', 'id', 'phone & email'] as const;
|
|
87
|
+
|
|
88
|
+
export const esmPatientRegistrationSchema = {
|
|
89
|
+
sections: {
|
|
90
|
+
_type: Type.Array,
|
|
91
|
+
_default: ['demographics', 'contact', 'relationships'],
|
|
92
|
+
_description: `An array of strings which are the keys from 'sectionDefinitions' or any of the following built-in sections: '${builtInSections
|
|
93
|
+
.map((s) => s.id)
|
|
94
|
+
.join("', '")}'.`,
|
|
95
|
+
_elements: {
|
|
96
|
+
_type: Type.String,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
sectionDefinitions: {
|
|
100
|
+
_type: Type.Array,
|
|
101
|
+
_elements: {
|
|
102
|
+
id: {
|
|
103
|
+
_type: Type.String,
|
|
104
|
+
_description: `How this section will be referred to in the \`sections\` configuration. To override a built-in section, use that section's id. The built in section ids are '${builtInSections
|
|
105
|
+
.map((s) => s.id)
|
|
106
|
+
.join("', '")}'.`,
|
|
107
|
+
},
|
|
108
|
+
name: {
|
|
109
|
+
_type: Type.String,
|
|
110
|
+
_description: 'The title to display at the top of the section.',
|
|
111
|
+
},
|
|
112
|
+
fields: {
|
|
113
|
+
_type: Type.Array,
|
|
114
|
+
_default: [],
|
|
115
|
+
_description: `The parts to include in the section. Can be any of the following built-in fields: ${builtInFields.join(
|
|
116
|
+
', ',
|
|
117
|
+
)}. Can also be an id from an object in the \`fieldDefinitions\` array, which you can use to define custom fields.`,
|
|
118
|
+
_elements: { _type: Type.String },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
_default: [],
|
|
122
|
+
},
|
|
123
|
+
fieldDefinitions: {
|
|
124
|
+
_type: Type.Array,
|
|
125
|
+
_elements: {
|
|
126
|
+
id: {
|
|
127
|
+
_type: Type.String,
|
|
128
|
+
_description:
|
|
129
|
+
'How this field will be referred to in the `fields` element of the `sectionDefinitions` configuration.',
|
|
130
|
+
},
|
|
131
|
+
type: {
|
|
132
|
+
_type: Type.String,
|
|
133
|
+
_description: "How this field's data will be stored—a person attribute or an obs.",
|
|
134
|
+
// _validators: [validators.oneOf(['person attribute', 'obs'])],
|
|
135
|
+
},
|
|
136
|
+
uuid: {
|
|
137
|
+
_type: Type.UUID,
|
|
138
|
+
_description: "Person attribute type UUID that this field's data should be saved to.",
|
|
139
|
+
},
|
|
140
|
+
showHeading: {
|
|
141
|
+
_type: Type.Boolean,
|
|
142
|
+
_description: 'Whether to show a heading above the person attribute field.',
|
|
143
|
+
_default: false,
|
|
144
|
+
},
|
|
145
|
+
label: {
|
|
146
|
+
_type: Type.String,
|
|
147
|
+
_default: null,
|
|
148
|
+
_description: 'The label of the input. By default, uses the metadata `display` attribute.',
|
|
149
|
+
},
|
|
150
|
+
placeholder: {
|
|
151
|
+
_type: Type.String,
|
|
152
|
+
_default: '',
|
|
153
|
+
_description: 'Placeholder that will appear in the input.',
|
|
154
|
+
},
|
|
155
|
+
validation: {
|
|
156
|
+
required: { _type: Type.Boolean, _default: false },
|
|
157
|
+
matches: {
|
|
158
|
+
_type: Type.String,
|
|
159
|
+
_default: null,
|
|
160
|
+
_description: 'Optional RegEx for testing the validity of the input.',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
answerConceptSetUuid: {
|
|
164
|
+
_type: Type.ConceptUuid,
|
|
165
|
+
_default: null,
|
|
166
|
+
_description:
|
|
167
|
+
'For coded questions only. A concept which has the possible responses either as answers or as set members.',
|
|
168
|
+
},
|
|
169
|
+
customConceptAnswers: {
|
|
170
|
+
_type: Type.Array,
|
|
171
|
+
_elements: {
|
|
172
|
+
uuid: {
|
|
173
|
+
_type: Type.UUID,
|
|
174
|
+
_description: 'Answer concept UUID',
|
|
175
|
+
},
|
|
176
|
+
label: {
|
|
177
|
+
_type: Type.String,
|
|
178
|
+
_default: null,
|
|
179
|
+
_description: 'The custom label for the answer concept.',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
_default: [],
|
|
183
|
+
_description: 'For coded questions only. Provide ability to add custom concept answers.',
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
_default: [
|
|
187
|
+
{
|
|
188
|
+
id: 'phone',
|
|
189
|
+
type: 'person attribute',
|
|
190
|
+
uuid: '14d4f066-15f5-102d-96e4-000c29c2a5d7',
|
|
191
|
+
showHeading: false,
|
|
192
|
+
validation: {
|
|
193
|
+
matches: '',
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
_description:
|
|
198
|
+
'Definitions for custom fields that can be used in sectionDefinitions. Can also be used to override built-in fields.',
|
|
199
|
+
},
|
|
200
|
+
fieldConfigurations: {
|
|
201
|
+
name: {
|
|
202
|
+
displayMiddleName: { _type: Type.Boolean, _default: true },
|
|
203
|
+
unidentifiedPatient: {
|
|
204
|
+
_type: Type.Boolean,
|
|
205
|
+
_default: true,
|
|
206
|
+
_description: 'Whether to allow patients to be registered without names.',
|
|
207
|
+
},
|
|
208
|
+
defaultUnknownGivenName: {
|
|
209
|
+
_type: Type.String,
|
|
210
|
+
_default: 'UNKNOWN',
|
|
211
|
+
_description: 'The given/first name to record for unidentified patients.',
|
|
212
|
+
},
|
|
213
|
+
defaultUnknownFamilyName: {
|
|
214
|
+
_type: Type.String,
|
|
215
|
+
_default: 'UNKNOWN',
|
|
216
|
+
_description: 'The family/last name to record for unidentified patients.',
|
|
217
|
+
},
|
|
218
|
+
displayCapturePhoto: {
|
|
219
|
+
_type: Type.Boolean,
|
|
220
|
+
_default: true,
|
|
221
|
+
_description: 'Whether to display capture patient photo slot on name field',
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
gender: {
|
|
225
|
+
_type: Type.Array,
|
|
226
|
+
_elements: {
|
|
227
|
+
value: {
|
|
228
|
+
_type: Type.String,
|
|
229
|
+
_description: 'The value for sex option',
|
|
230
|
+
},
|
|
231
|
+
label: {
|
|
232
|
+
_type: Type.String,
|
|
233
|
+
_default: null,
|
|
234
|
+
_description: 'The label displayed for sex option.',
|
|
235
|
+
},
|
|
236
|
+
id: {
|
|
237
|
+
_type: Type.String,
|
|
238
|
+
_default: null,
|
|
239
|
+
_description: 'The id for sex option.',
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
_default: [
|
|
243
|
+
{
|
|
244
|
+
id: 'male',
|
|
245
|
+
value: 'Male',
|
|
246
|
+
label: 'Male',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: 'female',
|
|
250
|
+
value: 'Female',
|
|
251
|
+
label: 'Female',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: 'other',
|
|
255
|
+
value: 'Other',
|
|
256
|
+
label: 'Other',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'unknown',
|
|
260
|
+
value: 'Unknown',
|
|
261
|
+
label: 'Unknown',
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
_description: 'Provide ability to configure sex options.',
|
|
265
|
+
},
|
|
266
|
+
address: {
|
|
267
|
+
useAddressHierarchy: {
|
|
268
|
+
enabled: {
|
|
269
|
+
_type: Type.Boolean,
|
|
270
|
+
_description: 'Whether to use the Address hierarchy in the registration form or not',
|
|
271
|
+
_default: true,
|
|
272
|
+
},
|
|
273
|
+
useQuickSearch: {
|
|
274
|
+
_type: Type.Boolean,
|
|
275
|
+
_description:
|
|
276
|
+
'Whether to use the quick searching through the address saved in the database pre-fill the form.',
|
|
277
|
+
_default: true,
|
|
278
|
+
},
|
|
279
|
+
searchAddressByLevel: {
|
|
280
|
+
_type: Type.Boolean,
|
|
281
|
+
_description:
|
|
282
|
+
"Whether to fill the addresses by levels, i.e. County => subCounty, the current field is dependent on it's previous field.",
|
|
283
|
+
_default: false,
|
|
284
|
+
},
|
|
285
|
+
useAddressHierarchyLabel: {
|
|
286
|
+
_type: Type.Object,
|
|
287
|
+
_description: 'Whether to use custom labels for address hierarchy',
|
|
288
|
+
_default: {},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
dateOfBirth: {
|
|
293
|
+
useEstimatedDateOfBirth: {
|
|
294
|
+
enabled: {
|
|
295
|
+
_type: Type.Boolean,
|
|
296
|
+
_description: 'Whether to use custom day and month for estimated date of birth',
|
|
297
|
+
_default: false,
|
|
298
|
+
},
|
|
299
|
+
dayOfMonth: {
|
|
300
|
+
_type: Type.Number,
|
|
301
|
+
_description: 'The custom day of the month use on the estimated date of birth',
|
|
302
|
+
_default: 0,
|
|
303
|
+
},
|
|
304
|
+
month: {
|
|
305
|
+
_type: Type.Number,
|
|
306
|
+
_description: 'The custom month to use on the estimated date of birth i.e 0 = Jan 11 = Dec',
|
|
307
|
+
_default: 0,
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
links: {
|
|
313
|
+
submitButton: {
|
|
314
|
+
_type: Type.String,
|
|
315
|
+
_default: '${openmrsSpaBase}/patient/${patientUuid}/chart',
|
|
316
|
+
_validators: [validators.isUrlWithTemplateParameters(['patientUuid'])],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
concepts: {
|
|
320
|
+
patientPhotoUuid: {
|
|
321
|
+
_type: Type.ConceptUuid,
|
|
322
|
+
_default: '736e8771-e501-4615-bfa7-570c03f4bef5',
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
defaultPatientIdentifierTypes: {
|
|
326
|
+
_type: Type.Array,
|
|
327
|
+
_elements: {
|
|
328
|
+
_type: Type.PatientIdentifierTypeUuid,
|
|
329
|
+
},
|
|
330
|
+
_default: [],
|
|
331
|
+
},
|
|
332
|
+
registrationObs: {
|
|
333
|
+
encounterTypeUuid: {
|
|
334
|
+
_type: Type.UUID,
|
|
335
|
+
_default: null,
|
|
336
|
+
_description:
|
|
337
|
+
'Obs created during registration will be associated with an encounter of this type. This must be set in order to use fields of type `obs`.',
|
|
338
|
+
},
|
|
339
|
+
encounterProviderRoleUuid: {
|
|
340
|
+
_type: Type.UUID,
|
|
341
|
+
_default: 'a0b03050-c99b-11e0-9572-0800200c9a66',
|
|
342
|
+
_description: "The provider role to use for the registration encounter. Default is 'Unkown'.",
|
|
343
|
+
},
|
|
344
|
+
registrationFormUuid: {
|
|
345
|
+
_type: Type.UUID,
|
|
346
|
+
_default: null,
|
|
347
|
+
_description:
|
|
348
|
+
'The form UUID to associate with the registration encounter. By default no form will be associated.',
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
_validators: [
|
|
352
|
+
validator(
|
|
353
|
+
(config: RegistrationConfig) =>
|
|
354
|
+
!config.fieldDefinitions.some((d) => d.type == 'obs') || config.registrationObs.encounterTypeUuid != null,
|
|
355
|
+
"If fieldDefinitions contains any fields of type 'obs', `registrationObs.encounterTypeUuid` must be specified.",
|
|
356
|
+
),
|
|
357
|
+
validator(
|
|
358
|
+
(config: RegistrationConfig) =>
|
|
359
|
+
config.sections.every((s) =>
|
|
360
|
+
[...builtInSections, ...config.sectionDefinitions].map((sDef) => sDef.id).includes(s),
|
|
361
|
+
),
|
|
362
|
+
(config: RegistrationConfig) => {
|
|
363
|
+
const allowedSections = [...builtInSections, ...config.sectionDefinitions].map((sDef) => sDef.id);
|
|
364
|
+
const badSection = config.sections.find((s) => !allowedSections.includes(s));
|
|
365
|
+
return (
|
|
366
|
+
`'${badSection}' is not a valid section ID. Valid section IDs include the built-in sections ${stringifyDefinitions(
|
|
367
|
+
builtInSections,
|
|
368
|
+
)}` +
|
|
369
|
+
(config.sectionDefinitions.length
|
|
370
|
+
? `; and the defined sections ${stringifyDefinitions(config.sectionDefinitions)}.`
|
|
371
|
+
: '.')
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
),
|
|
375
|
+
validator(
|
|
376
|
+
(config: RegistrationConfig) =>
|
|
377
|
+
config.sectionDefinitions.every((sectionDefinition) =>
|
|
378
|
+
sectionDefinition.fields.every((f) =>
|
|
379
|
+
[...builtInFields, ...config.fieldDefinitions.map((fDef) => fDef.id)].includes(f),
|
|
380
|
+
),
|
|
381
|
+
),
|
|
382
|
+
(config: RegistrationConfig) => {
|
|
383
|
+
const allowedFields = [...builtInFields, ...config.fieldDefinitions.map((fDef) => fDef.id)];
|
|
384
|
+
const badSection = config.sectionDefinitions.find((sectionDefinition) =>
|
|
385
|
+
sectionDefinition.fields.some((f) => !allowedFields.includes(f)),
|
|
386
|
+
);
|
|
387
|
+
const badField = badSection.fields.find((f) => !allowedFields.includes(f));
|
|
388
|
+
return (
|
|
389
|
+
`The section definition '${
|
|
390
|
+
badSection.id
|
|
391
|
+
}' contains an invalid field '${badField}'. 'fields' can only contain the built-in fields '${builtInFields.join(
|
|
392
|
+
"', '",
|
|
393
|
+
)}'` +
|
|
394
|
+
(config.fieldDefinitions.length
|
|
395
|
+
? `; or the defined fields ${stringifyDefinitions(config.fieldDefinitions)}.`
|
|
396
|
+
: '.')
|
|
397
|
+
);
|
|
398
|
+
},
|
|
399
|
+
),
|
|
400
|
+
],
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
function stringifyDefinitions(sectionDefinitions: Array<SectionDefinition | FieldDefinition>) {
|
|
404
|
+
return `'${sectionDefinitions.map((s) => s.id).join("', '")}'`;
|
|
405
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { omrsOfflineCachingStrategyHttpHeaderName, OmrsOfflineHttpHeaders } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export const personRelationshipRepresentation =
|
|
4
|
+
'custom:(display,uuid,' +
|
|
5
|
+
'personA:(age,display,birthdate,uuid),' +
|
|
6
|
+
'personB:(age,display,birthdate,uuid),' +
|
|
7
|
+
'relationshipType:(uuid,display,description,aIsToB,bIsToA))';
|
|
8
|
+
|
|
9
|
+
export const moduleName = '@openmrs/esm-patient-registration-app';
|
|
10
|
+
export const patientRegistration = 'patient-registration';
|
|
11
|
+
|
|
12
|
+
export const cacheForOfflineHeaders: OmrsOfflineHttpHeaders = {
|
|
13
|
+
[omrsOfflineCachingStrategyHttpHeaderName]: 'network-first',
|
|
14
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { registerBreadcrumbs, defineConfigSchema, getAsyncLifecycle } from '@openmrs/esm-framework';
|
|
2
|
+
import { FormManager } from './patient-registration/form-manager';
|
|
3
|
+
import { esmPatientRegistrationSchema } from './config-schema';
|
|
4
|
+
import { moduleName, patientRegistration } from './constants';
|
|
5
|
+
import { setupOffline } from './offline';
|
|
6
|
+
|
|
7
|
+
declare var __VERSION__: string;
|
|
8
|
+
// __VERSION__ is replaced by Webpack with the version from package.json
|
|
9
|
+
const version = __VERSION__;
|
|
10
|
+
|
|
11
|
+
const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
12
|
+
|
|
13
|
+
const backendDependencies = {
|
|
14
|
+
'webservices.rest': '^2.24.0',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function setupOpenMRS() {
|
|
18
|
+
const options = {
|
|
19
|
+
featureName: 'Patient Registration',
|
|
20
|
+
moduleName,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
defineConfigSchema(moduleName, esmPatientRegistrationSchema);
|
|
24
|
+
|
|
25
|
+
registerBreadcrumbs([
|
|
26
|
+
{
|
|
27
|
+
path: `${window.spaBase}/${patientRegistration}`,
|
|
28
|
+
title: 'Patient Registration',
|
|
29
|
+
parent: `${window.spaBase}/home`,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: `${window.spaBase}/patient/:patientUuid/edit`,
|
|
33
|
+
title: 'Edit patient details',
|
|
34
|
+
parent: `${window.spaBase}/patient/:patientUuid/chart`,
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
setupOffline();
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
pages: [
|
|
42
|
+
{
|
|
43
|
+
load: getAsyncLifecycle(() => import('./root.component'), options),
|
|
44
|
+
route: 'patient-registration',
|
|
45
|
+
online: {
|
|
46
|
+
savePatientForm: FormManager.savePatientFormOnline,
|
|
47
|
+
isOffline: false,
|
|
48
|
+
},
|
|
49
|
+
offline: {
|
|
50
|
+
savePatientForm: FormManager.savePatientFormOffline,
|
|
51
|
+
isOffline: true,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
load: getAsyncLifecycle(() => import('./root.component'), {
|
|
56
|
+
featureName: 'edit-patient-details-form',
|
|
57
|
+
moduleName,
|
|
58
|
+
}),
|
|
59
|
+
route: /patient\/([a-zA-Z0-9\-]+)\/edit/,
|
|
60
|
+
online: {
|
|
61
|
+
savePatientForm: FormManager.savePatientFormOnline,
|
|
62
|
+
},
|
|
63
|
+
offline: {
|
|
64
|
+
savePatientForm: FormManager.savePatientFormOffline,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
extensions: [
|
|
69
|
+
{
|
|
70
|
+
id: 'add-patient-action',
|
|
71
|
+
slot: 'top-nav-actions-slot',
|
|
72
|
+
load: getAsyncLifecycle(() => import('./add-patient-link'), options),
|
|
73
|
+
online: true,
|
|
74
|
+
offline: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'cancel-patient-edit-modal',
|
|
78
|
+
load: getAsyncLifecycle(() => import('./widgets/cancel-patient-edit.component'), options),
|
|
79
|
+
online: true,
|
|
80
|
+
offline: true,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'patient-photo-widget',
|
|
84
|
+
slot: 'patient-photo-slot',
|
|
85
|
+
load: getAsyncLifecycle(() => import('./widgets/display-photo.component'), options),
|
|
86
|
+
online: true,
|
|
87
|
+
offline: true,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'edit-patient-details-button',
|
|
91
|
+
slot: 'patient-actions-slot',
|
|
92
|
+
load: getAsyncLifecycle(() => import('./widgets/edit-patient-details-button.component'), options),
|
|
93
|
+
online: true,
|
|
94
|
+
offline: true,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'edit-patient-details-button',
|
|
98
|
+
slot: 'patient-search-actions-slot',
|
|
99
|
+
load: getAsyncLifecycle(() => import('./widgets/edit-patient-details-button.component'), options),
|
|
100
|
+
online: true,
|
|
101
|
+
offline: true,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'delete-identifier-confirmation-modal',
|
|
105
|
+
load: getAsyncLifecycle(() => import('./widgets/delete-identifier-confirmation-modal'), options),
|
|
106
|
+
online: true,
|
|
107
|
+
offline: true,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'empty-client-registry-modal',
|
|
111
|
+
load: getAsyncLifecycle(
|
|
112
|
+
() => import('./patient-verification/verification-modal/empty-prompt.component'),
|
|
113
|
+
options,
|
|
114
|
+
),
|
|
115
|
+
online: true,
|
|
116
|
+
offline: true,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'confirm-client-registry-modal',
|
|
120
|
+
load: getAsyncLifecycle(
|
|
121
|
+
() => import('./patient-verification/verification-modal/confirm-prompt.component'),
|
|
122
|
+
options,
|
|
123
|
+
),
|
|
124
|
+
online: true,
|
|
125
|
+
offline: true,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { backendDependencies, importTranslation, setupOpenMRS, version };
|
package/src/nav-link.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ConfigurableLink } from '@openmrs/esm-framework';
|
|
3
|
+
|
|
4
|
+
export default function Root() {
|
|
5
|
+
return (
|
|
6
|
+
<ConfigurableLink to="${openmrsSpaBase}/patient-registration" className="cds--side-nav__link">
|
|
7
|
+
Patient Registration
|
|
8
|
+
</ConfigurableLink>
|
|
9
|
+
);
|
|
10
|
+
}
|