diff --git a/src/dashboards/Teacher.tsx b/src/dashboards/Teacher.tsx index 855dfc69..41548af7 100644 --- a/src/dashboards/Teacher.tsx +++ b/src/dashboards/Teacher.tsx @@ -46,6 +46,7 @@ import ProgressBar from "@/components/Low/ProgressBar"; import AssignmentCreator from "./AssignmentCreator"; import AssignmentView from "./AssignmentView"; import { getUserCorporate } from "@/utils/groups"; +import { checkAccess } from "@/utils/permissions"; interface Props { user: User; @@ -373,13 +374,15 @@ export default function TeacherDashboard({ user }: Props) { ).toFixed(1)} color="purple" /> - setPage("groups")} - /> + {checkAccess(user, ["teacher", "developer"], "viewGroup") && ( + setPage("groups")} + /> + )}
setPage("assignments")} className="bg-white rounded-xl shadow p-4 flex flex-col gap-4 items-center w-96 h-52 justify-center cursor-pointer hover:shadow-xl transition ease-in-out duration-300" diff --git a/src/interfaces/permissions.ts b/src/interfaces/permissions.ts index 2e448af5..29efc1b7 100644 --- a/src/interfaces/permissions.ts +++ b/src/interfaces/permissions.ts @@ -26,18 +26,23 @@ export const permissions = [ "viewCorporate", "viewCountryManager", "viewAdmin", + "viewGroup", // edit data "editStudent", "editTeacher", "editCorporate", "editCountryManager", "editAdmin", + "editGroup", // delete data "deleteStudent", "deleteTeacher", "deleteCorporate", "deleteCountryManager", "deleteAdmin", + "deleteGroup", + // create pages + "createGroup", ] as const; export type PermissionType = (typeof permissions)[keyof typeof permissions]; diff --git a/src/pages/(admin)/Lists/GroupList.tsx b/src/pages/(admin)/Lists/GroupList.tsx index ae36a5cd..2d65ed8a 100644 --- a/src/pages/(admin)/Lists/GroupList.tsx +++ b/src/pages/(admin)/Lists/GroupList.tsx @@ -3,335 +3,474 @@ import Input from "@/components/Low/Input"; import Modal from "@/components/Modal"; import useGroups from "@/hooks/useGroups"; import useUsers from "@/hooks/useUsers"; -import {CorporateUser, Group, User} from "@/interfaces/user"; -import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import { CorporateUser, Group, User } from "@/interfaces/user"; +import { + createColumnHelper, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; import axios from "axios"; -import {capitalize, uniq} from "lodash"; -import {useEffect, useState} from "react"; -import {BsPencil, BsQuestionCircleFill, BsTrash} from "react-icons/bs"; +import { capitalize, uniq } from "lodash"; +import { useEffect, useState } from "react"; +import { BsPencil, BsQuestionCircleFill, BsTrash } from "react-icons/bs"; import Select from "react-select"; -import {toast} from "react-toastify"; +import { toast } from "react-toastify"; import readXlsxFile from "read-excel-file"; -import {useFilePicker} from "use-file-picker"; -import {getUserCorporate} from "@/utils/groups"; +import { useFilePicker } from "use-file-picker"; +import { getUserCorporate } from "@/utils/groups"; import { isAgentUser, isCorporateUser } from "@/resources/user"; +import { checkAccess } from "@/utils/permissions"; const columnHelper = createColumnHelper(); -const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/); +const EMAIL_REGEX = new RegExp( + /^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/ +); -const LinkedCorporate = ({userId, users, groups}: {userId: string, users: User[], groups: Group[]}) => { - const [companyName, setCompanyName] = useState(""); - const [isLoading, setIsLoading] = useState(false); +const LinkedCorporate = ({ + userId, + users, + groups, +}: { + userId: string; + users: User[]; + groups: Group[]; +}) => { + const [companyName, setCompanyName] = useState(""); + const [isLoading, setIsLoading] = useState(false); - useEffect(() => { - const user = users.find((u) => u.id === userId) - if (!user) return setCompanyName("") - - if (isCorporateUser(user)) return setCompanyName(user.corporateInformation?.companyInformation?.name || user.name) - if (isAgentUser(user)) return setCompanyName(user.agentInformation?.companyName || user.name) - - const belongingGroups = groups.filter((x) => x.participants.includes(userId)) - const belongingGroupsAdmins = belongingGroups.map((x) => users.find((u) => u.id === x.admin)).filter((x) => !!x && isCorporateUser(x)) - - if (belongingGroupsAdmins.length === 0) return setCompanyName("") - - const admin = (belongingGroupsAdmins[0] as CorporateUser) - setCompanyName(admin.corporateInformation?.companyInformation.name || admin.name) - }, [userId, users, groups]); + useEffect(() => { + const user = users.find((u) => u.id === userId); + if (!user) return setCompanyName(""); - return isLoading ? Loading... : <>{companyName}; + if (isCorporateUser(user)) + return setCompanyName( + user.corporateInformation?.companyInformation?.name || user.name + ); + if (isAgentUser(user)) + return setCompanyName(user.agentInformation?.companyName || user.name); + + const belongingGroups = groups.filter((x) => + x.participants.includes(userId) + ); + const belongingGroupsAdmins = belongingGroups + .map((x) => users.find((u) => u.id === x.admin)) + .filter((x) => !!x && isCorporateUser(x)); + + if (belongingGroupsAdmins.length === 0) return setCompanyName(""); + + const admin = belongingGroupsAdmins[0] as CorporateUser; + setCompanyName( + admin.corporateInformation?.companyInformation.name || admin.name + ); + }, [userId, users, groups]); + + return isLoading ? ( + Loading... + ) : ( + <>{companyName} + ); }; interface CreateDialogProps { - user: User; - users: User[]; - group?: Group; - onClose: () => void; + user: User; + users: User[]; + group?: Group; + onClose: () => void; } -const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => { - const [name, setName] = useState(group?.name || undefined); - const [admin, setAdmin] = useState(group?.admin || user.id); - const [participants, setParticipants] = useState(group?.participants || []); - const [isLoading, setIsLoading] = useState(false); +const CreatePanel = ({ user, users, group, onClose }: CreateDialogProps) => { + const [name, setName] = useState( + group?.name || undefined + ); + const [admin, setAdmin] = useState(group?.admin || user.id); + const [participants, setParticipants] = useState( + group?.participants || [] + ); + const [isLoading, setIsLoading] = useState(false); - const {openFilePicker, filesContent, clear} = useFilePicker({ - accept: ".xlsx", - multiple: false, - readAs: "ArrayBuffer", - }); + const { openFilePicker, filesContent, clear } = useFilePicker({ + accept: ".xlsx", + multiple: false, + readAs: "ArrayBuffer", + }); - useEffect(() => { - if (filesContent.length > 0) { - setIsLoading(true); + useEffect(() => { + if (filesContent.length > 0) { + setIsLoading(true); - const file = filesContent[0]; - readXlsxFile(file.content).then((rows) => { - const emails = uniq( - rows - .map((row) => { - const [email] = row as string[]; - return EMAIL_REGEX.test(email) && !users.map((u) => u.email).includes(email) ? email.toString().trim() : undefined; - }) - .filter((x) => !!x), - ); + const file = filesContent[0]; + readXlsxFile(file.content).then((rows) => { + const emails = uniq( + rows + .map((row) => { + const [email] = row as string[]; + return EMAIL_REGEX.test(email) && + !users.map((u) => u.email).includes(email) + ? email.toString().trim() + : undefined; + }) + .filter((x) => !!x) + ); - if (emails.length === 0) { - toast.error("Please upload an Excel file containing e-mails!"); - clear(); - setIsLoading(false); - return; - } + if (emails.length === 0) { + toast.error("Please upload an Excel file containing e-mails!"); + clear(); + setIsLoading(false); + return; + } - const emailUsers = [...new Set(emails)].map((x) => users.find((y) => y.email.toLowerCase() === x)).filter((x) => x !== undefined); - const filteredUsers = emailUsers.filter( - (x) => - ((user.type === "developer" || user.type === "admin" || user.type === "corporate" || user.type === "mastercorporate") && - (x?.type === "student" || x?.type === "teacher")) || - (user.type === "teacher" && x?.type === "student"), - ); + const emailUsers = [...new Set(emails)] + .map((x) => users.find((y) => y.email.toLowerCase() === x)) + .filter((x) => x !== undefined); + const filteredUsers = emailUsers.filter( + (x) => + ((user.type === "developer" || + user.type === "admin" || + user.type === "corporate" || + user.type === "mastercorporate") && + (x?.type === "student" || x?.type === "teacher")) || + (user.type === "teacher" && x?.type === "student") + ); - setParticipants(filteredUsers.filter((x) => !!x).map((x) => x!.id)); - toast.success( - user.type !== "teacher" - ? "Added all teachers and students found in the file you've provided!" - : "Added all students found in the file you've provided!", - {toastId: "upload-success"}, - ); - setIsLoading(false); - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filesContent, user.type, users]); + setParticipants(filteredUsers.filter((x) => !!x).map((x) => x!.id)); + toast.success( + user.type !== "teacher" + ? "Added all teachers and students found in the file you've provided!" + : "Added all students found in the file you've provided!", + { toastId: "upload-success" } + ); + setIsLoading(false); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filesContent, user.type, users]); - const submit = () => { - setIsLoading(true); + const submit = () => { + setIsLoading(true); - if (name !== group?.name && (name === "Students" || name === "Teachers")) { - toast.error("That group name is reserved and cannot be used, please enter another one."); - setIsLoading(false); - return; - } + if (name !== group?.name && (name === "Students" || name === "Teachers")) { + toast.error( + "That group name is reserved and cannot be used, please enter another one." + ); + setIsLoading(false); + return; + } - (group ? axios.patch : axios.post)(group ? `/api/groups/${group.id}` : "/api/groups", {name, admin, participants}) - .then(() => { - toast.success(`Group "${name}" ${group ? "edited" : "created"} successfully`); - return true; - }) - .catch(() => { - toast.error("Something went wrong, please try again later!"); - return false; - }) - .finally(() => { - setIsLoading(false); - onClose(); - }); - }; + (group ? axios.patch : axios.post)( + group ? `/api/groups/${group.id}` : "/api/groups", + { name, admin, participants } + ) + .then(() => { + toast.success( + `Group "${name}" ${group ? "edited" : "created"} successfully` + ); + return true; + }) + .catch(() => { + toast.error("Something went wrong, please try again later!"); + return false; + }) + .finally(() => { + setIsLoading(false); + onClose(); + }); + }; - return ( -
-
- -
-
- -
- -
-
-
- +
+
+ +
+ +
+
+
+