import { useMemo, useState } from "react"; import { PERMISSIONS } from "@/constants/userPermissions"; import useExams from "@/hooks/useExams"; import useUsers from "@/hooks/useUsers"; import { Module } from "@/interfaces"; import { Exam } from "@/interfaces/exam"; import { User } from "@/interfaces/user"; import useExamStore from "@/stores/exam"; import { getExamById } from "@/utils/exams"; import { countExercises } from "@/utils/moduleUtils"; import { createColumnHelper, flexRender, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; import axios from "axios"; import { capitalize } from "lodash"; import { useRouter } from "next/router"; import { BsCheck, BsPencil, BsTrash, BsUpload, BsX } from "react-icons/bs"; import { toast } from "react-toastify"; import { useListSearch } from "@/hooks/useListSearch"; import Modal from "@/components/Modal"; import { checkAccess, findAllowedEntities } from "@/utils/permissions"; import Button from "@/components/Low/Button"; import { EntityWithRoles } from "@/interfaces/entity"; import { BiEdit } from "react-icons/bi"; import { findBy, mapBy } from "@/utils"; const searchFields = [["module"], ["id"], ["createdBy"]]; 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(); export default function ExamList({ user, entities, }: { user: User; entities: EntityWithRoles[]; }) { const [selectedExam, setSelectedExam] = useState(); const canViewConfidentialEntities = useMemo( () => mapBy( findAllowedEntities(user, entities, "view_confidential_exams"), "id" ), [user, entities] ); const { exams, reload, isLoading } = useExams(); const { users } = useUsers(); // Pass this permission filter to the backend later const filteredExams = useMemo( () => ["admin", "developer"].includes(user?.type) ? exams : exams.filter((item) => { if ( item.access === "confidential" && !canViewConfidentialEntities.find((x) => (item.entities ?? []).includes(x) ) ) return false; return true; }), [canViewConfidentialEntities, exams, user?.type] ); const parsedExams = useMemo(() => { return filteredExams.map((exam) => { if (exam.createdBy) { const user = users.find((u) => u.id === exam.createdBy); if (!user) return exam; return { ...exam, createdBy: user.type === "developer" ? "system" : user.name, }; } return exam; }); }, [filteredExams, users]); const { rows: filteredRows, renderSearch } = useListSearch( searchFields, parsedExams ); const dispatch = useExamStore((state) => state.dispatch); const router = useRouter(); const loadExam = async (module: Module, examId: string) => { const exam = await getExamById(module, examId.trim()); if (!exam) { toast.error( "Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID", { toastId: "invalid-exam-id", } ); return; } dispatch({ type: "INIT_EXAM", payload: { exams: [exam], modules: [module] }, }); router.push("/exam"); }; /* const privatizeExam = async (exam: Exam) => { if ( !confirm( `Are you sure you want to make this ${capitalize(exam.module)} exam ${ exam.access }?` ) ) 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; axios .delete(`/api/exam/${exam.module}/${exam.id}`) .then(() => toast.success(`Deleted 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 delete this exam!"); return; } toast.error("Something went wrong, please try again later."); }) .finally(reload); }; const getTotalExercises = (exam: Exam) => { if ( exam.module === "reading" || exam.module === "listening" || exam.module === "level" ) { return countExercises((exam.parts ?? []).flatMap((x) => x.exercises)); } return countExercises(exam.exercises); }; const defaultColumns = [ columnHelper.accessor("id", { header: "ID", cell: (info) => info.getValue(), }), columnHelper.accessor("module", { header: "Module", cell: (info) => ( {capitalize(info.getValue())} ), }), columnHelper.accessor((x) => getTotalExercises(x), { header: "Exercises", cell: (info) => info.getValue(), }), columnHelper.accessor("minTimer", { header: "Timer", cell: (info) => <>{info.getValue()} minute(s), }), columnHelper.accessor("access", { header: "Access", cell: (info) => {capitalize(info.getValue())}, }), columnHelper.accessor("createdAt", { header: "Created At", cell: (info) => { const value = info.getValue(); if (value) { return new Date(value).toLocaleDateString(); } return null; }, }), columnHelper.accessor("createdBy", { header: "Created By", cell: (info) => !info.getValue() ? "System" : findBy(users, "id", info.getValue())?.name || "N/A", }), { header: "", id: "actions", cell: ({ row }: { row: { original: Exam } }) => { return (
{(row.original.owners?.includes(user.id) || checkAccess(user, ["admin", "developer"])) && ( <> {checkAccess(user, [ "admin", "developer", "mastercorporate", ]) && ( )} )} {PERMISSIONS.examManagement.delete.includes(user.type) && (
deleteExam(row.original)} >
)}
); }, }, ]; const table = useReactTable({ data: filteredRows, columns: defaultColumns, getCoreRowModel: getCoreRowModel(), }); const handleExamEdit = () => { router.push( `/generation?id=${selectedExam!.id}&module=${selectedExam!.module}` ); }; return (
{renderSearch()} setSelectedExam(undefined)} maxWidth="max-w-xl" > {!!selectedExam ? ( <>
Ready to Edit

Exam ID: {selectedExam.id}

Click 'Next' to proceed to the exam editor.

{/* updateExam(selectedExam, { owners })} />*/} ) : (
)} {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())}
{isLoading ? (
) : ( filteredRows.length === 0 && (
No data found...
) )}
); }