From 53c07fd20a70a012be49f0ade229cd25269ba62f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 31 Aug 2021 19:09:22 -0700 Subject: [PATCH 1/5] Fixed how adding a new role got rid of dropdown/ban/remove options and made the dropdown change state when new role added --- .../src/components/configPage/ConfigPanel.js | 10 ++++++- .../roleConfigComponents/RolePanel.js | 16 +++++++++-- .../userConfigComponents/UserPanel.js | 28 ++++++++++--------- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/client/src/components/configPage/ConfigPanel.js b/client/src/components/configPage/ConfigPanel.js index 95c27036..f3f50fd6 100644 --- a/client/src/components/configPage/ConfigPanel.js +++ b/client/src/components/configPage/ConfigPanel.js @@ -264,7 +264,15 @@ const ConfigPanel = ({ courseRoles != null ? [...courseRoles, role] : [role]; setCourseRoles(newCourseRoles); - setUserList(GenerateUserList(courseUsers, newCourseRoles)); + setUserList( + GenerateUserList( + courseUsers, + newCourseRoles, + userRole.admin.configure, + userRole.admin.banUsers, + userRole.admin.removeUsers + ) + ); }, onFailure: (err) => { console.log("Failed to Post Roles.", err?.response); diff --git a/client/src/components/configPage/roleConfigComponents/RolePanel.js b/client/src/components/configPage/roleConfigComponents/RolePanel.js index bd65affc..daaf6af2 100644 --- a/client/src/components/configPage/roleConfigComponents/RolePanel.js +++ b/client/src/components/configPage/roleConfigComponents/RolePanel.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; import { useParams } from "react-router-dom"; import PropTypes from "prop-types"; import styled, { css } from "styled-components"; @@ -12,6 +12,7 @@ import UserPanel from "../userConfigComponents/UserPanel"; // MATERIAL UI ----------------------------------------------------------- import { makeStyles, useTheme } from "@material-ui/core/styles"; import LazyFetch from "../../common/requests/LazyFetch"; +import { UserRoleContext } from "../../context/UserRoleProvider"; const useStyles = makeStyles((theme) => ({ formControl: { @@ -23,8 +24,9 @@ const useStyles = makeStyles((theme) => ({ /** * Generates a list of User Components for State Management */ -const GenerateUserList = (users, roles) => { +const GenerateUserList = (users, roles, displayDropdown) => { var test_simple_role = { roleName: "Regular User", roleColor: "#55cc88" }; + console.log("displayDropdown in RolePanel:", displayDropdown); return users.map((user, index) => ( { userImg={user.userImg} userRole={test_simple_role} allRoles={roles} + displayDropdown={displayDropdown} /> )); }; @@ -49,6 +52,7 @@ const RolePanel = ({ }) => { // MATERIAL UI -------------------------------- const styleClasses = useStyles(); + const userRole = useContext(UserRoleContext); const [publishAnchorEl, setPublishAnchorEl] = useState(null); @@ -575,7 +579,13 @@ const RolePanel = ({ // console.log("After Filter: ", newRolesList); setCourseRoles(newRolesList); - setUserList(GenerateUserList(userList, newRolesList)); + setUserList( + GenerateUserList( + userList, + newRolesList, + userRole.admin.configure + ) + ); }, onFailure: (err) => { console.log( diff --git a/client/src/components/configPage/userConfigComponents/UserPanel.js b/client/src/components/configPage/userConfigComponents/UserPanel.js index e4e37705..807f93fc 100644 --- a/client/src/components/configPage/userConfigComponents/UserPanel.js +++ b/client/src/components/configPage/userConfigComponents/UserPanel.js @@ -70,20 +70,22 @@ const UserPanel = ({ const [instructorId, setInstructorId] = useState(null); const [roleName, setRoleName] = useState(name); - // Varaible to store all of the dropdown options - let realRoleOptions = - allRoles != null - ? GenerateRoleOptions(allRoles, courseId, userId, setRoleName) - : [ - { - onClick: () => { - alert("NULL Role selected"); - }, - label: "NULL", - }, - ]; + // Dropdown options initially set to a temporary null value + const [roleOptions, setRoleOptions] = useState([ + { + onClick: () => { + alert("NULL Role selected"); + }, + label: "NULL", + }, + ]); - const [roleOptions, setRoleOptions] = useState(realRoleOptions); + // Change dropdown options each time a new role is added + useEffect(() => { + setRoleOptions( + GenerateRoleOptions(allRoles, courseId, userId, setRoleName) + ); + }, [allRoles]); // Grab the instructor ID for display purposes later useEffect(() => { From 24fb7efbf217dfa4765e17876138e2adcbe6a5dd Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 31 Aug 2021 19:15:35 -0700 Subject: [PATCH 2/5] Fixed small cursor styling issue --- .../configPage/userConfigComponents/UserPanel.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/client/src/components/configPage/userConfigComponents/UserPanel.js b/client/src/components/configPage/userConfigComponents/UserPanel.js index 807f93fc..1b53017b 100644 --- a/client/src/components/configPage/userConfigComponents/UserPanel.js +++ b/client/src/components/configPage/userConfigComponents/UserPanel.js @@ -157,13 +157,11 @@ const UserPanel = ({ borderColor={userRole.roleColor ? userRole.roleColor : "#e7e7e7"} > - - - {roleName} - + + {roleName} From bc975f01907fa9a0fbf3ed7bc920edf59ce464f3 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 31 Aug 2021 20:21:54 -0700 Subject: [PATCH 3/5] Updated GenerateUserList function so that upon deleting a role, state maintains it's form instead of not loading anything --- .../roleConfigComponents/RolePanel.js | 928 +++++++++--------- 1 file changed, 475 insertions(+), 453 deletions(-) diff --git a/client/src/components/configPage/roleConfigComponents/RolePanel.js b/client/src/components/configPage/roleConfigComponents/RolePanel.js index daaf6af2..2f2708e0 100644 --- a/client/src/components/configPage/roleConfigComponents/RolePanel.js +++ b/client/src/components/configPage/roleConfigComponents/RolePanel.js @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import PropTypes from "prop-types"; import styled, { css } from "styled-components"; @@ -24,20 +24,39 @@ const useStyles = makeStyles((theme) => ({ /** * Generates a list of User Components for State Management */ -const GenerateUserList = (users, roles, displayDropdown) => { - var test_simple_role = { roleName: "Regular User", roleColor: "#55cc88" }; - console.log("displayDropdown in RolePanel:", displayDropdown); - - return users.map((user, index) => ( - - )); +const GenerateUserList = ( + users, + roles, + displayDropdown, + displayBan, + displayRemove +) => { + var userRole = { name: "null", roleColor: "#e7e7e7" }; + var numRoles = roles.length; + // console.log("roles:", roles); + + return users.map((user, index) => { + console.log("user:", user.props); + for (let i = 0; i < numRoles; i++) { + if (roles[i]._id == user.props.userRole._id) { + userRole = roles[i]; + } + } + + return ( + + ); + }); }; const RolePanel = ({ @@ -131,476 +150,479 @@ const RolePanel = ({ } }; + const handleRoleDelete = () => { + LazyFetch({ + type: "delete", + endpoint: "/courses/" + urlParams.courseId + "/roles", + data: { + roleId: roleObject._id, + }, + onSuccess: (data) => { + console.log("Successful delete of role: ", roleObject.name, ". ", data); + // let newRolesList = ; + let newRolesList = courseRoles.filter((role) => { + // console.log( + // "role._id: ", + // role._id, + // " , roleObject._id: ", + // roleObject._id + // ); + return role._id != roleObject._id; + }); + // console.log("After Filter: ", newRolesList); + setCourseRoles(newRolesList); + + setUserList( + GenerateUserList( + userList, + newRolesList, + userRole.admin.configure, + userRole.admin.banUsers, + userRole.admin.removeUsers + ) + ); + }, + onFailure: (err) => { + console.log(err.response.data); + console.log( + "Failed to delete role: ", + roleObject.name, + ". ", + err?.response + ); + }, + }); + }; + // console.log("This role id: ", props.value); return ( - - - {nameFieldState ? ( - {nameField} - ) : ( - + + + {nameFieldState ? ( + {nameField} + ) : ( + + {nameField} + + )} + + - - - {/* PUBLISH DROPDOWN */} - { - roleObject.permissions.publish.question = val; - setPublishCheckedState({ - ...publishCheckedState, - question: val, - }); + + + + + {/* PUBLISH DROPDOWN */} + { + roleObject.permissions.publish.question = val; + setPublishCheckedState({ + ...publishCheckedState, + question: val, + }); + }, }, - }, - { - stateLabel: "announcement", - itemLabel: "Users in this role can publish Announcements", - changeRoleVal: (val) => { - roleObject.permissions.publish.announcement = val; - setPublishCheckedState({ - ...publishCheckedState, - announcement: val, - }); + { + stateLabel: "announcement", + itemLabel: "Users in this role can publish Announcements", + changeRoleVal: (val) => { + roleObject.permissions.publish.announcement = val; + setPublishCheckedState({ + ...publishCheckedState, + announcement: val, + }); + }, }, - }, - { - stateLabel: "poll", - itemLabel: "Users in this role can publish Polls", - changeRoleVal: (val) => { - roleObject.permissions.publish.poll = val; - setPublishCheckedState({ - ...publishCheckedState, - poll: val, - }); + { + stateLabel: "poll", + itemLabel: "Users in this role can publish Polls", + changeRoleVal: (val) => { + roleObject.permissions.publish.poll = val; + setPublishCheckedState({ + ...publishCheckedState, + poll: val, + }); + }, }, - }, - { - stateLabel: "general", - itemLabel: "Users in this role can publish General Posts", - changeRoleVal: (val) => { - roleObject.permissions.publish.general = val; - setPublishCheckedState({ - ...publishCheckedState, - general: val, - }); + { + stateLabel: "general", + itemLabel: "Users in this role can publish General Posts", + changeRoleVal: (val) => { + roleObject.permissions.publish.general = val; + setPublishCheckedState({ + ...publishCheckedState, + general: val, + }); + }, }, - }, - { - stateLabel: "comment", - itemLabel: "Users in this role can publish comments on posts", - changeRoleVal: (val) => { - roleObject.permissions.publish.comment = val; - setPublishCheckedState({ - ...publishCheckedState, - comment: val, - }); + { + stateLabel: "comment", + itemLabel: "Users in this role can publish comments on posts", + changeRoleVal: (val) => { + roleObject.permissions.publish.comment = val; + setPublishCheckedState({ + ...publishCheckedState, + comment: val, + }); + }, }, - }, - { - stateLabel: "reply", - itemLabel: "Users in this role can reply to comments", - changeRoleVal: (val) => { - roleObject.permissions.publish.reply = val; - setPublishCheckedState({ - ...publishCheckedState, - reply: val, - }); + { + stateLabel: "reply", + itemLabel: "Users in this role can reply to comments", + changeRoleVal: (val) => { + roleObject.permissions.publish.reply = val; + setPublishCheckedState({ + ...publishCheckedState, + reply: val, + }); + }, }, - }, - ], - }} - > - - {/* DELETE DROPDOWN */} - { - roleObject.permissions.delete.question = val; - setDeleteCheckedState({ - ...deleteCheckedState, - question: val, - }); + ], + }} + > + + {/* DELETE DROPDOWN */} + { + roleObject.permissions.delete.question = val; + setDeleteCheckedState({ + ...deleteCheckedState, + question: val, + }); + }, }, - }, - { - stateLabel: "announcement", - itemLabel: - "Users in this role can delete announcements they made", - changeRoleVal: (val) => { - roleObject.permissions.delete.announcement = val; - setDeleteCheckedState({ - ...deleteCheckedState, - announcement: val, - }); + { + stateLabel: "announcement", + itemLabel: + "Users in this role can delete announcements they made", + changeRoleVal: (val) => { + roleObject.permissions.delete.announcement = val; + setDeleteCheckedState({ + ...deleteCheckedState, + announcement: val, + }); + }, }, - }, - { - stateLabel: "poll", - itemLabel: "Users in this role can delete polls they made", - changeRoleVal: (val) => { - roleObject.permissions.delete.poll = val; - setDeleteCheckedState({ - ...deleteCheckedState, - poll: val, - }); + { + stateLabel: "poll", + itemLabel: "Users in this role can delete polls they made", + changeRoleVal: (val) => { + roleObject.permissions.delete.poll = val; + setDeleteCheckedState({ + ...deleteCheckedState, + poll: val, + }); + }, }, - }, - { - stateLabel: "general", - itemLabel: - "Users in this role can delete general posts they made", - changeRoleVal: (val) => { - roleObject.permissions.delete.general = val; - setDeleteCheckedState({ - ...deleteCheckedState, - general: val, - }); + { + stateLabel: "general", + itemLabel: + "Users in this role can delete general posts they made", + changeRoleVal: (val) => { + roleObject.permissions.delete.general = val; + setDeleteCheckedState({ + ...deleteCheckedState, + general: val, + }); + }, }, - }, - { - stateLabel: "comment", - itemLabel: "Users in this role can delete comments they made", - changeRoleVal: (val) => { - roleObject.permissions.delete.comment = val; - setDeleteCheckedState({ - ...deleteCheckedState, - comment: val, - }); + { + stateLabel: "comment", + itemLabel: "Users in this role can delete comments they made", + changeRoleVal: (val) => { + roleObject.permissions.delete.comment = val; + setDeleteCheckedState({ + ...deleteCheckedState, + comment: val, + }); + }, }, - }, - { - stateLabel: "reply", - itemLabel: "Users in this role can delete replies they made", - changeRoleVal: (val) => { - roleObject.permissions.delete.reply = val; - setDeleteCheckedState({ - ...deleteCheckedState, - reply: val, - }); + { + stateLabel: "reply", + itemLabel: "Users in this role can delete replies they made", + changeRoleVal: (val) => { + roleObject.permissions.delete.reply = val; + setDeleteCheckedState({ + ...deleteCheckedState, + reply: val, + }); + }, }, - }, - ], - }} - > - - {/* EDIT DROPDOWN */} - { - roleObject.permissions.edit.question = val; - setEditCheckedState({ - ...editCheckedState, - question: val, - }); + ], + }} + > + + {/* EDIT DROPDOWN */} + { + roleObject.permissions.edit.question = val; + setEditCheckedState({ + ...editCheckedState, + question: val, + }); + }, }, - }, - { - stateLabel: "announcement", - itemLabel: - "Users in this role can edit announcements that they made", - changeRoleVal: (val) => { - roleObject.permissions.edit.announcement = val; - setEditCheckedState({ - ...editCheckedState, - announcement: val, - }); + { + stateLabel: "announcement", + itemLabel: + "Users in this role can edit announcements that they made", + changeRoleVal: (val) => { + roleObject.permissions.edit.announcement = val; + setEditCheckedState({ + ...editCheckedState, + announcement: val, + }); + }, }, - }, - { - stateLabel: "poll", - itemLabel: "Users in this roll can edit polls that they made", - changeRoleVal: (val) => { - roleObject.permissions.edit.poll = val; - setEditCheckedState({ - ...editCheckedState, - poll: val, - }); + { + stateLabel: "poll", + itemLabel: "Users in this roll can edit polls that they made", + changeRoleVal: (val) => { + roleObject.permissions.edit.poll = val; + setEditCheckedState({ + ...editCheckedState, + poll: val, + }); + }, }, - }, - { - stateLabel: "general", - itemLabel: - "Users in this role can edit general posts that they made", - changeRoleVal: (val) => { - roleObject.permissions.edit.general = val; - setEditCheckedState({ - ...editCheckedState, - general: val, - }); + { + stateLabel: "general", + itemLabel: + "Users in this role can edit general posts that they made", + changeRoleVal: (val) => { + roleObject.permissions.edit.general = val; + setEditCheckedState({ + ...editCheckedState, + general: val, + }); + }, }, - }, - { - stateLabel: "comment", - itemLabel: "Users in this role can edit comments that they made", - changeRoleVal: (val) => { - roleObject.permissions.edit.comment = val; - setEditCheckedState({ - ...editCheckedState, - comment: val, - }); + { + stateLabel: "comment", + itemLabel: + "Users in this role can edit comments that they made", + changeRoleVal: (val) => { + roleObject.permissions.edit.comment = val; + setEditCheckedState({ + ...editCheckedState, + comment: val, + }); + }, }, - }, - { - stateLabel: "reply", - itemLabel: "Users in this role can edit replies that they made", - changeRoleVal: (val) => { - roleObject.permissions.edit.reply = val; - setEditCheckedState({ - ...editCheckedState, - reply: val, - }); + { + stateLabel: "reply", + itemLabel: "Users in this role can edit replies that they made", + changeRoleVal: (val) => { + roleObject.permissions.edit.reply = val; + setEditCheckedState({ + ...editCheckedState, + reply: val, + }); + }, }, - }, - ], - }} - > - - {/* PARTICIPATION DROPDOWN */} - { - roleObject.permissions.participation.reactions = val; - setParticipationCheckedState({ - ...participationCheckedState, - reactions: val, - }); + ], + }} + > + + {/* PARTICIPATION DROPDOWN */} + { + roleObject.permissions.participation.reactions = val; + setParticipationCheckedState({ + ...participationCheckedState, + reactions: val, + }); + }, }, - }, - { - stateLabel: "voteInPoll", - itemLabel: "Users in this role can vote in all polls", - changeRoleVal: (val) => { - roleObject.permissions.participation.voteInPoll = val; - setParticipationCheckedState({ - ...participationCheckedState, - voteInPoll: val, - }); + { + stateLabel: "voteInPoll", + itemLabel: "Users in this role can vote in all polls", + changeRoleVal: (val) => { + roleObject.permissions.participation.voteInPoll = val; + setParticipationCheckedState({ + ...participationCheckedState, + voteInPoll: val, + }); + }, }, - }, - { - stateLabel: "pin", - itemLabel: "Users in this role can can pin all types of posts", - changeRoleVal: (val) => { - roleObject.permissions.participation.pin = val; - setParticipationCheckedState({ - ...participationCheckedState, - pin: val, - }); + { + stateLabel: "pin", + itemLabel: "Users in this role can can pin all types of posts", + changeRoleVal: (val) => { + roleObject.permissions.participation.pin = val; + setParticipationCheckedState({ + ...participationCheckedState, + pin: val, + }); + }, }, - }, - ], - }} - > - - {/* PRIVACY DROPDOWN */} - { - roleObject.permissions.privacy.private = val; - setPrivacyCheckedState({ - ...privacyCheckedState, - private: val, - }); + ], + }} + > + + {/* PRIVACY DROPDOWN */} + { + roleObject.permissions.privacy.private = val; + setPrivacyCheckedState({ + ...privacyCheckedState, + private: val, + }); + }, }, - }, - { - stateLabel: "anonymous", - itemLabel: - "Users in this role can draft anonymous posts of all types, comments, and replies", - changeRoleVal: (val) => { - roleObject.permissions.privacy.anonymous = val; - setPrivacyCheckedState({ - ...privacyCheckedState, - anonymous: val, - }); + { + stateLabel: "anonymous", + itemLabel: + "Users in this role can draft anonymous posts of all types, comments, and replies", + changeRoleVal: (val) => { + roleObject.permissions.privacy.anonymous = val; + setPrivacyCheckedState({ + ...privacyCheckedState, + anonymous: val, + }); + }, }, - }, - ], - }} - > - - {/* ADMIN DROPDOWN */} - { - roleObject.permissions.admin.banUsers = val; - setAdminCheckedState({ - ...adminCheckedState, - banUsers: val, - }); + ], + }} + > + + {/* ADMIN DROPDOWN */} + { + roleObject.permissions.admin.banUsers = val; + setAdminCheckedState({ + ...adminCheckedState, + banUsers: val, + }); + }, }, - }, - { - stateLabel: "removeUsers", - itemLabel: - "Users in this role can remove users in the course (does not include course creator)", - changeRoleVal: (val) => { - roleObject.permissions.admin.removeUsers = val; - setAdminCheckedState({ - ...adminCheckedState, - removeUsers: val, - }); + { + stateLabel: "removeUsers", + itemLabel: + "Users in this role can remove users in the course (does not include course creator)", + changeRoleVal: (val) => { + roleObject.permissions.admin.removeUsers = val; + setAdminCheckedState({ + ...adminCheckedState, + removeUsers: val, + }); + }, }, - }, - { - stateLabel: "deleteOther", - itemLabel: - "Users in this role can delete other user's posts / comments / replies", - changeRoleVal: (val) => { - roleObject.permissions.admin.deleteOther = val; - setAdminCheckedState({ - ...adminCheckedState, - deleteOther: val, - }); + { + stateLabel: "deleteOther", + itemLabel: + "Users in this role can delete other user's posts / comments / replies", + changeRoleVal: (val) => { + roleObject.permissions.admin.deleteOther = val; + setAdminCheckedState({ + ...adminCheckedState, + deleteOther: val, + }); + }, }, - }, - { - stateLabel: "configure", - itemLabel: - "Users in this role can edit course configurations (this includes user roles and the ability to delete the course)", - changeRoleVal: (val) => { - roleObject.permissions.admin.configure = val; - setAdminCheckedState({ - ...adminCheckedState, - configure: val, - }); + { + stateLabel: "configure", + itemLabel: + "Users in this role can edit course configurations (this includes user roles and the ability to delete the course)", + changeRoleVal: (val) => { + roleObject.permissions.admin.configure = val; + setAdminCheckedState({ + ...adminCheckedState, + configure: val, + }); + }, }, - }, - { - stateLabel: "highlightName", - itemLabel: - "Users in this role with this permission will have their name highlighted orange every time they create a post, comment, or reply", - changeRoleVal: (val) => { - roleObject.permissions.admin.highlightName = val; - setAdminCheckedState({ - ...adminCheckedState, - highlightName: val, - }); + { + stateLabel: "highlightName", + itemLabel: + "Users in this role with this permission will have their name highlighted orange every time they create a post, comment, or reply", + changeRoleVal: (val) => { + roleObject.permissions.admin.highlightName = val; + setAdminCheckedState({ + ...adminCheckedState, + highlightName: val, + }); + }, }, - }, - ], - }} - > - - - + ], + }} + > + + + + ); }; From 99c1604362674d6cc2fd67b15cae769a5a57541e Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 31 Aug 2021 20:36:55 -0700 Subject: [PATCH 4/5] Start work on displaying errors for config page --- .../src/components/configPage/ConfigPanel.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/src/components/configPage/ConfigPanel.js b/client/src/components/configPage/ConfigPanel.js index f3f50fd6..342e722f 100644 --- a/client/src/components/configPage/ConfigPanel.js +++ b/client/src/components/configPage/ConfigPanel.js @@ -8,6 +8,8 @@ import UserPanel from "./userConfigComponents/UserPanel"; import LazyFetch from "../common/requests/LazyFetch"; import LoadingDots from "../common/animation/LoadingDots"; import { UserRoleContext } from "../context/UserRoleProvider"; +import Errors from "../common/Errors"; +import { FormHelperText } from "@material-ui/core"; const colorTest = [ "#dd0000", @@ -147,7 +149,7 @@ const GenerateUserList = ( }); }; -const GenerateBannedUserList = (blacklist, displayBan) => { +const GenerateBannedUserList = (blacklist, displayBan, setBannedErrors) => { if (!blacklist) { return <>; } @@ -160,6 +162,7 @@ const GenerateBannedUserList = (blacklist, displayBan) => { userImg={bannedUser.userImg} unbanList={true} displayBan={displayBan} + setBannedErrors={setBannedErrors} /> ); }); @@ -180,6 +183,9 @@ const ConfigPanel = ({ const [bannedUserList, setBannedUserList] = useState(null); const [numBannedUsers, changeNumBanned] = useState(0); const [displayBanned, setDisplayBanned] = useState(false); + const [configErrors, setConfigErrors] = useState(null); + const [assignErrors, setAssignErrors] = useState(null); + const [bannedErrors, setBannedErrors] = useState(null); // Grab the instructor ID for display purposes later useEffect(() => { if (!bannedUserList) { @@ -192,7 +198,11 @@ const ConfigPanel = ({ changeNumBanned(data.success.length); } setBannedUserList( - GenerateBannedUserList(data.success, userRole.admin.banUsers) + GenerateBannedUserList( + data.success, + userRole.admin.banUsers, + setBannedErrors + ) ); }, onFailure: () => { @@ -242,6 +252,7 @@ const ConfigPanel = ({ ) : ( realRoleList )} + + )} {userRole.admin.configure && ( @@ -326,10 +338,12 @@ const ConfigPanel = ({ panelHeader={"Assign roles to participants of this course here."} > {userList} + {displayBanned ? ( {bannedUserList} + ) : ( <> From d9875ce896929495b19ff7144cceb8fbde3da654 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 1 Sep 2021 18:01:08 -0700 Subject: [PATCH 5/5] Added changes for error messages to be added to config page Added 3 separate error message sections for the config page: configErrors, assignErrors, and banErrors. These are split into three so there is one for each respective section. --- .../src/components/configPage/ConfigPanel.js | 61 ++++++++++++++----- .../roleConfigComponents/RolePanel.js | 10 +-- .../userConfigComponents/UserPanel.js | 38 +++++++++--- server/inquire/resources/course_users.py | 56 +++++++++++------ server/inquire/resources/roles.py | 56 ++++++++++++----- 5 files changed, 153 insertions(+), 68 deletions(-) diff --git a/client/src/components/configPage/ConfigPanel.js b/client/src/components/configPage/ConfigPanel.js index 342e722f..a739c508 100644 --- a/client/src/components/configPage/ConfigPanel.js +++ b/client/src/components/configPage/ConfigPanel.js @@ -99,7 +99,13 @@ const createRoleObject = (itemId) => { /** * Generates a list of Role Components for State Management */ -const GenerateRoleList = (roles, setRoles, userList, setUserList) => { +const GenerateRoleList = ( + roles, + setRoles, + userList, + setUserList, + setConfigErrors +) => { return roles.map((role, index) => ( { setCourseRoles={setRoles} userList={userList} setUserList={setUserList} + setConfigErrors={setConfigErrors} /> )); }; @@ -123,7 +130,8 @@ const GenerateUserList = ( roles, displayDropdown, displayBan, - displayRemove + displayRemove, + setAssignErrors ) => { var userRole = { name: "null", roleColor: "#e7e7e7" }; @@ -144,12 +152,13 @@ const GenerateUserList = ( displayDropdown={displayDropdown} displayBan={displayBan} displayRemove={displayRemove} + setAssignErrors={setAssignErrors} /> ); }); }; -const GenerateBannedUserList = (blacklist, displayBan, setBannedErrors) => { +const GenerateBannedUserList = (blacklist, displayBan, setAssignErrors) => { if (!blacklist) { return <>; } @@ -162,7 +171,7 @@ const GenerateBannedUserList = (blacklist, displayBan, setBannedErrors) => { userImg={bannedUser.userImg} unbanList={true} displayBan={displayBan} - setBannedErrors={setBannedErrors} + setAssignErrors={setAssignErrors} /> ); }); @@ -181,12 +190,13 @@ const ConfigPanel = ({ const [loadingIcons, setLoadingIcons] = useState(true); const [bannedUserList, setBannedUserList] = useState(null); - const [numBannedUsers, changeNumBanned] = useState(0); + // const [numBannedUsers, changeNumBanned] = useState(0); const [displayBanned, setDisplayBanned] = useState(false); const [configErrors, setConfigErrors] = useState(null); const [assignErrors, setAssignErrors] = useState(null); const [bannedErrors, setBannedErrors] = useState(null); - // Grab the instructor ID for display purposes later + + // Grab the banned users useEffect(() => { if (!bannedUserList) { LazyFetch({ @@ -195,18 +205,18 @@ const ConfigPanel = ({ onSuccess: (data) => { if (data.success.length > 0) { setDisplayBanned(true); - changeNumBanned(data.success.length); + // changeNumBanned(data.success.length); } setBannedUserList( GenerateBannedUserList( data.success, userRole.admin.banUsers, - setBannedErrors + setAssignErrors ) ); }, onFailure: () => { - console.log( + setBannedErrors( "There was a problem getting the blacklist info for the course with id " + courseId ); @@ -220,14 +230,21 @@ const ConfigPanel = ({ courseRoles, userRole.admin.configure, userRole.admin.banUsers, - userRole.admin.removeUsers + userRole.admin.removeUsers, + setAssignErrors ); const [userList, setUserList] = useState(realUserList); // State for roles ------------------------------------------------------ let realRoleList = courseRoles != null ? ( - GenerateRoleList(courseRoles, setCourseRoles, userList, setUserList) + GenerateRoleList( + courseRoles, + setCourseRoles, + userList, + setUserList, + setConfigErrors + ) ) : ( <> ); @@ -265,8 +282,10 @@ const ConfigPanel = ({ type: "post", endpoint: "/courses/" + courseId + "/roles", data: { - name: newPerms.name, - permissions: newPerms.permissions, + // name: newPerms.name, + // permissions: newPerms.permissions, + name: null, + permissions: null, }, onSuccess: (data) => { let { status, role } = data; @@ -281,12 +300,14 @@ const ConfigPanel = ({ newCourseRoles, userRole.admin.configure, userRole.admin.banUsers, - userRole.admin.removeUsers + userRole.admin.removeUsers, + setAssignErrors ) ); + setConfigErrors(null); }, onFailure: (err) => { - console.log("Failed to Post Roles.", err?.response); + setConfigErrors(err.response.data.errors); }, }); }} @@ -321,10 +342,18 @@ const ConfigPanel = ({ onSuccess: (data) => { console.log("Success PUT Roles: ", data); alert("Changes saved successfully."); + setConfigErrors(null); }, onFailure: (err) => { console.log("Failed PUT Roles. ", err?.response); - alert("Error: Changes not saved. Please try again."); + if (err.response.data.errors) + setConfigErrors(err.response.data.errors); + else if (err.response.data.message) + setConfigErrors([err.response.data.message]); + else + setConfigErrors([ + "Unspecified error occurred. Please try again.", + ]); }, }); }} diff --git a/client/src/components/configPage/roleConfigComponents/RolePanel.js b/client/src/components/configPage/roleConfigComponents/RolePanel.js index 2f2708e0..6e40e9b1 100644 --- a/client/src/components/configPage/roleConfigComponents/RolePanel.js +++ b/client/src/components/configPage/roleConfigComponents/RolePanel.js @@ -67,6 +67,7 @@ const RolePanel = ({ setCourseRoles, userList, setUserList, + setConfigErrors, ...props }) => { // MATERIAL UI -------------------------------- @@ -181,15 +182,10 @@ const RolePanel = ({ userRole.admin.removeUsers ) ); + setConfigErrors(null); }, onFailure: (err) => { - console.log(err.response.data); - console.log( - "Failed to delete role: ", - roleObject.name, - ". ", - err?.response - ); + setConfigErrors(err.response.data.errors); }, }); }; diff --git a/client/src/components/configPage/userConfigComponents/UserPanel.js b/client/src/components/configPage/userConfigComponents/UserPanel.js index 1b53017b..9947168e 100644 --- a/client/src/components/configPage/userConfigComponents/UserPanel.js +++ b/client/src/components/configPage/userConfigComponents/UserPanel.js @@ -12,7 +12,13 @@ import Errors from "../../common/Errors"; const UserPerms = { canBan: true, canRemove: true }; /* Handle Role selection in the dropdown */ -const GenerateRoleOptions = (roles, courseId, userId, setRoleName) => { +const GenerateRoleOptions = ( + roles, + courseId, + userId, + setRoleName, + setAssignErrors +) => { return roles.map((role) => ({ onClick: () => { LazyFetch({ @@ -26,10 +32,10 @@ const GenerateRoleOptions = (roles, courseId, userId, setRoleName) => { console.log("Successful PUT (UserPanel). Status: ", data.status); alert(role.name + " Role selected and updated."); setRoleName(role.name); + setAssignErrors(null); }, onFailure: (err) => { - console.log("ERROR: failed PUT (UserPanel): ", err.response); - alert("There was an error updating the role for this user."); + setAssignErrors(err.response.data.errors); }, }); }, @@ -47,6 +53,7 @@ const UserPanel = ({ displayDropdown, displayBan, displayRemove, + setAssignErrors, ...props }) => { const { courseId } = useParams(); @@ -82,9 +89,16 @@ const UserPanel = ({ // Change dropdown options each time a new role is added useEffect(() => { - setRoleOptions( - GenerateRoleOptions(allRoles, courseId, userId, setRoleName) - ); + if (allRoles) + setRoleOptions( + GenerateRoleOptions( + allRoles, + courseId, + userId, + setRoleName, + setAssignErrors + ) + ); }, [allRoles]); // Grab the instructor ID for display purposes later @@ -95,12 +109,16 @@ const UserPanel = ({ endpoint: "/courses?courseId=" + courseId, onSuccess: (data) => { setInstructorId(data.success.instructorID); + setAssignErrors(null); }, onFailure: () => { - console.log( - "There was a problem fetching the course with id", - courseId - ); + // console.log( + // "There was a problem fetching the course with id", + // courseId + // ); + setAssignErrors([ + "There was a problem finding the course instructor. Refresh the page and try again.", + ]); }, }); } diff --git a/server/inquire/resources/course_users.py b/server/inquire/resources/course_users.py index 1fe39b4f..0c0a2930 100644 --- a/server/inquire/resources/course_users.py +++ b/server/inquire/resources/course_users.py @@ -12,6 +12,7 @@ from inquire.auth import current_user, permission_layer from inquire.mongo import * + class CourseUsers(Resource): # TODO: @permission_layer(require_login=False) def get(self, courseId=None): @@ -21,7 +22,7 @@ def get(self, courseId=None): return {"errors": "Error: Course with id " + courseId + " does not exist."}, 400 except Course.MultipleObjectsReturned: return {"errors": "Error: Multiple objects with id " + courseId + " were found."}, 400 - + result = [] for role in course.roles: # TODO: add a thingy for role color... @@ -29,13 +30,12 @@ def get(self, courseId=None): # print("Users Test: ", users) for user in users: # print("User Test: ", user) - result.append({"role": role, "userName": user.first + " " + user.last, "userImg": user.picture, "userId": user._id}) + result.append({"role": role, "userName": user.first + " " + + user.last, "userImg": user.picture, "userId": user._id}) return {"status": "Success!", "data": result}, 200 def put(self, courseId=None): - print("CourseUsers PUT request.") - parser = reqparse.RequestParser() parser.add_argument('role') parser.add_argument('user') @@ -46,44 +46,60 @@ def put(self, courseId=None): if(bool(errors)): return {"errors": errors}, 400 - new_role = args['role'] # the new role that will overwrite the old role + # the new role that will overwrite the old role + new_role = args['role'] user_to_update = args['user'] - print("Role ID: ", new_role) - try: user = User.objects.get({"_id": user_to_update}) except User.DoesNotExist: - return {"errors": "Error: Course with id " + user_to_update + " does not exist."}, 400 + # Print statement for debugging, return for frontend error display + print(f"ERROR: User with id {user_to_update} does not exist.") + return {"errors": ["The user you are trying to update was not found. Please try again."]}, 400 except User.MultipleObjectsReturned: - return {"errors": "Error: Multiple objects with id " + user_to_update + " were found."}, 400 - - print("User Id: ", user._id) - + # Print statement for debugging, return for frontend error display + print( + f"ERROR: Multiple users with id {user_to_update} were found.") + return {"errors": ["The user you are trying to update was found multiple times. Please try again."]}, 400 + + try: + Role.objects.get({"_id": new_role}) + except Role.DoesNotExist: + # Print statement for debugging, return for frontend error display + print(f"ERROR: Role with id {new_role} does not exist.") + return {"errors": ["The role you are trying to assign was not found. Please try again."]}, 400 + except Role.MultipleObjectsReturned: + # Print statement for debugging, return for frontend error display + print( + f"ERROR: Multiple roles with id {new_role} were found.") + return {"errors": ["The role you are trying to assign was found multiple times. Please try again."]}, 400 + # Changes the user's role for the course with ID: courseId for course in user.courses: if course.courseId == courseId: - print("Old Role: ", course.role) course.role = new_role user.save() - print("Updated User Role: ", course.role) - + try: course = Course.objects.get({"_id": courseId}) except Course.DoesNotExist: - return {"errors": "Error: Course with id " + courseId + " does not exist."}, 400 + # Print statement for debugging, return for frontend error display + print(f"ERROR: Course with id {courseId} does not exist.") + return {"errors": ["The course you are trying to modify roles for was not found. Please try again."]}, 400 except Course.MultipleObjectsReturned: - return {"errors": "Error: Multiple objects with id " + courseId + " were found."}, 400 - + # Print statement for debugging, return for frontend error display + print(f"ERROR: Multiple courses with id {courseId} were found.") + return {"errors": ["The course you are trying to modify roles for was found multiple times. Please try again."]}, 400 + for role in course.roles: for u in course.roles[role]: if user_to_update == u: course.roles[role].remove(user_to_update) - + for role in course.roles: if role == new_role: course.roles[role].append(user_to_update) - + course.save() return {"status": "Success!"}, 200 diff --git a/server/inquire/resources/roles.py b/server/inquire/resources/roles.py index 96659928..10b047a4 100644 --- a/server/inquire/resources/roles.py +++ b/server/inquire/resources/roles.py @@ -41,12 +41,16 @@ def post(self, courseId): course = Course.objects.get({"_id": courseId}) course.roles[new_role._id] = [] course.save() - return {"status": "success", "role": self._serialize(new_role)} + return {"status": "success", "role": self._serialize(new_role)}, 200 except Exception as exc: if type(exc) == list: - return {"errors": [str(e) for e in exc]} + # Print statement for debugging, error message for the front end + print([str(e) for e in exc]) + return {"errors": [f"Multiple errors occured. Please try again."]}, 401 else: - return {"errors": str(exc)} + # Print statement for debugging, error message for the front end + print(str(exc)) + return {"errors": [f"Oops, something went wrong! Please try again."]}, 401 @permission_layer(required_permissions=["admin-configure"]) def put(self, courseId): @@ -57,9 +61,11 @@ def put(self, courseId): try: course = Course.objects.get({"_id": courseId}) except Course.DoesNotExist: - return {"error": [f"No courses with id: {courseId}"]}, 400 + print(f"No courses with id: {courseId}") + return {"errors": [f"The course you are trying to set the default role for was not found. Please try again."]}, 400 except Course.MultipleObjectsReturned: - return {"error": [f"Multiple courses with id: {courseId}"]}, 400 + print(f"Multiple courses with id: {courseId}") + return {"errors": [f"The course you are trying to set the default role for was found multiple times. Please try again."]}, 400 default = data['default'] if default in course.roles: course.defaultRole = default @@ -72,9 +78,11 @@ def put(self, courseId): try: saved = Role.objects.get({"_id": _id}) except Role.DoesNotExist: - return {"error": [f"No role with id: {_id}"]}, 400 + print(f"No role with id: {_id}") + return {"errors": [f"The role you are trying to update was not found. Please try again."]}, 400 except Role.MultipleObjectsReturned: - return {"error": [f"Multiple roles with id: {_id}"]}, 400 + print(f"Multiple roles with id: {_id}") + return {"errors": [f"The role you are trying to update was found multiple times. Please try again."]}, 400 # Convert the role to a dict for comparison db_role = dict(self._serialize(saved)) db_role = dict(db_role) @@ -89,7 +97,9 @@ def put(self, courseId): # Make sure update is valid for updated_role in updated_list: if(not updated_role.is_valid()): - return {"error": [f"Update to the role with id {updated_role._id} is invalid"]}, 400 + print( + f"Update to the role with id {updated_role._id} is invalid") + return {"errors": [f"The update to the role you're trying to modify is invalid. Please try again."]}, 400 else: for updated_role in updated_list: updated_role.save() @@ -101,28 +111,44 @@ def delete(self, courseId): try: course = Course.objects.get({"_id": courseId}) except Course.DoesNotExit: - return {"deleted": False, "error": f"Course does not exist"} + # Print statement for debugging, error message for the front end + print(f"ERROR: Course with id {courseId} was not found.") + return {"deleted": False, "errors": [f"The course you are trying to delete a role in was not found. Please try again."]}, 400 + except Course.MultipleObjectsReturned: + # Print statement for debugging, error message for the front end + print(f"ERROR: Multiple courses with id {courseId} were found.") + return {"deleted": False, "errors": ["The course you are trying to delete a role in was found multiple times. Please try again."]}, 400 roleId = data["roleId"] role_users = course.roles[roleId] count = len(role_users) if roleId not in course.roles: - return {"deleted": False, "error": f"Role with id {str(roleId)} does not exit"} + print( + f"ERROR: Role with id {str(roleId)} does not exist in this course.") + return {"deleted": False, "errors": ["The role you are trying to delete was not found for this course. Please try again."]}, 400 elif course.defaultRole == roleId: - return {"deleted": False, "error": f"Cannot delete default role"} + print(f"ERROR: Cannot delete default role") + return {"deleted": False, "errors": [f"Cannot delete the default role for a course."]}, 400 elif count > 0: - return {"deleted": False, "error": f"Cannot delete role while it's in use"} + print(f"ERROR: Cannot delete role while it's in use") + return {"deleted": False, "errors": [f"Cannot delete role while it's in use. Make sure nobody in the course is still assigned this role."]}, 400 else: try: role = Role.objects.get({"_id": roleId}) role.delete() course.roles.pop(roleId, None) course.save() - return {"deleted": True} + return {"deleted": True}, 200 except Role.DoesNotExist: - return {"deleted": False, "error": f"Role with id {str(roleId)} does not exit"} + print( + f"ERROR: Role with id {str(roleId)} does not exist in the database.") + return {"deleted": False, "errors": ["The role you are trying to delete was not found. Please try again."]}, 400 + except Role.MultipleObjectsReturned: + print(f"ERROR: Multiple roles with id {roleId} were found.") + return {"deleted": False, "errors": ["The role you are trying to delete was found multiple times. Please try again."]}, 400 except Exception: - return {"deleted": False, "error": "Unspecified error occured"} + print("ERROR: Unspecified error.") + return {"deleted": False, "errors": ["Unspecified error occured. Please try again."]}, 400 def _serialize(self, role): j = role.to_son()