@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.1-pre.8
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/.eslintrc.js +10 -0
- package/.husky/pre-push +1 -6
- package/.yarn/plugins/@yarnpkg/plugin-version.cjs +550 -0
- package/.yarn/versions/7ee3eceb.yml +0 -0
- package/README.md +39 -12
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/docs/config-icrc-forms.png +0 -0
- package/docs/config-other-forms.png +0 -0
- package/docs/configuring-form-categories.md +77 -0
- package/docs/fde-workflow.mov +0 -0
- package/docs/form-workflow-state-diagram.png +0 -0
- package/jest.config.json +20 -18
- package/package.json +97 -106
- package/src/FormBootstrap.tsx +151 -0
- package/src/Root.tsx +14 -3
- package/src/add-group-modal/AddGroupModal.tsx +209 -0
- package/src/add-group-modal/styles.scss +35 -0
- package/src/config-schema.ts +63 -31
- package/src/context/FormWorkflowContext.tsx +114 -0
- package/src/context/FormWorkflowReducer.ts +277 -0
- package/src/context/GroupFormWorkflowContext.tsx +141 -0
- package/src/context/GroupFormWorkflowReducer.ts +272 -0
- package/src/empty-state/EmptyDataIllustration.tsx +51 -0
- package/src/empty-state/EmptyState.tsx +33 -0
- package/src/empty-state/styles.scss +55 -0
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +230 -0
- package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +50 -0
- package/src/form-entry-workflow/form-review-card/index.ts +3 -0
- package/src/form-entry-workflow/form-review-card/styles.scss +39 -0
- package/src/form-entry-workflow/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
- package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +86 -0
- package/src/form-entry-workflow/patient-banner/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/styles.scss +45 -0
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +63 -0
- package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
- package/src/form-entry-workflow/patient-search-header/styles.scss +22 -0
- package/src/form-entry-workflow/styles.scss +64 -0
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +35 -0
- package/src/form-entry-workflow/workflow-review/index.ts +3 -0
- package/src/form-entry-workflow/workflow-review/styles.scss +34 -0
- package/src/forms-app-menu-link.tsx +3 -2
- package/src/forms-page/FormsPage.tsx +129 -0
- package/src/forms-page/forms-table/FormsTable.tsx +131 -0
- package/src/forms-page/forms-table/index.ts +3 -0
- package/src/forms-page/forms-table/styles.scss +20 -0
- package/src/forms-page/index.ts +3 -0
- package/src/forms-page/styles.scss +11 -0
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +413 -0
- package/src/group-form-entry-workflow/group-banner/GroupBanner.test.tsx +9 -0
- package/src/group-form-entry-workflow/group-banner/GroupBanner.tsx +45 -0
- package/src/group-form-entry-workflow/group-banner/index.ts +3 -0
- package/src/group-form-entry-workflow/group-banner/styles.scss +60 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +106 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +63 -0
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +93 -0
- package/src/group-form-entry-workflow/group-search/compact-group-result.scss +64 -0
- package/src/group-form-entry-workflow/group-search/compact-group-search.scss +35 -0
- package/src/group-form-entry-workflow/group-search/group-search.scss +94 -0
- package/src/group-form-entry-workflow/group-search/mock-group-data.ts +79 -0
- package/src/group-form-entry-workflow/group-search/useGroupSearch.ts +14 -0
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +42 -0
- package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
- package/src/group-form-entry-workflow/index.ts +3 -0
- package/src/group-form-entry-workflow/styles.scss +86 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useFormState.ts +23 -0
- package/src/hooks/useGetAllForms.ts +45 -0
- package/src/hooks/useGetEncounter.ts +21 -0
- package/src/hooks/useGetPatient.ts +23 -0
- package/src/hooks/useKeyPress.ts +31 -0
- package/src/hooks/usePostCohort.ts +18 -0
- package/src/index.ts +20 -4
- package/src/patient-card/PatientCard.tsx +67 -0
- package/src/patient-card/index.ts +3 -0
- package/src/patient-card/styles.scss +45 -0
- package/translations/en.json +49 -4
- package/tsconfig.json +26 -23
- package/.eslintrc +0 -4
- package/.github/workflows/node.js.yml +0 -79
- package/.husky/pre-commit +0 -6
- package/dist/24.js +0 -3
- package/dist/24.js.LICENSE.txt +0 -16
- package/dist/24.js.map +0 -1
- package/dist/294.js +0 -3
- package/dist/294.js.LICENSE.txt +0 -14
- package/dist/294.js.map +0 -1
- package/dist/296.js +0 -2
- package/dist/296.js.map +0 -1
- package/dist/299.js +0 -2
- package/dist/299.js.map +0 -1
- package/dist/382.js +0 -3
- package/dist/382.js.LICENSE.txt +0 -8
- package/dist/382.js.map +0 -1
- package/dist/415.js +0 -2
- package/dist/415.js.map +0 -1
- package/dist/574.js +0 -1
- package/dist/595.js +0 -3
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/595.js.map +0 -1
- package/dist/69.js +0 -2
- package/dist/69.js.map +0 -1
- package/dist/735.js +0 -3
- package/dist/735.js.LICENSE.txt +0 -29
- package/dist/735.js.map +0 -1
- package/dist/777.js +0 -2
- package/dist/777.js.map +0 -1
- package/dist/860.js +0 -2
- package/dist/860.js.map +0 -1
- package/dist/906.js +0 -2
- package/dist/906.js.map +0 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +0 -369
- package/dist/openmrs-esm-fast-data-entry-app.js.map +0 -1
- package/dist/openmrs-esm-fast-data-entry-app.old +0 -2
- package/src/boxes/extensions/blue-box.tsx +0 -15
- package/src/boxes/extensions/box.scss +0 -23
- package/src/boxes/extensions/brand-box.tsx +0 -15
- package/src/boxes/extensions/red-box.tsx +0 -15
- package/src/boxes/slot/boxes.css +0 -23
- package/src/boxes/slot/boxes.tsx +0 -19
- package/src/forms/FormsRoot.tsx +0 -32
- package/src/forms/FormsTable.tsx +0 -64
- package/src/forms/mockData.ts +0 -43
- package/src/greeter/greeter.css +0 -4
- package/src/greeter/greeter.test.tsx +0 -29
- package/src/greeter/greeter.tsx +0 -25
- package/src/hello.css +0 -3
- package/src/hello.test.tsx +0 -45
- package/src/hello.tsx +0 -30
- package/src/patient-getter/patient-getter.resource.ts +0 -31
- package/src/patient-getter/patient-getter.test.tsx +0 -28
- package/src/patient-getter/patient-getter.tsx +0 -28
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React, { useCallback, useContext, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
ComposedModal,
|
|
4
|
+
Button,
|
|
5
|
+
ModalHeader,
|
|
6
|
+
ModalFooter,
|
|
7
|
+
ModalBody,
|
|
8
|
+
TextInput,
|
|
9
|
+
FormLabel,
|
|
10
|
+
} from "@carbon/react";
|
|
11
|
+
import { Add, Close } from "@carbon/react/icons";
|
|
12
|
+
import { useTranslation } from "react-i18next";
|
|
13
|
+
import { ExtensionSlot } from "@openmrs/esm-framework";
|
|
14
|
+
import styles from "./styles.scss";
|
|
15
|
+
import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
|
|
16
|
+
|
|
17
|
+
const MemExtension = React.memo(ExtensionSlot);
|
|
18
|
+
|
|
19
|
+
const PatientRow = ({ patient, removePatient }) => {
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
return (
|
|
22
|
+
<li key={patient.uuid} className={styles.patientRow}>
|
|
23
|
+
<span className={styles.patientName}>{patient?.display}</span>
|
|
24
|
+
<span>
|
|
25
|
+
<Button
|
|
26
|
+
kind="tertiary"
|
|
27
|
+
size="sm"
|
|
28
|
+
onClick={() => removePatient(patient.uuid)}
|
|
29
|
+
renderIcon={Close}
|
|
30
|
+
tooltipPosition="right"
|
|
31
|
+
>
|
|
32
|
+
{t("remove", "Remove")}
|
|
33
|
+
</Button>
|
|
34
|
+
</span>
|
|
35
|
+
</li>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const NewGroupForm = (props) => {
|
|
40
|
+
const {
|
|
41
|
+
name,
|
|
42
|
+
setName,
|
|
43
|
+
patientList,
|
|
44
|
+
updatePatientList,
|
|
45
|
+
errors,
|
|
46
|
+
validate,
|
|
47
|
+
removePatient,
|
|
48
|
+
} = props;
|
|
49
|
+
const { t } = useTranslation();
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
style={{
|
|
54
|
+
display: "flex",
|
|
55
|
+
flexDirection: "column",
|
|
56
|
+
rowGap: "1rem",
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<TextInput
|
|
60
|
+
labelText={t("newGroupName", "New Group Name")}
|
|
61
|
+
value={name}
|
|
62
|
+
onChange={(e) => setName(e.target.value)}
|
|
63
|
+
onBlur={() => validate("name")}
|
|
64
|
+
/>
|
|
65
|
+
{errors?.name && (
|
|
66
|
+
<p className={styles.formError}>
|
|
67
|
+
{t("groupNameError", "Please enter a group name.")}
|
|
68
|
+
</p>
|
|
69
|
+
)}
|
|
70
|
+
<FormLabel>Patients in group</FormLabel>
|
|
71
|
+
{errors?.patientList && (
|
|
72
|
+
<p className={styles.formError}>
|
|
73
|
+
{t("noPatientError", "Please enter at least one patient.")}
|
|
74
|
+
</p>
|
|
75
|
+
)}
|
|
76
|
+
{!errors?.patientList && (
|
|
77
|
+
<ul>
|
|
78
|
+
{patientList?.map((patient, index) => (
|
|
79
|
+
<PatientRow
|
|
80
|
+
patient={patient}
|
|
81
|
+
removePatient={removePatient}
|
|
82
|
+
key={index}
|
|
83
|
+
/>
|
|
84
|
+
))}
|
|
85
|
+
</ul>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
<FormLabel>Search for patients to add to group</FormLabel>
|
|
89
|
+
<div className={styles.searchBar}>
|
|
90
|
+
<MemExtension
|
|
91
|
+
extensionSlotName="patient-search-bar-slot"
|
|
92
|
+
state={{
|
|
93
|
+
selectPatientAction: updatePatientList,
|
|
94
|
+
buttonProps: {
|
|
95
|
+
kind: "primary",
|
|
96
|
+
},
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const AddGroupModal = () => {
|
|
105
|
+
const { setGroup } = useContext(GroupFormWorkflowContext);
|
|
106
|
+
const { t } = useTranslation();
|
|
107
|
+
const [open, setOpen] = useState(false);
|
|
108
|
+
const [errors, setErrors] = useState({});
|
|
109
|
+
const [name, setName] = useState("");
|
|
110
|
+
const [patientList, setPatientList] = useState([]);
|
|
111
|
+
|
|
112
|
+
const handleCancel = () => {
|
|
113
|
+
setOpen(false);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const removePatient = useCallback(
|
|
117
|
+
(patientUuid: string) =>
|
|
118
|
+
setPatientList((patientList) =>
|
|
119
|
+
patientList.filter((patient) => patient.uuid !== patientUuid)
|
|
120
|
+
),
|
|
121
|
+
[setPatientList]
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const validate = useCallback(
|
|
125
|
+
(field?: string | undefined) => {
|
|
126
|
+
let valid = true;
|
|
127
|
+
if (field) {
|
|
128
|
+
valid = field === "name" ? !!name : !!patientList.length;
|
|
129
|
+
setErrors((errors) => ({
|
|
130
|
+
...errors,
|
|
131
|
+
[field]: valid ? null : "required",
|
|
132
|
+
}));
|
|
133
|
+
} else {
|
|
134
|
+
if (!name) {
|
|
135
|
+
setErrors((errors) => ({ ...errors, name: "required" }));
|
|
136
|
+
valid = false;
|
|
137
|
+
} else {
|
|
138
|
+
setErrors((errors) => ({ ...errors, name: null }));
|
|
139
|
+
}
|
|
140
|
+
if (!patientList.length) {
|
|
141
|
+
setErrors((errors) => ({ ...errors, patientList: "required" }));
|
|
142
|
+
valid = false;
|
|
143
|
+
} else {
|
|
144
|
+
setErrors((errors) => ({ ...errors, patientList: null }));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return valid;
|
|
148
|
+
},
|
|
149
|
+
[name, patientList.length]
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const updatePatientList = useCallback(
|
|
153
|
+
(patient) => {
|
|
154
|
+
setPatientList((patientList) => {
|
|
155
|
+
if (!patientList.find((p) => p.uuid === patient.uuid)) {
|
|
156
|
+
return [...patientList, patient];
|
|
157
|
+
} else {
|
|
158
|
+
return patientList;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
setErrors((errors) => ({ ...errors, patientList: null }));
|
|
162
|
+
},
|
|
163
|
+
[setPatientList]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const handleSubmit = () => {
|
|
167
|
+
if (validate()) {
|
|
168
|
+
setGroup({ id: "1234", name: name, members: patientList });
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div className={styles.modal}>
|
|
174
|
+
<Button
|
|
175
|
+
onClick={() => setOpen(true)}
|
|
176
|
+
renderIcon={Add}
|
|
177
|
+
iconDescription="Add"
|
|
178
|
+
>
|
|
179
|
+
{t("createNewGroup", "Create New Group")}
|
|
180
|
+
</Button>
|
|
181
|
+
<ComposedModal open={open} onClose={() => setOpen(false)}>
|
|
182
|
+
<ModalHeader>{t("createNewGroup", "Create New Group")}</ModalHeader>
|
|
183
|
+
<ModalBody>
|
|
184
|
+
<NewGroupForm
|
|
185
|
+
{...{
|
|
186
|
+
name,
|
|
187
|
+
setName,
|
|
188
|
+
patientList,
|
|
189
|
+
updatePatientList,
|
|
190
|
+
errors,
|
|
191
|
+
validate,
|
|
192
|
+
removePatient,
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
</ModalBody>
|
|
196
|
+
<ModalFooter>
|
|
197
|
+
<Button kind="secondary" onClick={handleCancel}>
|
|
198
|
+
{t("cancel", "Cancel")}
|
|
199
|
+
</Button>
|
|
200
|
+
<Button kind="primary" onClick={handleSubmit}>
|
|
201
|
+
{t("createGroup", "Create Group")}
|
|
202
|
+
</Button>
|
|
203
|
+
</ModalFooter>
|
|
204
|
+
</ComposedModal>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default AddGroupModal;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/colors';
|
|
3
|
+
|
|
4
|
+
.modal {
|
|
5
|
+
:global(.cds--modal) {
|
|
6
|
+
z-index: 90;
|
|
7
|
+
}
|
|
8
|
+
:global(.cds--modal-container) {
|
|
9
|
+
height: 600px;
|
|
10
|
+
}
|
|
11
|
+
:global(.cds--modal-content) {
|
|
12
|
+
height: 100%;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.searchBar > div > div > div {
|
|
17
|
+
width: 100%
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.formError {
|
|
21
|
+
color: red;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.patientRow {
|
|
25
|
+
display: flex;
|
|
26
|
+
width: "100%";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.patientName {
|
|
30
|
+
flex-grow: 1;
|
|
31
|
+
padding: spacing.$spacing-02;
|
|
32
|
+
&:hover {
|
|
33
|
+
background-color: colors.$gray-20;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/config-schema.ts
CHANGED
|
@@ -1,46 +1,78 @@
|
|
|
1
|
-
import { Type
|
|
1
|
+
import { Type } from "@openmrs/esm-framework";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* This is the config schema.
|
|
5
|
-
* looks like this:
|
|
4
|
+
* This is the config schema.
|
|
6
5
|
*
|
|
7
|
-
* ```json
|
|
8
|
-
* { "casualGreeting": true, "whoToGreet": ["Mom"] }
|
|
9
|
-
* ```
|
|
10
|
-
*
|
|
11
|
-
* In OpenMRS Microfrontends, all config parameters are optional. Thus,
|
|
12
|
-
* all elements must have a reasonable default. A good default is one
|
|
13
|
-
* that works well with the reference application.
|
|
14
|
-
*
|
|
15
|
-
* To understand the schema below, please read the configuration system
|
|
16
|
-
* documentation:
|
|
17
|
-
* https://openmrs.github.io/openmrs-esm-core/#/main/config
|
|
18
|
-
* Note especially the section "How do I make my module configurable?"
|
|
19
|
-
* https://openmrs.github.io/openmrs-esm-core/#/main/config?id=im-developing-an-esm-module-how-do-i-make-it-configurable
|
|
20
|
-
* and the Schema Reference
|
|
21
|
-
* https://openmrs.github.io/openmrs-esm-core/#/main/config?id=schema-reference
|
|
22
6
|
*/
|
|
7
|
+
|
|
23
8
|
export const configSchema = {
|
|
24
|
-
|
|
25
|
-
_type: Type.Boolean,
|
|
26
|
-
_default: false,
|
|
27
|
-
_description: "Whether to use a casual greeting (or a formal one).",
|
|
28
|
-
},
|
|
29
|
-
whoToGreet: {
|
|
9
|
+
formCategories: {
|
|
30
10
|
_type: Type.Array,
|
|
31
|
-
_default: ["World"],
|
|
32
11
|
_description:
|
|
33
|
-
"
|
|
12
|
+
"Organize forms into categories. A form can belong to multiple categories.",
|
|
34
13
|
_elements: {
|
|
35
|
-
|
|
14
|
+
name: {
|
|
15
|
+
_type: Type.String,
|
|
16
|
+
_description: "Category name",
|
|
17
|
+
},
|
|
18
|
+
forms: {
|
|
19
|
+
_type: Type.Array,
|
|
20
|
+
_description: "List of forms for this category.",
|
|
21
|
+
_elements: {
|
|
22
|
+
formUUID: {
|
|
23
|
+
_type: Type.UUID,
|
|
24
|
+
_description: "UUID of form",
|
|
25
|
+
},
|
|
26
|
+
name: {
|
|
27
|
+
_type: Type.String,
|
|
28
|
+
_description: "Name of form",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
36
32
|
},
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
_default: [
|
|
34
|
+
{
|
|
35
|
+
name: "ICRC Forms",
|
|
36
|
+
forms: [
|
|
37
|
+
{
|
|
38
|
+
formUUID: "0cefb866-110c-4f16-af58-560932a1db1f",
|
|
39
|
+
name: "Adult Triage",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "Distress Scales",
|
|
45
|
+
forms: [
|
|
46
|
+
{
|
|
47
|
+
formUUID: "9f26aad4-244a-46ca-be49-1196df1a8c9a",
|
|
48
|
+
name: "POC Sample Form 1",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
39
52
|
],
|
|
40
53
|
},
|
|
54
|
+
formCategoriesToShow: {
|
|
55
|
+
_type: Type.Array,
|
|
56
|
+
_description: "Forms to show by default on the forms app home page.",
|
|
57
|
+
_elements: {
|
|
58
|
+
_type: Type.String,
|
|
59
|
+
_description: "Name of category",
|
|
60
|
+
},
|
|
61
|
+
_default: ["ICRC Forms", "Distress Scales"],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type Form = {
|
|
66
|
+
formUUID: Type.UUID;
|
|
67
|
+
name: Type.String;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type Category = {
|
|
71
|
+
name: string;
|
|
72
|
+
forms: Array<Form>;
|
|
41
73
|
};
|
|
42
74
|
|
|
43
75
|
export type Config = {
|
|
44
|
-
|
|
45
|
-
|
|
76
|
+
formCategories: Array<Category>;
|
|
77
|
+
formCategoriesToShow: Array<string>;
|
|
46
78
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useReducer } from "react";
|
|
2
|
+
import reducer from "./FormWorkflowReducer";
|
|
3
|
+
import { useParams, useLocation } from "react-router-dom";
|
|
4
|
+
interface ParamTypes {
|
|
5
|
+
formUuid?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const initialActions = {
|
|
9
|
+
addPatient: (uuid: string | number) => undefined,
|
|
10
|
+
openPatientSearch: () => undefined,
|
|
11
|
+
saveEncounter: (encounterUuid: string | number) => undefined,
|
|
12
|
+
editEncounter: (patientUuid: string | number) => undefined,
|
|
13
|
+
submitForNext: () => undefined,
|
|
14
|
+
submitForReview: () => undefined,
|
|
15
|
+
submitForComplete: () => undefined,
|
|
16
|
+
goToReview: () => undefined,
|
|
17
|
+
destroySession: () => undefined,
|
|
18
|
+
closeSession: () => undefined,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const initialWorkflowState = {
|
|
22
|
+
// activeFormUuid and forms are the only two real values stored at state root level
|
|
23
|
+
activeFormUuid: null, // the corrently open form
|
|
24
|
+
forms: {}, // object containing all forms session data
|
|
25
|
+
// the following fields will be available in context but are not stored at the
|
|
26
|
+
// state root level, but refer to nested values for the current
|
|
27
|
+
// aciveFormUuid
|
|
28
|
+
workflowState: null, // pseudo field from state[activeFormUuid].workflowState
|
|
29
|
+
activePatientUuid: null, // pseudo field from state[activeFormUuid].activePatientUuid
|
|
30
|
+
activeEncounterUuid: null, // pseudo field from state[activeFormUuid].encounterUuid
|
|
31
|
+
patientUuids: [], // pseudo field from state[activeFormUuid].patientUuids
|
|
32
|
+
encounters: {}, // pseudo field from state[activeFormUuid].encounters
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const FormWorkflowContext = React.createContext({
|
|
36
|
+
...initialWorkflowState,
|
|
37
|
+
...initialActions,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const FormWorkflowProvider = ({ children }) => {
|
|
41
|
+
const { formUuid } = useParams() as ParamTypes;
|
|
42
|
+
const activeFormUuid = formUuid.split("&")[0];
|
|
43
|
+
const { search } = useLocation();
|
|
44
|
+
const newPatientUuid = new URLSearchParams(search).get("patientUuid");
|
|
45
|
+
const [state, dispatch] = useReducer(reducer, {
|
|
46
|
+
...initialWorkflowState,
|
|
47
|
+
...initialActions,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const actions = useMemo(
|
|
51
|
+
() => ({
|
|
52
|
+
initializeWorkflowState: ({ activeFormUuid, newPatientUuid }) =>
|
|
53
|
+
dispatch({
|
|
54
|
+
type: "INITIALIZE_WORKFLOW_STATE",
|
|
55
|
+
activeFormUuid,
|
|
56
|
+
newPatientUuid,
|
|
57
|
+
}),
|
|
58
|
+
addPatient: (patientUuid) =>
|
|
59
|
+
dispatch({ type: "ADD_PATIENT", patientUuid }),
|
|
60
|
+
openPatientSearch: () => dispatch({ type: "OPEN_PATIENT_SEARCH" }),
|
|
61
|
+
saveEncounter: (encounterUuid) =>
|
|
62
|
+
dispatch({
|
|
63
|
+
type: "SAVE_ENCOUNTER",
|
|
64
|
+
encounterUuid,
|
|
65
|
+
}),
|
|
66
|
+
submitForNext: () => dispatch({ type: "SUBMIT_FOR_NEXT" }),
|
|
67
|
+
submitForReview: () => dispatch({ type: "SUBMIT_FOR_REVIEW" }),
|
|
68
|
+
submitForComplete: () => dispatch({ type: "SUBMIT_FOR_COMPLETE" }),
|
|
69
|
+
editEncounter: (patientUuid) =>
|
|
70
|
+
dispatch({ type: "EDIT_ENCOUNTER", patientUuid }),
|
|
71
|
+
goToReview: () => dispatch({ type: "GO_TO_REVIEW" }),
|
|
72
|
+
destroySession: () => dispatch({ type: "DESTROY_SESSION" }),
|
|
73
|
+
closeSession: () => dispatch({ type: "CLOSE_SESSION" }),
|
|
74
|
+
}),
|
|
75
|
+
[]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// if formUuid isn't a part of state yet, grab it from the url params
|
|
79
|
+
// this is the entry into the workflow system
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (state?.workflowState === null && activeFormUuid) {
|
|
82
|
+
actions.initializeWorkflowState({ activeFormUuid, newPatientUuid });
|
|
83
|
+
}
|
|
84
|
+
}, [activeFormUuid, newPatientUuid, state?.workflowState, actions]);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<FormWorkflowContext.Provider
|
|
88
|
+
value={{
|
|
89
|
+
...state,
|
|
90
|
+
...actions,
|
|
91
|
+
workflowState:
|
|
92
|
+
state.forms?.[state.activeFormUuid]?.workflowState ??
|
|
93
|
+
initialWorkflowState.workflowState,
|
|
94
|
+
activePatientUuid:
|
|
95
|
+
state.forms?.[state.activeFormUuid]?.activePatientUuid ??
|
|
96
|
+
initialWorkflowState.activePatientUuid,
|
|
97
|
+
activeEncounterUuid:
|
|
98
|
+
state.forms?.[state.activeFormUuid]?.activeEncounterUuid ??
|
|
99
|
+
initialWorkflowState.activeEncounterUuid,
|
|
100
|
+
patientUuids:
|
|
101
|
+
state.forms?.[state.activeFormUuid]?.patientUuids ??
|
|
102
|
+
initialWorkflowState.patientUuids,
|
|
103
|
+
encounters:
|
|
104
|
+
state.forms?.[state.activeFormUuid]?.encounters ??
|
|
105
|
+
initialWorkflowState.encounters,
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
{children}
|
|
109
|
+
</FormWorkflowContext.Provider>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default FormWorkflowContext;
|
|
114
|
+
export { FormWorkflowProvider };
|