Found the bug

This commit is contained in:
Carlos Mesquita
2024-08-24 00:54:55 +01:00
parent f0f38b335f
commit 2146ef1a92
6 changed files with 221 additions and 147 deletions

View File

@@ -44,39 +44,39 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
}, [hasExamEnded]);
let correctWords: any;
let correctWords: any;
if (exam && exam.module === "level" && exam.parts[partIndex].exercises[exerciseIndex].type === "fillBlanks") {
correctWords = (exam.parts[partIndex].exercises[exerciseIndex] as FillBlanksExercise).words;
}
}
const calculateScore = () => {
const total = text.match(/({{\d+}})/g)?.length || 0;
const correct = answers!.filter((x) => {
const solution = solutions.find((y) => x.id.toString() === y.id.toString())?.solution;
if (!solution) return false;
const option = correctWords!.find((w: any) => {
if (typeof w === "string") {
return w.toLowerCase() === x.solution.toLowerCase();
} else if ('letter' in w) {
return w.word.toLowerCase() === x.solution.toLowerCase();
} else {
return w.id.toString() === x.id.toString();
}
});
if (!option) return false;
const calculateScore = () => {
const total = text.match(/({{\d+}})/g)?.length || 0;
const correct = answers!.filter((x) => {
const solution = solutions.find((y) => x.id.toString() === y.id.toString())?.solution;
if (!solution) return false;
const option = correctWords!.find((w: any) => {
if (typeof w === "string") {
return w.toLowerCase() === x.solution.toLowerCase();
} else if ('letter' in w) {
return w.word.toLowerCase() === x.solution.toLowerCase();
} else {
return w.id.toString() === x.id.toString();
}
});
if (!option) return false;
if (typeof option === "string") {
return solution.toLowerCase() === option.toLowerCase();
} else if ('letter' in option) {
return solution.toLowerCase() === option.word.toLowerCase();
} else if ('options' in option) {
return option.options[solution as keyof typeof option.options] == x.solution;
}
return false;
}).length;
const missing = total - answers!.filter((x) => solutions.find((y) => x.id.toString() === y.id.toString())).length;
if (typeof option === "string") {
return solution.toLowerCase() === option.toLowerCase();
} else if ('letter' in option) {
return solution.toLowerCase() === option.word.toLowerCase();
} else if ('options' in option) {
return option.options[solution as keyof typeof option.options] == x.solution;
}
return false;
}).length;
const missing = total - answers!.filter((x) => solutions.find((y) => x.id.toString() === y.id.toString())).length;
return { total, correct, missing };
};
};
const renderLines = (line: string) => {
return (
<div className="text-base leading-5" key={v4()}>
@@ -125,9 +125,13 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
const onSelection = (questionID: string, value: string) => {
setAnswers((prev) => [...prev.filter((x) => x.id !== questionID), { id: questionID, solution: value }]);
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
}
useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers])
return (
<>
<div className="flex flex-col gap-4 mt-4 h-full w-full mb-20">
@@ -214,15 +218,18 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps })}
className="max-w-[200px] w-full"
disabled={
exam && typeof partIndex !== "undefined" && exam.module === "level" &&
typeof exam.parts[0].intro === "string" && questionIndex === 0}
exam && exam.module === "level" &&
typeof exam.parts[0].intro === "string" &&
partIndex === 0 &&
questionIndex === 0
}
>
Back
</Button>
<Button
color="purple"
onClick={() => {onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps })}}
onClick={() => { onNext({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps }) }}
className="max-w-[200px] self-end w-full">
Next
</Button>

View File

@@ -24,7 +24,7 @@ function Question({
const renderPrompt = (prompt: string) => {
return reactStringReplace(prompt, /(<u>.*?<\/u>)/g, (match) => {
const word = match.replaceAll("<u>", "").replaceAll("</u>", "");
return word.length > 0 ? <u>{word}</u> : null;
return word.length > 0 ? <u key={v4()}>{word}</u> : null;
});
};
@@ -49,7 +49,7 @@ function Question({
"flex flex-col items-center border border-mti-gray-platinum p-4 px-8 rounded-xl gap-4 cursor-pointer bg-white relative select-none",
userSolution === option.id.toString() && "border-mti-purple-light",
)}>
<span className={clsx("text-sm", userSolution !== option.id.toString() && "opacity-50")}>{option.id.toString()}</span>
<span key={v4()} className={clsx("text-sm", userSolution !== option.id.toString() && "opacity-50")}>{option.id.toString()}</span>
<img src={option.src!} alt={`Option ${option.id.toString()}`} />
</div>
))}
@@ -79,6 +79,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
exam,
shuffles,
hasExamEnded,
partIndex,
userSolutions: storeUserSolutions,
setQuestionIndex,
setUserSolutions,
@@ -107,7 +108,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
setAnswers((prev) => [...prev.filter((x) => x.question !== question.id), { option, question: question.id }]);
};
useEffect(()=> {
useEffect(() => {
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers])
@@ -136,7 +137,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
const shuffleMap = shuffleMaps.find((map) => map.questionID == x.question);
if (shuffleMap) {
isSolutionCorrect = getShuffledSolution(x.option, shuffleMap) == matchingQuestion?.solution;
}else {
} else {
isSolutionCorrect = matchingQuestion?.solution === x.option;
}
}
@@ -159,6 +160,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
if (questionIndex === 0) {
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps });
} else {
if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return;
setQuestionIndex(questionIndex - 1);
}
@@ -181,7 +183,11 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
<Button color="purple" variant="outline" onClick={back} className="max-w-[200px] w-full"
disabled={
exam && exam.module === "level" && typeof exam.parts[0].intro === "string" && questionIndex === 0}
exam && exam.module === "level" &&
typeof exam.parts[0].intro === "string" &&
partIndex === 0 &&
questionIndex === 0
}
>
Back
</Button>

View File

@@ -63,7 +63,7 @@ export default function Button({
type={type}
onClick={onClick}
className={clsx(
"rounded-full transition ease-in-out duration-300 disabled:cursor-not-allowed cursor-pointer",
"rounded-full transition ease-in-out duration-300 disabled:cursor-not-allowed cursor-pointer select-none",
padding,
colorClassNames[color][variant],
className,

View File

@@ -1,5 +1,5 @@
import { Dialog, Transition } from "@headlessui/react";
import { Fragment } from "react";
import { Fragment, useEffect, useState } from "react";
import Button from "./Low/Button";
interface Props {
@@ -10,6 +10,19 @@ interface Props {
}
export default function QuestionsModal({ isOpen, onClose, type = "module", unanswered = false }: Props) {
const [isClosing, setIsClosing] = useState(false);
const blockMultipleClicksClose = (x: boolean) => {
if (!isClosing) {
setIsClosing(true);
onClose(x);
}
setTimeout(() => {
setIsClosing(false);
}, 400);
}
return (
<Transition show={isOpen} as={Fragment}>
<Dialog onClose={() => onClose(false)} className="relative z-50">
@@ -44,10 +57,10 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
Are you sure you want to continue without completing those questions?
</span>
<div className="w-full flex justify-between mt-8">
<Button color="purple" onClick={() => onClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
<Button color="purple" onClick={() => blockMultipleClicksClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
Go Back
</Button>
<Button color="purple" onClick={() => onClose(true)} className="max-w-[200px] self-end w-full">
<Button color="purple" onClick={() => blockMultipleClicksClose(true)} className="max-w-[200px] self-end w-full">
Continue
</Button>
</div>
@@ -56,18 +69,16 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
{type === "blankQuestions" && (
<>
<Dialog.Title className="font-bold text-xl">Questions Unanswered</Dialog.Title>
<span>
You have left some questions unanswered in the current part. <br />
<br />
If you wish to continue, you can still access this part later using the navigation bar at the top. <br />
<br />
Do you want to proceed to the next part, or would you like to go back and complete the unanswered questions in the current part?
</span>
<div className="w-full flex justify-between mt-8">
<Button color="purple" onClick={() => onClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
<div className="flex flex-col text-lg gap-2">
<p>You have left some questions unanswered in the current part.</p>
<p>If you wish to continue, you can still access this part later using the navigation bar at the top or the &quot;Back&quot; button.</p>
<p>Do you want to proceed to the next part, or would you like to go back and complete the unanswered questions in the current part?</p>
</div>
<div className="w-full flex justify-between mt-6">
<Button color="purple" onClick={() => blockMultipleClicksClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
Go Back
</Button>
<Button color="purple" onClick={() => onClose(true)} className="max-w-[200px] self-end w-full">
<Button color="purple" onClick={() => blockMultipleClicksClose(true)} className="max-w-[200px] self-end w-full">
Continue
</Button>
</div>
@@ -92,10 +103,10 @@ export default function QuestionsModal({ isOpen, onClose, type = "module", unans
)}
</span>
<div className="w-full flex justify-between mt-8">
<Button color="purple" onClick={() => onClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
<Button color="purple" onClick={() => blockMultipleClicksClose(false)} variant="outline" className="max-w-[200px] self-end w-full">
Go Back
</Button>
<Button color="purple" onClick={() => onClose(true)} className="max-w-[200px] self-end w-full">
<Button color="purple" onClick={() => blockMultipleClicksClose(true)} className="max-w-[200px] self-end w-full">
Submit
</Button>
</div>