From ef18e304a10158a91d9e289bbc7eb16d698760d6 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 11 Dec 2023 13:43:23 +0000 Subject: [PATCH] - Created a package list for student packages; - Updated the group creation wizard to work as a modal; --- src/pages/(admin)/Lists/GroupList.tsx | 170 ++++++--------- src/pages/(admin)/Lists/PackageList.tsx | 261 ++++++++++++++++++++++++ src/pages/(admin)/Lists/index.tsx | 19 ++ src/pages/api/groups/index.ts | 3 +- src/pages/api/packages/[id].ts | 89 ++++++++ 5 files changed, 436 insertions(+), 106 deletions(-) create mode 100644 src/pages/(admin)/Lists/PackageList.tsx create mode 100644 src/pages/api/packages/[id].ts diff --git a/src/pages/(admin)/Lists/GroupList.tsx b/src/pages/(admin)/Lists/GroupList.tsx index 3c12cb8c..9d48254b 100644 --- a/src/pages/(admin)/Lists/GroupList.tsx +++ b/src/pages/(admin)/Lists/GroupList.tsx @@ -16,6 +16,7 @@ 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"; const columnHelper = createColumnHelper(); @@ -23,10 +24,10 @@ interface CreateDialogProps { user: User; users: User[]; group?: Group; - onCreate: (group: Group) => void; + onClose: () => void; } -const CreatePanel = ({user, users, group, onCreate}: CreateDialogProps) => { +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 || []); @@ -66,6 +67,24 @@ const CreatePanel = ({user, users, group, onCreate}: CreateDialogProps) => { } }, [filesContent, user.type, users]); + const submit = () => { + if (name !== group?.name && (name === "Students" || name === "Teachers")) { + toast.error("That group name is reserved and cannot be used, please enter another one."); + 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(onClose); + }; + return (
@@ -106,18 +125,14 @@ const CreatePanel = ({user, users, group, onCreate}: CreateDialogProps) => {
- +
+ + +
); }; @@ -125,56 +140,19 @@ const CreatePanel = ({user, users, group, onCreate}: CreateDialogProps) => { const filterTypes = ["corporate", "teacher"]; export default function GroupList({user}: {user: User}) { - const [editingID, setEditingID] = useState(); - const [showDisclosure, setShowDisclosure] = useState(false); + const [isCreating, setIsCreating] = useState(false); + const [editingGroup, setEditingGroup] = useState(); const [filterByUser, setFilterByUser] = useState(false); const {users} = useUsers(); const {groups, reload} = useGroups(user && filterTypes.includes(user?.type) ? user.id : undefined); - useEffect(() => { - if (editingID) setShowDisclosure(true); - }, [editingID]); - - useEffect(() => { - if (showDisclosure) document.getElementById("disclosure")?.scrollTo(); - if (!showDisclosure) setEditingID(undefined); - }, [showDisclosure]); - useEffect(() => { if (user && (user.type === "corporate" || user.type === "teacher")) { setFilterByUser(true); } }, [user]); - const createGroup = (group: Group) => { - return axios - .post<{ok: boolean}>("/api/groups", group) - .then(() => { - toast.success(`Group "${group.name}" created successfully`); - return true; - }) - .catch(() => { - toast.error("Something went wrong, please try again later!"); - return false; - }) - .finally(reload); - }; - - const updateGroup = (group: Group) => { - return axios - .patch<{ok: boolean}>(`/api/groups/${group.id}`, group) - .then(() => { - toast.success(`Group "${group.name}" created successfully`); - return true; - }) - .catch(() => { - toast.error("Something went wrong, please try again later!"); - return false; - }) - .finally(reload); - }; - const deleteGroup = (group: Group) => { if (!confirm(`Are you sure you want to delete "${group.name}"?`)) return; @@ -216,10 +194,10 @@ export default function GroupList({user}: {user: User}) { cell: ({row}: {row: {original: Group}}) => { return ( <> - {(user?.type === "developer" || user?.type === "admin" || user.id === row.original.admin) && ( + {user && (user.type === "developer" || user.type === "admin" || user.id === row.original.admin) && (
- {editingID !== row.original.id && ( -
setEditingID(row.original.id)}> + {!row.original.disableEditing && ( +
setEditingGroup(row.original)}>
)} @@ -242,8 +220,32 @@ export default function GroupList({user}: {user: User}) { getCoreRowModel: getCoreRowModel(), }); + const closeModal = () => { + setIsCreating(false); + setEditingGroup(undefined); + reload(); + }; + return ( - <> +
+ + + groups + .filter((g) => g.admin === user.id) + .flatMap((g) => g.participants) + .includes(u.id) || groups.flatMap((g) => g.participants).includes(u.id), + ) + : users + } + /> + {table.getHeaderGroups().map((headerGroup) => ( @@ -268,54 +270,12 @@ export default function GroupList({user}: {user: User}) { ))}
- <> -
setShowDisclosure((prev) => !prev)}> - {!showDisclosure ? : } - {!showDisclosure ? "Create group" : "Cancel"} -
- -
- x.id === editingID) : undefined} - user={user} - users={ - user?.type === "corporate" || user?.type === "teacher" - ? users.filter( - (u) => - groups - .filter((g) => g.admin === user.id) - .flatMap((g) => g.participants) - .includes(u.id) || groups.flatMap((g) => g.participants).includes(u.id), - ) - : users - } - onCreate={(group) => { - (!editingID ? createGroup : updateGroup)(group).then((result) => { - if (result) { - setShowDisclosure(false); - setEditingID(undefined); - reload(); - } - }); - }} - /> -
-
- - + +
); } diff --git a/src/pages/(admin)/Lists/PackageList.tsx b/src/pages/(admin)/Lists/PackageList.tsx new file mode 100644 index 00000000..5513215b --- /dev/null +++ b/src/pages/(admin)/Lists/PackageList.tsx @@ -0,0 +1,261 @@ +import Input from "@/components/Low/Input"; +import Modal from "@/components/Modal"; +import {PERMISSIONS} from "@/constants/userPermissions"; +import useExams from "@/hooks/useExams"; +import usePackages from "@/hooks/usePackages"; +import useUsers from "@/hooks/useUsers"; +import {Module} from "@/interfaces"; +import {Exam} from "@/interfaces/exam"; +import {Package} from "@/interfaces/paypal"; +import {Type, User} from "@/interfaces/user"; +import useExamStore from "@/stores/examStore"; +import {getExamById} from "@/utils/exams"; +import {countExercises} from "@/utils/moduleUtils"; +import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import axios from "axios"; +import clsx from "clsx"; +import {capitalize} from "lodash"; +import {useRouter} from "next/router"; +import {useState} from "react"; +import {BsCheck, BsPencil, BsTrash, BsUpload} from "react-icons/bs"; +import {toast} from "react-toastify"; +import Select from "react-select"; +import {CURRENCIES} from "@/resources/paypal"; +import Button from "@/components/Low/Button"; + +const CLASSES: {[key in Module]: string} = { + reading: "text-ielts-reading", + listening: "text-ielts-listening", + speaking: "text-ielts-speaking", + writing: "text-ielts-writing", + level: "text-ielts-level", +}; + +const columnHelper = createColumnHelper(); + +type DurationUnit = "days" | "weeks" | "months" | "years"; + +function PackageCreator({pack, onClose}: {pack?: Package; onClose: () => void}) { + const [duration, setDuration] = useState(pack?.duration || 1); + const [unit, setUnit] = useState(pack?.duration_unit || "months"); + + const [price, setPrice] = useState(pack?.price || 0); + const [currency, setCurrency] = useState(pack?.currency || "EUR"); + + const submit = () => { + (pack ? axios.patch : axios.post)(pack ? `/api/packages/${pack.id}` : "/api/packages", { + duration, + duration_unit: unit, + price, + currency, + }) + .then(() => { + toast.success("New payment has been created successfully!"); + onClose(); + }) + .catch(() => { + toast.error("Something went wrong, please try again later!"); + }); + }; + + return ( +
+
+ +
+ setPrice(parseInt(e))} /> + + setDuration(parseInt(e))} /> +