Solved the exercise counter bug
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import {ListeningExam, UserSolution} from "@/interfaces/exam";
|
import {ListeningExam, MultipleChoiceExercise, UserSolution} from "@/interfaces/exam";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {renderExercise} from "@/components/Exercises";
|
import {renderExercise} from "@/components/Exercises";
|
||||||
import {renderSolution} from "@/components/Solutions";
|
import {renderSolution} from "@/components/Solutions";
|
||||||
@@ -23,11 +23,13 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
|||||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||||
const [timesListened, setTimesListened] = useState(0);
|
const [timesListened, setTimesListened] = useState(0);
|
||||||
const [showBlankModal, setShowBlankModal] = useState(false);
|
const [showBlankModal, setShowBlankModal] = useState(false);
|
||||||
|
const [multipleChoicesDone, setMultipleChoicesDone] = useState<{id: string; amount: number}[]>([]);
|
||||||
|
|
||||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||||
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
||||||
const {exerciseIndex, setExerciseIndex} = 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));
|
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);
|
if (showSolutions) return setExerciseIndex(-1);
|
||||||
}, [setExerciseIndex, showSolutions]);
|
}, [setExerciseIndex, showSolutions]);
|
||||||
|
|
||||||
// useEffect(() => {
|
useEffect(() => {
|
||||||
// if (exam.variant !== "partial") setPartIndex(-1);
|
const previousParts = exam.parts.filter((_, index) => index < partIndex);
|
||||||
// }, [exam.variant, setPartIndex]);
|
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(() => {
|
useEffect(() => {
|
||||||
if (hasExamEnded && exerciseIndex === -1) {
|
if (hasExamEnded && exerciseIndex === -1) {
|
||||||
@@ -63,7 +76,11 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
|||||||
if (solution) {
|
if (solution) {
|
||||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
|
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) {
|
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
|
||||||
setExerciseIndex(exerciseIndex + 1);
|
setExerciseIndex(exerciseIndex + 1);
|
||||||
@@ -102,6 +119,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
|||||||
if (solution) {
|
if (solution) {
|
||||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
|
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
|
||||||
}
|
}
|
||||||
|
setStoreQuestionIndex(0);
|
||||||
|
|
||||||
setExerciseIndex(exerciseIndex - 1);
|
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 = () => (
|
const renderAudioInstructionsPlayer = () => (
|
||||||
<div className="flex flex-col gap-8 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16">
|
<div className="flex flex-col gap-8 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16">
|
||||||
<div className="flex flex-col w-full gap-2">
|
<div className="flex flex-col w-full gap-2">
|
||||||
@@ -153,18 +196,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props
|
|||||||
<BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} />
|
<BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} />
|
||||||
<div className="flex flex-col h-full w-full gap-8 justify-between">
|
<div className="flex flex-col h-full w-full gap-8 justify-between">
|
||||||
<ModuleTitle
|
<ModuleTitle
|
||||||
exerciseIndex={
|
exerciseIndex={calculateExerciseIndex()}
|
||||||
partIndex === -1
|
|
||||||
? 0
|
|
||||||
: (exam.parts
|
|
||||||
.flatMap((x) => x.exercises)
|
|
||||||
.findIndex(
|
|
||||||
(x) => x.id === exam.parts[partIndex].exercises[exerciseIndex === -1 ? exerciseIndex + 1 : exerciseIndex]?.id,
|
|
||||||
) || 0) +
|
|
||||||
(exerciseIndex === -1 ? 0 : 1) +
|
|
||||||
questionIndex +
|
|
||||||
currentQuestionIndex
|
|
||||||
}
|
|
||||||
minTimer={exam.minTimer}
|
minTimer={exam.minTimer}
|
||||||
module="listening"
|
module="listening"
|
||||||
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {ReadingExam, UserSolution} from "@/interfaces/exam";
|
import {MultipleChoiceExercise, ReadingExam, UserSolution} from "@/interfaces/exam";
|
||||||
import {Fragment, useEffect, useState} from "react";
|
import {Fragment, useEffect, useState} from "react";
|
||||||
import Icon from "@mdi/react";
|
import Icon from "@mdi/react";
|
||||||
import {mdiArrowRight, mdiNotebook} from "@mdi/js";
|
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 [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||||
const [showTextModal, setShowTextModal] = useState(false);
|
const [showTextModal, setShowTextModal] = useState(false);
|
||||||
const [showBlankModal, setShowBlankModal] = useState(false);
|
const [showBlankModal, setShowBlankModal] = useState(false);
|
||||||
|
const [multipleChoicesDone, setMultipleChoicesDone] = useState<{id: string; amount: number}[]>([]);
|
||||||
|
|
||||||
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
|
||||||
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
|
||||||
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
const {partIndex, setPartIndex} = useExamStore((state) => state);
|
||||||
const {exerciseIndex, setExerciseIndex} = 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));
|
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);
|
if (showSolutions) setExerciseIndex(-1);
|
||||||
}, [setExerciseIndex, showSolutions]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const listener = (e: KeyboardEvent) => {
|
const listener = (e: KeyboardEvent) => {
|
||||||
if (e.key === "F3" || ((e.ctrlKey || e.metaKey) && e.key === "f")) {
|
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) {
|
if (solution) {
|
||||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "reading", exam: exam.id}]);
|
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);
|
setStoreQuestionIndex(0);
|
||||||
|
|
||||||
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
|
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 = () => (
|
const renderText = () => (
|
||||||
<div className="flex flex-col gap-6 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16 mt-4">
|
<div className="flex flex-col gap-6 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16 mt-4">
|
||||||
<div className="flex flex-col w-full gap-2">
|
<div className="flex flex-col w-full gap-2">
|
||||||
@@ -216,19 +260,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props)
|
|||||||
{partIndex > -1 && <TextModal {...exam.parts[partIndex].text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} />}
|
{partIndex > -1 && <TextModal {...exam.parts[partIndex].text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} />}
|
||||||
<ModuleTitle
|
<ModuleTitle
|
||||||
minTimer={exam.minTimer}
|
minTimer={exam.minTimer}
|
||||||
exerciseIndex={
|
exerciseIndex={calculateExerciseIndex()}
|
||||||
(exam.parts
|
|
||||||
.flatMap((x) => 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
|
|
||||||
}
|
|
||||||
module="reading"
|
module="reading"
|
||||||
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
||||||
disableTimer={showSolutions}
|
disableTimer={showSolutions}
|
||||||
|
|||||||
Reference in New Issue
Block a user