From f02fff55e74b514313fbc9b450760bfd75b72449 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 25 Mar 2024 01:16:56 +0000 Subject: [PATCH] Solved the exercise counter bug --- src/exams/Listening.tsx | 66 ++++++++++++++++++++++++++++++----------- src/exams/Reading.tsx | 64 +++++++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 33 deletions(-) diff --git a/src/exams/Listening.tsx b/src/exams/Listening.tsx index 856bfd9a..90b2e0a3 100644 --- a/src/exams/Listening.tsx +++ b/src/exams/Listening.tsx @@ -1,4 +1,4 @@ -import {ListeningExam, UserSolution} from "@/interfaces/exam"; +import {ListeningExam, MultipleChoiceExercise, UserSolution} from "@/interfaces/exam"; import {useEffect, useState} from "react"; import {renderExercise} from "@/components/Exercises"; import {renderSolution} from "@/components/Solutions"; @@ -23,11 +23,13 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [timesListened, setTimesListened] = useState(0); const [showBlankModal, setShowBlankModal] = useState(false); + const [multipleChoicesDone, setMultipleChoicesDone] = useState<{id: string; amount: number}[]>([]); const {userSolutions, setUserSolutions} = useExamStore((state) => state); const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state); const {partIndex, setPartIndex} = useExamStore((state) => state); const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state); + const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]); const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0)); @@ -35,9 +37,20 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props if (showSolutions) return setExerciseIndex(-1); }, [setExerciseIndex, showSolutions]); - // useEffect(() => { - // if (exam.variant !== "partial") setPartIndex(-1); - // }, [exam.variant, setPartIndex]); + useEffect(() => { + const previousParts = exam.parts.filter((_, index) => index < partIndex); + let previousMultipleChoice = previousParts.flatMap((x) => x.exercises).filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[]; + + if (partIndex > -1 && exerciseIndex > -1) { + const previousPartExercises = exam.parts[partIndex].exercises.filter((_, index) => index < exerciseIndex); + const partMultipleChoice = previousPartExercises.filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[]; + + previousMultipleChoice = [...previousMultipleChoice, ...partMultipleChoice]; + } + + setMultipleChoicesDone(previousMultipleChoice.map((x) => ({id: x.id, amount: x.questions.length - 1}))); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { if (hasExamEnded && exerciseIndex === -1) { @@ -63,7 +76,11 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props if (solution) { setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]); } - setQuestionIndex((prev) => prev + currentQuestionIndex); + if (storeQuestionIndex > 0) { + const exercise = getExercise(); + setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== exercise.id), {id: exercise.id, amount: storeQuestionIndex}]); + } + setStoreQuestionIndex(0); if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) { setExerciseIndex(exerciseIndex + 1); @@ -102,6 +119,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props if (solution) { setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]); } + setStoreQuestionIndex(0); setExerciseIndex(exerciseIndex - 1); }; @@ -114,6 +132,31 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props }; }; + useEffect(() => { + if (partIndex > -1 && exerciseIndex > -1) { + const exercise = getExercise(); + setMultipleChoicesDone((prev) => prev.filter((x) => x.id !== exercise.id)); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exerciseIndex, partIndex]); + + const calculateExerciseIndex = () => { + if (partIndex === -1) return 0; + if (partIndex === 0) + return ( + (exerciseIndex === -1 ? 0 : exerciseIndex + 1) + storeQuestionIndex + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0) + ); + + const exercisesPerPart = exam.parts.map((x) => x.exercises.length); + const exercisesDone = exercisesPerPart.filter((_, index) => index < partIndex).reduce((acc, curr) => curr + acc, 0); + return ( + exercisesDone + + (exerciseIndex === -1 ? 0 : exerciseIndex + 1) + + storeQuestionIndex + + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0) + ); + }; + const renderAudioInstructionsPlayer = () => (
@@ -153,18 +196,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
x.exercises) - .findIndex( - (x) => x.id === exam.parts[partIndex].exercises[exerciseIndex === -1 ? exerciseIndex + 1 : exerciseIndex]?.id, - ) || 0) + - (exerciseIndex === -1 ? 0 : 1) + - questionIndex + - currentQuestionIndex - } + exerciseIndex={calculateExerciseIndex()} minTimer={exam.minTimer} module="listening" totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))} diff --git a/src/exams/Reading.tsx b/src/exams/Reading.tsx index b3575124..2497e892 100644 --- a/src/exams/Reading.tsx +++ b/src/exams/Reading.tsx @@ -1,4 +1,4 @@ -import {ReadingExam, UserSolution} from "@/interfaces/exam"; +import {MultipleChoiceExercise, ReadingExam, UserSolution} from "@/interfaces/exam"; import {Fragment, useEffect, useState} from "react"; import Icon from "@mdi/react"; import {mdiArrowRight, mdiNotebook} from "@mdi/js"; @@ -85,12 +85,13 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [showTextModal, setShowTextModal] = useState(false); const [showBlankModal, setShowBlankModal] = useState(false); + const [multipleChoicesDone, setMultipleChoicesDone] = useState<{id: string; amount: number}[]>([]); const {userSolutions, setUserSolutions} = useExamStore((state) => state); const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state); const {partIndex, setPartIndex} = useExamStore((state) => state); const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state); - const setStoreQuestionIndex = useExamStore((state) => state.setQuestionIndex); + const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]); const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0)); @@ -98,6 +99,21 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) if (showSolutions) setExerciseIndex(-1); }, [setExerciseIndex, showSolutions]); + useEffect(() => { + const previousParts = exam.parts.filter((_, index) => index < partIndex); + let previousMultipleChoice = previousParts.flatMap((x) => x.exercises).filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[]; + + if (partIndex > -1 && exerciseIndex > -1) { + const previousPartExercises = exam.parts[partIndex].exercises.filter((_, index) => index < exerciseIndex); + const partMultipleChoice = previousPartExercises.filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[]; + + previousMultipleChoice = [...previousMultipleChoice, ...partMultipleChoice]; + } + + setMultipleChoicesDone(previousMultipleChoice.map((x) => ({id: x.id, amount: x.questions.length - 1}))); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { const listener = (e: KeyboardEvent) => { if (e.key === "F3" || ((e.ctrlKey || e.metaKey) && e.key === "f")) { @@ -136,7 +152,10 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) if (solution) { setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "reading", exam: exam.id}]); } - setQuestionIndex((prev) => prev + currentQuestionIndex); + if (storeQuestionIndex > 0) { + const exercise = getExercise(); + setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== exercise.id), {id: exercise.id, amount: storeQuestionIndex}]); + } setStoreQuestionIndex(0); if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) { @@ -189,6 +208,31 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) }; }; + useEffect(() => { + if (partIndex > -1 && exerciseIndex > -1) { + const exercise = getExercise(); + setMultipleChoicesDone((prev) => prev.filter((x) => x.id !== exercise.id)); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exerciseIndex, partIndex]); + + const calculateExerciseIndex = () => { + if (partIndex === -1) return 0; + if (partIndex === 0) + return ( + (exerciseIndex === -1 ? 0 : exerciseIndex + 1) + storeQuestionIndex + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0) + ); + + const exercisesPerPart = exam.parts.map((x) => x.exercises.length); + const exercisesDone = exercisesPerPart.filter((_, index) => index < partIndex).reduce((acc, curr) => curr + acc, 0); + return ( + exercisesDone + + (exerciseIndex === -1 ? 0 : exerciseIndex + 1) + + storeQuestionIndex + + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0) + ); + }; + const renderText = () => (
@@ -216,19 +260,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) {partIndex > -1 && setShowTextModal(false)} />} x.exercises) - .findIndex( - (x) => - x.id === - exam.parts[partIndex > -1 ? partIndex : 0].exercises[exerciseIndex === -1 ? exerciseIndex + 1 : exerciseIndex] - ?.id, - ) || 0) + - (exerciseIndex === -1 ? 0 : 1) + - questionIndex + - currentQuestionIndex - } + exerciseIndex={calculateExerciseIndex()} module="reading" totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))} disableTimer={showSolutions}