@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
package/jest.config.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"\\.(s?css)$": "identity-obj-proxy",
|
|
9
9
|
"@openmrs/esm-framework": "@openmrs/esm-framework/mock",
|
|
10
10
|
"^lodash-es/(.*)$": "lodash/$1",
|
|
11
|
-
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js"
|
|
11
|
+
"^uuid$": "<rootDir>/node_modules/uuid/dist/index.js",
|
|
12
|
+
"^dexie$": "<rootDir>/node_modules/dexie"
|
|
12
13
|
},
|
|
13
14
|
"setupFilesAfterEnv": [
|
|
14
15
|
"<rootDir>/src/setup-tests.ts"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-fast-data-entry-app",
|
|
3
|
-
"version": "1.0.1-pre.
|
|
3
|
+
"version": "1.0.1-pre.85",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "An OpenMRS 3.x microfrontend",
|
|
6
6
|
"browser": "dist/openmrs-esm-fast-data-entry-app.js",
|
|
@@ -17,12 +17,8 @@
|
|
|
17
17
|
"test": "jest --config jest.config.json --passWithNoTests",
|
|
18
18
|
"verify": "concurrently 'yarn:lint' 'yarn:test' 'yarn:typescript'",
|
|
19
19
|
"coverage": "yarn test -- --coverage ",
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"husky": {
|
|
23
|
-
"hooks": {
|
|
24
|
-
"pre-commit": "pretty-quick --staged && yarn verify"
|
|
25
|
-
}
|
|
20
|
+
"postinstall": "husky install",
|
|
21
|
+
"extract-translations": "i18next 'src/**/*.tsx' --config ./tools/i18next-parser.config.js"
|
|
26
22
|
},
|
|
27
23
|
"browserslist": [
|
|
28
24
|
"extends browserslist-config-openmrs"
|
|
@@ -45,6 +41,7 @@
|
|
|
45
41
|
"peerDependencies": {
|
|
46
42
|
"@carbon/react": "^1.9.0",
|
|
47
43
|
"@openmrs/esm-framework": "*",
|
|
44
|
+
"lodash-es": "4.x",
|
|
48
45
|
"react": "18.x",
|
|
49
46
|
"react-dom": "18.x",
|
|
50
47
|
"react-i18next": "11.x",
|
|
@@ -76,6 +73,7 @@
|
|
|
76
73
|
"jest": "^28.1.3",
|
|
77
74
|
"jest-cli": "^28.1.3",
|
|
78
75
|
"jest-environment-jsdom": "^28.1.3",
|
|
76
|
+
"lodash-es": "^4.17.21",
|
|
79
77
|
"openmrs": "next",
|
|
80
78
|
"prettier": "^2.3.0",
|
|
81
79
|
"pretty-quick": "^3.1.0",
|
|
@@ -85,13 +83,15 @@
|
|
|
85
83
|
"react-router-dom": "^6.3.0",
|
|
86
84
|
"semver": "^7.3.7",
|
|
87
85
|
"swc-loader": "^0.2.3",
|
|
88
|
-
"swr": "^1.3.0",
|
|
89
86
|
"typescript": "^4.7.3",
|
|
90
87
|
"webpack": "^5.73.0"
|
|
91
88
|
},
|
|
92
89
|
"packageManager": "yarn@3.2.2",
|
|
93
90
|
"dependencies": {
|
|
94
|
-
"
|
|
91
|
+
"i18next": "^21.10.0",
|
|
92
|
+
"i18next-parser": "^6.6.0",
|
|
93
|
+
"react-hook-form": "^7.34.2",
|
|
94
|
+
"swr": "1.1.2"
|
|
95
95
|
},
|
|
96
96
|
"stableVersion": "1.0.0"
|
|
97
97
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ComposedModal,
|
|
4
|
+
ModalBody,
|
|
5
|
+
ModalFooter,
|
|
6
|
+
ModalHeader,
|
|
7
|
+
} from "@carbon/react";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { useTranslation } from "react-i18next";
|
|
10
|
+
|
|
11
|
+
const CancelModal = ({ open, setOpen, context }) => {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
|
|
14
|
+
const onCancel = () => setOpen(false);
|
|
15
|
+
|
|
16
|
+
const onDiscard = async () => {
|
|
17
|
+
await context.destroySession();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const onSaveAndClose = async () => {
|
|
21
|
+
await context.closeSession();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ComposedModal open={open}>
|
|
26
|
+
<ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
|
|
27
|
+
<ModalBody>
|
|
28
|
+
{t(
|
|
29
|
+
"cancelExplanation",
|
|
30
|
+
"You will lose any unsaved changes on the current form. Do you want to discard the current session?"
|
|
31
|
+
)}
|
|
32
|
+
</ModalBody>
|
|
33
|
+
<ModalFooter>
|
|
34
|
+
<Button kind="secondary" onClick={onCancel}>
|
|
35
|
+
{t("cancel", "Cancel")}
|
|
36
|
+
</Button>
|
|
37
|
+
<Button kind="danger" onClick={onDiscard}>
|
|
38
|
+
{t("discard", "Discard")}
|
|
39
|
+
</Button>
|
|
40
|
+
<Button kind="primary" onClick={onSaveAndClose}>
|
|
41
|
+
{t("saveSession", "Save Session")}
|
|
42
|
+
</Button>
|
|
43
|
+
</ModalFooter>
|
|
44
|
+
</ComposedModal>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default CancelModal;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ComposedModal,
|
|
4
|
+
ModalBody,
|
|
5
|
+
ModalFooter,
|
|
6
|
+
ModalHeader,
|
|
7
|
+
} from "@carbon/react";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { useTranslation } from "react-i18next";
|
|
10
|
+
|
|
11
|
+
const CompleteModal = ({ open, setOpen, context, validateFirst = false }) => {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
|
|
14
|
+
const onCancel = () => setOpen(false);
|
|
15
|
+
|
|
16
|
+
const onComplete = () => {
|
|
17
|
+
if (validateFirst) {
|
|
18
|
+
context.validateForComplete();
|
|
19
|
+
} else {
|
|
20
|
+
context.submitForComplete();
|
|
21
|
+
}
|
|
22
|
+
setOpen(false);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ComposedModal open={open}>
|
|
27
|
+
<ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
|
|
28
|
+
<ModalBody>
|
|
29
|
+
{t(
|
|
30
|
+
"saveExplanation",
|
|
31
|
+
"Do you want to save the current form and exit the workflow?"
|
|
32
|
+
)}
|
|
33
|
+
</ModalBody>
|
|
34
|
+
<ModalFooter>
|
|
35
|
+
<Button kind="secondary" onClick={onCancel}>
|
|
36
|
+
{t("cancel", "Cancel")}
|
|
37
|
+
</Button>
|
|
38
|
+
<Button kind="primary" onClick={onComplete}>
|
|
39
|
+
{t("complete", "Complete")}
|
|
40
|
+
</Button>
|
|
41
|
+
</ModalFooter>
|
|
42
|
+
</ComposedModal>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default CompleteModal;
|
package/src/FormBootstrap.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { detach, ExtensionSlot } from "@openmrs/esm-framework";
|
|
3
3
|
import useGetPatient from "./hooks/useGetPatient";
|
|
4
4
|
|
|
@@ -107,6 +107,7 @@ interface FormParams {
|
|
|
107
107
|
showDiscardSubmitButtons?: boolean;
|
|
108
108
|
handlePostResponse?: (Encounter) => void;
|
|
109
109
|
handleEncounterCreate?: (Object) => void;
|
|
110
|
+
handleOnValidate?: (boolean) => void;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
const FormBootstrap = ({
|
|
@@ -117,6 +118,7 @@ const FormBootstrap = ({
|
|
|
117
118
|
encounterUuid,
|
|
118
119
|
handlePostResponse,
|
|
119
120
|
handleEncounterCreate,
|
|
121
|
+
handleOnValidate,
|
|
120
122
|
}: FormParams) => {
|
|
121
123
|
const patient = useGetPatient(patientUuid);
|
|
122
124
|
|
|
@@ -124,11 +126,23 @@ const FormBootstrap = ({
|
|
|
124
126
|
return () => detach("form-widget-slot", "form-widget-slot");
|
|
125
127
|
});
|
|
126
128
|
|
|
129
|
+
// FIXME This should not be necessary
|
|
130
|
+
const [showForm, setShowForm] = useState(true);
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (patientUuid && formUuid && patient) {
|
|
134
|
+
setShowForm(false);
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
setShowForm(true);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}, [patientUuid, formUuid, patient]);
|
|
140
|
+
|
|
127
141
|
return (
|
|
128
142
|
<div>
|
|
129
|
-
{formUuid && patientUuid && patient && (
|
|
143
|
+
{showForm && formUuid && patientUuid && patient && (
|
|
130
144
|
<ExtensionSlot
|
|
131
|
-
|
|
145
|
+
name="form-widget-slot"
|
|
132
146
|
state={{
|
|
133
147
|
view: "form",
|
|
134
148
|
formUuid,
|
|
@@ -140,6 +154,7 @@ const FormBootstrap = ({
|
|
|
140
154
|
closeWorkspace: () => undefined,
|
|
141
155
|
handlePostResponse,
|
|
142
156
|
handleEncounterCreate,
|
|
157
|
+
handleOnValidate,
|
|
143
158
|
showDiscardSubmitButtons: false,
|
|
144
159
|
}}
|
|
145
160
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useContext, useState } from "react";
|
|
1
|
+
import React, { useCallback, useContext, useEffect, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
ComposedModal,
|
|
4
4
|
Button,
|
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
ModalBody,
|
|
8
8
|
TextInput,
|
|
9
9
|
FormLabel,
|
|
10
|
+
Loading,
|
|
10
11
|
} from "@carbon/react";
|
|
11
|
-
import { Add,
|
|
12
|
+
import { Add, TrashCan } from "@carbon/react/icons";
|
|
12
13
|
import { useTranslation } from "react-i18next";
|
|
13
|
-
import { ExtensionSlot } from "@openmrs/esm-framework";
|
|
14
|
+
import { ExtensionSlot, showToast } from "@openmrs/esm-framework";
|
|
14
15
|
import styles from "./styles.scss";
|
|
15
16
|
import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
|
|
17
|
+
import { usePostCohort } from "../hooks";
|
|
16
18
|
|
|
17
19
|
const MemExtension = React.memo(ExtensionSlot);
|
|
18
20
|
|
|
@@ -20,18 +22,19 @@ const PatientRow = ({ patient, removePatient }) => {
|
|
|
20
22
|
const { t } = useTranslation();
|
|
21
23
|
return (
|
|
22
24
|
<li key={patient.uuid} className={styles.patientRow}>
|
|
23
|
-
<span className={styles.patientName}>{patient?.display}</span>
|
|
24
25
|
<span>
|
|
25
26
|
<Button
|
|
26
27
|
kind="tertiary"
|
|
27
28
|
size="sm"
|
|
29
|
+
hasIconOnly
|
|
28
30
|
onClick={() => removePatient(patient.uuid)}
|
|
29
|
-
renderIcon={
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{t("remove", "Remove")}
|
|
33
|
-
|
|
31
|
+
renderIcon={TrashCan}
|
|
32
|
+
tooltipAlignment="start"
|
|
33
|
+
tooltipPosition="top"
|
|
34
|
+
iconDescription={t("remove", "Remove")}
|
|
35
|
+
/>
|
|
34
36
|
</span>
|
|
37
|
+
<span className={styles.patientName}>{patient?.display}</span>
|
|
35
38
|
</li>
|
|
36
39
|
);
|
|
37
40
|
};
|
|
@@ -64,17 +67,21 @@ const NewGroupForm = (props) => {
|
|
|
64
67
|
/>
|
|
65
68
|
{errors?.name && (
|
|
66
69
|
<p className={styles.formError}>
|
|
67
|
-
{
|
|
70
|
+
{errors.name === "required"
|
|
71
|
+
? t("groupNameError", "Please enter a group name.")
|
|
72
|
+
: errors.name}
|
|
68
73
|
</p>
|
|
69
74
|
)}
|
|
70
|
-
<FormLabel>
|
|
75
|
+
<FormLabel>
|
|
76
|
+
{patientList.length} {t("patientsInGroup", "Patients in group")}
|
|
77
|
+
</FormLabel>
|
|
71
78
|
{errors?.patientList && (
|
|
72
79
|
<p className={styles.formError}>
|
|
73
80
|
{t("noPatientError", "Please enter at least one patient.")}
|
|
74
81
|
</p>
|
|
75
82
|
)}
|
|
76
83
|
{!errors?.patientList && (
|
|
77
|
-
<ul>
|
|
84
|
+
<ul className={styles.patientList}>
|
|
78
85
|
{patientList?.map((patient, index) => (
|
|
79
86
|
<PatientRow
|
|
80
87
|
patient={patient}
|
|
@@ -92,7 +99,7 @@ const NewGroupForm = (props) => {
|
|
|
92
99
|
state={{
|
|
93
100
|
selectPatientAction: updatePatientList,
|
|
94
101
|
buttonProps: {
|
|
95
|
-
kind: "
|
|
102
|
+
kind: "secondary",
|
|
96
103
|
},
|
|
97
104
|
}}
|
|
98
105
|
/>
|
|
@@ -108,6 +115,7 @@ const AddGroupModal = () => {
|
|
|
108
115
|
const [errors, setErrors] = useState({});
|
|
109
116
|
const [name, setName] = useState("");
|
|
110
117
|
const [patientList, setPatientList] = useState([]);
|
|
118
|
+
const { post, result, isPosting, error } = usePostCohort();
|
|
111
119
|
|
|
112
120
|
const handleCancel = () => {
|
|
113
121
|
setOpen(false);
|
|
@@ -165,10 +173,46 @@ const AddGroupModal = () => {
|
|
|
165
173
|
|
|
166
174
|
const handleSubmit = () => {
|
|
167
175
|
if (validate()) {
|
|
168
|
-
|
|
176
|
+
post({
|
|
177
|
+
name: name,
|
|
178
|
+
cohortMembers: patientList.map((p) => ({ patient: p.uuid })),
|
|
179
|
+
});
|
|
169
180
|
}
|
|
170
181
|
};
|
|
171
182
|
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
if (result) {
|
|
185
|
+
setGroup({
|
|
186
|
+
...result,
|
|
187
|
+
// the result doesn't come with cohortMembers.
|
|
188
|
+
// need to add it in based on our local state
|
|
189
|
+
cohortMembers: patientList.map((p) => ({ patient: { uuid: p.uuid } })),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}, [result, setGroup, patientList]);
|
|
193
|
+
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
if (error) {
|
|
196
|
+
showToast({
|
|
197
|
+
kind: "error",
|
|
198
|
+
title: t("postError", "POST Error"),
|
|
199
|
+
description:
|
|
200
|
+
error.message ??
|
|
201
|
+
t("unknownPostError", "An unknown error occured while saving data"),
|
|
202
|
+
});
|
|
203
|
+
if (error.fieldErrors) {
|
|
204
|
+
setErrors(
|
|
205
|
+
Object.fromEntries(
|
|
206
|
+
Object.entries(error.fieldErrors).map(([key, value]) => [
|
|
207
|
+
key,
|
|
208
|
+
value?.[0]?.message,
|
|
209
|
+
])
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}, [error, t]);
|
|
215
|
+
|
|
172
216
|
return (
|
|
173
217
|
<div className={styles.modal}>
|
|
174
218
|
<Button
|
|
@@ -181,23 +225,32 @@ const AddGroupModal = () => {
|
|
|
181
225
|
<ComposedModal open={open} onClose={() => setOpen(false)}>
|
|
182
226
|
<ModalHeader>{t("createNewGroup", "Create New Group")}</ModalHeader>
|
|
183
227
|
<ModalBody>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
228
|
+
{result ? (
|
|
229
|
+
<p>Group saved succesfully</p>
|
|
230
|
+
) : isPosting ? (
|
|
231
|
+
<div className={styles.loading}>
|
|
232
|
+
<Loading withOverlay={false} />
|
|
233
|
+
<span>Saving new group...</span>
|
|
234
|
+
</div>
|
|
235
|
+
) : (
|
|
236
|
+
<NewGroupForm
|
|
237
|
+
{...{
|
|
238
|
+
name,
|
|
239
|
+
setName,
|
|
240
|
+
patientList,
|
|
241
|
+
updatePatientList,
|
|
242
|
+
errors,
|
|
243
|
+
validate,
|
|
244
|
+
removePatient,
|
|
245
|
+
}}
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
195
248
|
</ModalBody>
|
|
196
249
|
<ModalFooter>
|
|
197
|
-
<Button kind="secondary" onClick={handleCancel}>
|
|
250
|
+
<Button kind="secondary" onClick={handleCancel} disabled={isPosting}>
|
|
198
251
|
{t("cancel", "Cancel")}
|
|
199
252
|
</Button>
|
|
200
|
-
<Button kind="primary" onClick={handleSubmit}>
|
|
253
|
+
<Button kind="primary" onClick={handleSubmit} disabled={isPosting}>
|
|
201
254
|
{t("createGroup", "Create Group")}
|
|
202
255
|
</Button>
|
|
203
256
|
</ModalFooter>
|
|
@@ -23,13 +23,23 @@
|
|
|
23
23
|
|
|
24
24
|
.patientRow {
|
|
25
25
|
display: flex;
|
|
26
|
+
align-items: center;
|
|
26
27
|
width: "100%";
|
|
28
|
+
&:nth-child(odd) {
|
|
29
|
+
background-color: colors.$gray-20;
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
.patientName {
|
|
30
34
|
flex-grow: 1;
|
|
31
|
-
padding: spacing.$spacing-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
padding-left: spacing.$spacing-05;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.loading {
|
|
39
|
+
display: flex;
|
|
40
|
+
height: 100%;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
align-items: center;
|
|
44
|
+
row-gap: spacing.$spacing-05;
|
|
35
45
|
}
|
package/src/config-schema.ts
CHANGED
|
@@ -60,6 +60,28 @@ export const configSchema = {
|
|
|
60
60
|
},
|
|
61
61
|
_default: ["ICRC Forms", "Distress Scales"],
|
|
62
62
|
},
|
|
63
|
+
groupSessionConcepts: {
|
|
64
|
+
sessionName: {
|
|
65
|
+
_type: Type.UUID,
|
|
66
|
+
_description: "UUID of concept for Session Name",
|
|
67
|
+
_default: "e2559620-900b-4f66-ae41-0b9c4adfb654",
|
|
68
|
+
},
|
|
69
|
+
sessionDate: {
|
|
70
|
+
_type: Type.UUID,
|
|
71
|
+
_description: "UUID of concept for Session Date",
|
|
72
|
+
_default: "ceaca505-6dff-4940-8a43-8c060a0924d7",
|
|
73
|
+
},
|
|
74
|
+
practitionerName: {
|
|
75
|
+
_type: Type.UUID,
|
|
76
|
+
_description: "UUID of concept for Practitioner Name",
|
|
77
|
+
_default: "f1a2d58c-1a0e-4148-931a-aac224649fdc",
|
|
78
|
+
},
|
|
79
|
+
sessionNotes: {
|
|
80
|
+
_type: Type.UUID,
|
|
81
|
+
_description: "UUID of concept for Session Notes",
|
|
82
|
+
_default: "fa8fedc0-c066-4da3-8dc1-2ad8621fc480",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
63
85
|
};
|
|
64
86
|
|
|
65
87
|
export type Form = {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useReducer } from "react";
|
|
2
2
|
import reducer from "./FormWorkflowReducer";
|
|
3
3
|
import { useParams, useLocation } from "react-router-dom";
|
|
4
|
+
import useGetSystemSetting from "../hooks/useGetSystemSetting";
|
|
5
|
+
import { useSession } from "@openmrs/esm-framework";
|
|
4
6
|
interface ParamTypes {
|
|
5
7
|
formUuid?: string;
|
|
6
8
|
}
|
|
@@ -30,6 +32,8 @@ export const initialWorkflowState = {
|
|
|
30
32
|
activeEncounterUuid: null, // pseudo field from state[activeFormUuid].encounterUuid
|
|
31
33
|
patientUuids: [], // pseudo field from state[activeFormUuid].patientUuids
|
|
32
34
|
encounters: {}, // pseudo field from state[activeFormUuid].encounters
|
|
35
|
+
singleSessionVisitTypeUuid: null,
|
|
36
|
+
userUuid: null, // UUID of the user to which this workflow state belongs to
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
const FormWorkflowContext = React.createContext({
|
|
@@ -38,6 +42,7 @@ const FormWorkflowContext = React.createContext({
|
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
const FormWorkflowProvider = ({ children }) => {
|
|
45
|
+
const { user } = useSession();
|
|
41
46
|
const { formUuid } = useParams() as ParamTypes;
|
|
42
47
|
const activeFormUuid = formUuid.split("&")[0];
|
|
43
48
|
const { search } = useLocation();
|
|
@@ -46,6 +51,11 @@ const FormWorkflowProvider = ({ children }) => {
|
|
|
46
51
|
...initialWorkflowState,
|
|
47
52
|
...initialActions,
|
|
48
53
|
});
|
|
54
|
+
const systemSetting = useGetSystemSetting(
|
|
55
|
+
"@openmrs/esm-fast-data-entry-app.groupSessionVisitTypeUuid"
|
|
56
|
+
);
|
|
57
|
+
const singleSessionVisitTypeUuid =
|
|
58
|
+
systemSetting?.result?.data?.results?.[0]?.value;
|
|
49
59
|
|
|
50
60
|
const actions = useMemo(
|
|
51
61
|
() => ({
|
|
@@ -54,6 +64,7 @@ const FormWorkflowProvider = ({ children }) => {
|
|
|
54
64
|
type: "INITIALIZE_WORKFLOW_STATE",
|
|
55
65
|
activeFormUuid,
|
|
56
66
|
newPatientUuid,
|
|
67
|
+
userUuid: user.uuid,
|
|
57
68
|
}),
|
|
58
69
|
addPatient: (patientUuid) =>
|
|
59
70
|
dispatch({ type: "ADD_PATIENT", patientUuid }),
|
|
@@ -72,7 +83,7 @@ const FormWorkflowProvider = ({ children }) => {
|
|
|
72
83
|
destroySession: () => dispatch({ type: "DESTROY_SESSION" }),
|
|
73
84
|
closeSession: () => dispatch({ type: "CLOSE_SESSION" }),
|
|
74
85
|
}),
|
|
75
|
-
[]
|
|
86
|
+
[user]
|
|
76
87
|
);
|
|
77
88
|
|
|
78
89
|
// if formUuid isn't a part of state yet, grab it from the url params
|
|
@@ -103,6 +114,7 @@ const FormWorkflowProvider = ({ children }) => {
|
|
|
103
114
|
encounters:
|
|
104
115
|
state.forms?.[state.activeFormUuid]?.encounters ??
|
|
105
116
|
initialWorkflowState.encounters,
|
|
117
|
+
singleSessionVisitTypeUuid,
|
|
106
118
|
}}
|
|
107
119
|
>
|
|
108
120
|
{children}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { navigate } from "@openmrs/esm-framework";
|
|
2
2
|
import { initialWorkflowState } from "./FormWorkflowContext";
|
|
3
3
|
|
|
4
|
-
export const fdeWorkflowStorageVersion = "1.0
|
|
4
|
+
export const fdeWorkflowStorageVersion = "1.1.0";
|
|
5
5
|
export const fdeWorkflowStorageName = "openmrs:fastDataEntryWorkflowState";
|
|
6
6
|
const persistData = (data) => {
|
|
7
|
-
localStorage.setItem(
|
|
7
|
+
localStorage.setItem(
|
|
8
|
+
fdeWorkflowStorageName + ":" + data.userUuid,
|
|
9
|
+
JSON.stringify(data)
|
|
10
|
+
);
|
|
8
11
|
};
|
|
9
12
|
|
|
10
13
|
const initialFormState = {
|
|
@@ -18,7 +21,9 @@ const initialFormState = {
|
|
|
18
21
|
const reducer = (state, action) => {
|
|
19
22
|
switch (action.type) {
|
|
20
23
|
case "INITIALIZE_WORKFLOW_STATE": {
|
|
21
|
-
const savedData = localStorage.getItem(
|
|
24
|
+
const savedData = localStorage.getItem(
|
|
25
|
+
fdeWorkflowStorageName + ":" + action.userUuid
|
|
26
|
+
);
|
|
22
27
|
const savedDataObject = savedData ? JSON.parse(savedData) : {};
|
|
23
28
|
let newState: { [key: string]: unknown } = {};
|
|
24
29
|
const newPatient = action.newPatientUuid
|
|
@@ -69,6 +74,7 @@ const reducer = (state, action) => {
|
|
|
69
74
|
[action.activeFormUuid]: initialFormState,
|
|
70
75
|
},
|
|
71
76
|
activeFormUuid: action.activeFormUuid,
|
|
77
|
+
userUuid: action.userUuid,
|
|
72
78
|
};
|
|
73
79
|
}
|
|
74
80
|
persistData(newState);
|
|
@@ -259,6 +265,8 @@ const reducer = (state, action) => {
|
|
|
259
265
|
activeFormUuid: null,
|
|
260
266
|
};
|
|
261
267
|
persistData(newState);
|
|
268
|
+
//eslint-disable-next-line
|
|
269
|
+
navigate({ to: "${openmrsSpaBase}/forms" });
|
|
262
270
|
return newState;
|
|
263
271
|
}
|
|
264
272
|
case "CLOSE_SESSION": {
|
|
@@ -267,6 +275,8 @@ const reducer = (state, action) => {
|
|
|
267
275
|
activeFormUuid: null,
|
|
268
276
|
};
|
|
269
277
|
persistData(newState);
|
|
278
|
+
//eslint-disable-next-line
|
|
279
|
+
navigate({ to: "${openmrsSpaBase}/forms" });
|
|
270
280
|
return newState;
|
|
271
281
|
}
|
|
272
282
|
default:
|