@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.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.
Files changed (214) hide show
  1. package/README.md +58 -12
  2. package/__mocks__/react-i18next.js +9 -14
  3. package/dist/101.js +1 -0
  4. package/dist/101.js.map +1 -0
  5. package/dist/132.js +1 -0
  6. package/dist/188.js +1 -0
  7. package/dist/188.js.map +1 -0
  8. package/dist/197.js +1 -0
  9. package/dist/219.js +1 -0
  10. package/dist/219.js.map +1 -0
  11. package/dist/221.js +1 -0
  12. package/dist/221.js.map +1 -0
  13. package/dist/259.js +1 -0
  14. package/dist/259.js.map +1 -0
  15. package/dist/29.js +2 -0
  16. package/dist/29.js.LICENSE.txt +3 -0
  17. package/dist/29.js.map +1 -0
  18. package/dist/300.js +1 -0
  19. package/dist/326.js +1 -0
  20. package/dist/326.js.map +1 -0
  21. package/dist/335.js +1 -0
  22. package/dist/367.js +1 -0
  23. package/dist/367.js.map +1 -0
  24. package/dist/480.js +1 -0
  25. package/dist/540.js +2 -0
  26. package/dist/{382.js.LICENSE.txt → 540.js.LICENSE.txt} +3 -2
  27. package/dist/540.js.map +1 -0
  28. package/dist/55.js +1 -0
  29. package/dist/564.js +1 -0
  30. package/dist/564.js.map +1 -0
  31. package/dist/602.js +1 -0
  32. package/dist/602.js.map +1 -0
  33. package/dist/626.js +2 -0
  34. package/dist/{294.js.LICENSE.txt → 626.js.LICENSE.txt} +3 -8
  35. package/dist/626.js.map +1 -0
  36. package/dist/652.js +1 -0
  37. package/dist/685.js +1 -0
  38. package/dist/685.js.map +1 -0
  39. package/dist/773.js +2 -0
  40. package/dist/773.js.LICENSE.txt +32 -0
  41. package/dist/773.js.map +1 -0
  42. package/dist/893.js +1 -0
  43. package/dist/893.js.map +1 -0
  44. package/dist/91.js +1 -0
  45. package/dist/91.js.map +1 -0
  46. package/dist/941.js +2 -0
  47. package/dist/941.js.LICENSE.txt +30 -0
  48. package/dist/941.js.map +1 -0
  49. package/dist/961.js +2 -0
  50. package/dist/{735.js.LICENSE.txt → 961.js.LICENSE.txt} +6 -16
  51. package/dist/961.js.map +1 -0
  52. package/dist/99.js +1 -0
  53. package/dist/99.js.map +1 -0
  54. package/dist/991.js +1 -0
  55. package/dist/991.js.map +1 -0
  56. package/dist/main.js +1 -0
  57. package/dist/main.js.map +1 -0
  58. package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
  59. package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +537 -95
  60. package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -1
  61. package/dist/routes.json +1 -0
  62. package/docs/config-icrc-forms.png +0 -0
  63. package/docs/config-other-forms.png +0 -0
  64. package/docs/configuring-form-categories.md +77 -0
  65. package/docs/fde-workflow.mov +0 -0
  66. package/docs/form-workflow-state-diagram.png +0 -0
  67. package/jest.config.json +21 -18
  68. package/package.json +101 -106
  69. package/prettier.config.js +8 -0
  70. package/src/CancelModal.tsx +42 -0
  71. package/src/CompleteModal.tsx +35 -0
  72. package/src/FormBootstrap.tsx +179 -0
  73. package/src/Root.tsx +11 -5
  74. package/src/add-group-modal/AddGroupModal.tsx +249 -0
  75. package/src/add-group-modal/styles.scss +49 -0
  76. package/src/config-schema.ts +124 -31
  77. package/src/constant.ts +1 -1
  78. package/src/context/FormWorkflowContext.tsx +113 -0
  79. package/src/context/FormWorkflowReducer.ts +263 -0
  80. package/src/context/GroupFormWorkflowContext.tsx +155 -0
  81. package/src/context/GroupFormWorkflowReducer.ts +405 -0
  82. package/src/declarations.d.ts +4 -0
  83. package/src/empty-state/EmptyDataIllustration.tsx +39 -0
  84. package/src/empty-state/EmptyState.tsx +28 -0
  85. package/src/empty-state/styles.scss +55 -0
  86. package/src/form-entry-workflow/FormEntryWorkflow.tsx +184 -0
  87. package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +50 -0
  88. package/src/form-entry-workflow/form-review-card/index.ts +3 -0
  89. package/src/form-entry-workflow/form-review-card/styles.scss +37 -0
  90. package/src/form-entry-workflow/index.ts +3 -0
  91. package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
  92. package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +73 -0
  93. package/src/form-entry-workflow/patient-banner/index.ts +3 -0
  94. package/src/form-entry-workflow/patient-banner/styles.scss +44 -0
  95. package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +54 -0
  96. package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
  97. package/src/form-entry-workflow/patient-search-header/styles.scss +25 -0
  98. package/src/form-entry-workflow/styles.scss +63 -0
  99. package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +37 -0
  100. package/src/form-entry-workflow/workflow-review/index.ts +3 -0
  101. package/src/form-entry-workflow/workflow-review/styles.scss +30 -0
  102. package/src/forms-app-menu-link.tsx +6 -7
  103. package/src/forms-page/FormsPage.tsx +106 -0
  104. package/src/forms-page/forms-table/FormsTable.tsx +117 -0
  105. package/src/forms-page/forms-table/index.ts +3 -0
  106. package/src/forms-page/forms-table/styles.scss +19 -0
  107. package/src/forms-page/index.ts +3 -0
  108. package/src/forms-page/styles.scss +9 -0
  109. package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
  110. package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +207 -0
  111. package/src/group-form-entry-workflow/SessionDetailsForm.tsx +154 -0
  112. package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +99 -0
  113. package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +130 -0
  114. package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
  115. package/src/group-form-entry-workflow/configurable-questions/ConfigurableQuestionsSection.tsx +41 -0
  116. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.test.tsx +9 -0
  117. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +55 -0
  118. package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
  119. package/src/group-form-entry-workflow/group-display-header/styles.scss +60 -0
  120. package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +128 -0
  121. package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +66 -0
  122. package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +134 -0
  123. package/src/group-form-entry-workflow/group-search/compact-group-result.scss +63 -0
  124. package/src/group-form-entry-workflow/group-search/compact-group-search.scss +34 -0
  125. package/src/group-form-entry-workflow/group-search/group-search.scss +93 -0
  126. package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +72 -0
  127. package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
  128. package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
  129. package/src/group-form-entry-workflow/index.ts +3 -0
  130. package/src/group-form-entry-workflow/styles.scss +94 -0
  131. package/src/hooks/index.ts +8 -0
  132. package/src/hooks/useForm.ts +56 -0
  133. package/src/hooks/useFormState.ts +23 -0
  134. package/src/hooks/useGetAllForms.ts +37 -0
  135. package/src/hooks/useGetEncounter.ts +21 -0
  136. package/src/hooks/useGetPatient.ts +23 -0
  137. package/src/hooks/useGetPatients.ts +32 -0
  138. package/src/hooks/useGetSystemSetting.ts +36 -0
  139. package/src/hooks/useKeyPress.ts +31 -0
  140. package/src/hooks/usePostEndpoint.ts +76 -0
  141. package/src/hooks/useSearchEndpoint.ts +103 -0
  142. package/src/hooks/useStartVisit.ts +82 -0
  143. package/src/index.ts +18 -66
  144. package/src/patient-card/PatientCard.tsx +55 -0
  145. package/src/patient-card/index.ts +3 -0
  146. package/src/patient-card/styles.scss +44 -0
  147. package/src/routes.json +24 -0
  148. package/src/setup-tests.ts +1 -1
  149. package/src/types.ts +20 -0
  150. package/tools/i18next-parser.config.js +93 -0
  151. package/translations/am.json +75 -0
  152. package/translations/ar.json +75 -0
  153. package/translations/en.json +75 -4
  154. package/translations/es.json +75 -0
  155. package/translations/fr.json +75 -0
  156. package/translations/he.json +75 -0
  157. package/translations/km.json +75 -0
  158. package/tsconfig.json +26 -23
  159. package/turbo.json +18 -0
  160. package/webpack.config.js +1 -1
  161. package/.editorconfig +0 -12
  162. package/.eslintignore +0 -2
  163. package/.eslintrc +0 -4
  164. package/.github/workflows/node.js.yml +0 -79
  165. package/.husky/pre-commit +0 -6
  166. package/.husky/pre-push +0 -6
  167. package/.prettierignore +0 -14
  168. package/dist/24.js +0 -3
  169. package/dist/24.js.LICENSE.txt +0 -16
  170. package/dist/24.js.map +0 -1
  171. package/dist/294.js +0 -3
  172. package/dist/294.js.map +0 -1
  173. package/dist/296.js +0 -2
  174. package/dist/296.js.map +0 -1
  175. package/dist/299.js +0 -2
  176. package/dist/299.js.map +0 -1
  177. package/dist/382.js +0 -3
  178. package/dist/382.js.map +0 -1
  179. package/dist/415.js +0 -2
  180. package/dist/415.js.map +0 -1
  181. package/dist/574.js +0 -1
  182. package/dist/595.js +0 -3
  183. package/dist/595.js.LICENSE.txt +0 -1
  184. package/dist/595.js.map +0 -1
  185. package/dist/69.js +0 -2
  186. package/dist/69.js.map +0 -1
  187. package/dist/735.js +0 -3
  188. package/dist/735.js.map +0 -1
  189. package/dist/777.js +0 -2
  190. package/dist/777.js.map +0 -1
  191. package/dist/860.js +0 -2
  192. package/dist/860.js.map +0 -1
  193. package/dist/906.js +0 -2
  194. package/dist/906.js.map +0 -1
  195. package/dist/openmrs-esm-fast-data-entry-app.old +0 -2
  196. package/src/boxes/extensions/blue-box.tsx +0 -15
  197. package/src/boxes/extensions/box.scss +0 -23
  198. package/src/boxes/extensions/brand-box.tsx +0 -15
  199. package/src/boxes/extensions/red-box.tsx +0 -15
  200. package/src/boxes/slot/boxes.css +0 -23
  201. package/src/boxes/slot/boxes.tsx +0 -19
  202. package/src/declarations.d.tsx +0 -2
  203. package/src/forms/FormsRoot.tsx +0 -32
  204. package/src/forms/FormsTable.tsx +0 -64
  205. package/src/forms/mockData.ts +0 -43
  206. package/src/greeter/greeter.css +0 -4
  207. package/src/greeter/greeter.test.tsx +0 -29
  208. package/src/greeter/greeter.tsx +0 -25
  209. package/src/hello.css +0 -3
  210. package/src/hello.test.tsx +0 -45
  211. package/src/hello.tsx +0 -30
  212. package/src/patient-getter/patient-getter.resource.ts +0 -31
  213. package/src/patient-getter/patient-getter.test.tsx +0 -28
  214. package/src/patient-getter/patient-getter.tsx +0 -28
@@ -0,0 +1,405 @@
1
+ import { navigate } from '@openmrs/esm-framework';
2
+ import { initialWorkflowState } from './FormWorkflowContext';
3
+ import { v4 as uuid } from 'uuid';
4
+
5
+ export const fdeGroupWorkflowStorageVersion = '1.0.5';
6
+ export const fdeGroupWorkflowStorageName = 'openmrs:fastDataEntryGroupWorkflowState';
7
+ const persistData = (data) => {
8
+ localStorage.setItem(fdeGroupWorkflowStorageName + ':' + data.userUuid, JSON.stringify(data));
9
+ };
10
+
11
+ const initialFormState = {
12
+ workflowState: 'NEW_GROUP_SESSION',
13
+ groupUuid: null,
14
+ groupName: null,
15
+ groupMembers: [],
16
+ activePatientUuid: null,
17
+ activeEncounterUuid: null,
18
+ activeVisitUuid: null,
19
+ activeSessionUuid: null,
20
+ patientUuids: [],
21
+ encounters: {},
22
+ visits: {},
23
+ };
24
+
25
+ const reducer = (state, action) => {
26
+ switch (action.type) {
27
+ case 'INITIALIZE_WORKFLOW_STATE': {
28
+ const savedData = localStorage.getItem(fdeGroupWorkflowStorageName + ':' + action.userUuid);
29
+ const savedDataObject = savedData ? JSON.parse(savedData) : {};
30
+ let newState: { [key: string]: unknown } = {};
31
+ if (savedData && savedDataObject['_storageVersion'] === fdeGroupWorkflowStorageVersion) {
32
+ // there is localStorage data and it is still valid
33
+ const thisSavedForm = savedDataObject.forms?.[action.activeFormUuid];
34
+ // set active patient to the last one we were on
35
+ const activePatientUuid =
36
+ thisSavedForm?.activePatientUuid ||
37
+ // or set it to the first member in the list
38
+ thisSavedForm?.patientUuids?.[0] ||
39
+ // something probably went wrong...
40
+ null;
41
+ const activeSessionUuid = thisSavedForm?.activeSessionUuid || uuid();
42
+ newState = {
43
+ ...savedDataObject,
44
+ // set current form to this one
45
+ activeFormUuid: action.activeFormUuid,
46
+ forms: {
47
+ ...savedDataObject.forms,
48
+ // initialize this particular form if it hasn't been created already
49
+ [action.activeFormUuid]: {
50
+ ...initialFormState,
51
+ ...thisSavedForm,
52
+ activePatientUuid: activePatientUuid,
53
+ activeEncounterUuid: thisSavedForm?.encounters?.[activePatientUuid] || null,
54
+ activeVisitUuid: thisSavedForm?.visits?.[activePatientUuid] || null,
55
+ activeSessionUuid: activeSessionUuid,
56
+ },
57
+ },
58
+ };
59
+ } else {
60
+ // no localStorage data, or we should void it
61
+ newState = {
62
+ ...initialWorkflowState,
63
+ _storageVersion: fdeGroupWorkflowStorageVersion,
64
+ forms: {
65
+ [action.activeFormUuid]: initialFormState,
66
+ },
67
+ activeFormUuid: action.activeFormUuid,
68
+ userUuid: action.userUuid,
69
+ };
70
+ }
71
+ persistData(newState);
72
+ return newState;
73
+ }
74
+
75
+ case 'SET_GROUP': {
76
+ const newState = {
77
+ ...state,
78
+ forms: {
79
+ ...state.forms,
80
+ [state.activeFormUuid]: {
81
+ ...state.forms[state.activeFormUuid],
82
+ groupUuid: action.group.uuid,
83
+ groupName: action.group.name,
84
+ patientUuids:
85
+ // this translation is not preferred
86
+ // the only reason we tollerate it here is beause it should be the only time
87
+ // we add cohort information to state
88
+ action.group.cohortMembers?.map((member) => member?.patient?.uuid) ?? [],
89
+ groupMembers: action.group.cohortMembers?.map((member) => member?.patient?.uuid) ?? [],
90
+ activePatientUuid: null,
91
+ activeEncounterUuid: null,
92
+ activeVisitUuid: null,
93
+ activeSessionUuid: null,
94
+ },
95
+ },
96
+ };
97
+ persistData(newState);
98
+ return newState;
99
+ }
100
+ case 'UNSET_GROUP': {
101
+ const newState = {
102
+ ...state,
103
+ forms: {
104
+ ...state.forms,
105
+ [state.activeFormUuid]: {
106
+ ...state.forms[state.activeFormUuid],
107
+ groupUuid: null,
108
+ groupName: null,
109
+ patientUuids: [],
110
+ groupMembers: [],
111
+ activePatientUuid: null,
112
+ activeEncounterUuid: null,
113
+ activeVisitUuid: null,
114
+ activeSessionUuid: null,
115
+ },
116
+ },
117
+ };
118
+ persistData(newState);
119
+ return newState;
120
+ }
121
+ case 'SET_SESSION_META': {
122
+ // requires that group is already entered and contains patientUuids
123
+ const newState = {
124
+ ...state,
125
+ forms: {
126
+ ...state.forms,
127
+ [state.activeFormUuid]: {
128
+ ...state.forms[state.activeFormUuid],
129
+ sessionMeta: action.meta,
130
+ activePatientUuid: state.forms[state.activeFormUuid].patientUuids?.[0],
131
+ activeEncounterUuid:
132
+ state.forms[state.activeFormUuid].encounters[state.forms[state.activeFormUuid].patientUuids?.[0]] || null,
133
+ activeVisitUuid:
134
+ state.forms[state.activeFormUuid].visits[state.forms[state.activeFormUuid].patientUuids?.[0]] || null,
135
+ activeSessionUuid: uuid(),
136
+ workflowState: 'EDIT_FORM',
137
+ },
138
+ },
139
+ };
140
+ persistData(newState);
141
+ return newState;
142
+ }
143
+ case 'ADD_PATIENT_UUID': {
144
+ if (state.forms[state.activeFormUuid].patientUuids.includes(action.patientUuid)) {
145
+ return state;
146
+ }
147
+
148
+ const newState = {
149
+ ...state,
150
+ forms: {
151
+ ...state.forms,
152
+ [state.activeFormUuid]: {
153
+ ...state.forms[state.activeFormUuid],
154
+ patientUuids: [...state.forms[state.activeFormUuid].patientUuids, action.patientUuid],
155
+ },
156
+ },
157
+ };
158
+ persistData(newState);
159
+ return newState;
160
+ }
161
+ case 'REMOVE_PATIENT_UUID': {
162
+ const newState = {
163
+ ...state,
164
+ forms: {
165
+ ...state.forms,
166
+ [state.activeFormUuid]: {
167
+ ...state.forms[state.activeFormUuid],
168
+ patientUuids: state.forms[state.activeFormUuid].patientUuids?.filter((uuid) => action.patientUuid !== uuid),
169
+ },
170
+ },
171
+ };
172
+ persistData(newState);
173
+ return newState;
174
+ }
175
+ case 'SAVE_ENCOUNTER': {
176
+ const thisForm = state.forms[state.activeFormUuid];
177
+ if (thisForm.workflowState === 'SUBMIT_FOR_COMPLETE') {
178
+ const { [state.activeFormUuid]: activeForm, ...formRest } = state.forms;
179
+ const newState = {
180
+ ...state,
181
+ forms: formRest,
182
+ activeFormUuid: null,
183
+ };
184
+ persistData(newState);
185
+ // eslint-disable-next-line
186
+ navigate({ to: '${openmrsSpaBase}/forms' });
187
+ return newState;
188
+ } else if (thisForm.workflowState === 'SUBMIT_FOR_NEXT') {
189
+ const nextPatientUuid = state.nextPatientUuid
190
+ ? state.nextPatientUuid
191
+ : thisForm.patientUuids[
192
+ Math.min(thisForm.patientUuids.indexOf(thisForm.activePatientUuid) + 1, thisForm.patientUuids.length - 1)
193
+ ];
194
+ const encounters = {
195
+ ...thisForm.encounters,
196
+ [thisForm.activePatientUuid]: action.encounterUuid,
197
+ };
198
+ const newState = {
199
+ ...state,
200
+ forms: {
201
+ ...state.forms,
202
+ [state.activeFormUuid]: {
203
+ ...thisForm,
204
+ encounters,
205
+ activePatientUuid: nextPatientUuid,
206
+ activeEncounterUuid: encounters[nextPatientUuid] || null,
207
+ activeVisitUuid: thisForm.visits[nextPatientUuid] || null,
208
+ activeSessionUuid: thisForm.activeSessionUuid,
209
+ workflowState: 'EDIT_FORM',
210
+ },
211
+ },
212
+ };
213
+ persistData(newState);
214
+ return newState;
215
+ } else return state;
216
+ }
217
+ case 'EDIT_ENCOUNTER': {
218
+ const newState = {
219
+ ...state,
220
+ forms: {
221
+ ...state.forms,
222
+ [state.activeFormUuid]: {
223
+ ...state.forms[state.activeFormUuid],
224
+ activeEncounterUuid: state.forms[state.activeFormUuid].encounters[action.patientUuid],
225
+ activeVisitUuid: state.forms[state.activeFormUuid].visits[action.patientUuid],
226
+ activePatientUuid: action.patientUuid,
227
+ activeSessionUuid: action.activeSessionUuid,
228
+ workflowState: 'EDIT_FORM',
229
+ },
230
+ },
231
+ };
232
+ persistData(newState);
233
+ return newState;
234
+ }
235
+ case 'VALIDATE_FOR_NEXT':
236
+ // this state should not be persisted
237
+ window.dispatchEvent(
238
+ new CustomEvent('ampath-form-action', {
239
+ detail: {
240
+ formUuid: state.activeFormUuid,
241
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
242
+ action: 'validateForm',
243
+ },
244
+ }),
245
+ );
246
+ return {
247
+ ...state,
248
+ forms: {
249
+ ...state.forms,
250
+ [state.activeFormUuid]: {
251
+ ...state.forms[state.activeFormUuid],
252
+ workflowState: 'VALIDATE_FOR_NEXT',
253
+ },
254
+ },
255
+ };
256
+ case 'UPDATE_VISIT_UUID':
257
+ // this state should not be persisted
258
+ // we don't pers
259
+ return {
260
+ ...state,
261
+ forms: {
262
+ ...state.forms,
263
+ [state.activeFormUuid]: {
264
+ ...state.forms[state.activeFormUuid],
265
+ visits: {
266
+ ...state.forms[state.activeFormUuid].visits,
267
+ [state.forms[state.activeFormUuid].activePatientUuid]: action.visitUuid,
268
+ },
269
+ activeVisitUuid: action.visitUuid,
270
+ },
271
+ },
272
+ };
273
+
274
+ case 'SUBMIT_FOR_NEXT':
275
+ // this state should not be persisted
276
+ window.dispatchEvent(
277
+ new CustomEvent('ampath-form-action', {
278
+ detail: {
279
+ formUuid: state.activeFormUuid,
280
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
281
+ action: 'onSubmit',
282
+ },
283
+ }),
284
+ );
285
+ return {
286
+ ...state,
287
+ forms: {
288
+ ...state.forms,
289
+ [state.activeFormUuid]: {
290
+ ...state.forms[state.activeFormUuid],
291
+ workflowState: 'SUBMIT_FOR_NEXT',
292
+ },
293
+ },
294
+ nextPatientUuid: action.nextPatientUuid,
295
+ };
296
+ case 'SUBMIT_FOR_REVIEW':
297
+ // this state should not be persisted
298
+ window.dispatchEvent(
299
+ new CustomEvent('ampath-form-action', {
300
+ detail: {
301
+ formUuid: state.activeFormUuid,
302
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
303
+ action: 'onSubmit',
304
+ },
305
+ }),
306
+ );
307
+ return {
308
+ ...state,
309
+ forms: {
310
+ ...state.forms,
311
+ [state.activeFormUuid]: {
312
+ ...state.forms[state.activeFormUuid],
313
+ workflowState: 'SUBMIT_FOR_REVIEW',
314
+ },
315
+ },
316
+ };
317
+
318
+ case 'VALIDATE_FOR_COMPLETE':
319
+ // this state should not be persisted
320
+ window.dispatchEvent(
321
+ new CustomEvent('ampath-form-action', {
322
+ detail: {
323
+ formUuid: state.activeFormUuid,
324
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
325
+ action: 'validateForm',
326
+ },
327
+ }),
328
+ );
329
+ return {
330
+ ...state,
331
+ forms: {
332
+ ...state.forms,
333
+ [state.activeFormUuid]: {
334
+ ...state.forms[state.activeFormUuid],
335
+ workflowState: 'VALIDATE_FOR_COMPLETE',
336
+ },
337
+ },
338
+ };
339
+ case 'SUBMIT_FOR_COMPLETE':
340
+ // this state should not be persisted
341
+ window.dispatchEvent(
342
+ new CustomEvent('ampath-form-action', {
343
+ detail: {
344
+ formUuid: state.activeFormUuid,
345
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
346
+ action: 'onSubmit',
347
+ },
348
+ }),
349
+ );
350
+ return {
351
+ ...state,
352
+ forms: {
353
+ ...state.forms,
354
+ [state.activeFormUuid]: {
355
+ ...state.forms[state.activeFormUuid],
356
+ workflowState: 'SUBMIT_FOR_COMPLETE',
357
+ },
358
+ },
359
+ };
360
+ case 'GO_TO_REVIEW': {
361
+ const newState = {
362
+ ...state,
363
+ forms: {
364
+ ...state.forms,
365
+ [state.activeFormUuid]: {
366
+ ...state.forms[state.activeFormUuid],
367
+ activeEncounterUuid: null,
368
+ activVisitUuid: null,
369
+ activePatientUuid: null,
370
+ activeSessionUuid: null,
371
+ workflowState: 'REVIEW',
372
+ },
373
+ },
374
+ };
375
+ persistData(newState);
376
+ return newState;
377
+ }
378
+ case 'DESTROY_SESSION': {
379
+ const { [state.activeFormUuid]: activeForm, ...formRest } = state.forms;
380
+ const newState = {
381
+ ...state,
382
+ forms: formRest,
383
+ activeFormUuid: null,
384
+ };
385
+ persistData(newState);
386
+ //eslint-disable-next-line
387
+ navigate({ to: '${openmrsSpaBase}/forms' });
388
+ return { ...newState, formDestroyed: true };
389
+ }
390
+ case 'CLOSE_SESSION': {
391
+ const newState = {
392
+ ...state,
393
+ activeFormUuid: null,
394
+ };
395
+ persistData(newState);
396
+ //eslint-disable-next-line
397
+ navigate({ to: '${openmrsSpaBase}/forms' });
398
+ return newState;
399
+ }
400
+ default:
401
+ return state;
402
+ }
403
+ };
404
+
405
+ export default reducer;
@@ -0,0 +1,4 @@
1
+ declare module '@carbon/react';
2
+ declare module '*.css';
3
+ declare module '*.scss';
4
+ declare type SideNavProps = object;
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+
3
+ export const EmptyDataIllustration = ({ width = '64', height = '64' }) => {
4
+ return (
5
+ <svg width={width} height={height} viewBox="0 0 64 64">
6
+ <title>Empty data illustration</title>
7
+ <g fill="none" fillRule="evenodd">
8
+ <path
9
+ d="M38.133 13.186H21.947c-.768.001-1.39.623-1.39 1.391V50.55l-.186.057-3.97 1.216a.743.743 0 01-.927-.493L3.664 12.751a.742.742 0 01.492-.926l6.118-1.874 17.738-5.43 6.119-1.873a.741.741 0 01.926.492L38.076 13l.057.186z"
10
+ fill="#F4F4F4"
11
+ />
12
+ <path
13
+ d="M41.664 13L38.026 1.117A1.576 1.576 0 0036.056.07l-8.601 2.633-17.737 5.43-8.603 2.634a1.578 1.578 0 00-1.046 1.97l12.436 40.616a1.58 1.58 0 001.969 1.046l5.897-1.805.185-.057v-.194l-.185.057-5.952 1.822a1.393 1.393 0 01-1.737-.923L.247 12.682a1.39 1.39 0 01.923-1.738L9.772 8.31 27.51 2.881 36.112.247a1.393 1.393 0 011.737.923L41.47 13l.057.186h.193l-.057-.185z"
14
+ fill="#8D8D8D"
15
+ />
16
+ <path
17
+ d="M11.378 11.855a.836.836 0 01-.798-.59L9.385 7.361a.835.835 0 01.554-1.042l16.318-4.996a.836.836 0 011.042.554l1.195 3.902a.836.836 0 01-.554 1.043l-16.318 4.995a.831.831 0 01-.244.037z"
18
+ fill="#C6C6C6"
19
+ />
20
+ <circle fill="#C6C6C6" cx={17.636} cy={2.314} r={1.855} />
21
+ <circle fill="#FFF" fillRule="nonzero" cx={17.636} cy={2.314} r={1.175} />
22
+ <path
23
+ d="M55.893 53.995H24.544a.79.79 0 01-.788-.789V15.644a.79.79 0 01.788-.788h31.349a.79.79 0 01.788.788v37.562a.79.79 0 01-.788.789z"
24
+ fill="#F4F4F4"
25
+ />
26
+ <path
27
+ d="M41.47 13H21.948a1.579 1.579 0 00-1.576 1.577V52.4l.185-.057V14.577c.001-.768.623-1.39 1.391-1.39h19.581L41.471 13zm17.02 0H21.947a1.579 1.579 0 00-1.576 1.577v42.478c0 .87.706 1.576 1.576 1.577H58.49a1.579 1.579 0 001.576-1.577V14.577a1.579 1.579 0 00-1.576-1.576zm1.39 44.055c0 .768-.622 1.39-1.39 1.392H21.947c-.768-.001-1.39-.624-1.39-1.392V14.577c0-.768.622-1.39 1.39-1.39H58.49c.768 0 1.39.622 1.39 1.39v42.478z"
28
+ fill="#8D8D8D"
29
+ />
30
+ <path
31
+ d="M48.751 17.082H31.686a.836.836 0 01-.835-.835v-4.081c0-.46.374-.834.835-.835H48.75c.461 0 .834.374.835.835v4.08c0 .462-.374.835-.835.836z"
32
+ fill="#C6C6C6"
33
+ />
34
+ <circle fill="#C6C6C6" cx={40.218} cy={9.755} r={1.855} />
35
+ <circle fill="#FFF" fillRule="nonzero" cx={40.218} cy={9.755} r={1.13} />
36
+ </g>
37
+ </svg>
38
+ );
39
+ };
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { Tile, Layer } from '@carbon/react';
3
+ import styles from './styles.scss';
4
+ import { useLayoutType } from '@openmrs/esm-framework';
5
+ import { EmptyDataIllustration } from './EmptyDataIllustration';
6
+
7
+ export interface EmptyStateProps {
8
+ headerTitle: string;
9
+ displayText: string;
10
+ }
11
+ const EmptyState: React.FC<EmptyStateProps> = ({ headerTitle, displayText }) => {
12
+ const isTablet = useLayoutType() === 'tablet';
13
+
14
+ return (
15
+ <Layer>
16
+ <Tile className={styles.tile}>
17
+ <div className={isTablet ? styles.tabletHeading : styles.desktopHeading}>
18
+ <h4>{headerTitle}</h4>
19
+ </div>
20
+ <EmptyDataIllustration />
21
+ <p className={styles.content}>{displayText}</p>
22
+ </Tile>
23
+ </Layer>
24
+ );
25
+ };
26
+
27
+ export default EmptyState;
28
+ export { EmptyState };
@@ -0,0 +1,55 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .action {
6
+ margin-bottom: layout.$spacing-03;
7
+ }
8
+
9
+ .content {
10
+ @include type.type-style("heading-compact-01");
11
+ color: colors.$gray-70;
12
+ margin-top: layout.$spacing-05;
13
+ margin-bottom: layout.$spacing-03;
14
+ }
15
+
16
+ .desktopHeading {
17
+ h4 {
18
+ @include type.type-style('heading-compact-02');
19
+ color: colors.$gray-70;
20
+ }
21
+ }
22
+
23
+ .tabletHeading {
24
+ h4 {
25
+ @include type.type-style('heading-03');
26
+ color: colors.$gray-70;
27
+ }
28
+ }
29
+
30
+ .desktopHeading, .tabletHeading {
31
+ text-align: left;
32
+ text-transform: capitalize;
33
+ margin-bottom: layout.$spacing-05;
34
+
35
+ h4:after {
36
+ content: "";
37
+ display: block;
38
+ width: 2rem;
39
+ padding-top: 0.188rem;
40
+ border-bottom: 0.375rem solid var(--brand-03);
41
+ }
42
+ }
43
+
44
+ .heading:after {
45
+ content: "";
46
+ display: block;
47
+ width: 2rem;
48
+ padding-top: 0.188rem;
49
+ border-bottom: 0.375rem solid var(--brand-03);
50
+ }
51
+
52
+ .tile {
53
+ text-align: center;
54
+ border: 1px solid colors.$gray-20;
55
+ }