From c68e206aaedbd824bc3679808061cd3bc2b20ce2 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 15 Jan 2024 21:32:54 +0000 Subject: [PATCH] Updated the Group creation modal to use Excel --- src/pages/(admin)/BatchCodeGenerator.tsx | 8 ++- src/pages/(admin)/Lists/GroupList.tsx | 72 +++++++++++++++--------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/pages/(admin)/BatchCodeGenerator.tsx b/src/pages/(admin)/BatchCodeGenerator.tsx index d5aaeca8..e03cad7a 100644 --- a/src/pages/(admin)/BatchCodeGenerator.tsx +++ b/src/pages/(admin)/BatchCodeGenerator.tsx @@ -38,7 +38,7 @@ export default function BatchCodeGenerator({user}: {user: User}) { const {users} = useUsers(); - const {openFilePicker, filesContent} = useFilePicker({ + const {openFilePicker, filesContent, clear} = useFilePicker({ accept: ".xlsx", multiple: false, readAs: "ArrayBuffer", @@ -74,10 +74,12 @@ export default function BatchCodeGenerator({user}: {user: User}) { (x) => x.email, ); - if (information.length === 0) - return toast.error( + if (information.length === 0) { + toast.error( "Please upload an Excel file containing user information, one per line! All already registered e-mails have also been ignored!", ); + return clear(); + } setInfos(information); }); diff --git a/src/pages/(admin)/Lists/GroupList.tsx b/src/pages/(admin)/Lists/GroupList.tsx index 4f6efb89..1bde57c8 100644 --- a/src/pages/(admin)/Lists/GroupList.tsx +++ b/src/pages/(admin)/Lists/GroupList.tsx @@ -9,16 +9,18 @@ import {Disclosure, Transition} from "@headlessui/react"; import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; import axios from "axios"; import clsx from "clsx"; -import {capitalize} from "lodash"; +import {capitalize, uniq, uniqBy} from "lodash"; import {useEffect, useRef, useState} from "react"; -import {BsCheck, BsDash, BsPencil, BsPlus, BsTrash} from "react-icons/bs"; +import {BsCheck, BsDash, BsPencil, BsPlus, BsQuestionCircleFill, BsTrash} from "react-icons/bs"; import {toast} from "react-toastify"; import Select from "react-select"; import {uuidv4} from "@firebase/util"; import {useFilePicker} from "use-file-picker"; import Modal from "@/components/Modal"; +import readXlsxFile from "read-excel-file"; const columnHelper = createColumnHelper(); +const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/); interface CreateDialogProps { user: User; @@ -31,40 +33,49 @@ 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 {openFilePicker, filesContent} = useFilePicker({ - accept: ".txt", + const {openFilePicker, filesContent, clear} = useFilePicker({ + accept: ".xlsx", multiple: false, + readAs: "ArrayBuffer", }); useEffect(() => { if (filesContent.length > 0) { const file = filesContent[0]; - const emails = file.content - .toLowerCase() - .split("\n") - .filter((x) => new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/).test(x)); + 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 a .txt file containing e-mails, one per line!"); - return; - } + if (emails.length === 0) { + toast.error("Please upload an Excel file containing e-mails!"); + clear(); + 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") && - (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") && + (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"}, - ); + 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"}, + ); + }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [filesContent, user.type, users]); const submit = () => { @@ -90,7 +101,12 @@ const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => {
- +
+ +
+ +
+