@openmrs/esm-fast-data-entry-app 1.0.1-pre.10 → 1.0.1-pre.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +21 -2
  2. package/dist/153.js +1 -0
  3. package/dist/153.js.map +1 -0
  4. package/dist/233.js +2 -0
  5. package/dist/233.js.LICENSE.txt +9 -0
  6. package/dist/233.js.map +1 -0
  7. package/dist/262.js +1 -0
  8. package/dist/262.js.map +1 -0
  9. package/dist/279.js +1 -0
  10. package/dist/279.js.map +1 -0
  11. package/dist/294.js +2 -0
  12. package/dist/294.js.LICENSE.txt +9 -0
  13. package/dist/294.js.map +1 -0
  14. package/dist/327.js +1 -0
  15. package/dist/327.js.map +1 -0
  16. package/dist/409.js +2 -0
  17. package/dist/409.js.LICENSE.txt +27 -0
  18. package/dist/409.js.map +1 -0
  19. package/dist/415.js +1 -0
  20. package/dist/415.js.map +1 -0
  21. package/dist/559.js +1 -0
  22. package/dist/559.js.map +1 -0
  23. package/dist/574.js +1 -0
  24. package/dist/651.js +1 -0
  25. package/dist/651.js.map +1 -0
  26. package/dist/706.js +1 -0
  27. package/dist/706.js.map +1 -0
  28. package/dist/757.js +1 -0
  29. package/dist/800.js +2 -0
  30. package/dist/800.js.LICENSE.txt +5 -0
  31. package/dist/800.js.map +1 -0
  32. package/dist/820.js +1 -0
  33. package/dist/820.js.map +1 -0
  34. package/dist/883.js +1 -0
  35. package/dist/883.js.map +1 -0
  36. package/dist/889.js +1 -0
  37. package/dist/889.js.map +1 -0
  38. package/dist/897.js +2 -0
  39. package/dist/897.js.LICENSE.txt +21 -0
  40. package/dist/897.js.map +1 -0
  41. package/dist/92.js +1 -0
  42. package/dist/92.js.map +1 -0
  43. package/dist/935.js +2 -0
  44. package/dist/935.js.LICENSE.txt +19 -0
  45. package/dist/935.js.map +1 -0
  46. package/dist/959.js +1 -0
  47. package/dist/959.js.map +1 -0
  48. package/dist/main.js +1 -0
  49. package/dist/main.js.map +1 -0
  50. package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
  51. package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +654 -0
  52. package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -0
  53. package/dist/routes.json +1 -0
  54. package/jest.config.json +2 -1
  55. package/package.json +13 -10
  56. package/src/CancelModal.tsx +48 -0
  57. package/src/CompleteModal.tsx +46 -0
  58. package/src/FormBootstrap.tsx +18 -3
  59. package/src/add-group-modal/AddGroupModal.tsx +113 -34
  60. package/src/add-group-modal/styles.scss +14 -4
  61. package/src/config-schema.ts +22 -0
  62. package/src/context/FormWorkflowContext.tsx +13 -1
  63. package/src/context/FormWorkflowReducer.ts +13 -3
  64. package/src/context/GroupFormWorkflowContext.tsx +41 -6
  65. package/src/context/GroupFormWorkflowReducer.ts +170 -12
  66. package/src/form-entry-workflow/FormEntryWorkflow.tsx +67 -101
  67. package/src/form-entry-workflow/styles.scss +2 -1
  68. package/src/forms-page/FormsPage.tsx +8 -3
  69. package/src/forms-page/forms-table/FormsTable.tsx +11 -5
  70. package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +13 -400
  71. package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
  72. package/src/group-form-entry-workflow/SessionDetailsForm.tsx +131 -0
  73. package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
  74. package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +144 -0
  75. package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
  76. package/src/group-form-entry-workflow/{group-banner/GroupBanner.test.tsx → group-display-header/GroupDisplayHeader.test.tsx} +2 -2
  77. package/src/group-form-entry-workflow/{group-banner/GroupBanner.tsx → group-display-header/GroupDisplayHeader.tsx} +23 -5
  78. package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
  79. package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +61 -28
  80. package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +5 -0
  81. package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +65 -8
  82. package/src/group-form-entry-workflow/group-search/group-search.scss +8 -6
  83. package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +41 -10
  84. package/src/group-form-entry-workflow/styles.scss +12 -1
  85. package/src/hooks/index.ts +1 -0
  86. package/src/hooks/useGetPatient.ts +1 -1
  87. package/src/hooks/useGetPatients.ts +34 -0
  88. package/src/hooks/useGetSystemSetting.ts +38 -0
  89. package/src/hooks/usePostEndpoint.ts +76 -0
  90. package/src/hooks/useSearchEndpoint.ts +120 -0
  91. package/src/hooks/useStartVisit.ts +92 -0
  92. package/src/index.ts +13 -65
  93. package/src/patient-card/styles.scss +1 -0
  94. package/src/routes.json +24 -0
  95. package/tools/i18next-parser.config.js +93 -0
  96. package/translations/en.json +29 -9
  97. package/translations/fr.json +50 -0
  98. package/.editorconfig +0 -12
  99. package/.eslintignore +0 -2
  100. package/.eslintrc.js +0 -10
  101. package/.husky/pre-push +0 -1
  102. package/.prettierignore +0 -14
  103. package/.tx/config +0 -9
  104. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
  105. package/.yarn/versions/45b499b6.yml +0 -0
  106. package/src/group-form-entry-workflow/group-banner/index.ts +0 -3
  107. package/src/group-form-entry-workflow/group-search/mock-group-data.ts +0 -79
  108. package/src/group-form-entry-workflow/group-search/useGroupSearch.ts +0 -14
  109. package/src/hooks/usePostCohort.ts +0 -18
  110. /package/src/group-form-entry-workflow/{group-banner → group-display-header}/styles.scss +0 -0
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.10",
3
+ "version": "1.0.1-pre.101",
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
- "prepare": "husky install"
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",
@@ -53,6 +50,7 @@
53
50
  "devDependencies": {
54
51
  "@carbon/react": "^1.9.0",
55
52
  "@openmrs/esm-framework": "next",
53
+ "@swc-node/loader": "^1.3.5",
56
54
  "@swc/core": "^1.2.245",
57
55
  "@swc/jest": "^0.2.22",
58
56
  "@testing-library/dom": "^7.20.0",
@@ -66,6 +64,7 @@
66
64
  "@types/webpack-env": "^1.16.0",
67
65
  "@typescript-eslint/parser": "^5.14.0",
68
66
  "concurrently": "^6.2.0",
67
+ "css-loader": "^6.8.1",
69
68
  "eslint": "^8.20.0",
70
69
  "eslint-config-prettier": "^8.3.0",
71
70
  "eslint-config-react-app": "^7.0.1",
@@ -76,6 +75,7 @@
76
75
  "jest": "^28.1.3",
77
76
  "jest-cli": "^28.1.3",
78
77
  "jest-environment-jsdom": "^28.1.3",
78
+ "lodash-es": "^4.17.21",
79
79
  "openmrs": "next",
80
80
  "prettier": "^2.3.0",
81
81
  "pretty-quick": "^3.1.0",
@@ -85,13 +85,16 @@
85
85
  "react-router-dom": "^6.3.0",
86
86
  "semver": "^7.3.7",
87
87
  "swc-loader": "^0.2.3",
88
- "swr": "^1.3.0",
89
88
  "typescript": "^4.7.3",
90
- "webpack": "^5.73.0"
89
+ "webpack": "^5.73.0",
90
+ "webpack-cli": "^5.1.4"
91
91
  },
92
92
  "packageManager": "yarn@3.2.2",
93
93
  "dependencies": {
94
- "react-hook-form": "^7.34.2"
94
+ "i18next": "^21.10.0",
95
+ "i18next-parser": "^6.6.0",
96
+ "react-hook-form": "^7.34.2",
97
+ "swr": "^2.1.3"
95
98
  },
96
99
  "stableVersion": "1.0.0"
97
100
  }
@@ -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;
@@ -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
- extensionSlotName="form-widget-slot"
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,10 @@
1
- import React, { useCallback, useContext, useState } from "react";
1
+ import React, {
2
+ useCallback,
3
+ useContext,
4
+ useEffect,
5
+ useMemo,
6
+ useState,
7
+ } from "react";
2
8
  import {
3
9
  ComposedModal,
4
10
  Button,
@@ -8,30 +14,58 @@ import {
8
14
  TextInput,
9
15
  FormLabel,
10
16
  } from "@carbon/react";
11
- import { Add, Close } from "@carbon/react/icons";
17
+ import { TrashCan } from "@carbon/react/icons";
12
18
  import { useTranslation } from "react-i18next";
13
- import { ExtensionSlot } from "@openmrs/esm-framework";
19
+ import { ExtensionSlot, showToast } from "@openmrs/esm-framework";
14
20
  import styles from "./styles.scss";
15
21
  import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
22
+ import { usePostCohort } from "../hooks";
16
23
 
17
24
  const MemExtension = React.memo(ExtensionSlot);
18
25
 
26
+ const buildPatientDisplay = (patient) => {
27
+ const givenName = patient?.name?.[0]?.given?.[0];
28
+ const familyName = patient?.name?.[0]?.family;
29
+ const identifier = patient?.identifier?.[0]?.value;
30
+
31
+ let display = identifier ? identifier + " - " : "";
32
+ display += (givenName || "") + " " + (familyName || "");
33
+ return display.replace(/\s+/g, " ");
34
+ };
35
+
19
36
  const PatientRow = ({ patient, removePatient }) => {
20
37
  const { t } = useTranslation();
38
+ const onClickHandler = useCallback(
39
+ () => removePatient(patient?.uuid),
40
+ [patient, removePatient]
41
+ );
42
+ const patientDisplay = useMemo(() => {
43
+ if (!patient) {
44
+ return "";
45
+ }
46
+
47
+ if (patient.display) {
48
+ return patient.display;
49
+ }
50
+
51
+ return buildPatientDisplay(patient);
52
+ }, [patient]);
53
+
21
54
  return (
22
- <li key={patient.uuid} className={styles.patientRow}>
23
- <span className={styles.patientName}>{patient?.display}</span>
55
+ <li key={patient?.uuid} className={styles.patientRow}>
24
56
  <span>
25
57
  <Button
26
58
  kind="tertiary"
27
59
  size="sm"
28
- onClick={() => removePatient(patient.uuid)}
29
- renderIcon={Close}
30
- tooltipPosition="right"
31
- >
32
- {t("remove", "Remove")}
33
- </Button>
60
+ hasIconOnly
61
+ onClick={onClickHandler}
62
+ renderIcon={TrashCan}
63
+ tooltipAlignment="start"
64
+ tooltipPosition="top"
65
+ iconDescription={t("remove", "Remove")}
66
+ />
34
67
  </span>
68
+ <span className={styles.patientName}>{patientDisplay}</span>
35
69
  </li>
36
70
  );
37
71
  };
@@ -64,17 +98,21 @@ const NewGroupForm = (props) => {
64
98
  />
65
99
  {errors?.name && (
66
100
  <p className={styles.formError}>
67
- {t("groupNameError", "Please enter a group name.")}
101
+ {errors.name === "required"
102
+ ? t("groupNameError", "Please enter a group name.")
103
+ : errors.name}
68
104
  </p>
69
105
  )}
70
- <FormLabel>Patients in group</FormLabel>
106
+ <FormLabel>
107
+ {patientList.length} {t("patientsInGroup", "Patients in group")}
108
+ </FormLabel>
71
109
  {errors?.patientList && (
72
110
  <p className={styles.formError}>
73
111
  {t("noPatientError", "Please enter at least one patient.")}
74
112
  </p>
75
113
  )}
76
114
  {!errors?.patientList && (
77
- <ul>
115
+ <ul className={styles.patientList}>
78
116
  {patientList?.map((patient, index) => (
79
117
  <PatientRow
80
118
  patient={patient}
@@ -92,7 +130,7 @@ const NewGroupForm = (props) => {
92
130
  state={{
93
131
  selectPatientAction: updatePatientList,
94
132
  buttonProps: {
95
- kind: "primary",
133
+ kind: "secondary",
96
134
  },
97
135
  }}
98
136
  />
@@ -101,17 +139,21 @@ const NewGroupForm = (props) => {
101
139
  );
102
140
  };
103
141
 
104
- const AddGroupModal = () => {
142
+ const AddGroupModal = ({
143
+ patients = undefined,
144
+ isCreate = undefined,
145
+ groupName = "",
146
+ cohortUuid = undefined,
147
+ isOpen,
148
+ handleCancel,
149
+ onPostSubmit,
150
+ }) => {
105
151
  const { setGroup } = useContext(GroupFormWorkflowContext);
106
152
  const { t } = useTranslation();
107
- const [open, setOpen] = useState(false);
108
153
  const [errors, setErrors] = useState({});
109
- const [name, setName] = useState("");
110
- const [patientList, setPatientList] = useState([]);
111
-
112
- const handleCancel = () => {
113
- setOpen(false);
114
- };
154
+ const [name, setName] = useState(groupName);
155
+ const [patientList, setPatientList] = useState(patients || []);
156
+ const { post, result, error } = usePostCohort();
115
157
 
116
158
  const removePatient = useCallback(
117
159
  (patientUuid: string) =>
@@ -165,21 +207,58 @@ const AddGroupModal = () => {
165
207
 
166
208
  const handleSubmit = () => {
167
209
  if (validate()) {
168
- setGroup({ id: "1234", name: name, members: patientList });
210
+ post({
211
+ uuid: cohortUuid,
212
+ name: name,
213
+ cohortMembers: patientList.map((p) => ({ patient: p.uuid })),
214
+ });
215
+ if (onPostSubmit) {
216
+ onPostSubmit();
217
+ }
169
218
  }
170
219
  };
171
220
 
221
+ useEffect(() => {
222
+ if (result) {
223
+ setGroup({
224
+ ...result,
225
+ // the result doesn't come with cohortMembers.
226
+ // need to add it in based on our local state
227
+ cohortMembers: patientList.map((p) => ({ patient: { uuid: p.uuid } })),
228
+ });
229
+ }
230
+ }, [result, setGroup, patientList]);
231
+
232
+ useEffect(() => {
233
+ if (error) {
234
+ showToast({
235
+ kind: "error",
236
+ title: t("postError", "POST Error"),
237
+ description:
238
+ error.message ??
239
+ t("unknownPostError", "An unknown error occurred while saving data"),
240
+ });
241
+ if (error.fieldErrors) {
242
+ setErrors(
243
+ Object.fromEntries(
244
+ Object.entries(error.fieldErrors).map(([key, value]) => [
245
+ key,
246
+ value?.[0]?.message,
247
+ ])
248
+ )
249
+ );
250
+ }
251
+ }
252
+ }, [error, t]);
253
+
172
254
  return (
173
255
  <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>
256
+ <ComposedModal open={isOpen} onClose={handleCancel}>
257
+ <ModalHeader>
258
+ {isCreate
259
+ ? t("createNewGroup", "Create New Group")
260
+ : t("editGroup", "Edit Group")}
261
+ </ModalHeader>
183
262
  <ModalBody>
184
263
  <NewGroupForm
185
264
  {...{
@@ -198,7 +277,7 @@ const AddGroupModal = () => {
198
277
  {t("cancel", "Cancel")}
199
278
  </Button>
200
279
  <Button kind="primary" onClick={handleSubmit}>
201
- {t("createGroup", "Create Group")}
280
+ {isCreate ? t("createGroup", "Create Group") : t("save", "Save")}
202
281
  </Button>
203
282
  </ModalFooter>
204
283
  </ComposedModal>
@@ -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-02;
32
- &:hover {
33
- background-color: colors.$gray-20;
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
  }
@@ -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.13";
4
+ export const fdeWorkflowStorageVersion = "1.1.0";
5
5
  export const fdeWorkflowStorageName = "openmrs:fastDataEntryWorkflowState";
6
6
  const persistData = (data) => {
7
- localStorage.setItem(fdeWorkflowStorageName, JSON.stringify(data));
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(fdeWorkflowStorageName);
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: