@openmrs/esm-fast-data-entry-app 1.0.1-pre.85 → 1.0.1-pre.93

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.
@@ -89,7 +89,7 @@
89
89
  "132.js"
90
90
  ],
91
91
  "auxiliaryFiles": [],
92
- "hash": "40bb1c715e131d6c01b0",
92
+ "hash": "0cc4d3100aace33b5e13",
93
93
  "childrenByOrder": {}
94
94
  },
95
95
  {
@@ -97,9 +97,9 @@
97
97
  "initial": false,
98
98
  "entry": false,
99
99
  "recorded": false,
100
- "size": 118386,
100
+ "size": 135684,
101
101
  "sizes": {
102
- "javascript": 118386
102
+ "javascript": 135684
103
103
  },
104
104
  "names": [],
105
105
  "idHints": [],
@@ -108,10 +108,10 @@
108
108
  "main"
109
109
  ],
110
110
  "files": [
111
- "168.js"
111
+ "153.js"
112
112
  ],
113
113
  "auxiliaryFiles": [],
114
- "hash": "ac8a8e711522f166ca67",
114
+ "hash": "8aabfac7cc2b193a4cc0",
115
115
  "childrenByOrder": {}
116
116
  },
117
117
  {
@@ -137,7 +137,7 @@
137
137
  "main.js"
138
138
  ],
139
139
  "auxiliaryFiles": [],
140
- "hash": "4d1672badf0a60407f63",
140
+ "hash": "0445de82093ec647b280",
141
141
  "childrenByOrder": {}
142
142
  },
143
143
  {
@@ -275,7 +275,7 @@
275
275
  "327.js"
276
276
  ],
277
277
  "auxiliaryFiles": [],
278
- "hash": "99f1fbe84b07ca271277",
278
+ "hash": "c9ed3e3407a512dc9bfd",
279
279
  "childrenByOrder": {}
280
280
  },
281
281
  {
@@ -299,31 +299,6 @@
299
299
  "hash": "89ec6a0bec2a70e391a3",
300
300
  "childrenByOrder": {}
301
301
  },
302
- {
303
- "rendered": true,
304
- "initial": false,
305
- "entry": false,
306
- "recorded": false,
307
- "reason": "split chunk (cache group: defaultVendors)",
308
- "size": 262576,
309
- "sizes": {
310
- "javascript": 262576
311
- },
312
- "names": [],
313
- "idHints": [
314
- "vendors"
315
- ],
316
- "runtime": [
317
- "@openmrs/esm-fast-data-entry-app",
318
- "main"
319
- ],
320
- "files": [
321
- "403.js"
322
- ],
323
- "auxiliaryFiles": [],
324
- "hash": "fe3e72da71c970ea281a",
325
- "childrenByOrder": {}
326
- },
327
302
  {
328
303
  "rendered": true,
329
304
  "initial": true,
@@ -355,9 +330,9 @@
355
330
  "entry": false,
356
331
  "recorded": false,
357
332
  "reason": "split chunk (cache group: defaultVendors)",
358
- "size": 52269,
333
+ "size": 254320,
359
334
  "sizes": {
360
- "javascript": 52269
335
+ "javascript": 254320
361
336
  },
362
337
  "names": [],
363
338
  "idHints": [
@@ -368,10 +343,10 @@
368
343
  "main"
369
344
  ],
370
345
  "files": [
371
- "553.js"
346
+ "571.js"
372
347
  ],
373
348
  "auxiliaryFiles": [],
374
- "hash": "aaf7c553fc74f87c94a5",
349
+ "hash": "72f70e2a11cfde796bdd",
375
350
  "childrenByOrder": {}
376
351
  },
377
352
  {
@@ -379,9 +354,9 @@
379
354
  "initial": false,
380
355
  "entry": false,
381
356
  "recorded": false,
382
- "size": 2653,
357
+ "size": 2693,
383
358
  "sizes": {
384
- "javascript": 2653
359
+ "javascript": 2693
385
360
  },
386
361
  "names": [],
387
362
  "idHints": [],
@@ -393,7 +368,7 @@
393
368
  "574.js"
394
369
  ],
395
370
  "auxiliaryFiles": [],
396
- "hash": "6765217c3a3f27a18a03",
371
+ "hash": "d6170165d0b5cf47dea0",
397
372
  "childrenByOrder": {}
398
373
  },
399
374
  {
@@ -427,9 +402,9 @@
427
402
  "entry": false,
428
403
  "recorded": false,
429
404
  "reason": "split chunk (cache group: default)",
430
- "size": 57293,
405
+ "size": 57424,
431
406
  "sizes": {
432
- "javascript": 57251,
407
+ "javascript": 57382,
433
408
  "consume-shared": 42
434
409
  },
435
410
  "names": [],
@@ -442,7 +417,32 @@
442
417
  "617.js"
443
418
  ],
444
419
  "auxiliaryFiles": [],
445
- "hash": "a33453b3a1e5e07901b4",
420
+ "hash": "4135e212a76cc74d6fee",
421
+ "childrenByOrder": {}
422
+ },
423
+ {
424
+ "rendered": true,
425
+ "initial": false,
426
+ "entry": false,
427
+ "recorded": false,
428
+ "reason": "split chunk (cache group: defaultVendors)",
429
+ "size": 58881,
430
+ "sizes": {
431
+ "javascript": 58881
432
+ },
433
+ "names": [],
434
+ "idHints": [
435
+ "vendors"
436
+ ],
437
+ "runtime": [
438
+ "@openmrs/esm-fast-data-entry-app",
439
+ "main"
440
+ ],
441
+ "files": [
442
+ "656.js"
443
+ ],
444
+ "auxiliaryFiles": [],
445
+ "hash": "6834b8d0c2e4e07071de",
446
446
  "childrenByOrder": {}
447
447
  },
448
448
  {
@@ -467,7 +467,7 @@
467
467
  "658.js"
468
468
  ],
469
469
  "auxiliaryFiles": [],
470
- "hash": "6e977757a62e76cac252",
470
+ "hash": "2906b87fd1f33d88432d",
471
471
  "childrenByOrder": {}
472
472
  },
473
473
  {
@@ -555,7 +555,7 @@
555
555
  "804.js"
556
556
  ],
557
557
  "auxiliaryFiles": [],
558
- "hash": "8178ba7ddf3eecea471b",
558
+ "hash": "60473f47b765dc6b2615",
559
559
  "childrenByOrder": {}
560
560
  },
561
561
  {
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.85",
3
+ "version": "1.0.1-pre.93",
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",
@@ -91,7 +91,7 @@
91
91
  "i18next": "^21.10.0",
92
92
  "i18next-parser": "^6.6.0",
93
93
  "react-hook-form": "^7.34.2",
94
- "swr": "1.1.2"
94
+ "swr": "^2.1.3"
95
95
  },
96
96
  "stableVersion": "1.0.0"
97
97
  }
@@ -1,4 +1,10 @@
1
- import React, { useCallback, useContext, useEffect, 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,
@@ -7,9 +13,8 @@ import {
7
13
  ModalBody,
8
14
  TextInput,
9
15
  FormLabel,
10
- Loading,
11
16
  } from "@carbon/react";
12
- import { Add, TrashCan } from "@carbon/react/icons";
17
+ import { TrashCan } from "@carbon/react/icons";
13
18
  import { useTranslation } from "react-i18next";
14
19
  import { ExtensionSlot, showToast } from "@openmrs/esm-framework";
15
20
  import styles from "./styles.scss";
@@ -18,23 +23,49 @@ import { usePostCohort } from "../hooks";
18
23
 
19
24
  const MemExtension = React.memo(ExtensionSlot);
20
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
+
21
36
  const PatientRow = ({ patient, removePatient }) => {
22
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
+
23
54
  return (
24
- <li key={patient.uuid} className={styles.patientRow}>
55
+ <li key={patient?.uuid} className={styles.patientRow}>
25
56
  <span>
26
57
  <Button
27
58
  kind="tertiary"
28
59
  size="sm"
29
60
  hasIconOnly
30
- onClick={() => removePatient(patient.uuid)}
61
+ onClick={onClickHandler}
31
62
  renderIcon={TrashCan}
32
63
  tooltipAlignment="start"
33
64
  tooltipPosition="top"
34
65
  iconDescription={t("remove", "Remove")}
35
66
  />
36
67
  </span>
37
- <span className={styles.patientName}>{patient?.display}</span>
68
+ <span className={styles.patientName}>{patientDisplay}</span>
38
69
  </li>
39
70
  );
40
71
  };
@@ -108,18 +139,21 @@ const NewGroupForm = (props) => {
108
139
  );
109
140
  };
110
141
 
111
- const AddGroupModal = () => {
142
+ const AddGroupModal = ({
143
+ patients = undefined,
144
+ isCreate = undefined,
145
+ groupName = "",
146
+ cohortUuid = undefined,
147
+ isOpen,
148
+ handleCancel,
149
+ onPostSubmit,
150
+ }) => {
112
151
  const { setGroup } = useContext(GroupFormWorkflowContext);
113
152
  const { t } = useTranslation();
114
- const [open, setOpen] = useState(false);
115
153
  const [errors, setErrors] = useState({});
116
- const [name, setName] = useState("");
117
- const [patientList, setPatientList] = useState([]);
118
- const { post, result, isPosting, error } = usePostCohort();
119
-
120
- const handleCancel = () => {
121
- setOpen(false);
122
- };
154
+ const [name, setName] = useState(groupName);
155
+ const [patientList, setPatientList] = useState(patients || []);
156
+ const { post, result, error } = usePostCohort();
123
157
 
124
158
  const removePatient = useCallback(
125
159
  (patientUuid: string) =>
@@ -174,9 +208,13 @@ const AddGroupModal = () => {
174
208
  const handleSubmit = () => {
175
209
  if (validate()) {
176
210
  post({
211
+ uuid: cohortUuid,
177
212
  name: name,
178
213
  cohortMembers: patientList.map((p) => ({ patient: p.uuid })),
179
214
  });
215
+ if (onPostSubmit) {
216
+ onPostSubmit();
217
+ }
180
218
  }
181
219
  };
182
220
 
@@ -198,7 +236,7 @@ const AddGroupModal = () => {
198
236
  title: t("postError", "POST Error"),
199
237
  description:
200
238
  error.message ??
201
- t("unknownPostError", "An unknown error occured while saving data"),
239
+ t("unknownPostError", "An unknown error occurred while saving data"),
202
240
  });
203
241
  if (error.fieldErrors) {
204
242
  setErrors(
@@ -215,43 +253,31 @@ const AddGroupModal = () => {
215
253
 
216
254
  return (
217
255
  <div className={styles.modal}>
218
- <Button
219
- onClick={() => setOpen(true)}
220
- renderIcon={Add}
221
- iconDescription="Add"
222
- >
223
- {t("createNewGroup", "Create New Group")}
224
- </Button>
225
- <ComposedModal open={open} onClose={() => setOpen(false)}>
226
- <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>
227
262
  <ModalBody>
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
- )}
263
+ <NewGroupForm
264
+ {...{
265
+ name,
266
+ setName,
267
+ patientList,
268
+ updatePatientList,
269
+ errors,
270
+ validate,
271
+ removePatient,
272
+ }}
273
+ />
248
274
  </ModalBody>
249
275
  <ModalFooter>
250
- <Button kind="secondary" onClick={handleCancel} disabled={isPosting}>
276
+ <Button kind="secondary" onClick={handleCancel}>
251
277
  {t("cancel", "Cancel")}
252
278
  </Button>
253
- <Button kind="primary" onClick={handleSubmit} disabled={isPosting}>
254
- {t("createGroup", "Create Group")}
279
+ <Button kind="primary" onClick={handleSubmit}>
280
+ {isCreate ? t("createGroup", "Create Group") : t("save", "Save")}
255
281
  </Button>
256
282
  </ModalFooter>
257
283
  </ComposedModal>
@@ -6,11 +6,13 @@ import {
6
6
  DatePicker,
7
7
  DatePickerInput,
8
8
  } from "@carbon/react";
9
- import React from "react";
9
+ import React, { useContext } from "react";
10
10
  import styles from "./styles.scss";
11
11
  import { useTranslation } from "react-i18next";
12
12
  import { Controller, useFormContext } from "react-hook-form";
13
13
  import { AttendanceTable } from "./attendance-table";
14
+ import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
15
+ import useGetPatients from "../hooks/useGetPatients";
14
16
 
15
17
  const SessionDetailsForm = () => {
16
18
  const { t } = useTranslation();
@@ -20,101 +22,108 @@ const SessionDetailsForm = () => {
20
22
  control,
21
23
  } = useFormContext();
22
24
 
25
+ const { patientUuids } = useContext(GroupFormWorkflowContext);
26
+ const { patients, isLoading } = useGetPatients(patientUuids);
27
+
23
28
  return (
24
- <div className={styles.formSection}>
25
- <h4>{t("sessionDetails", "1. Session details")}</h4>
26
- <div>
27
- <p>
28
- {t(
29
- "allFieldsRequired",
30
- "All fields are required unless marked optional"
31
- )}
32
- </p>
33
- </div>
34
- <Layer>
35
- <Tile className={styles.formSectionTile}>
29
+ <div>
30
+ {!isLoading && (
31
+ <div className={styles.formSection}>
32
+ <h4>{t("sessionDetails", "1. Session details")}</h4>
33
+ <div>
34
+ <p>
35
+ {t(
36
+ "allFieldsRequired",
37
+ "All fields are required unless marked optional"
38
+ )}
39
+ </p>
40
+ </div>
36
41
  <Layer>
37
- <div
38
- style={{
39
- display: "flex",
40
- flexDirection: "column",
41
- rowGap: "1.5rem",
42
- }}
43
- >
44
- <TextInput
45
- id="text"
46
- type="text"
47
- labelText={t("sessionName", "Session Name")}
48
- {...register("sessionName", { required: true })}
49
- invalid={errors.sessionName}
50
- invalidText={"This field is required"}
51
- />
52
- <TextInput
53
- id="text"
54
- type="text"
55
- labelText={t("practitionerName", "Practitioner Name")}
56
- {...register("practitionerName", { required: true })}
57
- invalid={errors.practitionerName}
58
- invalidText={"This field is required"}
59
- />
60
- <Controller
61
- name="sessionDate"
62
- control={control}
63
- rules={{ required: true }}
64
- render={({ field }) => (
65
- <DatePicker
66
- datePickerType="single"
67
- size="md"
68
- maxDate={new Date()}
69
- {...field}
70
- >
71
- <DatePickerInput
72
- id="session-date"
73
- labelText={t("sessionDate", "Session Date")}
74
- placeholder="mm/dd/yyyy"
75
- size="md"
76
- invalid={errors.sessionDate}
77
- invalidText={"This field is required"}
78
- />
79
- </DatePicker>
80
- )}
81
- />
82
- <TextArea
83
- id="text"
84
- type="text"
85
- labelText={t("sessionNotes", "Session Notes")}
86
- {...register("sessionNotes", { required: true })}
87
- invalid={errors.sessionNotes}
88
- invalidText={"This field is required"}
89
- />
90
- </div>
42
+ <Tile className={styles.formSectionTile}>
43
+ <Layer>
44
+ <div
45
+ style={{
46
+ display: "flex",
47
+ flexDirection: "column",
48
+ rowGap: "1.5rem",
49
+ }}
50
+ >
51
+ <TextInput
52
+ id="text"
53
+ type="text"
54
+ labelText={t("sessionName", "Session Name")}
55
+ {...register("sessionName", { required: true })}
56
+ invalid={errors.sessionName}
57
+ invalidText={"This field is required"}
58
+ />
59
+ <TextInput
60
+ id="text"
61
+ type="text"
62
+ labelText={t("practitionerName", "Practitioner Name")}
63
+ {...register("practitionerName", { required: true })}
64
+ invalid={errors.practitionerName}
65
+ invalidText={"This field is required"}
66
+ />
67
+ <Controller
68
+ name="sessionDate"
69
+ control={control}
70
+ rules={{ required: true }}
71
+ render={({ field }) => (
72
+ <DatePicker
73
+ datePickerType="single"
74
+ size="md"
75
+ maxDate={new Date()}
76
+ {...field}
77
+ >
78
+ <DatePickerInput
79
+ id="session-date"
80
+ labelText={t("sessionDate", "Session Date")}
81
+ placeholder="mm/dd/yyyy"
82
+ size="md"
83
+ invalid={errors.sessionDate}
84
+ invalidText={"This field is required"}
85
+ />
86
+ </DatePicker>
87
+ )}
88
+ />
89
+ <TextArea
90
+ id="text"
91
+ type="text"
92
+ labelText={t("sessionNotes", "Session Notes")}
93
+ {...register("sessionNotes", { required: true })}
94
+ invalid={errors.sessionNotes}
95
+ invalidText={"This field is required"}
96
+ />
97
+ </div>
98
+ </Layer>
99
+ </Tile>
91
100
  </Layer>
92
- </Tile>
93
- </Layer>
94
- <h4>{t("sessionParticipants", "2. Session participants")}</h4>
95
- <div>
96
- <p>
97
- {t(
98
- "markAbsentPatients",
99
- "The patients in this group. Patients that are not present in the session should be marked as absent."
100
- )}
101
- </p>
102
- </div>
103
- <Layer>
104
- <Tile className={styles.formSectionTile}>
101
+ <h4>{t("sessionParticipants", "2. Session participants")}</h4>
102
+ <div>
103
+ <p>
104
+ {t(
105
+ "markAbsentPatients",
106
+ "The patients in this group. Patients that are not present in the session should be marked as absent."
107
+ )}
108
+ </p>
109
+ </div>
105
110
  <Layer>
106
- <div
107
- style={{
108
- display: "flex",
109
- flexDirection: "column",
110
- rowGap: "1.5rem",
111
- }}
112
- >
113
- <AttendanceTable />
114
- </div>
111
+ <Tile className={styles.formSectionTile}>
112
+ <Layer>
113
+ <div
114
+ style={{
115
+ display: "flex",
116
+ flexDirection: "column",
117
+ rowGap: "1.5rem",
118
+ }}
119
+ >
120
+ <AttendanceTable patients={patients} />
121
+ </div>
122
+ </Layer>
123
+ </Tile>
115
124
  </Layer>
116
- </Tile>
117
- </Layer>
125
+ </div>
126
+ )}
118
127
  </div>
119
128
  );
120
129
  };