@ampath/esm-patient-registration-app 6.0.1-pre.6

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