Updated the Group creation modal to use Excel
This commit is contained in:
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,21 +33,28 @@ 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!");
|
||||||
|
clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +73,9 @@ const CreatePanel = ({user, users, group, onClose}: CreateDialogProps) => {
|
|||||||
: "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">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
<label className="font-normal text-base text-mti-gray-dim">Participants</label>
|
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user