Updated some troubles related to the Level Exam
This commit is contained in:
@@ -3,39 +3,30 @@ import Modal from "@/components/Modal";
|
|||||||
import { Exam, LevelExam, MultipleChoiceExercise, ShuffleMap } from "@/interfaces/exam";
|
import { Exam, LevelExam, MultipleChoiceExercise, ShuffleMap } from "@/interfaces/exam";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { BsFillGrid3X3GapFill } from "react-icons/bs";
|
import { BsFillGrid3X3GapFill } from "react-icons/bs";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
exam: LevelExam
|
||||||
showSolutions: boolean;
|
showSolutions: boolean;
|
||||||
runOnClick: ((index: number) => void) | undefined;
|
runOnClick: ((index: number) => void) | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MCQuestionGrid: React.FC<Props> = ({showSolutions, runOnClick}) => {
|
const MCQuestionGrid: React.FC<Props> = ({ exam, showSolutions, runOnClick }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
userSolutions,
|
userSolutions,
|
||||||
partIndex: sectionIndex,
|
partIndex: sectionIndex,
|
||||||
exerciseIndex,
|
exerciseIndex,
|
||||||
exam
|
|
||||||
} = useExamStore((state) => state);
|
} = useExamStore((state) => state);
|
||||||
|
|
||||||
const isMultipleChoiceLevelExercise = () => {
|
const currentExercise = useMemo(() => (exam as LevelExam).parts[sectionIndex!].exercises[exerciseIndex] as MultipleChoiceExercise, [exam, exerciseIndex, sectionIndex])
|
||||||
if (exam?.module === 'level' && typeof sectionIndex === "number" && sectionIndex > -1) {
|
const userSolution = useMemo(() => userSolutions!.find((x) => x.exercise.toString() == currentExercise.id.toString())!, [currentExercise.id, userSolutions])
|
||||||
const currentExercise = (exam as LevelExam).parts[sectionIndex].exercises[exerciseIndex];
|
const answeredQuestions = useMemo(() => new Set(userSolution.solutions.map(sol => sol.question.toString())), [userSolution.solutions])
|
||||||
return currentExercise && currentExercise.type === 'multipleChoice';
|
const exerciseOffset = useMemo(() => Number(currentExercise.questions[0].id), [currentExercise.questions])
|
||||||
}
|
const lastExercise = useMemo(() => exerciseOffset + (currentExercise.questions.length - 1),
|
||||||
return false;
|
[currentExercise.questions.length, exerciseOffset]);
|
||||||
};
|
|
||||||
|
|
||||||
if (!isMultipleChoiceLevelExercise() && !userSolutions) return null;
|
|
||||||
|
|
||||||
const currentExercise = (exam as LevelExam).parts[sectionIndex!].exercises[exerciseIndex] as MultipleChoiceExercise;
|
|
||||||
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) => {
|
const getQuestionColor = (questionId: string, solution: string, userQuestionSolution: string | undefined) => {
|
||||||
const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => {
|
const questionShuffleMap = userSolutions.reduce((foundMap, userSolution) => {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Module } from "@/interfaces";
|
import { Module } from "@/interfaces";
|
||||||
import { moduleLabels } from "@/utils/moduleUtils";
|
import { moduleLabels } from "@/utils/moduleUtils";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { ReactNode, useState } from "react";
|
import { ReactNode, useMemo, useState } from "react";
|
||||||
import { BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen } from "react-icons/bs";
|
import { BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen } from "react-icons/bs";
|
||||||
import ProgressBar from "../../Low/ProgressBar";
|
import ProgressBar from "../../Low/ProgressBar";
|
||||||
import Timer from "../Timer";
|
import Timer from "../Timer";
|
||||||
import { Exercise } from "@/interfaces/exam";
|
import { Exercise, LevelExam } from "@/interfaces/exam";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import MCQuestionGrid from "./MCQuestionGrid";
|
import MCQuestionGrid from "./MCQuestionGrid";
|
||||||
@@ -38,9 +38,7 @@ export default function ModuleTitle({
|
|||||||
showSolutions = false,
|
showSolutions = false,
|
||||||
runOnClick = undefined
|
runOnClick = undefined
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const {
|
const { exam, partIndex, exerciseIndex: examExerciseIndex, userSolutions } = useExamStore((state) => state);
|
||||||
exam
|
|
||||||
} = useExamStore((state) => state);
|
|
||||||
|
|
||||||
const moduleIcon: { [key in Module]: ReactNode } = {
|
const moduleIcon: { [key in Module]: ReactNode } = {
|
||||||
reading: <BsBook className="text-ielts-reading w-6 h-6" />,
|
reading: <BsBook className="text-ielts-reading w-6 h-6" />,
|
||||||
@@ -50,6 +48,14 @@ export default function ModuleTitle({
|
|||||||
level: <BsClipboard className="text-ielts-level w-6 h-6" />,
|
level: <BsClipboard className="text-ielts-level w-6 h-6" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showGrid = useMemo(() =>
|
||||||
|
exam?.module === "level"
|
||||||
|
&& partIndex > -1
|
||||||
|
&& exam.parts[partIndex].exercises[examExerciseIndex].type === "multipleChoice"
|
||||||
|
&& !!userSolutions,
|
||||||
|
[exam, examExerciseIndex, partIndex, userSolutions]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showTimer && <Timer minTimer={minTimer} disableTimer={disableTimer} />}
|
{showTimer && <Timer minTimer={minTimer} disableTimer={disableTimer} />}
|
||||||
@@ -67,7 +73,7 @@ export default function ModuleTitle({
|
|||||||
return (
|
return (
|
||||||
<div key={index} className="text-2xl font-semibold flex flex-col gap-2">
|
<div key={index} className="text-2xl font-semibold flex flex-col gap-2">
|
||||||
{partInstructions.split("\\n").map((line, lineIndex) => (
|
{partInstructions.split("\\n").map((line, lineIndex) => (
|
||||||
<span key={lineIndex} dangerouslySetInnerHTML={{__html: line.replace('that is not correct', 'that is <span class="font-bold"><u>not correct</u></span>')}}></span>
|
<span key={lineIndex} dangerouslySetInnerHTML={{ __html: line.replace('that is not correct', 'that is <span class="font-bold"><u>not correct</u></span>') }}></span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -87,7 +93,7 @@ export default function ModuleTitle({
|
|||||||
</div>
|
</div>
|
||||||
<ProgressBar color={module} label="" percentage={(exerciseIndex * 100) / totalExercises} className="h-2 w-full" />
|
<ProgressBar color={module} label="" percentage={(exerciseIndex * 100) / totalExercises} className="h-2 w-full" />
|
||||||
</div>
|
</div>
|
||||||
{exam?.module === "level" && <MCQuestionGrid showSolutions={showSolutions} runOnClick={runOnClick}/>}
|
{showGrid && <MCQuestionGrid exam={exam as LevelExam} showSolutions={showSolutions} runOnClick={runOnClick} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { Assignment } from "@/interfaces/results";
|
|||||||
import { Stat, User } from "@/interfaces/user";
|
import { Stat, User } from "@/interfaces/user";
|
||||||
import { sessionOptions } from "@/lib/session";
|
import { sessionOptions } from "@/lib/session";
|
||||||
import useExamStore from "@/stores/examStore";
|
import useExamStore from "@/stores/examStore";
|
||||||
import { findBy, mapBy, redirect, serialize } from "@/utils";
|
import { filterBy, findBy, mapBy, redirect, serialize } from "@/utils";
|
||||||
import { requestUser } from "@/utils/api";
|
import { requestUser } from "@/utils/api";
|
||||||
import { activeAssignmentFilter } from "@/utils/assignments";
|
import { activeAssignmentFilter } from "@/utils/assignments";
|
||||||
import { getAssignmentsByAssignee } from "@/utils/assignments.be";
|
import { getAssignmentsByAssignee } from "@/utils/assignments.be";
|
||||||
@@ -60,7 +60,7 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
|||||||
|
|
||||||
const examIDs = uniqBy(
|
const examIDs = uniqBy(
|
||||||
assignments.flatMap((a) =>
|
assignments.flatMap((a) =>
|
||||||
a.exams.filter((e) => e.assignee === user.id).map((e) => ({ module: e.module, id: e.id, key: `${e.module}_${e.id}` })),
|
filterBy(a.exams, 'assignee', user.id).map((e) => ({ module: e.module, id: e.id, key: `${e.module}_${e.id}` })),
|
||||||
),
|
),
|
||||||
"key",
|
"key",
|
||||||
);
|
);
|
||||||
@@ -93,7 +93,7 @@ export default function OfficialExam({ user, entities, assignments, sessions, ex
|
|||||||
state.setSelectedModules(mapBy(assignmentExams.sort(sortByModule), 'module'));
|
state.setSelectedModules(mapBy(assignmentExams.sort(sortByModule), 'module'));
|
||||||
state.setAssignment(assignment);
|
state.setAssignment(assignment);
|
||||||
|
|
||||||
router.push("/exam");
|
router.push(`/exam?assignment=${assignment.id}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ export default function OfficialExam({ user, entities, assignments, sessions, ex
|
|||||||
state.setShowSolutions(false);
|
state.setShowSolutions(false);
|
||||||
state.setQuestionIndex(session.questionIndex);
|
state.setQuestionIndex(session.questionIndex);
|
||||||
|
|
||||||
router.push("/exam");
|
router.push(`/exam?assignment=${session.assignment?.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user