@olaboot/esm-patient-registration-app 9.2.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/ADDRESS_CONFIGURATION.md +152 -0
- package/IDENTIFIER_CONFIGURATION.md +142 -0
- package/IMPLEMENTATION_SUMMARY.md +111 -0
- package/QUICK_START.md +95 -0
- package/README.md +7 -0
- package/address-required-fields-config.json +26 -0
- package/dist/126.js +1 -0
- package/dist/15.js +1 -0
- package/dist/1564.js +1 -0
- package/dist/1567.js +1 -0
- package/dist/1845.js +1 -0
- package/dist/1953.js +1 -0
- package/dist/200.js +1 -0
- package/dist/200.js.map +1 -0
- package/dist/215.js +1 -0
- package/dist/2178.js +1 -0
- package/dist/250.js +1 -0
- package/dist/250.js.map +1 -0
- package/dist/2523.js +1 -0
- package/dist/2523.js.map +1 -0
- package/dist/2566.js +1 -0
- package/dist/2586.js +1 -0
- package/dist/2586.js.map +1 -0
- package/dist/2716.js +1 -0
- package/dist/2716.js.map +1 -0
- package/dist/2759.js +1 -0
- package/dist/2821.js +6 -0
- package/dist/2821.js.map +1 -0
- package/dist/3089.js +1 -0
- package/dist/3089.js.map +1 -0
- package/dist/3230.js +1 -0
- package/dist/3441.js +1 -0
- package/dist/3565.js +1 -0
- package/dist/3571.js +1 -0
- package/dist/3571.js.map +1 -0
- package/dist/3746.js +1 -0
- package/dist/3925.js +1 -0
- package/dist/3946.js +1 -0
- package/dist/4024.js +1 -0
- package/dist/4024.js.map +1 -0
- package/dist/4744.js +1 -0
- package/dist/4744.js.map +1 -0
- package/dist/4809.js +1 -0
- package/dist/4894.js +1 -0
- package/dist/4970.js +1 -0
- package/dist/4970.js.map +1 -0
- package/dist/5130.js +1 -0
- package/dist/5187.js +1 -0
- package/dist/5491.js +1 -0
- package/dist/5491.js.map +1 -0
- package/dist/5595.js +1 -0
- package/dist/5961.js +1 -0
- package/dist/6133.js +1 -0
- package/dist/634.js +1 -0
- package/dist/634.js.map +1 -0
- package/dist/6456.js +1 -0
- package/dist/6466.js +1 -0
- package/dist/6613.js +1 -0
- package/dist/6783.js +1 -0
- package/dist/7073.js +38 -0
- package/dist/7073.js.map +1 -0
- package/dist/7154.js +1 -0
- package/dist/7154.js.map +1 -0
- package/dist/7348.js +1 -0
- package/dist/7439.js +1 -0
- package/dist/7439.js.map +1 -0
- package/dist/7543.js +1 -0
- package/dist/7607.js +1 -0
- package/dist/772.js +1 -0
- package/dist/7984.js +1 -0
- package/dist/7984.js.map +1 -0
- package/dist/8538.js +1 -0
- package/dist/8538.js.map +1 -0
- package/dist/8599.js +1 -0
- package/dist/8727.js +1 -0
- package/dist/8847.js +1 -0
- package/dist/9015.js +1 -0
- package/dist/906.js +1 -0
- package/dist/9065.js +1 -0
- package/dist/9182.js +1 -0
- package/dist/9339.js +1 -0
- package/dist/9453.js +1 -0
- package/dist/9833.js +1 -0
- package/dist/9833.js.map +1 -0
- package/dist/9856.js +1 -0
- package/dist/9856.js.map +1 -0
- package/dist/9920.js +1 -0
- package/dist/9938.js +1 -0
- package/dist/9943.js +1 -0
- package/dist/9943.js.map +1 -0
- package/dist/main.js +10 -0
- package/dist/main.js.map +1 -0
- package/dist/olaboot-esm-patient-registration-app.js +5 -0
- package/dist/olaboot-esm-patient-registration-app.js.buildmanifest.json +1627 -0
- package/dist/olaboot-esm-patient-registration-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/docs/images/patient-registration-hierarchy.png +0 -0
- package/example-config.json +14 -0
- package/jest.config.js +3 -0
- package/package.json +60 -0
- package/rspack.config.js +1 -0
- package/src/add-patient-link.extension.tsx +21 -0
- package/src/add-patient-link.scss +3 -0
- package/src/add-patient-link.test.tsx +16 -0
- package/src/config-schema.ts +507 -0
- package/src/constants.ts +14 -0
- package/src/declarations.d.ts +4 -0
- package/src/index.ts +59 -0
- package/src/nav-link.test.tsx +13 -0
- package/src/nav-link.tsx +10 -0
- package/src/offline.resources.ts +157 -0
- package/src/offline.ts +93 -0
- package/src/patient-photo.extension.tsx +11 -0
- package/src/patient-registration/before-save-prompt.component.tsx +72 -0
- package/src/patient-registration/field/__mocks__/field.resource.ts +60 -0
- package/src/patient-registration/field/address/address-field.component.tsx +186 -0
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +71 -0
- package/src/patient-registration/field/address/address-hierarchy.resource.tsx +157 -0
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +296 -0
- package/src/patient-registration/field/address/address-search.component.tsx +87 -0
- package/src/patient-registration/field/address/address-search.scss +53 -0
- package/src/patient-registration/field/address/address-search.test.tsx +141 -0
- package/src/patient-registration/field/address/custom-address-field.component.tsx +32 -0
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +98 -0
- package/src/patient-registration/field/custom-field.component.tsx +25 -0
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +79 -0
- package/src/patient-registration/field/dob/dob.component.tsx +167 -0
- package/src/patient-registration/field/dob/dob.test.tsx +90 -0
- package/src/patient-registration/field/field.component.tsx +53 -0
- package/src/patient-registration/field/field.resource.ts +42 -0
- package/src/patient-registration/field/field.scss +171 -0
- package/src/patient-registration/field/field.test.tsx +330 -0
- package/src/patient-registration/field/gender/gender-field.component.tsx +54 -0
- package/src/patient-registration/field/gender/gender-field.test.tsx +99 -0
- package/src/patient-registration/field/id/id-field.component.tsx +136 -0
- package/src/patient-registration/field/id/id-field.test.tsx +121 -0
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +200 -0
- package/src/patient-registration/field/id/identifier-selection.scss +41 -0
- package/src/patient-registration/field/name/name-field.component.tsx +148 -0
- package/src/patient-registration/field/obs/obs-field.component.tsx +261 -0
- package/src/patient-registration/field/obs/obs-field.test.tsx +299 -0
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +120 -0
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +141 -0
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +105 -0
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +48 -0
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +100 -0
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +193 -0
- package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +20 -0
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.component.tsx +58 -0
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +90 -0
- package/src/patient-registration/field/phone/phone-field.component.tsx +17 -0
- package/src/patient-registration/form-manager.test.ts +91 -0
- package/src/patient-registration/form-manager.ts +443 -0
- package/src/patient-registration/input/basic-input/input/input.component.tsx +183 -0
- package/src/patient-registration/input/basic-input/input/input.test.tsx +72 -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 +49 -0
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +130 -0
- package/src/patient-registration/input/combo-input/selection-tick.component.tsx +20 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +187 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +62 -0
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +164 -0
- package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +193 -0
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +335 -0
- package/src/patient-registration/input/custom-input/identifier/utils.test.ts +81 -0
- package/src/patient-registration/input/custom-input/identifier/utils.ts +19 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +56 -0
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +34 -0
- package/src/patient-registration/input/input.scss +122 -0
- package/src/patient-registration/patient-registration-context.ts +35 -0
- package/src/patient-registration/patient-registration-hooks.ts +376 -0
- package/src/patient-registration/patient-registration-utils.test.ts +33 -0
- package/src/patient-registration/patient-registration-utils.ts +214 -0
- package/src/patient-registration/patient-registration.component.tsx +266 -0
- package/src/patient-registration/patient-registration.resource.test.tsx +22 -0
- package/src/patient-registration/patient-registration.resource.ts +198 -0
- package/src/patient-registration/patient-registration.scss +103 -0
- package/src/patient-registration/patient-registration.test.tsx +580 -0
- package/src/patient-registration/patient-registration.types.ts +322 -0
- package/src/patient-registration/section/death-info/death-info-section.component.tsx +36 -0
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +47 -0
- package/src/patient-registration/section/demographics/demographics-section.component.tsx +30 -0
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +98 -0
- package/src/patient-registration/section/generic-section.component.tsx +17 -0
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +234 -0
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +113 -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 +21 -0
- package/src/patient-registration/ui-components/overlay/overlay.component.tsx +51 -0
- package/src/patient-registration/ui-components/overlay/overlay.scss +63 -0
- package/src/patient-registration/validation/patient-registration-validation.test.ts +205 -0
- package/src/patient-registration/validation/patient-registration-validation.ts +123 -0
- package/src/resource.ts +12 -0
- package/src/resources-context.ts +14 -0
- package/src/root.component.tsx +63 -0
- package/src/root.scss +7 -0
- package/src/routes.json +61 -0
- package/src/widgets/cancel-patient-edit.modal.tsx +33 -0
- package/src/widgets/cancel-patient-edit.test.tsx +22 -0
- package/src/widgets/delete-identifier-confirmation.modal.tsx +48 -0
- package/src/widgets/delete-identifier-confirmation.test.tsx +32 -0
- package/src/widgets/edit-patient-details-button.component.tsx +33 -0
- package/src/widgets/edit-patient-details-button.scss +3 -0
- package/src/widgets/edit-patient-details-button.test.tsx +35 -0
- package/translations/am.json +120 -0
- package/translations/ar.json +120 -0
- package/translations/ar_SY.json +120 -0
- package/translations/bn.json +120 -0
- package/translations/cs.json +120 -0
- package/translations/de.json +120 -0
- package/translations/en.json +120 -0
- package/translations/en_US.json +120 -0
- package/translations/es.json +120 -0
- package/translations/es_MX.json +120 -0
- package/translations/fr.json +120 -0
- package/translations/he.json +120 -0
- package/translations/hi.json +120 -0
- package/translations/hi_IN.json +120 -0
- package/translations/id.json +120 -0
- package/translations/it.json +120 -0
- package/translations/ka.json +120 -0
- package/translations/km.json +120 -0
- package/translations/ku.json +120 -0
- package/translations/ky.json +120 -0
- package/translations/lg.json +120 -0
- package/translations/ne.json +120 -0
- package/translations/pl.json +120 -0
- package/translations/pt.json +120 -0
- package/translations/pt_BR.json +120 -0
- package/translations/qu.json +120 -0
- package/translations/ro_RO.json +120 -0
- package/translations/ru_RU.json +120 -0
- package/translations/si.json +120 -0
- package/translations/sq.json +120 -0
- package/translations/sw.json +120 -0
- package/translations/sw_KE.json +120 -0
- package/translations/tr.json +120 -0
- package/translations/tr_TR.json +120 -0
- package/translations/uk.json +120 -0
- package/translations/uz.json +120 -0
- package/translations/uz@Latn.json +120 -0
- package/translations/uz_UZ.json +120 -0
- package/translations/vi.json +120 -0
- package/translations/zh.json +120 -0
- package/translations/zh_CN.json +120 -0
- package/translations/zh_TW.json +120 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { Type, validator, validators } from '@openmrs/esm-framework';
|
|
2
|
+
import _default from 'yup/lib/locale';
|
|
3
|
+
|
|
4
|
+
export interface SectionDefinition {
|
|
5
|
+
id: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
fields: Array<string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FieldDefinition {
|
|
11
|
+
id: string;
|
|
12
|
+
type: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
uuid: string;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
allowFutureDates?: boolean;
|
|
17
|
+
allowPastDates?: boolean;
|
|
18
|
+
showHeading: boolean;
|
|
19
|
+
validation?: {
|
|
20
|
+
required: boolean;
|
|
21
|
+
matches?: string;
|
|
22
|
+
};
|
|
23
|
+
locationTag?: string;
|
|
24
|
+
answerConceptSetUuid?: string;
|
|
25
|
+
customConceptAnswers?: Array<CustomConceptAnswer>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CustomConceptAnswer {
|
|
29
|
+
uuid: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Gender {
|
|
34
|
+
label?: string;
|
|
35
|
+
value: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RegistrationConfig {
|
|
39
|
+
sections: Array<string>;
|
|
40
|
+
sectionDefinitions: Array<SectionDefinition>;
|
|
41
|
+
fieldDefinitions: Array<FieldDefinition>;
|
|
42
|
+
fieldConfigurations: {
|
|
43
|
+
causeOfDeath: {
|
|
44
|
+
conceptUuid: string;
|
|
45
|
+
required?: boolean;
|
|
46
|
+
};
|
|
47
|
+
name: {
|
|
48
|
+
displayMiddleName: boolean;
|
|
49
|
+
allowUnidentifiedPatients: boolean;
|
|
50
|
+
defaultUnknownGivenName: string;
|
|
51
|
+
defaultUnknownFamilyName: string;
|
|
52
|
+
displayCapturePhoto: boolean;
|
|
53
|
+
displayReverseFieldOrder: boolean;
|
|
54
|
+
};
|
|
55
|
+
gender: Array<Gender>;
|
|
56
|
+
address: {
|
|
57
|
+
useAddressHierarchy: {
|
|
58
|
+
enabled: boolean;
|
|
59
|
+
useQuickSearch: boolean;
|
|
60
|
+
searchAddressByLevel: boolean;
|
|
61
|
+
};
|
|
62
|
+
requiredAddressFields: Array<string>;
|
|
63
|
+
};
|
|
64
|
+
dateOfBirth: {
|
|
65
|
+
allowEstimatedDateOfBirth: boolean;
|
|
66
|
+
useEstimatedDateOfBirth: {
|
|
67
|
+
enabled: boolean;
|
|
68
|
+
dayOfMonth: number;
|
|
69
|
+
month: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
phone: {
|
|
73
|
+
personAttributeUuid: string;
|
|
74
|
+
validation?: {
|
|
75
|
+
required: boolean;
|
|
76
|
+
matches?: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
links: {
|
|
81
|
+
submitButton: string;
|
|
82
|
+
};
|
|
83
|
+
defaultPatientIdentifierTypes: Array<string>;
|
|
84
|
+
identifierTypeOverrides: Array<{
|
|
85
|
+
identifierTypeUuid: string;
|
|
86
|
+
required?: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
registrationObs: {
|
|
89
|
+
encounterTypeUuid: string | null;
|
|
90
|
+
encounterProviderRoleUuid: string;
|
|
91
|
+
registrationFormUuid: string | null;
|
|
92
|
+
};
|
|
93
|
+
freeTextFieldConceptUuid: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const builtInSections: Array<SectionDefinition> = [
|
|
97
|
+
{
|
|
98
|
+
id: 'demographics',
|
|
99
|
+
name: 'Basic Info',
|
|
100
|
+
fields: ['name', 'gender', 'dob', 'id'],
|
|
101
|
+
},
|
|
102
|
+
{ id: 'contact', name: 'Contact Details', fields: ['address', 'phone'] },
|
|
103
|
+
{ id: 'death', name: 'Death Info', fields: ['dateAndTimeOfDeath', 'causeOfDeath'] },
|
|
104
|
+
{ id: 'relationships', name: 'Relationships', fields: [] },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
// These fields are handled specially in field.component.tsx
|
|
108
|
+
export const builtInFields = [
|
|
109
|
+
'name',
|
|
110
|
+
'gender',
|
|
111
|
+
'dob',
|
|
112
|
+
'id',
|
|
113
|
+
'address',
|
|
114
|
+
'phone',
|
|
115
|
+
'causeOfDeath',
|
|
116
|
+
'dateAndTimeOfDeath',
|
|
117
|
+
] as const;
|
|
118
|
+
|
|
119
|
+
export const esmPatientRegistrationSchema = {
|
|
120
|
+
sections: {
|
|
121
|
+
_type: Type.Array,
|
|
122
|
+
_default: ['demographics', 'contact', 'relationships'],
|
|
123
|
+
_description: `An array of strings which are the keys from 'sectionDefinitions' or any of the following built-in sections: '${builtInSections
|
|
124
|
+
.map((s) => s.id)
|
|
125
|
+
.join("', '")}'.`,
|
|
126
|
+
_elements: {
|
|
127
|
+
_type: Type.String,
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
sectionDefinitions: {
|
|
131
|
+
_type: Type.Array,
|
|
132
|
+
_default: [],
|
|
133
|
+
_elements: {
|
|
134
|
+
id: {
|
|
135
|
+
_type: Type.String,
|
|
136
|
+
_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
|
|
137
|
+
.map((s) => s.id)
|
|
138
|
+
.join("', '")}'.`,
|
|
139
|
+
},
|
|
140
|
+
name: {
|
|
141
|
+
_type: Type.String,
|
|
142
|
+
_description: 'The title to display at the top of the section.',
|
|
143
|
+
},
|
|
144
|
+
fields: {
|
|
145
|
+
_type: Type.Array,
|
|
146
|
+
_default: [],
|
|
147
|
+
_description: `The parts to include in the section. Can be any of the following built-in fields: ${builtInFields.join(
|
|
148
|
+
', ',
|
|
149
|
+
)}. Can also be an id from an object in the \`fieldDefinitions\` array, which you can use to define custom fields.`,
|
|
150
|
+
_elements: {
|
|
151
|
+
_type: Type.String,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
fieldDefinitions: {
|
|
157
|
+
_type: Type.Array,
|
|
158
|
+
_default: [],
|
|
159
|
+
_description:
|
|
160
|
+
'Definitions for custom fields that can be used in sectionDefinitions. Can also be used to override built-in fields.',
|
|
161
|
+
_elements: {
|
|
162
|
+
id: {
|
|
163
|
+
_type: Type.String,
|
|
164
|
+
_description:
|
|
165
|
+
'How this field will be referred to in the `fields` element of the `sectionDefinitions` configuration.',
|
|
166
|
+
},
|
|
167
|
+
type: {
|
|
168
|
+
_type: Type.String,
|
|
169
|
+
_description: "How this field's data will be stored—a person attribute or an obs.",
|
|
170
|
+
_validators: [validators.oneOf(['person attribute', 'obs'])],
|
|
171
|
+
},
|
|
172
|
+
uuid: {
|
|
173
|
+
_type: Type.UUID,
|
|
174
|
+
_description: "Person attribute type UUID that this field's data should be saved to.",
|
|
175
|
+
},
|
|
176
|
+
showHeading: {
|
|
177
|
+
_type: Type.Boolean,
|
|
178
|
+
_default: false,
|
|
179
|
+
_description: 'Whether to show a heading above the person attribute field.',
|
|
180
|
+
},
|
|
181
|
+
label: {
|
|
182
|
+
_type: Type.String,
|
|
183
|
+
_default: '',
|
|
184
|
+
_description: 'The label of the input. By default, uses the metadata `display` attribute.',
|
|
185
|
+
},
|
|
186
|
+
placeholder: {
|
|
187
|
+
_type: Type.String,
|
|
188
|
+
_default: '',
|
|
189
|
+
_description: 'Placeholder that will appear in the input.',
|
|
190
|
+
},
|
|
191
|
+
allowFutureDates: {
|
|
192
|
+
_type: Type.Boolean,
|
|
193
|
+
_default: true,
|
|
194
|
+
_description: 'Indicates whether the date input field should allow the selection of future dates or not.',
|
|
195
|
+
},
|
|
196
|
+
allowPastDates: {
|
|
197
|
+
_type: Type.Boolean,
|
|
198
|
+
_default: true,
|
|
199
|
+
_description: 'Indicates whether the date input field should allow the selection of past dates or not.',
|
|
200
|
+
},
|
|
201
|
+
validation: {
|
|
202
|
+
required: {
|
|
203
|
+
_type: Type.Boolean,
|
|
204
|
+
_default: false,
|
|
205
|
+
},
|
|
206
|
+
matches: {
|
|
207
|
+
_type: Type.String,
|
|
208
|
+
_default: '',
|
|
209
|
+
_description: 'Optional RegEx for testing the validity of the input.',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
locationTag: {
|
|
213
|
+
_type: Type.String,
|
|
214
|
+
_default: '',
|
|
215
|
+
_description:
|
|
216
|
+
'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.',
|
|
217
|
+
},
|
|
218
|
+
answerConceptSetUuid: {
|
|
219
|
+
_type: Type.ConceptUuid,
|
|
220
|
+
_default: '',
|
|
221
|
+
_description:
|
|
222
|
+
'For coded questions only. A concept which has the possible responses either as answers or as set members.',
|
|
223
|
+
},
|
|
224
|
+
customConceptAnswers: {
|
|
225
|
+
_type: Type.Array,
|
|
226
|
+
_default: [],
|
|
227
|
+
_description:
|
|
228
|
+
'For coded questions only (obs or person attrbute). A list of custom concept answers. Overrides answers that come from the obs concept or from `answerSetConceptUuid`.',
|
|
229
|
+
_elements: {
|
|
230
|
+
uuid: {
|
|
231
|
+
_type: Type.UUID,
|
|
232
|
+
_description: 'Answer concept UUID',
|
|
233
|
+
},
|
|
234
|
+
label: {
|
|
235
|
+
_type: Type.String,
|
|
236
|
+
_default: '',
|
|
237
|
+
_description: 'The custom label for the answer concept.',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
// Do not add fields here. If you want to add a field in code, add it to built-in fields above.
|
|
243
|
+
},
|
|
244
|
+
fieldConfigurations: {
|
|
245
|
+
causeOfDeath: {
|
|
246
|
+
conceptUuid: {
|
|
247
|
+
_type: Type.ConceptUuid,
|
|
248
|
+
_default: '9272a14b-7260-4353-9e5b-5787b5dead9d',
|
|
249
|
+
_description: 'The concept UUID to get cause of death answers',
|
|
250
|
+
},
|
|
251
|
+
required: {
|
|
252
|
+
_type: Type.Boolean,
|
|
253
|
+
_default: false,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
name: {
|
|
257
|
+
displayMiddleName: {
|
|
258
|
+
_type: Type.Boolean,
|
|
259
|
+
_default: true,
|
|
260
|
+
},
|
|
261
|
+
allowUnidentifiedPatients: {
|
|
262
|
+
_type: Type.Boolean,
|
|
263
|
+
_default: true,
|
|
264
|
+
_description: 'Whether to allow registering unidentified patients.',
|
|
265
|
+
},
|
|
266
|
+
defaultUnknownGivenName: {
|
|
267
|
+
_type: Type.String,
|
|
268
|
+
_default: 'UNKNOWN',
|
|
269
|
+
_description: 'The given/first name to record for unidentified patients.',
|
|
270
|
+
},
|
|
271
|
+
defaultUnknownFamilyName: {
|
|
272
|
+
_type: Type.String,
|
|
273
|
+
_default: 'UNKNOWN',
|
|
274
|
+
_description: 'The family/last name to record for unidentified patients.',
|
|
275
|
+
},
|
|
276
|
+
displayCapturePhoto: {
|
|
277
|
+
_type: Type.Boolean,
|
|
278
|
+
_default: true,
|
|
279
|
+
_description: 'Whether to display capture patient photo slot on name field',
|
|
280
|
+
},
|
|
281
|
+
displayReverseFieldOrder: {
|
|
282
|
+
_type: Type.Boolean,
|
|
283
|
+
_default: false,
|
|
284
|
+
_description: "Whether to display the name fields in the order 'Family name' -> 'Middle name' -> 'First name'",
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
gender: {
|
|
288
|
+
_type: Type.Array,
|
|
289
|
+
_default: [
|
|
290
|
+
{
|
|
291
|
+
value: 'male',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
value: 'female',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
value: 'other',
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
value: 'unknown',
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
_description:
|
|
304
|
+
'The options for sex selection during patient registration. This is Administrative Gender as it is called by FHIR (Possible options are limited to those defined in FHIR Administrative Gender, see https://hl7.org/fhir/R4/valueset-administrative-gender.html).',
|
|
305
|
+
_elements: {
|
|
306
|
+
value: {
|
|
307
|
+
_type: Type.String,
|
|
308
|
+
_description:
|
|
309
|
+
'Value that will be sent to the server. Limited to FHIR-supported values for Administrative Gender',
|
|
310
|
+
_validators: [validators.oneOf(['male', 'female', 'other', 'unknown'])],
|
|
311
|
+
},
|
|
312
|
+
label: {
|
|
313
|
+
_type: Type.String,
|
|
314
|
+
_default: '',
|
|
315
|
+
_description:
|
|
316
|
+
'The label displayed for the sex option, if it should be different from the value (the value will be translated; the English "translation" is upper-case).',
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
address: {
|
|
321
|
+
useAddressHierarchy: {
|
|
322
|
+
enabled: {
|
|
323
|
+
_type: Type.Boolean,
|
|
324
|
+
_default: true,
|
|
325
|
+
_description: 'Whether to use the Address hierarchy in the registration form or not',
|
|
326
|
+
},
|
|
327
|
+
useQuickSearch: {
|
|
328
|
+
_type: Type.Boolean,
|
|
329
|
+
_default: true,
|
|
330
|
+
_description:
|
|
331
|
+
'Whether to use the quick searching through the address saved in the database pre-fill the form.',
|
|
332
|
+
},
|
|
333
|
+
searchAddressByLevel: {
|
|
334
|
+
_type: Type.Boolean,
|
|
335
|
+
_default: false,
|
|
336
|
+
_description:
|
|
337
|
+
"Whether to fill the addresses by levels, i.e. County => subCounty, the current field is dependent on it's previous field.",
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
requiredAddressFields: {
|
|
341
|
+
_type: Type.Array,
|
|
342
|
+
_default: [],
|
|
343
|
+
_description:
|
|
344
|
+
'An array of address field names that should be required in the registration form. This overrides the backend address template configuration. Common field names include: country, stateProvince, countyDistrict, cityVillage, address1, address2, postalCode, etc.',
|
|
345
|
+
_elements: {
|
|
346
|
+
_type: Type.String,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
dateOfBirth: {
|
|
351
|
+
allowEstimatedDateOfBirth: {
|
|
352
|
+
_type: Type.Boolean,
|
|
353
|
+
_default: true,
|
|
354
|
+
_description: 'Whether to allow estimated date of birth for a patient during registration',
|
|
355
|
+
},
|
|
356
|
+
useEstimatedDateOfBirth: {
|
|
357
|
+
enabled: {
|
|
358
|
+
_type: Type.Boolean,
|
|
359
|
+
_default: false,
|
|
360
|
+
_description: 'Whether to use a fixed day and month for estimated date of birth',
|
|
361
|
+
},
|
|
362
|
+
dayOfMonth: {
|
|
363
|
+
_type: Type.Number,
|
|
364
|
+
_default: 0,
|
|
365
|
+
_description:
|
|
366
|
+
'The custom day of the month use on the estimated date of birth (0 = last day of previous month, 1-31 = specific day)',
|
|
367
|
+
_validators: [validators.inRange(0, 31)],
|
|
368
|
+
},
|
|
369
|
+
month: {
|
|
370
|
+
_type: Type.Number,
|
|
371
|
+
_default: 0,
|
|
372
|
+
_description: 'The custom month to use on the estimated date of birth i.e 0 = Jan & 11 = Dec',
|
|
373
|
+
_validators: [validators.inRange(0, 11)],
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
phone: {
|
|
378
|
+
personAttributeUuid: {
|
|
379
|
+
_type: Type.UUID,
|
|
380
|
+
_default: '14d4f066-15f5-102d-96e4-000c29c2a5d7',
|
|
381
|
+
_description: 'The UUID of the phone number person attribute type',
|
|
382
|
+
},
|
|
383
|
+
validation: {
|
|
384
|
+
required: {
|
|
385
|
+
_type: Type.Boolean,
|
|
386
|
+
_default: false,
|
|
387
|
+
},
|
|
388
|
+
matches: {
|
|
389
|
+
_type: Type.String,
|
|
390
|
+
_default: '',
|
|
391
|
+
_description: 'Optional RegEx for testing the validity of the input.',
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
links: {
|
|
397
|
+
submitButton: {
|
|
398
|
+
_type: Type.String,
|
|
399
|
+
_default: '${openmrsSpaBase}/patient/${patientUuid}/chart',
|
|
400
|
+
_validators: [validators.isUrlWithTemplateParameters(['patientUuid'])],
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
defaultPatientIdentifierTypes: {
|
|
404
|
+
_type: Type.Array,
|
|
405
|
+
_default: [],
|
|
406
|
+
_description:
|
|
407
|
+
'A list of patient identifier type UUIDs to be displayed by default in the registration form. These identifiers will be shown in addition to any identifiers marked as required or primary in the backend.',
|
|
408
|
+
_elements: {
|
|
409
|
+
_type: Type.PatientIdentifierTypeUuid,
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
identifierTypeOverrides: {
|
|
413
|
+
_type: Type.Array,
|
|
414
|
+
_default: [],
|
|
415
|
+
_description:
|
|
416
|
+
'Allows overriding the required/optional status of specific identifier types. Use this to make backend-required identifiers optional in the frontend.',
|
|
417
|
+
_elements: {
|
|
418
|
+
identifierTypeUuid: {
|
|
419
|
+
_type: Type.UUID,
|
|
420
|
+
_description: 'The UUID of the patient identifier type to override.',
|
|
421
|
+
},
|
|
422
|
+
required: {
|
|
423
|
+
_type: Type.Boolean,
|
|
424
|
+
_default: false,
|
|
425
|
+
_description:
|
|
426
|
+
'Whether this identifier should be required. Set to false to make a backend-required identifier optional in the frontend.',
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
registrationObs: {
|
|
431
|
+
encounterTypeUuid: {
|
|
432
|
+
_type: Type.UUID,
|
|
433
|
+
_default: '',
|
|
434
|
+
_description:
|
|
435
|
+
'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`.',
|
|
436
|
+
},
|
|
437
|
+
encounterProviderRoleUuid: {
|
|
438
|
+
_type: Type.UUID,
|
|
439
|
+
_default: 'a0b03050-c99b-11e0-9572-0800200c9a66',
|
|
440
|
+
_description: "The provider role to use for the registration encounter. Default is 'Unkown'.",
|
|
441
|
+
},
|
|
442
|
+
registrationFormUuid: {
|
|
443
|
+
_type: Type.UUID,
|
|
444
|
+
_default: '',
|
|
445
|
+
_description:
|
|
446
|
+
'The form UUID to associate with the registration encounter. By default no form will be associated.',
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
freeTextFieldConceptUuid: {
|
|
450
|
+
_type: Type.ConceptUuid,
|
|
451
|
+
_default: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
|
|
452
|
+
},
|
|
453
|
+
_validators: [
|
|
454
|
+
validator(
|
|
455
|
+
(config: RegistrationConfig) =>
|
|
456
|
+
!config.fieldDefinitions.some((d) => d.type == 'obs') || config.registrationObs.encounterTypeUuid != null,
|
|
457
|
+
"If fieldDefinitions contains any fields of type 'obs', `registrationObs.encounterTypeUuid` must be specified.",
|
|
458
|
+
),
|
|
459
|
+
validator(
|
|
460
|
+
(config: RegistrationConfig) =>
|
|
461
|
+
config.sections.every((s) =>
|
|
462
|
+
[...builtInSections, ...config.sectionDefinitions].map((sDef) => sDef.id).includes(s),
|
|
463
|
+
),
|
|
464
|
+
(config: RegistrationConfig) => {
|
|
465
|
+
const allowedSections = [...builtInSections, ...config.sectionDefinitions].map((sDef) => sDef.id);
|
|
466
|
+
const badSection = config.sections.find((s) => !allowedSections.includes(s));
|
|
467
|
+
return (
|
|
468
|
+
`'${badSection}' is not a valid section ID. Valid section IDs include the built-in sections ${stringifyDefinitions(
|
|
469
|
+
builtInSections,
|
|
470
|
+
)}` +
|
|
471
|
+
(config.sectionDefinitions.length
|
|
472
|
+
? `; and the defined sections ${stringifyDefinitions(config.sectionDefinitions)}.`
|
|
473
|
+
: '.')
|
|
474
|
+
);
|
|
475
|
+
},
|
|
476
|
+
),
|
|
477
|
+
validator(
|
|
478
|
+
(config: RegistrationConfig) =>
|
|
479
|
+
config.sectionDefinitions.every((sectionDefinition) =>
|
|
480
|
+
sectionDefinition.fields.every((f) =>
|
|
481
|
+
[...builtInFields, ...config.fieldDefinitions.map((fDef) => fDef.id)].includes(f),
|
|
482
|
+
),
|
|
483
|
+
),
|
|
484
|
+
(config: RegistrationConfig) => {
|
|
485
|
+
const allowedFields = [...builtInFields, ...config.fieldDefinitions.map((fDef) => fDef.id)];
|
|
486
|
+
const badSection = config.sectionDefinitions.find((sectionDefinition) =>
|
|
487
|
+
sectionDefinition.fields.some((f) => !allowedFields.includes(f)),
|
|
488
|
+
);
|
|
489
|
+
const badField = badSection.fields.find((f) => !allowedFields.includes(f));
|
|
490
|
+
return (
|
|
491
|
+
`The section definition '${
|
|
492
|
+
badSection.id
|
|
493
|
+
}' contains an invalid field '${badField}'. 'fields' can only contain the built-in fields '${builtInFields.join(
|
|
494
|
+
"', '",
|
|
495
|
+
)}'` +
|
|
496
|
+
(config.fieldDefinitions.length
|
|
497
|
+
? `; or the defined fields ${stringifyDefinitions(config.fieldDefinitions)}.`
|
|
498
|
+
: '.')
|
|
499
|
+
);
|
|
500
|
+
},
|
|
501
|
+
),
|
|
502
|
+
],
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
function stringifyDefinitions(sectionDefinitions: Array<SectionDefinition | FieldDefinition>) {
|
|
506
|
+
return `'${sectionDefinitions.map((s) => s.id).join("', '")}'`;
|
|
507
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { omrsOfflineCachingStrategyHttpHeaderName, type 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,59 @@
|
|
|
1
|
+
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle, registerBreadcrumbs } from '@openmrs/esm-framework';
|
|
2
|
+
import { esmPatientRegistrationSchema } from './config-schema';
|
|
3
|
+
import { moduleName, patientRegistration } from './constants';
|
|
4
|
+
import { setupOffline } from './offline';
|
|
5
|
+
import addPatientLinkComponent from './add-patient-link.extension';
|
|
6
|
+
|
|
7
|
+
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
8
|
+
|
|
9
|
+
const options = {
|
|
10
|
+
featureName: 'Patient Registration',
|
|
11
|
+
moduleName,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function startupApp() {
|
|
15
|
+
defineConfigSchema(moduleName, esmPatientRegistrationSchema);
|
|
16
|
+
|
|
17
|
+
registerBreadcrumbs([
|
|
18
|
+
{
|
|
19
|
+
path: `${window.spaBase}/${patientRegistration}`,
|
|
20
|
+
// t('patientRegistrationBreadcrumb', 'Patient Registration')
|
|
21
|
+
title: () =>
|
|
22
|
+
Promise.resolve(
|
|
23
|
+
window.i18next.t('patientRegistrationBreadcrumb', { defaultValue: 'Patient Registration', ns: moduleName }),
|
|
24
|
+
),
|
|
25
|
+
parent: `${window.spaBase}/home`,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
path: `${window.spaBase}/patient/:patientUuid/edit`,
|
|
29
|
+
// t('editPatientDetailsBreadcrumb', 'Edit patient details')
|
|
30
|
+
title: () =>
|
|
31
|
+
Promise.resolve(
|
|
32
|
+
window.i18next.t('editPatientDetailsBreadcrumb', { defaultValue: 'Edit patient details', ns: moduleName }),
|
|
33
|
+
),
|
|
34
|
+
parent: `${window.spaBase}/patient/:patientUuid/chart`,
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
setupOffline();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const root = getAsyncLifecycle(() => import('./root.component'), options);
|
|
42
|
+
|
|
43
|
+
export const editPatient = getAsyncLifecycle(() => import('./root.component'), options);
|
|
44
|
+
|
|
45
|
+
export const addPatientLink = getSyncLifecycle(addPatientLinkComponent, options);
|
|
46
|
+
|
|
47
|
+
export const cancelPatientEditModal = getAsyncLifecycle(() => import('./widgets/cancel-patient-edit.modal'), options);
|
|
48
|
+
|
|
49
|
+
export const patientPhotoExtension = getAsyncLifecycle(() => import('./patient-photo.extension'), options);
|
|
50
|
+
|
|
51
|
+
export const editPatientDetailsButton = getAsyncLifecycle(
|
|
52
|
+
() => import('./widgets/edit-patient-details-button.component'),
|
|
53
|
+
options,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
export const deleteIdentifierConfirmationModal = getAsyncLifecycle(
|
|
57
|
+
() => import('./widgets/delete-identifier-confirmation.modal'),
|
|
58
|
+
options,
|
|
59
|
+
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import Root from './nav-link';
|
|
4
|
+
|
|
5
|
+
describe('Nav link component', () => {
|
|
6
|
+
it('renders a link to the patient registration page', () => {
|
|
7
|
+
render(<Root />);
|
|
8
|
+
const linkElement = screen.getByText('Patient Registration');
|
|
9
|
+
|
|
10
|
+
expect(linkElement).toBeInTheDocument();
|
|
11
|
+
expect(linkElement).toHaveAttribute('href', '/openmrs/spa/patient-registration');
|
|
12
|
+
});
|
|
13
|
+
});
|
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
|
+
}
|