-
- {part.context!.split('\n\n').map((line, index) => {
- return
{index + 1}{line}
- })}
+
+ {lineNumbers.map(num => (
+
+ {num}
+
+ ))}
+
+
);
diff --git a/src/exams/Level/index.tsx b/src/exams/Level/index.tsx
index b88aa109..60e6bf65 100644
--- a/src/exams/Level/index.tsx
+++ b/src/exams/Level/index.tsx
@@ -8,7 +8,7 @@ import { Exercise, FillBlanksMCOption, LevelExam, MultipleChoiceExercise, Multip
import useExamStore from "@/stores/examStore";
import { countExercises } from "@/utils/moduleUtils";
import clsx from "clsx";
-import { use, useEffect, useState } from "react";
+import { use, useEffect, useMemo, useState } from "react";
import TextComponent from "./TextComponent";
import PartDivider from "./PartDivider";
import Timer from "@/components/Medium/Timer";
@@ -51,11 +51,13 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
setCurrentSolution
} = useExamStore((state) => state);
-
const [multipleChoicesDone, setMultipleChoicesDone] = useState<{ id: string; amount: number }[]>([]);
const [showQuestionsModal, setShowQuestionsModal] = useState(false);
const [continueAnyways, setContinueAnyways] = useState(false);
const [textRender, setTextRender] = useState(false);
+ const [changedPrompt, setChangedPrompt] = useState(false);
+ const [nextExerciseCalled, setNextExerciseCalled] = useState(false);
+ const [currentSolutionSet, setCurrentSolutionSet] = useState(false);
const [seenParts, setSeenParts] = useState
(showSolutions ? exam.parts.map((_, index) => index) : [0]);
@@ -66,8 +68,6 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
onClose: function (x: boolean | undefined) { if (x) { setShowQuestionsModal(false); nextExercise(); } else { setShowQuestionsModal(false) } }
});
-
-
const [currentExercise, setCurrentExercise] = useState(exam.parts[0].exercises[0]);
const [showPartDivider, setShowPartDivider] = useState(typeof exam.parts[0].intro === "string" && !showSolutions);
@@ -81,6 +81,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
useEffect(() => {
if (typeof currentSolution !== "undefined") {
setUserSolutions([...userSolutions.filter((x) => x.exercise !== currentSolution.exercise), { ...currentSolution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
+ setCurrentSolutionSet(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentSolution, exam.id, exam.shuffle, shuffles, currentExercise])
@@ -119,39 +120,11 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [partIndex, exerciseIndex, questionIndex]);
+ const next = () => {
+ setNextExerciseCalled(true);
+ }
- useEffect(() => {
- const regex = /.*?['"](.*?)['"] in line (\d+)\?$/;
- if (
- exerciseIndex !== -1 && currentExercise &&
- currentExercise.type === "multipleChoice" &&
- currentExercise.questions[questionIndex] &&
- currentExercise.questions[questionIndex].prompt &&
- exam.parts[partIndex].context
- ) {
- const match = currentExercise.questions[questionIndex].prompt.match(regex);
- if (match) {
- const word = match[1];
- const originalLineNumber = match[2];
-
- if (word !== contextWord) {
- setContextWord(word);
- }
-
- const updatedPrompt = currentExercise.questions[questionIndex].prompt.replace(
- `in line ${originalLineNumber}`,
- `in line ${contextWordLine || originalLineNumber}`
- );
-
- currentExercise.questions[questionIndex].prompt = updatedPrompt;
- } else {
- setContextWord(undefined);
- }
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [currentExercise, questionIndex]);
-
- const nextExercise = (solution?: UserSolution) => {
+ const nextExercise = () => {
scrollToTop();
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
@@ -187,13 +160,27 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
return;
}
+ if(partIndex + 1 === exam.parts.length && exerciseIndex === exam.parts[partIndex].exercises.length - 1 && !continueAnyways) {
+ modalKwargs();
+ setShowQuestionsModal(true);
+ }
+
setHasExamEnded(false);
if (typeof showSolutionsSave !== "undefined") {
onFinish(showSolutionsSave);
} else {
onFinish(userSolutions);
}
- };
+ }
+
+ useEffect(() => {
+ if (nextExerciseCalled && currentSolutionSet) {
+ nextExercise();
+ setNextExerciseCalled(false);
+ setCurrentSolutionSet(false);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [nextExerciseCalled, currentSolutionSet])
const previousExercise = (solution?: UserSolution) => {
scrollToTop();
@@ -341,6 +328,38 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
});
}
+ useEffect(() => {
+ const regex = /.*?['"](.*?)['"] in line (\d+)\?$/;
+ if (
+ exerciseIndex !== -1 && currentExercise &&
+ currentExercise.type === "multipleChoice" &&
+ currentExercise.questions[questionIndex] &&
+ currentExercise.questions[questionIndex].prompt &&
+ exam.parts[partIndex].context
+ ) {
+ const match = currentExercise.questions[questionIndex].prompt.match(regex);
+ if (match) {
+ const word = match[1];
+ const originalLineNumber = match[2];
+
+ if (word !== contextWord) {
+ setContextWord(word);
+ }
+
+ const updatedPrompt = currentExercise.questions[questionIndex].prompt.replace(
+ `in line ${originalLineNumber}`,
+ `in line ${contextWordLine || originalLineNumber}`
+ );
+
+ currentExercise.questions[questionIndex].prompt = updatedPrompt;
+ setChangedPrompt(true);
+ } else {
+ setContextWord(undefined);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [currentExercise, questionIndex, contextWordLine]);
+
useEffect(() => {
if (continueAnyways) {
setContinueAnyways(false);
@@ -375,6 +394,24 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
}
+ const memoizedRender = useMemo(() => {
+ setChangedPrompt(false);
+ return (
+ <>
+ {textRender ?
+ renderText() :
+ <>
+ {exam.parts[partIndex].context && renderText()}
+ {(showSolutions || editing) ?
+ renderSolution(currentExercise, nextExercise, previousExercise) :
+ renderExercise(currentExercise, exam.id, next, previousExercise)
+ }
+ >
+ }
+ >)
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [textRender, currentExercise, changedPrompt]);
+
return (
<>
@@ -429,18 +466,7 @@ export default function Level({ exam, showSolutions = false, onFinish, editing =
"mb-20 w-full",
!!exam.parts[partIndex].context && !textRender && "grid grid-cols-2 gap-4",
)}>
-
- {textRender ?
- renderText() :
- <>
- {exam.parts[partIndex].context && renderText()}
- {(showSolutions || editing) ?
- renderSolution(currentExercise, nextExercise, previousExercise)
- :
- renderExercise(currentExercise, exam.id, nextExercise, previousExercise)
- }
- >
- }
+ {memoizedRender}
{/*exerciseIndex === -1 && partIndex > 0 && (