127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
import { renderExercise } from "@/components/Exercises";
|
|
import ModuleTitle from "@/components/Medium/ModuleTitle";
|
|
import { renderSolution } from "@/components/Solutions";
|
|
import { UserSolution, WritingExam } from "@/interfaces/exam";
|
|
import useExamStore, { usePersistentExamStore } from "@/stores/examStore";
|
|
import { countExercises } from "@/utils/moduleUtils";
|
|
import PartDivider from "./Navigation/SectionDivider";
|
|
import { useEffect, useState } from "react";
|
|
|
|
interface Props {
|
|
exam: WritingExam;
|
|
showSolutions?: boolean;
|
|
preview?: boolean;
|
|
onFinish: (userSolutions: UserSolution[]) => void;
|
|
}
|
|
|
|
export default function Writing({ exam, showSolutions = false, preview = false, onFinish }: Props) {
|
|
const writingBgColor = "bg-ielts-writing-light";
|
|
|
|
const examState = useExamStore((state) => state);
|
|
const persistentExamState = usePersistentExamStore((state) => state);
|
|
|
|
const {
|
|
userSolutions,
|
|
exerciseIndex,
|
|
hasExamEnded,
|
|
setBgColor,
|
|
setUserSolutions,
|
|
setHasExamEnded,
|
|
setExerciseIndex,
|
|
} = !preview ? examState : persistentExamState;
|
|
|
|
const [seenParts, setSeenParts] = useState<Set<number>>(new Set(showSolutions ? exam.exercises.map((_, index) => index) : []));
|
|
const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.exercises[0].intro === "string" && exam.exercises[0].intro !== "");
|
|
|
|
useEffect(() => {
|
|
if (hasExamEnded && exerciseIndex === -1) {
|
|
setExerciseIndex(exerciseIndex + 1);
|
|
}
|
|
}, [hasExamEnded, exerciseIndex, setExerciseIndex]);
|
|
|
|
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
|
|
|
useEffect(() => {
|
|
if (!showSolutions && exam.exercises[exerciseIndex]?.intro !== undefined && exam.exercises[exerciseIndex]?.intro !== "" && !seenParts.has(exerciseIndex)) {
|
|
setShowPartDivider(true);
|
|
setBgColor(writingBgColor);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [exerciseIndex]);
|
|
|
|
|
|
const nextExercise = (solution?: UserSolution) => {
|
|
scrollToTop();
|
|
if (solution) {
|
|
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "writing", exam: exam.id }]);
|
|
}
|
|
|
|
if (exerciseIndex + 1 < exam.exercises.length) {
|
|
setExerciseIndex(exerciseIndex + 1);
|
|
return;
|
|
}
|
|
|
|
if (exerciseIndex >= exam.exercises.length) return;
|
|
|
|
setHasExamEnded(false);
|
|
|
|
if (solution) {
|
|
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "writing", exam: exam.id }]);
|
|
} else {
|
|
onFinish(userSolutions);
|
|
}
|
|
};
|
|
|
|
const previousExercise = (solution?: UserSolution) => {
|
|
scrollToTop();
|
|
if (solution) {
|
|
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "writing", exam: exam.id }]);
|
|
}
|
|
|
|
if (exerciseIndex > 0) {
|
|
setExerciseIndex(exerciseIndex - 1);
|
|
}
|
|
};
|
|
|
|
const getExercise = () => {
|
|
const exercise = exam.exercises[exerciseIndex];
|
|
return {
|
|
...exercise,
|
|
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
|
|
};
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{(showPartDivider) ?
|
|
<PartDivider
|
|
module="writing"
|
|
sectionLabel="Task"
|
|
defaultTitle="Writing exam"
|
|
section={exam.exercises[exerciseIndex]}
|
|
sectionIndex={exerciseIndex}
|
|
onNext={() => { setShowPartDivider(false); setBgColor("bg-white"); setSeenParts((prev) => new Set(prev).add(exerciseIndex))}}
|
|
/> : (
|
|
<div className="flex flex-col h-full w-full gap-8 items-center">
|
|
<ModuleTitle
|
|
minTimer={exam.minTimer}
|
|
exerciseIndex={exerciseIndex + 1}
|
|
module="writing"
|
|
totalExercises={countExercises(exam.exercises)}
|
|
disableTimer={showSolutions || preview}
|
|
/>
|
|
{exerciseIndex > -1 &&
|
|
exerciseIndex < exam.exercises.length &&
|
|
!showSolutions &&
|
|
renderExercise(getExercise(), exam.id, nextExercise, previousExercise, preview)}
|
|
{exerciseIndex > -1 &&
|
|
exerciseIndex < exam.exercises.length &&
|
|
showSolutions &&
|
|
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
|
|
</div>
|
|
)
|
|
}
|
|
</>
|
|
);
|
|
}
|