@openmrs/esm-fast-data-entry-app 1.0.1-pre.8 → 1.0.1-pre.85

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 (86) hide show
  1. package/README.md +21 -2
  2. package/dist/132.js +1 -0
  3. package/dist/168.js +1 -0
  4. package/dist/229.js +1 -0
  5. package/dist/247.js +1 -0
  6. package/dist/255.js +1 -0
  7. package/dist/294.js +2 -0
  8. package/dist/294.js.LICENSE.txt +9 -0
  9. package/dist/32.js +1 -0
  10. package/dist/327.js +1 -0
  11. package/dist/403.js +2 -0
  12. package/dist/403.js.LICENSE.txt +14 -0
  13. package/dist/553.js +2 -0
  14. package/dist/553.js.LICENSE.txt +14 -0
  15. package/dist/574.js +1 -0
  16. package/dist/595.js +2 -0
  17. package/dist/595.js.LICENSE.txt +3 -0
  18. package/dist/617.js +1 -0
  19. package/dist/658.js +2 -0
  20. package/dist/658.js.LICENSE.txt +27 -0
  21. package/dist/68.js +2 -0
  22. package/dist/68.js.LICENSE.txt +21 -0
  23. package/dist/74.js +1 -0
  24. package/dist/757.js +1 -0
  25. package/dist/776.js +1 -0
  26. package/dist/804.js +1 -0
  27. package/dist/820.js +1 -0
  28. package/dist/935.js +2 -0
  29. package/dist/935.js.LICENSE.txt +19 -0
  30. package/dist/main.js +1 -0
  31. package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
  32. package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +612 -0
  33. package/dist/openmrs-esm-fast-data-entry-app.old +1 -0
  34. package/jest.config.json +2 -1
  35. package/package.json +9 -9
  36. package/src/CancelModal.tsx +48 -0
  37. package/src/CompleteModal.tsx +46 -0
  38. package/src/FormBootstrap.tsx +18 -3
  39. package/src/add-group-modal/AddGroupModal.tsx +80 -27
  40. package/src/add-group-modal/styles.scss +14 -4
  41. package/src/config-schema.ts +22 -0
  42. package/src/context/FormWorkflowContext.tsx +13 -1
  43. package/src/context/FormWorkflowReducer.ts +13 -3
  44. package/src/context/GroupFormWorkflowContext.tsx +41 -6
  45. package/src/context/GroupFormWorkflowReducer.ts +170 -12
  46. package/src/form-entry-workflow/FormEntryWorkflow.tsx +67 -101
  47. package/src/form-entry-workflow/styles.scss +2 -1
  48. package/src/forms-page/FormsPage.tsx +8 -3
  49. package/src/forms-page/forms-table/FormsTable.tsx +11 -5
  50. package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +13 -400
  51. package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
  52. package/src/group-form-entry-workflow/SessionDetailsForm.tsx +122 -0
  53. package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
  54. package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +105 -0
  55. package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
  56. package/src/group-form-entry-workflow/{group-banner/GroupBanner.test.tsx → group-display-header/GroupDisplayHeader.test.tsx} +2 -2
  57. package/src/group-form-entry-workflow/{group-banner/GroupBanner.tsx → group-display-header/GroupDisplayHeader.tsx} +23 -5
  58. package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
  59. package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +61 -28
  60. package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +5 -0
  61. package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +65 -8
  62. package/src/group-form-entry-workflow/group-search/group-search.scss +8 -6
  63. package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +11 -7
  64. package/src/group-form-entry-workflow/styles.scss +12 -1
  65. package/src/hooks/index.ts +1 -0
  66. package/src/hooks/useGetPatient.ts +1 -1
  67. package/src/hooks/useGetSystemSetting.ts +38 -0
  68. package/src/hooks/usePostEndpoint.ts +70 -0
  69. package/src/hooks/useSearchEndpoint.ts +120 -0
  70. package/src/hooks/useStartVisit.ts +92 -0
  71. package/src/patient-card/styles.scss +1 -0
  72. package/tools/i18next-parser.config.js +93 -0
  73. package/translations/en.json +27 -9
  74. package/translations/fr.json +50 -0
  75. package/.editorconfig +0 -12
  76. package/.eslintignore +0 -2
  77. package/.eslintrc.js +0 -10
  78. package/.husky/pre-push +0 -1
  79. package/.prettierignore +0 -14
  80. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
  81. package/.yarn/versions/7ee3eceb.yml +0 -0
  82. package/src/group-form-entry-workflow/group-banner/index.ts +0 -3
  83. package/src/group-form-entry-workflow/group-search/mock-group-data.ts +0 -79
  84. package/src/group-form-entry-workflow/group-search/useGroupSearch.ts +0 -14
  85. package/src/hooks/usePostCohort.ts +0 -18
  86. /package/src/group-form-entry-workflow/{group-banner → group-display-header}/styles.scss +0 -0
@@ -1,7 +1,8 @@
1
1
  import React, { useEffect, useMemo, useReducer } from "react";
2
2
  import reducer from "./GroupFormWorkflowReducer";
3
3
  import { useParams } from "react-router-dom";
4
- import { Type } from "@openmrs/esm-framework";
4
+ import { Type, useSession } from "@openmrs/esm-framework";
5
+ import useGetSystemSetting from "../hooks/useGetSystemSetting";
5
6
  interface ParamTypes {
6
7
  formUuid?: string;
7
8
  }
@@ -20,13 +21,19 @@ export interface MetaType {
20
21
 
21
22
  const initialActions = {
22
23
  setGroup: (group: GroupType) => undefined,
24
+ unsetGroup: () => undefined,
23
25
  setSessionMeta: (meta: MetaType) => undefined,
24
26
  openPatientSearch: () => undefined,
25
27
  saveEncounter: (encounterUuid: string | number) => undefined,
26
28
  editEncounter: (patientUuid: string | number) => undefined,
27
- submitForNext: () => undefined,
29
+ validateForNext: () => undefined,
30
+ validateForComplete: () => undefined,
31
+ updateVisitUuid: (visitUuid: string) => undefined,
32
+ submitForNext: (nextPatientUuid: string = null) => undefined,
28
33
  submitForReview: () => undefined,
29
34
  submitForComplete: () => undefined,
35
+ addPatientUuid: (patientUuid: string) => undefined,
36
+ removePatientUuid: (patientUuid: string) => undefined,
30
37
  goToReview: () => undefined,
31
38
  destroySession: () => undefined,
32
39
  closeSession: () => undefined,
@@ -41,17 +48,22 @@ export const initialWorkflowState = {
41
48
  // aciveFormUuid
42
49
  workflowState: null, // pseudo field from state[activeFormUuid].workflowState
43
50
  activePatientUuid: null, // pseudo field from state[activeFormUuid].activePatientUuid
44
- activeEncounterUuid: null, // pseudo field from state[activeFormUuid].encounterUuid
51
+ activeEncounterUuid: null, // pseudo field from state[activeFormUuid].activeEncounterUuid
52
+ activeVisitUuid: null, // pseudo field from state[activeFormUuid].activeVisitUuid
45
53
  patientUuids: [], // pseudo field from state[activeFormUuid].patientUuids
46
54
  encounters: {}, // pseudo field from state[activeFormUuid].encounters
55
+ visits: {}, // pseudo field from state[activeFormUuid].visits
47
56
  activeGroupUuid: null, // pseudo field from state[activeFormUuid].groupUuid
48
- activeGroupName: null, // pseudo field from state[activeFormUuid].groupname
57
+ activeGroupName: null, // pseudo field from state[activeFormUuid].groupName
58
+ activeGroupMembers: [], // pseudo field from state[activeFormUuid].groupMembers
49
59
  activeSessionMeta: {
50
60
  sessionName: null,
51
61
  practitionerName: null,
52
62
  sessionDate: null,
53
63
  sessionNotes: null,
54
64
  },
65
+ groupVisitTypeUuid: null,
66
+ userUuid: null, // UUID of the user to which this workflow state belongs to
55
67
  };
56
68
 
57
69
  const GroupFormWorkflowContext = React.createContext({
@@ -60,8 +72,13 @@ const GroupFormWorkflowContext = React.createContext({
60
72
  });
61
73
 
62
74
  const GroupFormWorkflowProvider = ({ children }) => {
75
+ const { user } = useSession();
63
76
  const { formUuid } = useParams() as ParamTypes;
64
77
  const activeFormUuid = formUuid.split("&")[0];
78
+ const systemSetting = useGetSystemSetting(
79
+ "@openmrs/esm-fast-data-entry-app.groupSessionVisitTypeUuid"
80
+ );
81
+ const groupVisitTypeUuid = systemSetting?.result?.data?.results?.[0]?.value;
65
82
  const [state, dispatch] = useReducer(reducer, {
66
83
  ...initialWorkflowState,
67
84
  ...initialActions,
@@ -73,16 +90,27 @@ const GroupFormWorkflowProvider = ({ children }) => {
73
90
  dispatch({
74
91
  type: "INITIALIZE_WORKFLOW_STATE",
75
92
  activeFormUuid,
93
+ userUuid: user.uuid,
76
94
  }),
77
95
  setGroup: (group) => dispatch({ type: "SET_GROUP", group }),
96
+ unsetGroup: () => dispatch({ type: "UNSET_GROUP" }),
78
97
  setSessionMeta: (meta) => dispatch({ type: "SET_SESSION_META", meta }),
98
+ addPatientUuid: (patientUuid) =>
99
+ dispatch({ type: "ADD_PATIENT_UUID", patientUuid }),
100
+ removePatientUuid: (patientUuid) =>
101
+ dispatch({ type: "REMOVE_PATIENT_UUID", patientUuid }),
79
102
  openPatientSearch: () => dispatch({ type: "OPEN_PATIENT_SEARCH" }),
80
103
  saveEncounter: (encounterUuid) =>
81
104
  dispatch({
82
105
  type: "SAVE_ENCOUNTER",
83
106
  encounterUuid,
84
107
  }),
85
- submitForNext: () => dispatch({ type: "SUBMIT_FOR_NEXT" }),
108
+ validateForNext: () => dispatch({ type: "VALIDATE_FOR_NEXT" }),
109
+ validateForComplete: () => dispatch({ type: "VALIDATE_FOR_COMPLETE" }),
110
+ updateVisitUuid: (visitUuid) =>
111
+ dispatch({ type: "UPDATE_VISIT_UUID", visitUuid }),
112
+ submitForNext: (nextPatientUuid) =>
113
+ dispatch({ type: "SUBMIT_FOR_NEXT", nextPatientUuid }),
86
114
  submitForComplete: () => dispatch({ type: "SUBMIT_FOR_COMPLETE" }),
87
115
  editEncounter: (patientUuid) =>
88
116
  dispatch({ type: "EDIT_ENCOUNTER", patientUuid }),
@@ -90,7 +118,7 @@ const GroupFormWorkflowProvider = ({ children }) => {
90
118
  destroySession: () => dispatch({ type: "DESTROY_SESSION" }),
91
119
  closeSession: () => dispatch({ type: "CLOSE_SESSION" }),
92
120
  }),
93
- []
121
+ [user]
94
122
  );
95
123
 
96
124
  // if formUuid isn't a part of state yet, grab it from the url params
@@ -104,6 +132,7 @@ const GroupFormWorkflowProvider = ({ children }) => {
104
132
  return (
105
133
  <GroupFormWorkflowContext.Provider
106
134
  value={{
135
+ groupVisitTypeUuid,
107
136
  ...state,
108
137
  ...actions,
109
138
  workflowState:
@@ -115,6 +144,9 @@ const GroupFormWorkflowProvider = ({ children }) => {
115
144
  activeEncounterUuid:
116
145
  state.forms?.[state.activeFormUuid]?.activeEncounterUuid ??
117
146
  initialWorkflowState.activeEncounterUuid,
147
+ activeVisitUuid:
148
+ state.forms?.[state.activeFormUuid]?.activeVisitUuid ??
149
+ initialWorkflowState.activeVisitUuid,
118
150
  patientUuids:
119
151
  state.forms?.[state.activeFormUuid]?.patientUuids ??
120
152
  initialWorkflowState.patientUuids,
@@ -127,6 +159,9 @@ const GroupFormWorkflowProvider = ({ children }) => {
127
159
  activeGroupName:
128
160
  state.forms?.[state.activeFormUuid]?.groupName ??
129
161
  initialWorkflowState.activeGroupName,
162
+ activeGroupMembers:
163
+ state.forms?.[state.activeFormUuid]?.groupMembers ??
164
+ initialWorkflowState.activeGroupMembers,
130
165
  activeSessionMeta:
131
166
  state.forms?.[state.activeFormUuid]?.sessionMeta ??
132
167
  initialWorkflowState.activeSessionMeta,
@@ -5,23 +5,31 @@ export const fdeGroupWorkflowStorageVersion = "1.0.5";
5
5
  export const fdeGroupWorkflowStorageName =
6
6
  "openmrs:fastDataEntryGroupWorkflowState";
7
7
  const persistData = (data) => {
8
- localStorage.setItem(fdeGroupWorkflowStorageName, JSON.stringify(data));
8
+ localStorage.setItem(
9
+ fdeGroupWorkflowStorageName + ":" + data.userUuid,
10
+ JSON.stringify(data)
11
+ );
9
12
  };
10
13
 
11
14
  const initialFormState = {
12
15
  workflowState: "NEW_GROUP_SESSION",
13
16
  groupUuid: null,
14
17
  groupName: null,
18
+ groupMembers: [],
15
19
  activePatientUuid: null,
16
20
  activeEncounterUuid: null,
21
+ activeVisitUuid: null,
17
22
  patientUuids: [],
18
23
  encounters: {},
24
+ visits: {},
19
25
  };
20
26
 
21
27
  const reducer = (state, action) => {
22
28
  switch (action.type) {
23
29
  case "INITIALIZE_WORKFLOW_STATE": {
24
- const savedData = localStorage.getItem(fdeGroupWorkflowStorageName);
30
+ const savedData = localStorage.getItem(
31
+ fdeGroupWorkflowStorageName + ":" + action.userUuid
32
+ );
25
33
  const savedDataObject = savedData ? JSON.parse(savedData) : {};
26
34
  let newState: { [key: string]: unknown } = {};
27
35
  if (
@@ -50,6 +58,8 @@ const reducer = (state, action) => {
50
58
  activePatientUuid: activePatientUuid,
51
59
  activeEncounterUuid:
52
60
  thisSavedForm?.encounters?.[activePatientUuid] || null,
61
+ activeVisitUuid:
62
+ thisSavedForm?.visits?.[activePatientUuid] || null,
53
63
  },
54
64
  },
55
65
  };
@@ -62,6 +72,7 @@ const reducer = (state, action) => {
62
72
  [action.activeFormUuid]: initialFormState,
63
73
  },
64
74
  activeFormUuid: action.activeFormUuid,
75
+ userUuid: action.userUuid,
65
76
  };
66
77
  }
67
78
  persistData(newState);
@@ -75,11 +86,42 @@ const reducer = (state, action) => {
75
86
  ...state.forms,
76
87
  [state.activeFormUuid]: {
77
88
  ...state.forms[state.activeFormUuid],
78
- groupUuid: action.group.id,
89
+ groupUuid: action.group.uuid,
79
90
  groupName: action.group.name,
80
- patientUuids: action.group.members.map((member) => member.uuid),
91
+ patientUuids:
92
+ // this translation is not preferred
93
+ // the only reason we tollerate it here is beause it should be the only time
94
+ // we add cohort information to state
95
+ action.group.cohortMembers?.map(
96
+ (member) => member?.patient?.uuid
97
+ ) ?? [],
98
+ groupMembers:
99
+ action.group.cohortMembers?.map(
100
+ (member) => member?.patient?.uuid
101
+ ) ?? [],
102
+ activePatientUuid: null,
103
+ activeEncounterUuid: null,
104
+ activeVisitUuid: null,
105
+ },
106
+ },
107
+ };
108
+ persistData(newState);
109
+ return newState;
110
+ }
111
+ case "UNSET_GROUP": {
112
+ const newState = {
113
+ ...state,
114
+ forms: {
115
+ ...state.forms,
116
+ [state.activeFormUuid]: {
117
+ ...state.forms[state.activeFormUuid],
118
+ groupUuid: null,
119
+ groupName: null,
120
+ patientUuids: [],
121
+ groupMembers: [],
81
122
  activePatientUuid: null,
82
123
  activeEncounterUuid: null,
124
+ activeVisitUuid: null,
83
125
  },
84
126
  },
85
127
  };
@@ -101,6 +143,10 @@ const reducer = (state, action) => {
101
143
  state.forms[state.activeFormUuid].encounters[
102
144
  state.forms[state.activeFormUuid].patientUuids?.[0]
103
145
  ] || null,
146
+ activeVisitUuid:
147
+ state.forms[state.activeFormUuid].visits[
148
+ state.forms[state.activeFormUuid].patientUuids?.[0]
149
+ ] || null,
104
150
  workflowState: "EDIT_FORM",
105
151
  },
106
152
  },
@@ -108,7 +154,47 @@ const reducer = (state, action) => {
108
154
  persistData(newState);
109
155
  return newState;
110
156
  }
157
+ case "ADD_PATIENT_UUID": {
158
+ if (
159
+ state.forms[state.activeFormUuid].patientUuids.includes(
160
+ action.patientUuid
161
+ )
162
+ ) {
163
+ return state;
164
+ }
111
165
 
166
+ const newState = {
167
+ ...state,
168
+ forms: {
169
+ ...state.forms,
170
+ [state.activeFormUuid]: {
171
+ ...state.forms[state.activeFormUuid],
172
+ patientUuids: [
173
+ ...state.forms[state.activeFormUuid].patientUuids,
174
+ action.patientUuid,
175
+ ],
176
+ },
177
+ },
178
+ };
179
+ persistData(newState);
180
+ return newState;
181
+ }
182
+ case "REMOVE_PATIENT_UUID": {
183
+ const newState = {
184
+ ...state,
185
+ forms: {
186
+ ...state.forms,
187
+ [state.activeFormUuid]: {
188
+ ...state.forms[state.activeFormUuid],
189
+ patientUuids: state.forms[
190
+ state.activeFormUuid
191
+ ].patientUuids?.filter((uuid) => action.patientUuid !== uuid),
192
+ },
193
+ },
194
+ };
195
+ persistData(newState);
196
+ return newState;
197
+ }
112
198
  case "SAVE_ENCOUNTER": {
113
199
  const thisForm = state.forms[state.activeFormUuid];
114
200
  if (thisForm.workflowState === "SUBMIT_FOR_COMPLETE") {
@@ -123,13 +209,14 @@ const reducer = (state, action) => {
123
209
  navigate({ to: "${openmrsSpaBase}/forms" });
124
210
  return newState;
125
211
  } else if (thisForm.workflowState === "SUBMIT_FOR_NEXT") {
126
- const nextPatientUuid =
127
- thisForm.patientUuids[
128
- Math.min(
129
- thisForm.patientUuids.indexOf(thisForm.activePatientUuid) + 1,
130
- thisForm.patientUuids.length - 1
131
- )
132
- ];
212
+ const nextPatientUuid = state.nextPatientUuid
213
+ ? state.nextPatientUuid
214
+ : thisForm.patientUuids[
215
+ Math.min(
216
+ thisForm.patientUuids.indexOf(thisForm.activePatientUuid) + 1,
217
+ thisForm.patientUuids.length - 1
218
+ )
219
+ ];
133
220
  const newState = {
134
221
  ...state,
135
222
  forms: {
@@ -142,6 +229,7 @@ const reducer = (state, action) => {
142
229
  },
143
230
  activePatientUuid: nextPatientUuid,
144
231
  activeEncounterUuid: thisForm.encounters[nextPatientUuid] || null,
232
+ activeVisitUuid: thisForm.visits[nextPatientUuid] || null,
145
233
  workflowState: "EDIT_FORM",
146
234
  },
147
235
  },
@@ -159,6 +247,8 @@ const reducer = (state, action) => {
159
247
  ...state.forms[state.activeFormUuid],
160
248
  activeEncounterUuid:
161
249
  state.forms[state.activeFormUuid].encounters[action.patientUuid],
250
+ activeVisitUuid:
251
+ state.forms[state.activeFormUuid].visits[action.patientUuid],
162
252
  activePatientUuid: action.patientUuid,
163
253
  workflowState: "EDIT_FORM",
164
254
  },
@@ -167,6 +257,46 @@ const reducer = (state, action) => {
167
257
  persistData(newState);
168
258
  return newState;
169
259
  }
260
+ case "VALIDATE_FOR_NEXT":
261
+ // this state should not be persisted
262
+ window.dispatchEvent(
263
+ new CustomEvent("ampath-form-action", {
264
+ detail: {
265
+ formUuid: state.activeFormUuid,
266
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
267
+ action: "validateForm",
268
+ },
269
+ })
270
+ );
271
+ return {
272
+ ...state,
273
+ forms: {
274
+ ...state.forms,
275
+ [state.activeFormUuid]: {
276
+ ...state.forms[state.activeFormUuid],
277
+ workflowState: "VALIDATE_FOR_NEXT",
278
+ },
279
+ },
280
+ };
281
+ case "UPDATE_VISIT_UUID":
282
+ // this state should not be persisted
283
+ // we don't pers
284
+ return {
285
+ ...state,
286
+ forms: {
287
+ ...state.forms,
288
+ [state.activeFormUuid]: {
289
+ ...state.forms[state.activeFormUuid],
290
+ visits: {
291
+ ...state.forms[state.activeFormUuid].visits,
292
+ [state.forms[state.activeFormUuid].activePatientUuid]:
293
+ action.visitUuid,
294
+ },
295
+ activeVisitUuid: action.visitUuid,
296
+ },
297
+ },
298
+ };
299
+
170
300
  case "SUBMIT_FOR_NEXT":
171
301
  // this state should not be persisted
172
302
  window.dispatchEvent(
@@ -187,6 +317,7 @@ const reducer = (state, action) => {
187
317
  workflowState: "SUBMIT_FOR_NEXT",
188
318
  },
189
319
  },
320
+ nextPatientUuid: action.nextPatientUuid,
190
321
  };
191
322
  case "SUBMIT_FOR_REVIEW":
192
323
  // this state should not be persisted
@@ -209,6 +340,28 @@ const reducer = (state, action) => {
209
340
  },
210
341
  },
211
342
  };
343
+
344
+ case "VALIDATE_FOR_COMPLETE":
345
+ // this state should not be persisted
346
+ window.dispatchEvent(
347
+ new CustomEvent("ampath-form-action", {
348
+ detail: {
349
+ formUuid: state.activeFormUuid,
350
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
351
+ action: "validateForm",
352
+ },
353
+ })
354
+ );
355
+ return {
356
+ ...state,
357
+ forms: {
358
+ ...state.forms,
359
+ [state.activeFormUuid]: {
360
+ ...state.forms[state.activeFormUuid],
361
+ workflowState: "VALIDATE_FOR_COMPLETE",
362
+ },
363
+ },
364
+ };
212
365
  case "SUBMIT_FOR_COMPLETE":
213
366
  // this state should not be persisted
214
367
  window.dispatchEvent(
@@ -238,6 +391,7 @@ const reducer = (state, action) => {
238
391
  [state.activeFormUuid]: {
239
392
  ...state.forms[state.activeFormUuid],
240
393
  activeEncounterUuid: null,
394
+ activVisitUuid: null,
241
395
  activePatientUuid: null,
242
396
  workflowState: "REVIEW",
243
397
  },
@@ -254,7 +408,9 @@ const reducer = (state, action) => {
254
408
  activeFormUuid: null,
255
409
  };
256
410
  persistData(newState);
257
- return newState;
411
+ //eslint-disable-next-line
412
+ navigate({ to: "${openmrsSpaBase}/forms" });
413
+ return { ...newState, formDestroyed: true };
258
414
  }
259
415
  case "CLOSE_SESSION": {
260
416
  const newState = {
@@ -262,6 +418,8 @@ const reducer = (state, action) => {
262
418
  activeFormUuid: null,
263
419
  };
264
420
  persistData(newState);
421
+ //eslint-disable-next-line
422
+ navigate({ to: "${openmrsSpaBase}/forms" });
265
423
  return newState;
266
424
  }
267
425
  default:
@@ -1,17 +1,6 @@
1
- import {
2
- ExtensionSlot,
3
- getGlobalStore,
4
- useStore,
5
- } from "@openmrs/esm-framework";
6
- import {
7
- Button,
8
- ComposedModal,
9
- ModalBody,
10
- ModalFooter,
11
- ModalHeader,
12
- } from "@carbon/react";
13
- import React, { useContext, useState } from "react";
14
- import { useNavigate } from "react-router-dom";
1
+ import { ExtensionSlot, useSession } from "@openmrs/esm-framework";
2
+ import { Button } from "@carbon/react";
3
+ import React, { useCallback, useContext, useEffect, useState } from "react";
15
4
  import FormBootstrap from "../FormBootstrap";
16
5
  import PatientCard from "../patient-card/PatientCard";
17
6
  import styles from "./styles.scss";
@@ -22,86 +11,13 @@ import FormWorkflowContext, {
22
11
  } from "../context/FormWorkflowContext";
23
12
  import WorkflowReview from "./workflow-review";
24
13
  import PatientBanner from "./patient-banner";
25
-
26
- const formStore = getGlobalStore("ampath-form-state");
27
-
28
- const CancelModal = ({ open, setOpen }) => {
29
- const { destroySession, closeSession } = useContext(FormWorkflowContext);
30
- const { t } = useTranslation();
31
- const navigate = useNavigate();
32
-
33
- const discard = async () => {
34
- await destroySession();
35
- setOpen(false);
36
- navigate("../");
37
- };
38
-
39
- const saveAndClose = async () => {
40
- await closeSession();
41
- setOpen(false);
42
- navigate("../");
43
- };
44
-
45
- return (
46
- <ComposedModal open={open}>
47
- <ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
48
- <ModalBody>
49
- {t(
50
- "cancelExplanation",
51
- "You will lose any unsaved changes on the current form. Do you want to discard the current session?"
52
- )}
53
- </ModalBody>
54
- <ModalFooter>
55
- <Button kind="secondary" onClick={() => setOpen(false)}>
56
- {t("cancel", "Cancel")}
57
- </Button>
58
- <Button kind="danger" onClick={discard}>
59
- {t("discard", "Discard")}
60
- </Button>
61
- <Button kind="primary" onClick={saveAndClose}>
62
- {t("saveSession", "Save Session")}
63
- </Button>
64
- </ModalFooter>
65
- </ComposedModal>
66
- );
67
- };
68
-
69
- const CompleteModal = ({ open, setOpen }) => {
70
- const { submitForComplete } = useContext(FormWorkflowContext);
71
- const { t } = useTranslation();
72
-
73
- const completeSession = () => {
74
- submitForComplete();
75
- setOpen(false);
76
- };
77
-
78
- return (
79
- <ComposedModal open={open}>
80
- <ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
81
- <ModalBody>
82
- {t(
83
- "saveExplanation",
84
- "Do you want to save the current form and exit the workflow?"
85
- )}
86
- </ModalBody>
87
- <ModalFooter>
88
- <Button kind="secondary" onClick={() => setOpen(false)}>
89
- {t("cancel", "Cancel")}
90
- </Button>
91
- <Button kind="primary" onClick={completeSession}>
92
- {t("complete", "Complete")}
93
- </Button>
94
- </ModalFooter>
95
- </ComposedModal>
96
- );
97
- };
14
+ import CompleteModal from "../CompleteModal";
15
+ import CancelModal from "../CancelModal";
16
+ import useStartVisit from "../hooks/useStartVisit";
98
17
 
99
18
  const WorkflowNavigationButtons = () => {
100
- const { activeFormUuid, submitForNext, workflowState, destroySession } =
101
- useContext(FormWorkflowContext);
102
- const store = useStore(formStore);
103
- const formState = store[activeFormUuid];
104
- const navigationDisabled = formState !== "ready";
19
+ const context = useContext(FormWorkflowContext);
20
+ const { workflowState, destroySession } = context;
105
21
  const [cancelModalOpen, setCancelModalOpen] = useState(false);
106
22
  const [completeModalOpen, setCompleteModalOpen] = useState(false);
107
23
  const { t } = useTranslation();
@@ -111,13 +27,6 @@ const WorkflowNavigationButtons = () => {
111
27
  return (
112
28
  <>
113
29
  <div className={styles.rightPanelActionButtons}>
114
- <Button
115
- kind="primary"
116
- onClick={() => submitForNext()}
117
- disabled={navigationDisabled || workflowState === "NEW_PATIENT"}
118
- >
119
- {t("nextPatient", "Next Patient")}
120
- </Button>
121
30
  <Button
122
31
  kind="secondary"
123
32
  onClick={
@@ -132,8 +41,16 @@ const WorkflowNavigationButtons = () => {
132
41
  {t("cancel", "Cancel")}
133
42
  </Button>
134
43
  </div>
135
- <CancelModal open={cancelModalOpen} setOpen={setCancelModalOpen} />
136
- <CompleteModal open={completeModalOpen} setOpen={setCompleteModalOpen} />
44
+ <CancelModal
45
+ open={cancelModalOpen}
46
+ setOpen={setCancelModalOpen}
47
+ context={context}
48
+ />
49
+ <CompleteModal
50
+ open={completeModalOpen}
51
+ setOpen={setCompleteModalOpen}
52
+ context={context}
53
+ />
137
54
  </>
138
55
  );
139
56
  };
@@ -147,15 +64,63 @@ const FormWorkspace = () => {
147
64
  activeFormUuid,
148
65
  editEncounter,
149
66
  encounters,
67
+ singleSessionVisitTypeUuid,
150
68
  } = useContext(FormWorkflowContext);
151
69
  const { t } = useTranslation();
152
70
 
71
+ const [encounter, setEncounter] = useState(null);
72
+ const [visit, setVisit] = useState(null);
73
+ const { sessionLocation } = useSession();
74
+
75
+ const {
76
+ saveVisit,
77
+ updateEncounter,
78
+ success: visitSaveSuccess,
79
+ } = useStartVisit({
80
+ showSuccessNotification: false,
81
+ showErrorNotification: true,
82
+ });
83
+
153
84
  const handlePostResponse = (encounter) => {
154
85
  if (encounter && encounter.uuid) {
155
86
  saveEncounter(encounter.uuid);
87
+ setEncounter(encounter);
156
88
  }
157
89
  };
158
90
 
91
+ useEffect(() => {
92
+ if (encounter && visit) {
93
+ // Update encounter so that it belongs to the created visit
94
+ updateEncounter({ uuid: encounter.uuid, visit: visit.uuid });
95
+ }
96
+ }, [encounter, visit, updateEncounter]);
97
+
98
+ useEffect(() => {
99
+ if (visitSaveSuccess) {
100
+ setVisit(visitSaveSuccess.data);
101
+ }
102
+ }, [visitSaveSuccess]);
103
+
104
+ const handleEncounterCreate = useCallback(
105
+ (payload) => {
106
+ payload.location = sessionLocation?.uuid;
107
+ payload.encounterDatetime = payload.encounterDatetime
108
+ ? payload.encounterDatetime
109
+ : new Date().toISOString();
110
+ // Create a visit with the same date as the encounter being saved
111
+ const visitStartDatetime = new Date(payload.encounterDatetime);
112
+ const visitStopDatetime = new Date(payload.encounterDatetime);
113
+ saveVisit({
114
+ patientUuid: activePatientUuid,
115
+ startDatetime: visitStartDatetime.toISOString(),
116
+ stopDatetime: visitStopDatetime.toISOString(),
117
+ visitType: singleSessionVisitTypeUuid,
118
+ location: sessionLocation?.uuid,
119
+ });
120
+ },
121
+ [activePatientUuid, singleSessionVisitTypeUuid, saveVisit, sessionLocation]
122
+ );
123
+
159
124
  return (
160
125
  <div className={styles.workspace}>
161
126
  {!patientUuids.length && (
@@ -172,6 +137,7 @@ const FormWorkspace = () => {
172
137
  {...{
173
138
  formUuid: activeFormUuid,
174
139
  handlePostResponse,
140
+ handleEncounterCreate,
175
141
  }}
176
142
  />
177
143
  </div>
@@ -36,6 +36,7 @@
36
36
  flex-grow: 1;
37
37
  max-height: calc(100vh - 14rem);
38
38
  overflow-y: scroll;
39
+ text-align: left;
39
40
  }
40
41
 
41
42
  .formContainer :global(.cds--form-item) :global(.question-area) {
@@ -43,7 +44,7 @@
43
44
  }
44
45
 
45
46
  .rightPanel {
46
- width: 13rem;
47
+ min-width: 13rem;
47
48
  text-align: left;
48
49
  overflow-y: scroll;
49
50
  }
@@ -1,4 +1,4 @@
1
- import { useConfig } from "@openmrs/esm-framework";
1
+ import { useConfig, useSession } from "@openmrs/esm-framework";
2
2
  import { Tab, Tabs, TabList, TabPanels, TabPanel } from "@carbon/react";
3
3
  import React from "react";
4
4
  import { Config } from "../config-schema";
@@ -49,8 +49,13 @@ const FormsPage = () => {
49
49
  const { formCategories, formCategoriesToShow } = config;
50
50
  const { forms, isLoading, error } = useGetAllForms();
51
51
  const cleanRows = prepareRowsForTable(forms);
52
- const savedFormsData = localStorage.getItem(fdeWorkflowStorageName);
53
- const savedGroupFormsData = localStorage.getItem(fdeGroupWorkflowStorageName);
52
+ const { user } = useSession();
53
+ const savedFormsData = localStorage.getItem(
54
+ fdeWorkflowStorageName + ":" + user?.uuid
55
+ );
56
+ const savedGroupFormsData = localStorage.getItem(
57
+ fdeGroupWorkflowStorageName + ":" + user?.uuid
58
+ );
54
59
  const activeForms = [];
55
60
  const activeGroupForms = [];
56
61