From afdd0f84264ca3c509ebbd9eb8305da04b722239 Mon Sep 17 00:00:00 2001 From: Woo Jin Lee Date: Wed, 29 May 2024 22:07:12 +1200 Subject: [PATCH 1/3] fix(manage-groups): fix groups table only showing a single group. --- .../pages/ManageGroups/ManageGroups.jsx | 0 .../pages/ManageGroups/ManageGroupsPage.jsx | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 frontend/src/containers/pages/ManageGroups/ManageGroups.jsx diff --git a/frontend/src/containers/pages/ManageGroups/ManageGroups.jsx b/frontend/src/containers/pages/ManageGroups/ManageGroups.jsx new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx b/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx index 70f9f7c0..1a457f17 100644 --- a/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx +++ b/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx @@ -1,12 +1,12 @@ import { Button } from "@material-ui/core"; -import { useRef, useState } from "react"; -import { useParams } from "react-router-dom"; -import Papa from "papaparse"; -import ScreenContainer from "components/ScreenContainer"; import axios from "axios"; +import ScreenContainer from "components/ScreenContainer"; import { useGet } from "hooks/crudHooks"; -import TopBar from "./TopBar"; +import Papa from "papaparse"; +import { useRef, useState } from "react"; +import { useParams } from "react-router-dom"; import GroupsTable from "./GroupTable"; +import TopBar from "./TopBar"; /** * Page that shows the groups that the admin can manipulate @@ -17,15 +17,21 @@ import GroupsTable from "./GroupTable"; export default function ManageGroupsPage() { const { scenarioId } = useParams(); const [scenarioGroupInfo, setScenarioGroupInfo] = useState([]); - let groups; + let users = []; // fetch groups assigned to this scenario const fetchGroups = () => { useGet(`/api/group/scenario/${scenarioId}`, setScenarioGroupInfo); - if (scenarioGroupInfo[0]) { - groups = scenarioGroupInfo[0].users; + console.log(scenarioGroupInfo); + if (scenarioGroupInfo.length) { + // Iterate groups and flatten to user list + scenarioGroupInfo.forEach((group) => { + if (group) { + users.push(...group.users); + } + }); } else { - groups = []; + users = []; } }; @@ -131,7 +137,7 @@ export default function ManageGroupsPage() { }; const download = () => { - const csv = convertToCSV(groups); + const csv = convertToCSV(users); const blob = new Blob([csv], { type: "text/csv" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); @@ -171,7 +177,7 @@ export default function ManageGroupsPage() { - + ); } From a5887aed6dc9150ed7b6dbed7fd81eeaeea5a4cc Mon Sep 17 00:00:00 2001 From: Woo Jin Lee Date: Wed, 29 May 2024 22:27:22 +1200 Subject: [PATCH 2/3] feat(manage-groups): add user feedback for CSV upload status via toasts. --- .../pages/ManageGroups/ManageGroupsPage.jsx | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx b/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx index 1a457f17..fb93138a 100644 --- a/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx +++ b/frontend/src/containers/pages/ManageGroups/ManageGroupsPage.jsx @@ -1,4 +1,5 @@ import { Button } from "@material-ui/core"; +import { Alert, Snackbar } from "@mui/material"; import axios from "axios"; import ScreenContainer from "components/ScreenContainer"; import { useGet } from "hooks/crudHooks"; @@ -13,30 +14,39 @@ import TopBar from "./TopBar"; * * @container */ - export default function ManageGroupsPage() { const { scenarioId } = useParams(); const [scenarioGroupInfo, setScenarioGroupInfo] = useState([]); + + const [isToastShowing, setIsToastShowing] = useState(false); + const [toastText, setToastText] = useState(""); + const [toastType, setToastType] = useState(""); + let users = []; // fetch groups assigned to this scenario - const fetchGroups = () => { - useGet(`/api/group/scenario/${scenarioId}`, setScenarioGroupInfo); - console.log(scenarioGroupInfo); - if (scenarioGroupInfo.length) { - // Iterate groups and flatten to user list - scenarioGroupInfo.forEach((group) => { - if (group) { - users.push(...group.users); - } - }); - } else { - users = []; - } + const { reFetch } = useGet( + `/api/group/scenario/${scenarioId}`, + setScenarioGroupInfo + ); + console.log(scenarioGroupInfo); + if (scenarioGroupInfo.length) { + // Iterate groups and flatten to user list + scenarioGroupInfo.forEach((group) => { + if (group) { + users.push(...group.users); + } + }); + } else { + users = []; + } + + const showToast = (text, type = "success") => { + setToastText(text); + setToastType(type); + setIsToastShowing(true); }; - fetchGroups(); - // File input is a hidden input element that is activated via a click handler // This allows us to have an UI button that acts like a file element. const fileInputRef = useRef(null); @@ -94,12 +104,12 @@ export default function ManageGroupsPage() { jsonData ); + reFetch(); console.log("CSV data uploaded to MongoDB:", response.status); - // TODO: ESLINT ignore; change alerts so unexpected alerts error goes away - // alert("CSV successfully uploaded and data stored in MongoDB!"); + showToast("Successfully formed groups!"); } catch (error) { console.error("Error uploading CSV data:", error); - // alert("Error uploading CSV data."); + showToast("Error uploading CSV data!", "error"); } }, }); @@ -149,6 +159,14 @@ export default function ManageGroupsPage() { window.URL.revokeObjectURL(url); }; + // Toast close handler + const handleToastDismiss = (_, reason) => { + if (reason === "clickaway") { + return; + } + setIsToastShowing(false); + }; + return ( @@ -178,8 +196,20 @@ export default function ManageGroupsPage() { + + + {toastText} + + ); } - -// export FileUpload; From 52d79e53326e8bdf458212154ec25b49baacfcd7 Mon Sep 17 00:00:00 2001 From: Woo Jin Lee Date: Wed, 29 May 2024 23:08:51 +1200 Subject: [PATCH 3/3] fix(invalid-role): fix notes menus on invalid role page. --- frontend/src/components/NotesDisplayCard.jsx | 19 +++++++-------- frontend/src/containers/App.jsx | 12 ++-------- .../src/containers/pages/InvalidRolePage.jsx | 23 ++++++++++++++----- .../PlayScenarioPage/PlayScenarioResolver.jsx | 10 +++++--- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/frontend/src/components/NotesDisplayCard.jsx b/frontend/src/components/NotesDisplayCard.jsx index a1bf4d33..824c6374 100644 --- a/frontend/src/components/NotesDisplayCard.jsx +++ b/frontend/src/components/NotesDisplayCard.jsx @@ -1,5 +1,5 @@ -import { useState, useEffect } from "react"; import { usePost } from "hooks/crudHooks"; +import { useEffect, useState } from "react"; import styles from "../styling/NotesDisplayCard.module.scss"; import Note from "./Note"; @@ -16,25 +16,22 @@ export default function NotesDisplayCard({ group, user, handleClose }) { } // refetch group data to get updated notes - async function loadGroup() { + async function fetchNotesData() { const groupData = await usePost("/api/group/", { groupId: group._id, }); - loadNotes(groupData); + await loadNotes(groupData); } - const checkRole = () => { + useEffect(() => { + // Check roles group.users.forEach((userToCheck) => { if (userToCheck.email === user.email) { setRole(userToCheck.role); } }); - }; - useEffect(() => { - console.log(user); - loadGroup(); - checkRole(); + fetchNotesData(); }, []); const handleCreate = async () => { @@ -47,7 +44,7 @@ export default function NotesDisplayCard({ group, user, handleClose }) { role: userRole, }); console.log("note created"); - loadGroup(); + fetchNotesData(); }; const handleKeyPress = (e) => { @@ -77,7 +74,7 @@ export default function NotesDisplayCard({ group, user, handleClose }) { id={note.id} group={group} user={user} - refetchGroup={loadGroup} + refetchGroup={fetchNotesData} /> ))}
- - - diff --git a/frontend/src/containers/pages/InvalidRolePage.jsx b/frontend/src/containers/pages/InvalidRolePage.jsx index 590a3452..d1bf768f 100644 --- a/frontend/src/containers/pages/InvalidRolePage.jsx +++ b/frontend/src/containers/pages/InvalidRolePage.jsx @@ -1,9 +1,10 @@ +import { Button } from "@mui/material"; import { useContext, useState } from "react"; -import AuthenticationContext from "../../context/AuthenticationContext"; -import NotesDisplayCard from "../../components/NotesDisplayCard"; import BacktoScenarioSelectionButton from "../../components/BacktoScenarioSelectionButton"; +import NotesDisplayCard from "../../components/NotesDisplayCard"; +import AuthenticationContext from "../../context/AuthenticationContext"; -function InvalidRolePage(group) { +function InvalidRolePage({ group }) { const currentUserRole = "Doctor"; const rolesWithAccess = ["Nurse", "Patient"]; const [noteOpen, setNoteOpen] = useState(false); @@ -42,13 +43,23 @@ function InvalidRolePage(group) { Someone else is playing through this section of the scenario!

Please wait for your role: {currentUserRole}

+ +

+ — or — +

Roles with access to this scene: {rolesWithAccess.join(", ")}

- {noteOpen && ( )} diff --git a/frontend/src/containers/pages/PlayScenarioPage/PlayScenarioResolver.jsx b/frontend/src/containers/pages/PlayScenarioPage/PlayScenarioResolver.jsx index e0a4cf94..6291f1e8 100644 --- a/frontend/src/containers/pages/PlayScenarioPage/PlayScenarioResolver.jsx +++ b/frontend/src/containers/pages/PlayScenarioPage/PlayScenarioResolver.jsx @@ -1,4 +1,5 @@ -import { useEffect, useContext, useState } from "react"; +import axios from "axios"; +import { useContext, useEffect, useState } from "react"; import { Route, Switch, @@ -6,14 +7,14 @@ import { useLocation, useParams, } from "react-router-dom"; -import axios from "axios"; import AuthenticationContext from "context/AuthenticationContext"; import useGraph from "hooks/useGraph"; +import DesyncPage from "../DesyncPage"; +import InvalidRolePage from "../InvalidRolePage"; import PlayScenarioPage from "./PlayScenarioPage"; import PlayScenarioPageMulti from "./PlayScenarioPageMulti"; -import DesyncPage from "../DesyncPage"; // TODO: move this somewhere else and add error handling async function get(url, userIdToken) { @@ -54,6 +55,9 @@ export default function PlayScenarioResolver() { return ( + + +