@openmrs/esm-fast-data-entry-app 1.0.0-pre.53 → 1.0.0-pre.59
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/dist/187.js +1 -0
- package/dist/247.js +1 -1
- package/dist/312.js +1 -0
- package/dist/412.js +1 -0
- package/dist/574.js +1 -1
- package/dist/592.js +1 -0
- package/dist/804.js +1 -1
- package/dist/880.js +2 -0
- package/dist/{120.js.LICENSE.txt → 880.js.LICENSE.txt} +0 -0
- package/dist/990.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +107 -61
- package/dist/openmrs-esm-fast-data-entry-app.old +1 -1
- package/package.json +1 -1
- package/src/context/FormWorkflowContext.tsx +65 -19
- package/src/context/FormWorkflowReducer.ts +241 -43
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +114 -40
- package/src/form-entry-workflow/styles.scss +0 -3
- package/src/forms-page/FormsPage.tsx +21 -2
- package/src/forms-table/FormsTable.tsx +8 -2
- package/src/patient-card/PatientCard.tsx +19 -8
- package/src/patient-card/styles.scss +13 -0
- package/src/patient-search-header/PatientSearchHeader.tsx +15 -3
- package/translations/en.json +8 -2
- package/dist/120.js +0 -2
- package/dist/61.js +0 -1
- package/dist/84.js +0 -1
|
@@ -1,76 +1,274 @@
|
|
|
1
|
+
import { navigate } from "@openmrs/esm-framework";
|
|
2
|
+
import { initialWorkflowState } from "./FormWorkflowContext";
|
|
3
|
+
|
|
4
|
+
export const fdeWorkflowStorageVersion = "1.0.12";
|
|
5
|
+
export const fdeWorkflowStorageName = "openmrs:fastDataEntryWorkflowState";
|
|
6
|
+
const persistData = (data) => {
|
|
7
|
+
localStorage.setItem(fdeWorkflowStorageName, JSON.stringify(data));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const initialFormState = {
|
|
11
|
+
workflowState: "NEW_PATIENT",
|
|
12
|
+
activePatientUuid: null,
|
|
13
|
+
activeEncounterUuid: null,
|
|
14
|
+
patientUuids: [],
|
|
15
|
+
encounters: {},
|
|
16
|
+
};
|
|
17
|
+
|
|
1
18
|
const reducer = (state, action) => {
|
|
2
19
|
switch (action.type) {
|
|
3
|
-
case "
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
case "INITIALIZE_WORKFLOW_STATE": {
|
|
21
|
+
const savedData = localStorage.getItem(fdeWorkflowStorageName);
|
|
22
|
+
const savedDataObject = savedData ? JSON.parse(savedData) : {};
|
|
23
|
+
let newState: { [key: string]: unknown } = {};
|
|
24
|
+
const newPatient = action.newPatientUuid
|
|
25
|
+
? {
|
|
26
|
+
activePatientUuid: action.newPatientUuid,
|
|
27
|
+
workflowState: "EDIT_FORM",
|
|
28
|
+
}
|
|
29
|
+
: {};
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
savedData &&
|
|
33
|
+
savedDataObject["_storageVersion"] === fdeWorkflowStorageVersion
|
|
34
|
+
) {
|
|
35
|
+
// there is localStorage data and it is still valid
|
|
36
|
+
newState = {
|
|
37
|
+
...savedDataObject,
|
|
38
|
+
activeFormUuid: action.activeFormUuid,
|
|
39
|
+
forms: {
|
|
40
|
+
...savedDataObject.forms,
|
|
41
|
+
// initialize this particular form if it hasn't been created already
|
|
42
|
+
[action.activeFormUuid]: {
|
|
43
|
+
...initialFormState,
|
|
44
|
+
...savedDataObject.forms[action.activeFormUuid],
|
|
45
|
+
// if we receive activePatientUuid from a query parameter use that one
|
|
46
|
+
...newPatient,
|
|
47
|
+
patientUuids:
|
|
48
|
+
savedDataObject.forms[action.activeFormUuid]?.patientUuids ||
|
|
49
|
+
initialFormState.patientUuids,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
if (
|
|
54
|
+
action.newPatientUuid &&
|
|
55
|
+
!newState.forms[action.activeFormUuid].patientUuids.includes(
|
|
56
|
+
action.newPatientUuid
|
|
57
|
+
)
|
|
58
|
+
) {
|
|
59
|
+
newState.forms[action.activeFormUuid].patientUuids.push(
|
|
60
|
+
action.newPatientUuid
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// no localStorage data, or we should void it
|
|
65
|
+
newState = {
|
|
66
|
+
...initialWorkflowState,
|
|
67
|
+
_storageVersion: fdeWorkflowStorageVersion,
|
|
68
|
+
forms: {
|
|
69
|
+
[action.activeFormUuid]: initialFormState,
|
|
70
|
+
},
|
|
71
|
+
activeFormUuid: action.activeFormUuid,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
persistData(newState);
|
|
75
|
+
return { ...newState };
|
|
76
|
+
}
|
|
77
|
+
case "ADD_PATIENT": {
|
|
78
|
+
const newState = {
|
|
14
79
|
...state,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
80
|
+
forms: {
|
|
81
|
+
...state.forms,
|
|
82
|
+
[state.activeFormUuid]: {
|
|
83
|
+
...state.forms[state.activeFormUuid],
|
|
84
|
+
patientUuids: [
|
|
85
|
+
...state.forms[state.activeFormUuid].patientUuids,
|
|
86
|
+
action.patientUuid,
|
|
87
|
+
],
|
|
88
|
+
activePatientUuid: action.patientUuid,
|
|
89
|
+
activeEncounterUuid: null,
|
|
90
|
+
workflowState: "EDIT_FORM",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
18
93
|
};
|
|
19
|
-
|
|
20
|
-
return
|
|
94
|
+
persistData(newState);
|
|
95
|
+
return newState;
|
|
96
|
+
}
|
|
97
|
+
case "OPEN_PATIENT_SEARCH": {
|
|
98
|
+
const newState = {
|
|
21
99
|
...state,
|
|
22
|
-
|
|
23
|
-
...state.
|
|
24
|
-
[state.
|
|
100
|
+
forms: {
|
|
101
|
+
...state.forms,
|
|
102
|
+
[state.activeFormUuid]: {
|
|
103
|
+
...state.forms[state.activeFormUuid],
|
|
104
|
+
activePatientUuid: null,
|
|
105
|
+
activeEncounterUuid: null,
|
|
106
|
+
workflowState: "NEW_PATIENT",
|
|
107
|
+
},
|
|
25
108
|
},
|
|
26
|
-
activePatientUuid: null,
|
|
27
|
-
activeEncounterUuid: null,
|
|
28
|
-
workflowState:
|
|
29
|
-
state.workflowState === "SUBMIT_FOR_NEXT"
|
|
30
|
-
? "NEW_PATIENT"
|
|
31
|
-
: state.workflowState === "SUBMIT_FOR_REVIEW"
|
|
32
|
-
? "REVIEW"
|
|
33
|
-
: state.workflowState,
|
|
34
109
|
};
|
|
35
|
-
|
|
36
|
-
|
|
110
|
+
// the persist here is optional...
|
|
111
|
+
persistData(newState);
|
|
112
|
+
return newState;
|
|
113
|
+
}
|
|
114
|
+
case "SAVE_ENCOUNTER": {
|
|
115
|
+
if (
|
|
116
|
+
state.forms[state.activeFormUuid].workflowState ===
|
|
117
|
+
"SUBMIT_FOR_COMPLETE"
|
|
118
|
+
) {
|
|
119
|
+
const { [state.activeFormUuid]: activeForm, ...formRest } = state.forms;
|
|
120
|
+
const newState = {
|
|
121
|
+
...state,
|
|
122
|
+
forms: formRest,
|
|
123
|
+
activeFormUuid: null,
|
|
124
|
+
};
|
|
125
|
+
persistData(newState);
|
|
126
|
+
// eslint-disable-next-line
|
|
127
|
+
navigate({ to: "${openmrsSpaBase}/forms" });
|
|
128
|
+
return newState;
|
|
129
|
+
} else {
|
|
130
|
+
const newState = {
|
|
131
|
+
...state,
|
|
132
|
+
forms: {
|
|
133
|
+
...state.forms,
|
|
134
|
+
[state.activeFormUuid]: {
|
|
135
|
+
...state.forms[state.activeFormUuid],
|
|
136
|
+
encounters: {
|
|
137
|
+
...state.forms[state.activeFormUuid].encounters,
|
|
138
|
+
[state.forms[state.activeFormUuid].activePatientUuid]:
|
|
139
|
+
action.encounterUuid,
|
|
140
|
+
},
|
|
141
|
+
activePatientUuid: null,
|
|
142
|
+
activeEncounterUuid: null,
|
|
143
|
+
workflowState:
|
|
144
|
+
state.forms[state.activeFormUuid].workflowState ===
|
|
145
|
+
"SUBMIT_FOR_NEXT"
|
|
146
|
+
? "NEW_PATIENT"
|
|
147
|
+
: state.forms[state.activeFormUuid].workflowState ===
|
|
148
|
+
"SUBMIT_FOR_REVIEW"
|
|
149
|
+
? "REVIEW"
|
|
150
|
+
: state.forms[state.activeFormUuid].workflowState,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
persistData(newState);
|
|
155
|
+
return newState;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
case "EDIT_ENCOUNTER": {
|
|
159
|
+
const newState = {
|
|
37
160
|
...state,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
161
|
+
forms: {
|
|
162
|
+
...state.forms,
|
|
163
|
+
[state.activeFormUuid]: {
|
|
164
|
+
...state.forms[state.activeFormUuid],
|
|
165
|
+
activeEncounterUuid:
|
|
166
|
+
state.forms[state.activeFormUuid].encounters[action.patientUuid],
|
|
167
|
+
activePatientUuid: action.patientUuid,
|
|
168
|
+
workflowState: "EDIT_FORM",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
41
171
|
};
|
|
172
|
+
persistData(newState);
|
|
173
|
+
return newState;
|
|
174
|
+
}
|
|
42
175
|
case "SUBMIT_FOR_NEXT":
|
|
176
|
+
// this state should not be persisted
|
|
43
177
|
window.dispatchEvent(
|
|
44
178
|
new CustomEvent("ampath-form-action", {
|
|
45
|
-
detail: {
|
|
179
|
+
detail: {
|
|
180
|
+
formUuid: state.activeFormUuid,
|
|
181
|
+
patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
|
|
182
|
+
action: "onSubmit",
|
|
183
|
+
},
|
|
46
184
|
})
|
|
47
185
|
);
|
|
48
186
|
return {
|
|
49
187
|
...state,
|
|
50
|
-
|
|
188
|
+
forms: {
|
|
189
|
+
...state.forms,
|
|
190
|
+
[state.activeFormUuid]: {
|
|
191
|
+
...state.forms[state.activeFormUuid],
|
|
192
|
+
workflowState: "SUBMIT_FOR_NEXT",
|
|
193
|
+
},
|
|
194
|
+
},
|
|
51
195
|
};
|
|
52
196
|
case "SUBMIT_FOR_REVIEW":
|
|
197
|
+
// this state should not be persisted
|
|
53
198
|
window.dispatchEvent(
|
|
54
199
|
new CustomEvent("ampath-form-action", {
|
|
55
|
-
detail: {
|
|
200
|
+
detail: {
|
|
201
|
+
formUuid: state.activeFormUuid,
|
|
202
|
+
patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
|
|
203
|
+
action: "onSubmit",
|
|
204
|
+
},
|
|
56
205
|
})
|
|
57
206
|
);
|
|
58
207
|
return {
|
|
59
208
|
...state,
|
|
60
|
-
|
|
209
|
+
forms: {
|
|
210
|
+
...state.forms,
|
|
211
|
+
[state.activeFormUuid]: {
|
|
212
|
+
...state.forms[state.activeFormUuid],
|
|
213
|
+
workflowState: "SUBMIT_FOR_REVIEW",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
61
216
|
};
|
|
62
|
-
case "
|
|
217
|
+
case "SUBMIT_FOR_COMPLETE":
|
|
218
|
+
// this state should not be persisted
|
|
219
|
+
window.dispatchEvent(
|
|
220
|
+
new CustomEvent("ampath-form-action", {
|
|
221
|
+
detail: {
|
|
222
|
+
formUuid: state.activeFormUuid,
|
|
223
|
+
patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
|
|
224
|
+
action: "onSubmit",
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
);
|
|
63
228
|
return {
|
|
64
229
|
...state,
|
|
65
|
-
|
|
230
|
+
forms: {
|
|
231
|
+
...state.forms,
|
|
232
|
+
[state.activeFormUuid]: {
|
|
233
|
+
...state.forms[state.activeFormUuid],
|
|
234
|
+
workflowState: "SUBMIT_FOR_COMPLETE",
|
|
235
|
+
},
|
|
236
|
+
},
|
|
66
237
|
};
|
|
67
|
-
case "GO_TO_REVIEW":
|
|
68
|
-
|
|
238
|
+
case "GO_TO_REVIEW": {
|
|
239
|
+
const newState = {
|
|
240
|
+
...state,
|
|
241
|
+
forms: {
|
|
242
|
+
...state.forms,
|
|
243
|
+
[state.activeFormUuid]: {
|
|
244
|
+
...state.forms[state.activeFormUuid],
|
|
245
|
+
activeEncounterUuid: null,
|
|
246
|
+
activePatientUuid: null,
|
|
247
|
+
workflowState: "REVIEW",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
persistData(newState);
|
|
252
|
+
return newState;
|
|
253
|
+
}
|
|
254
|
+
case "DESTROY_SESSION": {
|
|
255
|
+
const { [state.activeFormUuid]: activeForm, ...formRest } = state.forms;
|
|
256
|
+
const newState = {
|
|
257
|
+
...state,
|
|
258
|
+
forms: formRest,
|
|
259
|
+
activeFormUuid: null,
|
|
260
|
+
};
|
|
261
|
+
persistData(newState);
|
|
262
|
+
return newState;
|
|
263
|
+
}
|
|
264
|
+
case "CLOSE_SESSION": {
|
|
265
|
+
const newState = {
|
|
69
266
|
...state,
|
|
70
|
-
|
|
71
|
-
activePatientUuid: null,
|
|
72
|
-
workflowState: "REVIEW",
|
|
267
|
+
activeFormUuid: null,
|
|
73
268
|
};
|
|
269
|
+
persistData(newState);
|
|
270
|
+
return newState;
|
|
271
|
+
}
|
|
74
272
|
default:
|
|
75
273
|
return state;
|
|
76
274
|
}
|
|
@@ -3,8 +3,14 @@ import {
|
|
|
3
3
|
getGlobalStore,
|
|
4
4
|
useStore,
|
|
5
5
|
} from "@openmrs/esm-framework";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
Button,
|
|
8
|
+
ComposedModal,
|
|
9
|
+
ModalBody,
|
|
10
|
+
ModalFooter,
|
|
11
|
+
ModalHeader,
|
|
12
|
+
} from "carbon-components-react";
|
|
13
|
+
import React, { useContext, useState } from "react";
|
|
8
14
|
import { useHistory } from "react-router-dom";
|
|
9
15
|
import FormBootstrap from "../FormBootstrap";
|
|
10
16
|
import PatientCard from "../patient-card/PatientCard";
|
|
@@ -19,48 +25,116 @@ import WorkflowReview from "../workflow-review";
|
|
|
19
25
|
|
|
20
26
|
const formStore = getGlobalStore("ampath-form-state");
|
|
21
27
|
|
|
22
|
-
const
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
submitForReview,
|
|
26
|
-
submitForNext,
|
|
27
|
-
workflowState,
|
|
28
|
-
goToReview,
|
|
29
|
-
} = useContext(FormWorkflowContext);
|
|
28
|
+
const CancelModal = ({ open, setOpen }) => {
|
|
29
|
+
const { destroySession, closeSession } = useContext(FormWorkflowContext);
|
|
30
|
+
const { t } = useTranslation();
|
|
30
31
|
const history = useHistory();
|
|
32
|
+
|
|
33
|
+
const discard = () => {
|
|
34
|
+
destroySession();
|
|
35
|
+
setOpen(false);
|
|
36
|
+
history.push("/");
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const saveAndClose = () => {
|
|
40
|
+
closeSession();
|
|
41
|
+
setOpen(false);
|
|
42
|
+
history.push("/");
|
|
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
|
+
};
|
|
98
|
+
|
|
99
|
+
const WorkflowNavigationButtons = () => {
|
|
100
|
+
const { activeFormUuid, submitForNext, workflowState, destroySession } =
|
|
101
|
+
useContext(FormWorkflowContext);
|
|
31
102
|
const store = useStore(formStore);
|
|
32
|
-
const formState = store[
|
|
103
|
+
const formState = store[activeFormUuid];
|
|
33
104
|
const navigationDisabled = formState !== "ready";
|
|
105
|
+
const [cancelModalOpen, setCancelModalOpen] = useState(false);
|
|
106
|
+
const [completeModalOpen, setCompleteModalOpen] = useState(false);
|
|
34
107
|
const { t } = useTranslation();
|
|
35
108
|
|
|
109
|
+
if (!workflowState) return null;
|
|
110
|
+
|
|
36
111
|
return (
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</div>
|
|
112
|
+
<>
|
|
113
|
+
<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
|
+
<Button
|
|
122
|
+
kind="secondary"
|
|
123
|
+
onClick={
|
|
124
|
+
workflowState === "NEW_PATIENT"
|
|
125
|
+
? () => destroySession()
|
|
126
|
+
: () => setCompleteModalOpen(true)
|
|
127
|
+
}
|
|
128
|
+
>
|
|
129
|
+
{t("saveAndComplete", "Save & Complete")}
|
|
130
|
+
</Button>
|
|
131
|
+
<Button kind="tertiary" onClick={() => setCancelModalOpen(true)}>
|
|
132
|
+
{t("cancel", "Cancel")}
|
|
133
|
+
</Button>
|
|
134
|
+
</div>
|
|
135
|
+
<CancelModal open={cancelModalOpen} setOpen={setCancelModalOpen} />
|
|
136
|
+
<CompleteModal open={completeModalOpen} setOpen={setCompleteModalOpen} />
|
|
137
|
+
</>
|
|
64
138
|
);
|
|
65
139
|
};
|
|
66
140
|
|
|
@@ -70,7 +144,7 @@ const FormWorkspace = () => {
|
|
|
70
144
|
activePatientUuid,
|
|
71
145
|
activeEncounterUuid,
|
|
72
146
|
saveEncounter,
|
|
73
|
-
|
|
147
|
+
activeFormUuid,
|
|
74
148
|
} = useContext(FormWorkflowContext);
|
|
75
149
|
const { t } = useTranslation();
|
|
76
150
|
|
|
@@ -94,7 +168,7 @@ const FormWorkspace = () => {
|
|
|
94
168
|
patientUuid={activePatientUuid}
|
|
95
169
|
encounterUuid={activeEncounterUuid}
|
|
96
170
|
{...{
|
|
97
|
-
formUuid,
|
|
171
|
+
formUuid: activeFormUuid,
|
|
98
172
|
handlePostResponse,
|
|
99
173
|
}}
|
|
100
174
|
/>
|
|
@@ -6,6 +6,10 @@ import { useGetAllForms } from "../hooks";
|
|
|
6
6
|
import FormsTable from "../forms-table";
|
|
7
7
|
import styles from "./styles.scss";
|
|
8
8
|
import { useTranslation } from "react-i18next";
|
|
9
|
+
import {
|
|
10
|
+
fdeWorkflowStorageName,
|
|
11
|
+
fdeWorkflowStorageVersion,
|
|
12
|
+
} from "../context/FormWorkflowReducer";
|
|
9
13
|
|
|
10
14
|
// helper function useful for debugging
|
|
11
15
|
// given a list of forms, it will organize into permissions
|
|
@@ -41,6 +45,18 @@ const FormsPage = () => {
|
|
|
41
45
|
const { formCategories, formCategoriesToShow } = config;
|
|
42
46
|
const { forms, isLoading, error } = useGetAllForms();
|
|
43
47
|
const cleanRows = prepareRowsForTable(forms);
|
|
48
|
+
const savedData = localStorage.getItem(fdeWorkflowStorageName);
|
|
49
|
+
const activeForms = [];
|
|
50
|
+
if (
|
|
51
|
+
savedData &&
|
|
52
|
+
JSON.parse(savedData)?.["_storageVersion"] === fdeWorkflowStorageVersion
|
|
53
|
+
) {
|
|
54
|
+
Object.entries(JSON.parse(savedData).forms).forEach(
|
|
55
|
+
([formUuid, form]: [string, { [key: string]: unknown }]) => {
|
|
56
|
+
if (form.workflowState) activeForms.push(formUuid);
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
44
60
|
|
|
45
61
|
const categoryRows = formCategoriesToShow.map((name) => {
|
|
46
62
|
const category = formCategories.find((category) => category.name === name);
|
|
@@ -61,11 +77,14 @@ const FormsPage = () => {
|
|
|
61
77
|
cleanRows ? cleanRows?.length : "??"
|
|
62
78
|
})`}
|
|
63
79
|
>
|
|
64
|
-
<FormsTable rows={cleanRows} {...{ error, isLoading }} />
|
|
80
|
+
<FormsTable rows={cleanRows} {...{ error, isLoading, activeForms }} />
|
|
65
81
|
</Tab>
|
|
66
82
|
{categoryRows?.map((category, index) => (
|
|
67
83
|
<Tab label={`${category.name} (${category.rows.length})`} key={index}>
|
|
68
|
-
<FormsTable
|
|
84
|
+
<FormsTable
|
|
85
|
+
rows={category.rows}
|
|
86
|
+
{...{ error, isLoading, activeForms }}
|
|
87
|
+
/>
|
|
69
88
|
</Tab>
|
|
70
89
|
))}
|
|
71
90
|
</Tabs>
|
|
@@ -19,7 +19,7 @@ import { Link } from "react-router-dom";
|
|
|
19
19
|
import EmptyState from "../empty-state/EmptyState";
|
|
20
20
|
import styles from "./styles.scss";
|
|
21
21
|
|
|
22
|
-
const FormsTable = ({ rows, error, isLoading }) => {
|
|
22
|
+
const FormsTable = ({ rows, error, isLoading, activeForms }) => {
|
|
23
23
|
const { t } = useTranslation();
|
|
24
24
|
|
|
25
25
|
const formsHeader = [
|
|
@@ -39,7 +39,13 @@ const FormsTable = ({ rows, error, isLoading }) => {
|
|
|
39
39
|
|
|
40
40
|
const augmenteRows = rows?.map((row) => ({
|
|
41
41
|
...row,
|
|
42
|
-
actions:
|
|
42
|
+
actions: (
|
|
43
|
+
<Link to={row.uuid}>
|
|
44
|
+
{activeForms.includes(row.uuid)
|
|
45
|
+
? t("resumeSession", "Resume Session")
|
|
46
|
+
: t("fillForm", "Fill Form")}
|
|
47
|
+
</Link>
|
|
48
|
+
),
|
|
43
49
|
actions2: (
|
|
44
50
|
<Link to="#" className={styles.inactiveLink}>
|
|
45
51
|
{t("startGroupSession", "Start Group Session")}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CheckmarkOutline16, WarningAlt16 } from "@carbon/icons-react";
|
|
1
2
|
import { SkeletonText } from "carbon-components-react";
|
|
2
3
|
import React, { useContext } from "react";
|
|
3
4
|
import FormWorkflowContext from "../context/FormWorkflowContext";
|
|
@@ -18,7 +19,8 @@ const CardContainer = ({ onClick = () => undefined, active, children }) => {
|
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const PatientCard = ({ patientUuid }) => {
|
|
21
|
-
const { activePatientUuid, editEncounter } =
|
|
22
|
+
const { activePatientUuid, editEncounter, encounters } =
|
|
23
|
+
useContext(FormWorkflowContext);
|
|
22
24
|
const patient = useGetPatient(patientUuid);
|
|
23
25
|
const givenName = patient?.name?.[0]?.given?.[0];
|
|
24
26
|
const familyName = patient?.name?.[0]?.family;
|
|
@@ -39,13 +41,22 @@ const PatientCard = ({ patientUuid }) => {
|
|
|
39
41
|
onClick={active ? () => undefined : () => editEncounter(patientUuid)}
|
|
40
42
|
active={active}
|
|
41
43
|
>
|
|
42
|
-
<div className={styles.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
<div className={styles.patientInfo}>
|
|
45
|
+
<div className={styles.identifier}>{identifier}</div>
|
|
46
|
+
<div
|
|
47
|
+
className={`${styles.displayName} ${
|
|
48
|
+
active && styles.activeDisplayName
|
|
49
|
+
}`}
|
|
50
|
+
>
|
|
51
|
+
{givenName} {familyName}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div>
|
|
55
|
+
{patientUuid in encounters ? (
|
|
56
|
+
<CheckmarkOutline16 className={styles.statusSuccess} />
|
|
57
|
+
) : (
|
|
58
|
+
<WarningAlt16 className={styles.statusWarning} />
|
|
59
|
+
)}
|
|
49
60
|
</div>
|
|
50
61
|
</CardContainer>
|
|
51
62
|
);
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
.cardContainer {
|
|
6
6
|
padding: $spacing-05;
|
|
7
|
+
display: flex;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
.skeletonText {
|
|
@@ -29,3 +30,15 @@
|
|
|
29
30
|
background-color: $carbon--gray-40;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
33
|
+
|
|
34
|
+
.patientInfo {
|
|
35
|
+
flex-grow: 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.statusSuccess {
|
|
39
|
+
fill: $support-02;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.statusWarning {
|
|
43
|
+
fill: $support-03;
|
|
44
|
+
}
|