From fe2abaacae0f88aeb42a773313681bfe04070628 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 4 Apr 2024 23:05:12 +0100 Subject: [PATCH] Added a list for codes, for users to delete unused ones --- src/pages/(admin)/Lists/CodeList.tsx | 106 +++++++++++++++++++++++++++ src/pages/(admin)/Lists/index.tsx | 19 +++++ src/pages/api/code/[id].ts | 11 +++ src/pages/api/code/index.ts | 2 +- 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/pages/(admin)/Lists/CodeList.tsx diff --git a/src/pages/(admin)/Lists/CodeList.tsx b/src/pages/(admin)/Lists/CodeList.tsx new file mode 100644 index 00000000..c5c0d548 --- /dev/null +++ b/src/pages/(admin)/Lists/CodeList.tsx @@ -0,0 +1,106 @@ +import useCodes from "@/hooks/useCodes"; +import {Code, User} from "@/interfaces/user"; +import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import axios from "axios"; +import {BsTrash} from "react-icons/bs"; +import {toast} from "react-toastify"; + +const columnHelper = createColumnHelper(); + +export default function CodeList({user}: {user: User}) { + const {codes, reload} = useCodes(user?.type === "corporate" ? user?.id : undefined); + + const deleteCode = async (code: Code) => { + if (!confirm(`Are you sure you want to delete this "${code.code}" code?`)) return; + + axios + .delete(`/api/code/${code.code}`) + .then(() => toast.success(`Deleted the "${code.code}" exam`)) + .catch((reason) => { + if (reason.response.status === 404) { + toast.error("Code not found!"); + return; + } + + if (reason.response.status === 403) { + toast.error("You do not have permission to delete this code!"); + return; + } + + toast.error("Something went wrong, please try again later."); + }) + .finally(reload); + }; + + const defaultColumns = [ + columnHelper.accessor("code", { + header: "Code", + cell: (info) => info.getValue(), + }), + columnHelper.accessor("email", { + header: "Invited E-mail", + cell: (info) => info.getValue() || "N/A", + }), + columnHelper.accessor("userId", { + header: "Availability", + cell: (info) => + info.getValue() ? ( + +
In Use + + ) : ( + +
Unused + + ), + }), + { + header: "", + id: "actions", + cell: ({row}: {row: {original: Code}}) => { + return ( +
+ {!row.original.userId && ( +
deleteCode(row.original)}> + +
+ )} +
+ ); + }, + }, + ]; + + const table = useReactTable({ + data: codes, + columns: defaultColumns, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+ ); +} diff --git a/src/pages/(admin)/Lists/index.tsx b/src/pages/(admin)/Lists/index.tsx index b7a94146..e83e1017 100644 --- a/src/pages/(admin)/Lists/index.tsx +++ b/src/pages/(admin)/Lists/index.tsx @@ -1,6 +1,7 @@ import {User} from "@/interfaces/user"; import {Tab} from "@headlessui/react"; import clsx from "clsx"; +import CodeList from "./CodeList"; import ExamList from "./ExamList"; import GroupList from "./GroupList"; import PackageList from "./PackageList"; @@ -45,6 +46,19 @@ export default function Lists({user}: {user: User}) { }> Group List + {user && ["developer", "admin", "corporate"].includes(user.type) && ( + + clsx( + "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", + "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", + "transition duration-300 ease-in-out", + selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark", + ) + }> + Code List + + )} {user && ["developer", "admin"].includes(user.type) && ( @@ -71,6 +85,11 @@ export default function Lists({user}: {user: User}) { + {user && ["developer", "admin", "corporate"].includes(user.type) && ( + + + + )} {user && ["developer", "admin"].includes(user.type) && ( diff --git a/src/pages/api/code/[id].ts b/src/pages/api/code/[id].ts index 75456c11..3f636c7a 100644 --- a/src/pages/api/code/[id].ts +++ b/src/pages/api/code/[id].ts @@ -10,6 +10,7 @@ const db = getFirestore(app); export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "GET") return GET(req, res); + if (req.method === "DELETE") return DELETE(req, res); res.status(404).json({ok: false}); } @@ -21,3 +22,13 @@ async function GET(req: NextApiRequest, res: NextApiResponse) { res.status(200).json({...snapshot.data(), id: snapshot.id}); } + +async function DELETE(req: NextApiRequest, res: NextApiResponse) { + const {id} = req.query; + + const snapshot = await getDoc(doc(db, "codes", id as string)); + if (!snapshot.exists()) return res.status(404).json; + + await deleteDoc(snapshot.ref); + res.status(200).json({...snapshot.data(), id: snapshot.id}); +} diff --git a/src/pages/api/code/index.ts b/src/pages/api/code/index.ts index bad31fc9..680786fc 100644 --- a/src/pages/api/code/index.ts +++ b/src/pages/api/code/index.ts @@ -27,7 +27,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) { } const {creator} = req.query as {creator?: string}; - const q = query(collection(db, "codes"), where("creator", "==", creator)); + const q = query(collection(db, "codes"), where("creator", "==", creator || "")); const snapshot = await getDocs(creator ? q : collection(db, "codes")); res.status(200).json(snapshot.docs.map((doc) => doc.data()));