diff --git a/src/components/ExamEditor/Exercises/MultipleChoice/Underline/UnderlineQuestion.tsx b/src/components/ExamEditor/Exercises/MultipleChoice/Underline/UnderlineQuestion.tsx index 5ed4ce33..12365ae3 100644 --- a/src/components/ExamEditor/Exercises/MultipleChoice/Underline/UnderlineQuestion.tsx +++ b/src/components/ExamEditor/Exercises/MultipleChoice/Underline/UnderlineQuestion.tsx @@ -78,6 +78,7 @@ export const UnderlineQuestion: React.FC = ({ useEffect(() => { validateQuestion(question); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [question]); const handlePromptChange = (value: string) => { diff --git a/src/components/ExamEditor/Hooks/useSectionEdit.tsx b/src/components/ExamEditor/Hooks/useSectionEdit.tsx index 4100c4fc..f43056a6 100644 --- a/src/components/ExamEditor/Hooks/useSectionEdit.tsx +++ b/src/components/ExamEditor/Hooks/useSectionEdit.tsx @@ -32,6 +32,7 @@ const useSectionEdit = ({ const handleEdit = useCallback(() => { setEditing(true); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [sectionId, setEditing, updateRoot]); const handleSave = useCallback(() => { @@ -41,17 +42,20 @@ const useSectionEdit = ({ setEditing(false); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [setEditing, updateRoot, onSave, sectionId]); const handleDiscard = useCallback(() => { setEditing(false); onDiscard?.(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [setEditing, updateRoot, onDiscard, sectionId]); const modeHandle = useCallback(() => { setEditing(!editing); onMode?.(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [setEditing, editing, updateRoot, onMode, sectionId]); return { diff --git a/src/components/ExamEditor/Shared/ImportExam/WordUploader.tsx b/src/components/ExamEditor/Shared/ImportExam/WordUploader.tsx index 1e40744a..23ed7d01 100644 --- a/src/components/ExamEditor/Shared/ImportExam/WordUploader.tsx +++ b/src/components/ExamEditor/Shared/ImportExam/WordUploader.tsx @@ -10,7 +10,7 @@ import { ReadingPart } from '@/interfaces/exam'; import { defaultSectionSettings } from '@/stores/examEditor/defaults'; const WordUploader: React.FC<{ module: Module }> = ({ module }) => { - const {currentModule, dispatch} = useExamEditorStore(); + const { currentModule, dispatch } = useExamEditorStore(); const examInputRef = useRef(null); const solutionsInputRef = useRef(null); @@ -38,14 +38,14 @@ const WordUploader: React.FC<{ module: Module }> = ({ module }) => { } }; - const handleImport = useCallback( async () => { + const handleImport = useCallback(async () => { try { if (!examFile) { toast.error('Exam file is required'); return; } - dispatch({type: "UPDATE_MODULE", payload: {updates: {importing: true}, module}}) + dispatch({ type: "UPDATE_MODULE", payload: { updates: { importing: true }, module } }) const formData = new FormData(); formData.append('exercises', examFile); @@ -76,27 +76,30 @@ const WordUploader: React.FC<{ module: Module }> = ({ module }) => { const newSectionsStates = data.parts.map( (part: ReadingPart, index: number) => defaultSectionSettings(module, index + 1, part) ); - dispatch({type: "UPDATE_MODULE", payload: { - updates: { - sections: newSectionsStates, - minTimer: data.minTimer, - importModule: false, - importing: false, - }, - module - }}); + dispatch({ + type: "UPDATE_MODULE", payload: { + updates: { + sections: newSectionsStates, + minTimer: data.minTimer, + importModule: false, + importing: false, + }, + module + } + }); break; } } catch (error) { toast.error(`An unknown error has occured while import ${module} exam!`); } finally { - dispatch({type: "UPDATE_MODULE", payload: {updates: {importing: false}, module}}) + dispatch({ type: "UPDATE_MODULE", payload: { updates: { importing: false }, module } }) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ examFile, solutionsFile, dispatch, - module + currentModule ]); return ( diff --git a/src/components/ExamEditor/index.tsx b/src/components/ExamEditor/index.tsx index 84151bd7..aa2e0ca8 100644 --- a/src/components/ExamEditor/index.tsx +++ b/src/components/ExamEditor/index.tsx @@ -7,7 +7,7 @@ import { capitalize } from "lodash"; import { Difficulty } from "@/interfaces/exam"; import { useCallback, useEffect, useState } from "react"; import { toast } from "react-toastify"; -import { ModuleState } from "@/stores/examEditor/types"; +import { ModuleState, SectionState } from "@/stores/examEditor/types"; import { Module } from "@/interfaces"; import useExamEditorStore from "@/stores/examEditor"; import WritingSettings from "./SettingsEditor/writing"; @@ -38,8 +38,8 @@ const ExamEditor: React.FC = () => { useEffect(() => { const currentSections = sections; const currentLabels = sectionLabels; - let updatedSections; - let updatedLabels; + let updatedSections: SectionState[]; + let updatedLabels: any; if (numberOfParts > currentSections.length) { const newSections = [...currentSections]; @@ -76,6 +76,7 @@ const ExamEditor: React.FC = () => { } } }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [numberOfParts]); diff --git a/src/components/Solutions/TrueFalse.tsx b/src/components/Solutions/TrueFalse.tsx index 0c0e21d1..31905dc6 100644 --- a/src/components/Solutions/TrueFalse.tsx +++ b/src/components/Solutions/TrueFalse.tsx @@ -88,7 +88,7 @@ export default function TrueFalseSolution({prompt, type, id, questions, userSolu {userSolutions && questions.map((question, index) => { const userSolution = userSolutions.find((x) => x.id === question.id.toString()); - const solution = question.solution.toString().toLowerCase() as Solution; + const solution = question?.solution?.toString().toLowerCase() as Solution; return (
diff --git a/src/components/UserDisplayList.tsx b/src/components/UserDisplayList.tsx index 8f1e803f..bf0b58f5 100644 --- a/src/components/UserDisplayList.tsx +++ b/src/components/UserDisplayList.tsx @@ -8,6 +8,7 @@ interface Props { const UserDisplay = (displayUser: User) => (
+ {/* eslint-disable-next-line @next/next/no-img-element */} {displayUser.name}
{displayUser.name} diff --git a/src/pages/(admin)/BatchCreateUser/UserTable.tsx b/src/components/UserTable.tsx similarity index 99% rename from src/pages/(admin)/BatchCreateUser/UserTable.tsx rename to src/components/UserTable.tsx index ec93e8f6..79d597df 100644 --- a/src/pages/(admin)/BatchCreateUser/UserTable.tsx +++ b/src/components/UserTable.tsx @@ -9,7 +9,7 @@ import { getFilteredRowModel, FilterFn, } from '@tanstack/react-table'; -import { UserImport } from "./IUserImport"; +import { UserImport } from "../interfaces/IUserImport"; const globalFilterFn: FilterFn = (row, columnId, filterValue: string) => { const value = row.getValue(columnId); diff --git a/src/exams/Listening.tsx b/src/exams/Listening.tsx index 28f17cb7..3a006586 100644 --- a/src/exams/Listening.tsx +++ b/src/exams/Listening.tsx @@ -198,23 +198,27 @@ export default function Listening({ exam, showSolutions = false, preview = false const renderAudioPlayer = () => (
- {exam.parts[partIndex].audio ? ( + {exam?.parts[partIndex]?.audio?.source ? ( <>

Please listen to the following audio attentively.

- {exam.parts[partIndex].audio.repeatableTimes > 0 - ? `You will only be allowed to listen to the audio ${exam.parts[partIndex].audio.repeatableTimes - timesListened} time(s).` - : "You may listen to the audio as many times as you would like."} + {(() => { + const audioRepeatTimes = exam?.parts[partIndex]?.audio?.repeatableTimes; + return audioRepeatTimes && audioRepeatTimes > 0 + ? `You will only be allowed to listen to the audio ${audioRepeatTimes - timesListened} time(s).` + : "You may listen to the audio as many times as you would like."; + })()}
setTimesListened((prev) => prev + 1)} - disabled={timesListened === exam.parts[partIndex].audio.repeatableTimes} + disabled={exam?.parts[partIndex]?.audio?.repeatableTimes != null && + timesListened === exam.parts[partIndex]?.audio?.repeatableTimes} disablePause />
diff --git a/src/exams/Writing.tsx b/src/exams/Writing.tsx index 352cafcd..acfea4fb 100644 --- a/src/exams/Writing.tsx +++ b/src/exams/Writing.tsx @@ -23,6 +23,7 @@ export default function Writing({ exam, showSolutions = false, preview = false, const { userSolutions, exerciseIndex, + hasExamEnded, setBgColor, setUserSolutions, setHasExamEnded, diff --git a/src/pages/(admin)/BatchCreateUser/IUserImport.ts b/src/interfaces/IUserImport.ts similarity index 100% rename from src/pages/(admin)/BatchCreateUser/IUserImport.ts rename to src/interfaces/IUserImport.ts diff --git a/src/pages/(admin)/BatchCreateUser/index.tsx b/src/pages/(admin)/BatchCreateUser/index.tsx index 9eae0799..b7c6d264 100644 --- a/src/pages/(admin)/BatchCreateUser/index.tsx +++ b/src/pages/(admin)/BatchCreateUser/index.tsx @@ -15,8 +15,8 @@ import ReactDatePicker from "react-datepicker"; import clsx from "clsx"; import countryCodes from "country-codes-list"; import { User, Type as UserType } from "@/interfaces/user"; -import { Type, UserImport } from "./IUserImport"; -import UserTable from "./UserTable"; +import { Type, UserImport } from "../../../interfaces/IUserImport"; +import UserTable from "../../../components/UserTable"; import { EntityWithRoles } from "@/interfaces/entity"; import Select from "@/components/Low/Select"; diff --git a/src/pages/(admin)/Lists/ExamList.tsx b/src/pages/(admin)/Lists/ExamList.tsx index 3e3ffe6f..a7891939 100644 --- a/src/pages/(admin)/Lists/ExamList.tsx +++ b/src/pages/(admin)/Lists/ExamList.tsx @@ -20,6 +20,7 @@ import Modal from "@/components/Modal"; import {checkAccess} from "@/utils/permissions"; import useGroups from "@/hooks/useGroups"; import Button from "@/components/Low/Button"; +import { EntityWithRoles } from "@/interfaces/entity"; const searchFields = [["module"], ["id"], ["createdBy"]]; @@ -56,7 +57,7 @@ const ExamOwnerSelector = ({options, exam, onSave}: {options: User[]; exam: Exam ); }; -export default function ExamList({user}: {user: User}) { +export default function ExamList({user, entities}: {user: User; entities: EntityWithRoles[];}) { const [selectedExam, setSelectedExam] = useState(); const {exams, reload} = useExams(); diff --git a/src/pages/api/exam/generate/[...module].ts b/src/pages/api/exam/generate/[...module].ts index 8bf95b4e..71ca7a68 100644 --- a/src/pages/api/exam/generate/[...module].ts +++ b/src/pages/api/exam/generate/[...module].ts @@ -21,7 +21,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) { queryParams.delete('module'); - const result = await axios.get(`${process.env.BACKEND_URL}/${endpoint}${queryParams.size > 0 ? `?${queryParams.toString()}` : ""}`, { + const result = await axios.get(`${process.env.BACKEND_URL}/${endpoint}${Array.from(queryParams.entries()).length > 0 ? `?${queryParams.toString()}` : ""}`, { headers: { Authorization: `Bearer ${process.env.BACKEND_JWT}` }, }); res.status(200).json(result.data);