Part intro's, modals between parts and some fixes

This commit is contained in:
Carlos Mesquita
2024-08-20 18:52:38 +01:00
parent 3299acee36
commit 505df31d6b
19 changed files with 907 additions and 708 deletions

View File

@@ -20,7 +20,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
onNext,
onBack,
}) => {
//const { shuffleMaps } = useExamStore((state) => state);
const { shuffleMaps, exam, partIndex, questionIndex, exerciseIndex } = useExamStore((state) => state);
const [answers, setAnswers] = useState<{ id: string; solution: string }[]>(userSolutions);
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
@@ -41,47 +41,40 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]);
const calculateScore = () => {
const total = text.match(/({{\d+}})/g)?.length || 0;
const correct = userSolutions.filter((x) => {
const solution = solutions.find((y) => x.id.toString() === y.id.toString())?.solution;
if (!solution) return false;
const option = words.find((w) => {
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 === x.id;
}
});
if (!option) return false;
let correctWords: any;
if (exam && exam.module === "level" && exam.parts[partIndex].exercises[exerciseIndex].type === "fillBlanks") {
correctWords = (exam.parts[partIndex].exercises[exerciseIndex] as FillBlanksExercise).words;
}
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) {
/*
if (shuffleMaps.length !== 0) {
const shuffleMap = shuffleMaps.find((map) => map.id == x.id)
if (!shuffleMap) {
return false;
}
const original = shuffleMap[x.solution as keyof typeof shuffleMap];
return solution.toLowerCase() === (option.options[original as keyof typeof option.options] || '').toLowerCase();
}*/
return solution.toLowerCase() === (option.options[x.solution as keyof typeof option.options] || '').toLowerCase();
}
return false;
}).length;
const missing = total - userSolutions.filter((x) => solutions.find((y) => x.id.toString() === y.id.toString())).length;
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;
return { total, correct, missing };
};
};
const renderLines = (line: string) => {
return (
<div className="text-base leading-5">
@@ -132,7 +125,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
setAnswers((prev) => [...prev.filter((x) => x.id !== id), { id: id, solution: value }]);
}
/*const getShuffles = () => {
const getShuffles = () => {
let shuffle = {};
if (shuffleMaps.length !== 0) {
shuffle = {
@@ -142,7 +135,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
}
}
return shuffle;
}*/
}
return (
<>
@@ -227,14 +220,18 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
<Button
color="purple"
variant="outline"
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, })}//...getShuffles() })}
className="max-w-[200px] w-full">
onClick={() => onBack({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() })}
className="max-w-[200px] w-full"
disabled={
exam && typeof partIndex !== "undefined" && exam.module === "level" &&
typeof exam.parts[0].intro === "string" && questionIndex === 0}
>
Back
</Button>
<Button
color="purple"
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, })}//...getShuffles() })}
onClick={() => onNext({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() })}
className="max-w-[200px] self-end w-full">
Next
</Button>

View File

@@ -6,6 +6,7 @@ import { useEffect, useState } from "react";
import reactStringReplace from "react-string-replace";
import { CommonProps } from ".";
import Button from "../Low/Button";
import { v4 } from "uuid";
function Question({
id,
@@ -30,9 +31,9 @@ function Question({
return (
<div className="flex flex-col gap-8">
{isNaN(Number(id)) ? (
<span className={clsx(true ? "text-lg" : "text-base")}>{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")}</span>
<span className="text-lg">{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")}</span>
) : (
<span className={clsx(true ? "text-lg" : "text-base")}>
<span className="text-lg">
<>
{id} - <span>{renderPrompt(prompt).filter((x) => x?.toString() !== "<u>")}</span>
</>
@@ -42,10 +43,10 @@ function Question({
{variant === "image" &&
options.map((option) => (
<div
key={option.id.toString()}
key={v4()}
onClick={() => (onSelectOption ? onSelectOption(option.id.toString()) : null)}
className={clsx(
"flex flex-col items-center border border-mti-gray-platinum p-4 px-8 rounded-xl gap-4 cursor-pointer bg-white relative",
"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>
@@ -55,10 +56,10 @@ function Question({
{variant === "text" &&
options.map((option) => (
<div
key={option.id.toString()}
key={v4()}
onClick={() => (onSelectOption ? onSelectOption(option.id.toString()) : null)}
className={clsx(
"flex border p-4 rounded-xl gap-2 cursor-pointer bg-white text-base",
"flex border p-4 rounded-xl gap-2 cursor-pointer bg-white text-base select-none",
userSolution === option.id.toString() && "border-mti-purple-light",
)}>
<span className="font-semibold">{option.id.toString()}.</span>
@@ -73,10 +74,15 @@ function Question({
export default function MultipleChoice({ id, prompt, type, questions, userSolutions, onNext, onBack }: MultipleChoiceExercise & CommonProps) {
const [answers, setAnswers] = useState<{ question: string; option: string }[]>(userSolutions);
//const { shuffleMaps } = useExamStore((state) => state);
const { questionIndex, setQuestionIndex } = useExamStore((state) => state);
const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state);
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
const {
questionIndex,
exam,
shuffleMaps,
hasExamEnded,
userSolutions: storeUserSolutions,
setQuestionIndex,
setUserSolutions
} = useExamStore((state) => state);
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
@@ -106,12 +112,12 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
});
let isSolutionCorrect;
//if (shuffleMaps.length == 0) {
isSolutionCorrect = matchingQuestion?.solution === x.option;
//} else {
// const shuffleMap = shuffleMaps.find((map) => map.id == x.question)
// isSolutionCorrect = shuffleMap?.map[x.option] == matchingQuestion?.solution;
//}
if (shuffleMaps.length == 0) {
isSolutionCorrect = matchingQuestion?.solution === x.option;
} else {
const shuffleMap = shuffleMaps.find((map) => map.id == x.question)
isSolutionCorrect = shuffleMap?.map[x.option] == matchingQuestion?.solution;
}
return isSolutionCorrect || false;
}).length;
const missing = total - correct;
@@ -119,7 +125,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
return { total, correct, missing };
};
/*const getShuffles = () => {
const getShuffles = () => {
let shuffle = {};
if (shuffleMaps.length !== 0) {
shuffle = {
@@ -129,11 +135,11 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
}
}
return shuffle;
}*/
}
const next = () => {
if (questionIndex === questions.length - 1) {
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, });//...getShuffles() });
onNext({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() });
} else {
setQuestionIndex(questionIndex + 1);
}
@@ -142,7 +148,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
const back = () => {
if (questionIndex === 0) {
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, });// ...getShuffles() });
onBack({ exercise: id, solutions: answers, score: calculateScore(), type, ...getShuffles() });
} else {
setQuestionIndex(questionIndex - 1);
}
@@ -164,7 +170,10 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
</div>
<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">
<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}
>
Back
</Button>