@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.
- package/README.md +21 -2
- package/dist/132.js +1 -0
- package/dist/168.js +1 -0
- package/dist/229.js +1 -0
- package/dist/247.js +1 -0
- package/dist/255.js +1 -0
- package/dist/294.js +2 -0
- package/dist/294.js.LICENSE.txt +9 -0
- package/dist/32.js +1 -0
- package/dist/327.js +1 -0
- package/dist/403.js +2 -0
- package/dist/403.js.LICENSE.txt +14 -0
- package/dist/553.js +2 -0
- package/dist/553.js.LICENSE.txt +14 -0
- package/dist/574.js +1 -0
- package/dist/595.js +2 -0
- package/dist/595.js.LICENSE.txt +3 -0
- package/dist/617.js +1 -0
- package/dist/658.js +2 -0
- package/dist/658.js.LICENSE.txt +27 -0
- package/dist/68.js +2 -0
- package/dist/68.js.LICENSE.txt +21 -0
- package/dist/74.js +1 -0
- package/dist/757.js +1 -0
- package/dist/776.js +1 -0
- package/dist/804.js +1 -0
- package/dist/820.js +1 -0
- package/dist/935.js +2 -0
- package/dist/935.js.LICENSE.txt +19 -0
- package/dist/main.js +1 -0
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +612 -0
- package/dist/openmrs-esm-fast-data-entry-app.old +1 -0
- package/jest.config.json +2 -1
- package/package.json +9 -9
- package/src/CancelModal.tsx +48 -0
- package/src/CompleteModal.tsx +46 -0
- package/src/FormBootstrap.tsx +18 -3
- package/src/add-group-modal/AddGroupModal.tsx +80 -27
- package/src/add-group-modal/styles.scss +14 -4
- package/src/config-schema.ts +22 -0
- package/src/context/FormWorkflowContext.tsx +13 -1
- package/src/context/FormWorkflowReducer.ts +13 -3
- package/src/context/GroupFormWorkflowContext.tsx +41 -6
- package/src/context/GroupFormWorkflowReducer.ts +170 -12
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +67 -101
- package/src/form-entry-workflow/styles.scss +2 -1
- package/src/forms-page/FormsPage.tsx +8 -3
- package/src/forms-page/forms-table/FormsTable.tsx +11 -5
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +13 -400
- package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
- package/src/group-form-entry-workflow/SessionDetailsForm.tsx +122 -0
- package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
- package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +105 -0
- package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
- package/src/group-form-entry-workflow/{group-banner/GroupBanner.test.tsx → group-display-header/GroupDisplayHeader.test.tsx} +2 -2
- package/src/group-form-entry-workflow/{group-banner/GroupBanner.tsx → group-display-header/GroupDisplayHeader.tsx} +23 -5
- package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +61 -28
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +5 -0
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +65 -8
- package/src/group-form-entry-workflow/group-search/group-search.scss +8 -6
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +11 -7
- package/src/group-form-entry-workflow/styles.scss +12 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useGetPatient.ts +1 -1
- package/src/hooks/useGetSystemSetting.ts +38 -0
- package/src/hooks/usePostEndpoint.ts +70 -0
- package/src/hooks/useSearchEndpoint.ts +120 -0
- package/src/hooks/useStartVisit.ts +92 -0
- package/src/patient-card/styles.scss +1 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/en.json +27 -9
- package/translations/fr.json +50 -0
- package/.editorconfig +0 -12
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -10
- package/.husky/pre-push +0 -1
- package/.prettierignore +0 -14
- package/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
- package/.yarn/versions/7ee3eceb.yml +0 -0
- package/src/group-form-entry-workflow/group-banner/index.ts +0 -3
- package/src/group-form-entry-workflow/group-search/mock-group-data.ts +0 -79
- package/src/group-form-entry-workflow/group-search/useGroupSearch.ts +0 -14
- package/src/hooks/usePostCohort.ts +0 -18
- /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
|
-
|
|
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].
|
|
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].
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
89
|
+
groupUuid: action.group.uuid,
|
|
79
90
|
groupName: action.group.name,
|
|
80
|
-
patientUuids:
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
101
|
-
|
|
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
|
|
136
|
-
|
|
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
|
|
53
|
-
const
|
|
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
|
|