ENCOA-154: Increase the number of questions per page in all modules (Priority in Level Test)

This commit is contained in:
Tiago Ribeiro
2024-09-04 11:41:48 +01:00
parent becc91d8ea
commit 4654c21d92
2 changed files with 82 additions and 72 deletions

View File

@@ -18,9 +18,8 @@ function Question({
}: MultipleChoiceQuestion & { }: MultipleChoiceQuestion & {
userSolution: string | undefined; userSolution: string | undefined;
onSelectOption?: (option: string) => void; onSelectOption?: (option: string) => void;
showSolution?: boolean, showSolution?: boolean;
}) { }) {
const renderPrompt = (prompt: string) => { const renderPrompt = (prompt: string) => {
return reactStringReplace(prompt, /(<u>.*?<\/u>)/g, (match) => { return reactStringReplace(prompt, /(<u>.*?<\/u>)/g, (match) => {
const word = match.replaceAll("<u>", "").replaceAll("</u>", ""); const word = match.replaceAll("<u>", "").replaceAll("</u>", "");
@@ -49,7 +48,9 @@ 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", "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", userSolution === option.id.toString() && "border-mti-purple-light",
)}> )}>
<span key={v4()} 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()}`} /> <img src={option.src!} alt={`Option ${option.id.toString()}`} />
</div> </div>
))} ))}
@@ -74,16 +75,9 @@ function Question({
export default function MultipleChoice({id, prompt, type, questions, userSolutions, onNext, onBack}: MultipleChoiceExercise & CommonProps) { export default function MultipleChoice({id, prompt, type, questions, userSolutions, onNext, onBack}: MultipleChoiceExercise & CommonProps) {
const [answers, setAnswers] = useState<{question: string; option: string}[]>(userSolutions); const [answers, setAnswers] = useState<{question: string; option: string}[]>(userSolutions);
const { const {questionIndex, exerciseIndex, exam, shuffles, hasExamEnded, partIndex, setQuestionIndex, setCurrentSolution} = useExamStore(
questionIndex, (state) => state,
exerciseIndex, );
exam,
shuffles,
hasExamEnded,
partIndex,
setQuestionIndex,
setCurrentSolution
} = useExamStore((state) => state);
const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles; const shuffleMaps = shuffles.find((x) => x.exerciseID == id)?.shuffles;
@@ -94,15 +88,14 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasExamEnded]); }, [hasExamEnded]);
const onSelectOption = (option: string) => { const onSelectOption = (option: string, question: MultipleChoiceQuestion) => {
const question = questions[questionIndex];
setAnswers((prev) => [...prev.filter((x) => x.question !== question.id), {option, question: question.id}]); 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}); setCurrentSolution({exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps});
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, setAnswers]) }, [answers, setAnswers]);
const getShuffledSolution = (originalSolution: string, questionShuffleMap: ShuffleMap) => { const getShuffledSolution = (originalSolution: string, questionShuffleMap: ShuffleMap) => {
for (const [newPosition, originalPosition] of Object.entries(questionShuffleMap.map)) { for (const [newPosition, originalPosition] of Object.entries(questionShuffleMap.map)) {
@@ -111,8 +104,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
} }
} }
return originalSolution; return originalSolution;
} };
const calculateScore = () => { const calculateScore = () => {
const total = questions.length; const total = questions.length;
@@ -139,10 +131,10 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
}; };
const next = () => { const next = () => {
if (questionIndex === questions.length - 1) { if (questionIndex + 1 >= questions.length - 1) {
onNext({exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps}); onNext({exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps});
} else { } else {
setQuestionIndex(questionIndex + 1); setQuestionIndex(questionIndex + 2);
} }
scrollToTop(); scrollToTop();
}; };
@@ -152,7 +144,7 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
onBack({exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps}); onBack({exercise: id, solutions: answers, score: calculateScore(), type, shuffleMaps: shuffleMaps});
} else { } else {
if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return; if (exam?.module === "level" && typeof exam.parts[0].intro !== "undefined" && questionIndex === 0) return;
setQuestionIndex(questionIndex - 1); setQuestionIndex(questionIndex - 2);
} }
scrollToTop(); scrollToTop();
@@ -160,35 +152,47 @@ export default function MultipleChoice({ id, prompt, type, questions, userSoluti
return ( return (
<> <>
<div className="flex flex-col gap-2 mt-4 h-fit w-full mb-20 bg-mti-gray-smoke rounded-xl px-16 py-8"> <div className="flex flex-col gap-4 mt-2 mb-20">
<div className="flex flex-col gap-8 h-fit w-full bg-mti-gray-smoke rounded-xl px-16 py-8">
{/*<span className="text-xl font-semibold mb-2">{"Select the appropriate option."}</span>*/} {/*<span className="text-xl font-semibold mb-2">{"Select the appropriate option."}</span>*/}
{questionIndex < questions.length && ( {questionIndex < questions.length && (
<Question <Question
{...questions[questionIndex]} {...questions[questionIndex]}
userSolution={answers.find((x) => questions[questionIndex].id === x.question)?.option} userSolution={answers.find((x) => questions[questionIndex].id === x.question)?.option}
onSelectOption={onSelectOption} onSelectOption={(option) => onSelectOption(option, questions[questionIndex])}
/> />
)} )}
</div> </div>
{questionIndex + 1 < questions.length && (
<div className="flex flex-col gap-8 h-fit w-full bg-mti-gray-smoke rounded-xl px-16 py-8">
<Question
{...questions[questionIndex + 1]}
userSolution={answers.find((x) => questions[questionIndex + 1].id === x.question)?.option}
onSelectOption={(option) => onSelectOption(option, questions[questionIndex + 1])}
/>
</div>
)}
</div>
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8"> <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
disabled={ color="purple"
exam && exam.module === "level" && variant="outline"
partIndex === 0 && onClick={back}
questionIndex === 0 className="max-w-[200px] w-full"
} disabled={exam && exam.module === "level" && partIndex === 0 && questionIndex === 0}>
>
Back Back
</Button> </Button>
<Button color="purple" onClick={next} className="max-w-[200px] self-end w-full"> <Button color="purple" onClick={next} className="max-w-[200px] self-end w-full">
{ {exam &&
exam && exam.module === "level" && exam.module === "level" &&
partIndex === exam.parts.length - 1 && partIndex === exam.parts.length - 1 &&
exerciseIndex === exam.parts[partIndex].exercises.length - 1 && exerciseIndex === exam.parts[partIndex].exercises.length - 1 &&
questionIndex === questions.length - 1 questionIndex + 1 >= questions.length - 1
? "Submit" : "Next"} ? "Submit"
: "Next"}
</Button> </Button>
</div> </div>
</> </>

View File

@@ -111,10 +111,10 @@ export default function MultipleChoice({id, type, prompt, questions, userSolutio
}; };
const next = () => { const next = () => {
if (questionIndex === questions.length - 1) { if (questionIndex + 1 >= questions.length - 1) {
onNext({exercise: id, solutions: userSolutions, score: calculateScore(), type}); onNext({exercise: id, solutions: userSolutions, score: calculateScore(), type});
} else { } else {
setQuestionIndex(questionIndex + 1); setQuestionIndex(questionIndex + 2);
} }
}; };
@@ -122,14 +122,15 @@ export default function MultipleChoice({id, type, prompt, questions, userSolutio
if (questionIndex === 0) { if (questionIndex === 0) {
onBack({exercise: id, solutions: userSolutions, score: calculateScore(), type}); onBack({exercise: id, solutions: userSolutions, score: calculateScore(), type});
} else { } else {
setQuestionIndex(questionIndex - 1); setQuestionIndex(questionIndex - 2);
} }
}; };
return ( return (
<> <>
<div className="flex flex-col gap-4 w-full h-full mb-20"> <div className="flex flex-col gap-4 w-full h-full mb-20">
<div className="flex flex-col gap-2 mt-4 h-full bg-mti-gray-smoke rounded-xl px-16 py-8"> <div className="flex flex-col gap-4 mt-2">
<div className="flex flex-col gap-8 h-fit w-full bg-mti-gray-smoke rounded-xl px-16 py-8">
{/*<span className="text-xl font-semibold">{prompt}</span>*/} {/*<span className="text-xl font-semibold">{prompt}</span>*/}
{userSolutions && questionIndex < questions.length && ( {userSolutions && questionIndex < questions.length && (
<Question <Question
@@ -138,6 +139,17 @@ export default function MultipleChoice({id, type, prompt, questions, userSolutio
/> />
)} )}
</div> </div>
{userSolutions && questionIndex + 1 < questions.length && (
<div className="flex flex-col gap-8 h-fit w-full bg-mti-gray-smoke rounded-xl px-16 py-8">
<Question
{...questions[questionIndex + 1]}
userSolution={userSolutions.find((x) => questions[questionIndex + 1].id === x.question)?.option}
/>
</div>
)}
</div>
<div className="flex gap-4 items-center"> <div className="flex gap-4 items-center">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<div className="w-4 h-4 rounded-full bg-mti-purple" /> <div className="w-4 h-4 rounded-full bg-mti-purple" />
@@ -160,13 +172,7 @@ export default function MultipleChoice({id, type, prompt, questions, userSolutio
variant="outline" variant="outline"
onClick={back} onClick={back}
className="max-w-[200px] w-full" className="max-w-[200px] w-full"
disabled={ disabled={exam && typeof partIndex !== "undefined" && exam.module === "level" && questionIndex === 0 && partIndex === 0}>
exam &&
typeof partIndex !== "undefined" &&
exam.module === "level" &&
questionIndex === 0 &&
partIndex === 0
}>
Back Back
</Button> </Button>