Updated the Group creation modal to use Excel

This commit is contained in:
Tiago Ribeiro
2024-01-15 21:32:54 +00:00
parent 2bad3ad09f
commit c68e206aae
2 changed files with 49 additions and 31 deletions

View File

@@ -38,7 +38,7 @@ export default function BatchCodeGenerator({user}: {user: User}) {
const {users} = useUsers(); const {users} = useUsers();
const {openFilePicker, filesContent} = useFilePicker({ const {openFilePicker, filesContent, clear} = useFilePicker({
accept: ".xlsx", accept: ".xlsx",
multiple: false, multiple: false,
readAs: "ArrayBuffer", readAs: "ArrayBuffer",
@@ -74,10 +74,12 @@ export default function BatchCodeGenerator({user}: {user: User}) {
(x) => x.email, (x) => x.email,
); );
if (information.length === 0) if (information.length === 0) {
return toast.error( toast.error(
"Please upload an Excel file containing user information, one per line! All already registered e-mails have also been ignored!", "Please upload an Excel file containing user information, one per line! All already registered e-mails have also been ignored!",
); );
return clear();
}
setInfos(information); setInfos(information);
}); });

View File

@@ -9,16 +9,18 @@ import {Disclosure, Transition} from "@headlessui/react";
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
import axios from "axios"; import axios from "axios";
import clsx from "clsx"; import clsx from "clsx";
import {capitalize} from "lodash"; import {capitalize, uniq, uniqBy} from "lodash";
import {useEffect, useRef, useState} from "react"; 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 {toast} from "react-toastify";
import Select from "react-select"; import Select from "react-select";
import {uuidv4} from "@firebase/util"; import {uuidv4} from "@firebase/util";
import {useFilePicker} from "use-file-picker"; import {useFilePicker} from "use-file-picker";
import Modal from "@/components/Modal"; import Modal from "@/components/Modal";
import readXlsxFile from "read-excel-file";
const columnHelper = createColumnHelper<Group>(); const columnHelper = createColumnHelper<Group>();
const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/);
interface CreateDialogProps { interface CreateDialogProps {
user: User; user: User;
@@ -31,40 +33,49 @@ const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => {
const [name, setName] = useState<string | undefined>(group?.name || undefined); const [name, setName] = useState<string | undefined>(group?.name || undefined);
const [admin, setAdmin] = useState<string>(group?.admin || user.id); const [admin, setAdmin] = useState<string>(group?.admin || user.id);
const [participants, setParticipants] = useState<string[]>(group?.participants || []); const [participants, setParticipants] = useState<string[]>(group?.participants || []);
const {openFilePicker, filesContent} = useFilePicker({ const {openFilePicker, filesContent, clear} = useFilePicker({
accept: ".txt", accept: ".xlsx",
multiple: false, multiple: false,
readAs: "ArrayBuffer",
}); });
useEffect(() => { useEffect(() => {
if (filesContent.length > 0) { if (filesContent.length > 0) {
const file = filesContent[0]; const file = filesContent[0];
const emails = file.content readXlsxFile(file.content).then((rows) => {
.toLowerCase() const emails = uniq(
.split("\n") rows
.filter((x) => new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/).test(x)); .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) { if (emails.length === 0) {
toast.error("Please upload a .txt file containing e-mails, one per line!"); toast.error("Please upload an Excel file containing e-mails!");
return; clear();
} return;
}
const emailUsers = [...new Set(emails)].map((x) => users.find((y) => y.email.toLowerCase() === x)).filter((x) => x !== undefined); const emailUsers = [...new Set(emails)].map((x) => users.find((y) => y.email.toLowerCase() === x)).filter((x) => x !== undefined);
const filteredUsers = emailUsers.filter( const filteredUsers = emailUsers.filter(
(x) => (x) =>
((user.type === "developer" || user.type === "admin" || user.type === "corporate") && ((user.type === "developer" || user.type === "admin" || user.type === "corporate") &&
(x?.type === "student" || x?.type === "teacher")) || (x?.type === "student" || x?.type === "teacher")) ||
(user.type === "teacher" && x?.type === "student"), (user.type === "teacher" && x?.type === "student"),
); );
setParticipants(filteredUsers.filter((x) => !!x).map((x) => x!.id)); setParticipants(filteredUsers.filter((x) => !!x).map((x) => x!.id));
toast.success( toast.success(
user.type !== "teacher" user.type !== "teacher"
? "Added all teachers and students found in the file you've provided!" ? "Added all teachers and students found in the file you've provided!"
: "Added all students found in the file you've provided!", : "Added all students found in the file you've provided!",
{toastId: "upload-success"}, {toastId: "upload-success"},
); );
});
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filesContent, user.type, users]); }, [filesContent, user.type, users]);
const submit = () => { const submit = () => {
@@ -90,7 +101,12 @@ const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => {
<div className="flex flex-col gap-8"> <div className="flex flex-col gap-8">
<Input name="name" type="text" label="Name" defaultValue={name} onChange={setName} required disabled={group?.disableEditing} /> <Input name="name" type="text" label="Name" defaultValue={name} onChange={setName} required disabled={group?.disableEditing} />
<div className="flex flex-col gap-3 w-full"> <div className="flex flex-col gap-3 w-full">
<label className="font-normal text-base text-mti-gray-dim">Participants</label> <div className="flex gap-2 items-center">
<label className="font-normal text-base text-mti-gray-dim">Participants</label>
<div className="tooltip" data-tip="The Excel file should only include a column with the desired e-mails.">
<BsQuestionCircleFill />
</div>
</div>
<div className="flex gap-8 w-full"> <div className="flex gap-8 w-full">
<Select <Select
className="w-full" className="w-full"
@@ -121,7 +137,7 @@ const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => {
/> />
{user.type !== "teacher" && ( {user.type !== "teacher" && (
<Button className="w-full max-w-[300px]" onClick={openFilePicker} variant="outline"> <Button className="w-full max-w-[300px]" onClick={openFilePicker} variant="outline">
{filesContent.length === 0 ? "Upload participants .txt file" : filesContent[0].name} {filesContent.length === 0 ? "Upload participants Excel file" : filesContent[0].name}
</Button> </Button>
)} )}
</div> </div>