diff --git a/src/pages/(admin)/Lists/ExamList.tsx b/src/pages/(admin)/Lists/ExamList.tsx index 93ffb05c..2c990677 100644 --- a/src/pages/(admin)/Lists/ExamList.tsx +++ b/src/pages/(admin)/Lists/ExamList.tsx @@ -13,7 +13,7 @@ import axios from "axios"; import clsx from "clsx"; import {capitalize} from "lodash"; import {useRouter} from "next/router"; -import {BsCheck, BsTrash, BsUpload} from "react-icons/bs"; +import {BsBan, BsBanFill, BsCheck, BsCircle, BsStop, BsTrash, BsUpload, BsX} from "react-icons/bs"; import {toast} from "react-toastify"; import {useListSearch} from "@/hooks/useListSearch"; @@ -72,6 +72,28 @@ export default function ExamList({user}: {user: User}) { router.push("/exercises"); }; + const privatizeExam = async (exam: Exam) => { + if (!confirm(`Are you sure you want to make this ${capitalize(exam.module)} exam ${exam.private ? "public" : "private"}?`)) return; + + axios + .patch(`/api/exam/${exam.module}/${exam.id}`, {private: !exam.private}) + .then(() => toast.success(`Updated the "${exam.id}" exam`)) + .catch((reason) => { + if (reason.response.status === 404) { + toast.error("Exam not found!"); + return; + } + + if (reason.response.status === 403) { + toast.error("You do not have permission to update this exam!"); + return; + } + + toast.error("Something went wrong, please try again later."); + }) + .finally(reload); + }; + const deleteExam = async (exam: Exam) => { if (!confirm(`Are you sure you want to delete this ${capitalize(exam.module)} exam?`)) return; @@ -119,6 +141,10 @@ export default function ExamList({user}: {user: User}) { header: "Timer", cell: (info) => <>{info.getValue()} minute(s), }), + columnHelper.accessor("private", { + header: "Private", + cell: (info) => {!info.getValue() ? : }, + }), columnHelper.accessor("createdAt", { header: "Created At", cell: (info) => { @@ -140,12 +166,18 @@ export default function ExamList({user}: {user: User}) { cell: ({row}: {row: {original: Exam}}) => { return (
-
await privatizeExam(row.original)} + className="cursor-pointer tooltip"> + {row.original.private ? : } + +
+ {PERMISSIONS.examManagement.delete.includes(user.type) && (
deleteExam(row.original)}> diff --git a/src/pages/api/exam/[module]/[id].ts b/src/pages/api/exam/[module]/[id].ts index fc80debf..50bf3c7d 100644 --- a/src/pages/api/exam/[module]/[id].ts +++ b/src/pages/api/exam/[module]/[id].ts @@ -1,7 +1,7 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type {NextApiRequest, NextApiResponse} from "next"; import {app} from "@/firebase"; -import {getFirestore, doc, getDoc, deleteDoc} from "firebase/firestore"; +import {getFirestore, doc, getDoc, deleteDoc, setDoc} from "firebase/firestore"; import {withIronSessionApiRoute} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; import {PERMISSIONS} from "@/constants/userPermissions"; @@ -12,6 +12,7 @@ export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === "GET") return get(req, res); + if (req.method === "PATCH") return patch(req, res); if (req.method === "DELETE") return del(req, res); } @@ -37,6 +38,25 @@ async function get(req: NextApiRequest, res: NextApiResponse) { } } +async function patch(req: NextApiRequest, res: NextApiResponse) { + if (!req.session.user) { + res.status(401).json({ok: false}); + return; + } + + const {module, id} = req.query as {module: string; id: string}; + + const docRef = doc(db, module, id); + const docSnap = await getDoc(docRef); + + if (docSnap.exists()) { + await setDoc(docRef, req.body, {merge: true}); + res.status(200).json({ok: true}); + } else { + res.status(404).json({ok: false}); + } +} + async function del(req: NextApiRequest, res: NextApiResponse) { if (!req.session.user) { res.status(401).json({ok: false});