Previous Level exams were being broken by the part divider changes, fixed it.
This commit is contained in:
@@ -228,7 +228,6 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={
|
||||
exam && exam.module === "level" &&
|
||||
typeof exam.parts[0].intro === "string" &&
|
||||
partIndex === 0 &&
|
||||
questionIndex === 0
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ export default function MatchSentences({id, options, type, prompt, sentences, us
|
||||
const [answers, setAnswers] = useState<{question: string; option: string}[]>(userSolutions);
|
||||
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
|
||||
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers])
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
if (event.over && event.over.id.toString().startsWith("droppable")) {
|
||||
|
||||
@@ -174,8 +174,7 @@ 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" &&
|
||||
exam && exam.module === "level" &&
|
||||
partIndex === 0 &&
|
||||
questionIndex === 0
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export default function TrueFalse({id, type, prompt, questions, userSolutions, o
|
||||
const [answers, setAnswers] = useState<{id: string; solution: "true" | "false" | "not_given"}[]>(userSolutions);
|
||||
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
const setCurrentSolution = useExamStore((state) => state.setCurrentSolution);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({exercise: id, solutions: answers, score: calculateScore(), type});
|
||||
@@ -28,6 +29,12 @@ export default function TrueFalse({id, type, prompt, questions, userSolutions, o
|
||||
return {total, correct, missing};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers])
|
||||
|
||||
|
||||
const toggleAnswer = (solution: "true" | "false" | "not_given", questionId: string) => {
|
||||
const answer = answers.find((x) => x.id === questionId);
|
||||
if (answer && answer.solution === solution) {
|
||||
|
||||
@@ -49,7 +49,7 @@ function Blank({
|
||||
export default function WriteBlanks({id, prompt, type, maxWords, solutions, userSolutions, text, onNext, onBack}: WriteBlanksExercise & CommonProps) {
|
||||
const [answers, setAnswers] = useState<{id: string; solution: string}[]>(userSolutions);
|
||||
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
const {hasExamEnded, setCurrentSolution} = useExamStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded) onNext({exercise: id, solutions: answers, score: calculateScore(), type});
|
||||
@@ -70,6 +70,11 @@ export default function WriteBlanks({id, prompt, type, maxWords, solutions, user
|
||||
return {total, correct, missing};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSolution({ exercise: id, solutions: answers, score: calculateScore(), type });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [answers, setAnswers])
|
||||
|
||||
const renderLines = (line: string) => {
|
||||
return (
|
||||
<span className="text-base leading-5">
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Fragment, ReactNode, useCallback, useState } from "react";
|
||||
import { BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsStopwatch } from "react-icons/bs";
|
||||
import ProgressBar from "../Low/ProgressBar";
|
||||
import Timer from "./Timer";
|
||||
import { Exam, LevelExam, MultipleChoiceExercise, ShuffleMap, UserSolution } from "@/interfaces/exam";
|
||||
import { Exam, Exercise, LevelExam, MultipleChoiceExercise, ShuffleMap, UserSolution } from "@/interfaces/exam";
|
||||
import { BsFillGrid3X3GapFill } from "react-icons/bs";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import Button from "../Low/Button";
|
||||
@@ -24,6 +24,7 @@ interface Props {
|
||||
partLabel?: string;
|
||||
showTimer?: boolean;
|
||||
showSolutions?: boolean;
|
||||
currentExercise?: Exercise;
|
||||
runOnClick?: ((questionIndex: number) => void) | undefined;
|
||||
}
|
||||
|
||||
@@ -68,9 +69,9 @@ export default function ModuleTitle({
|
||||
if (!isMultipleChoiceLevelExercise() && !userSolutions) return null;
|
||||
|
||||
const currentExercise = (exam as LevelExam).parts[partIndex!].exercises[examExerciseIndex] as MultipleChoiceExercise;
|
||||
const userSolution = userSolutions!.find((x) => x.exercise == currentExercise.id)!;
|
||||
const answeredQuestions = new Set(userSolution.solutions.map(sol => sol.question));
|
||||
const exerciseOffset = currentExercise.questions[0].id;
|
||||
const userSolution = userSolutions!.find((x) => x.exercise.toString() == currentExercise.id.toString())!;
|
||||
const answeredQuestions = new Set(userSolution.solutions.map(sol => sol.question.toString()));
|
||||
const exerciseOffset = Number(currentExercise.questions[0].id);
|
||||
const lastExercise = exerciseOffset + (currentExercise.questions.length - 1);
|
||||
|
||||
const getQuestionColor = (questionId: string, solution: string, userQuestionSolution: string | undefined) => {
|
||||
@@ -96,10 +97,10 @@ export default function ModuleTitle({
|
||||
<div className="grid grid-cols-5 gap-3 px-4 py-2">
|
||||
{currentExercise.questions.map((_, index) => {
|
||||
const questionNumber = exerciseOffset + index;
|
||||
const isAnswered = answeredQuestions.has(questionNumber);
|
||||
const solution = currentExercise.questions.find((x) => x.id == questionNumber)!.solution;
|
||||
const isAnswered = answeredQuestions.has(questionNumber.toString());
|
||||
const solution = currentExercise.questions.find((x) => x.id.toString() == questionNumber.toString())!.solution;
|
||||
|
||||
const userQuestionSolution = currentExercise.userSolutions?.find((x) => x.question == questionNumber)?.option;
|
||||
const userQuestionSolution = currentExercise.userSolutions?.find((x) => x.question.toString() == questionNumber.toString())?.option;
|
||||
return (
|
||||
<Button
|
||||
variant={showSolutions ? "solid" : (isAnswered ? "solid" : "outline")}
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function FillBlanksSolutions({
|
||||
onBack,
|
||||
}: FillBlanksExercise & CommonProps) {
|
||||
const storeUserSolutions = useExamStore((state) => state.userSolutions);
|
||||
const {questionIndex, setQuestionIndex, partIndex, exam} = useExamStore((state) => state);
|
||||
|
||||
const correctUserSolutions = storeUserSolutions.find(
|
||||
(solution) => solution.exercise === id
|
||||
@@ -201,7 +202,14 @@ export default function FillBlanksSolutions({
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({ exercise: id, solutions: correctUserSolutions!, score: calculateScore(), type })}
|
||||
className="max-w-[200px] w-full">
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={
|
||||
exam &&
|
||||
typeof partIndex !== "undefined" &&
|
||||
exam.module === "level" &&
|
||||
questionIndex === 0 &&
|
||||
partIndex === 0
|
||||
}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import Icon from "@mdi/react";
|
||||
import {Fragment} from "react";
|
||||
import Button from "../Low/Button";
|
||||
import Xarrow from "react-xarrows";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
|
||||
function QuestionSolutionArea({
|
||||
question,
|
||||
@@ -61,6 +62,8 @@ export default function MatchSentencesSolutions({
|
||||
onNext,
|
||||
onBack,
|
||||
}: MatchSentencesExercise & CommonProps) {
|
||||
const {questionIndex, setQuestionIndex, partIndex, exam} = useExamStore((state) => state);
|
||||
|
||||
const calculateScore = () => {
|
||||
const total = sentences.length;
|
||||
const correct = userSolutions.filter(
|
||||
@@ -112,7 +115,14 @@ export default function MatchSentencesSolutions({
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({exercise: id, solutions: userSolutions, score: calculateScore(), type})}
|
||||
className="max-w-[200px] w-full">
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={
|
||||
exam &&
|
||||
typeof partIndex !== "undefined" &&
|
||||
exam.module === "level" &&
|
||||
questionIndex === 0 &&
|
||||
partIndex === 0
|
||||
}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -164,7 +164,6 @@ export default function MultipleChoice({id, type, prompt, questions, userSolutio
|
||||
exam &&
|
||||
typeof partIndex !== "undefined" &&
|
||||
exam.module === "level" &&
|
||||
typeof exam.parts[0].intro === "string" &&
|
||||
questionIndex === 0 &&
|
||||
partIndex === 0
|
||||
}>
|
||||
|
||||
@@ -4,10 +4,13 @@ import reactStringReplace from "react-string-replace";
|
||||
import {CommonProps} from ".";
|
||||
import {Fragment} from "react";
|
||||
import Button from "../Low/Button";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
|
||||
type Solution = "true" | "false" | "not_given";
|
||||
|
||||
export default function TrueFalseSolution({prompt, type, id, questions, userSolutions, onNext, onBack}: TrueFalseExercise & CommonProps) {
|
||||
const {questionIndex, setQuestionIndex, partIndex, exam} = useExamStore((state) => state);
|
||||
|
||||
const calculateScore = () => {
|
||||
const total = questions.length || 0;
|
||||
const correct = userSolutions.filter(
|
||||
@@ -121,7 +124,14 @@ export default function TrueFalseSolution({prompt, type, id, questions, userSolu
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({exercise: id, solutions: userSolutions, score: calculateScore(), type})}
|
||||
className="max-w-[200px] w-full">
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={
|
||||
exam &&
|
||||
typeof partIndex !== "undefined" &&
|
||||
exam.module === "level" &&
|
||||
questionIndex === 0 &&
|
||||
partIndex === 0
|
||||
}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import reactStringReplace from "react-string-replace";
|
||||
import {CommonProps} from ".";
|
||||
import {toast} from "react-toastify";
|
||||
import Button from "../Low/Button";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
|
||||
function Blank({
|
||||
id,
|
||||
@@ -71,6 +72,8 @@ export default function WriteBlanksSolutions({
|
||||
onNext,
|
||||
onBack,
|
||||
}: WriteBlanksExercise & CommonProps) {
|
||||
const {questionIndex, setQuestionIndex, partIndex, exam} = useExamStore((state) => state);
|
||||
|
||||
const calculateScore = () => {
|
||||
const total = text.match(/({{\d+}})/g)?.length || 0;
|
||||
const correct = userSolutions.filter(
|
||||
@@ -142,7 +145,14 @@ export default function WriteBlanksSolutions({
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => onBack({exercise: id, solutions: userSolutions, score: calculateScore(), type})}
|
||||
className="max-w-[200px] w-full">
|
||||
className="max-w-[200px] w-full"
|
||||
disabled={
|
||||
exam &&
|
||||
typeof partIndex !== "undefined" &&
|
||||
exam.module === "level" &&
|
||||
questionIndex === 0 &&
|
||||
partIndex === 0
|
||||
}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user