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}