80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import {renderExercise} from "@/components/Exercises";
|
|
import {renderSolution} from "@/components/Solutions";
|
|
import {infoButtonStyle} from "@/constants/buttonStyles";
|
|
import {UserSolution, SpeakingExam} from "@/interfaces/exam";
|
|
import {mdiArrowRight} from "@mdi/js";
|
|
import Icon from "@mdi/react";
|
|
import clsx from "clsx";
|
|
import {Fragment, useEffect, useState} from "react";
|
|
import {toast} from "react-toastify";
|
|
|
|
interface Props {
|
|
exam: SpeakingExam;
|
|
showSolutions?: boolean;
|
|
onFinish: (userSolutions: UserSolution[]) => void;
|
|
}
|
|
|
|
export default function Speaking({exam, showSolutions = false, onFinish}: Props) {
|
|
const [exerciseIndex, setExerciseIndex] = useState(0);
|
|
const [timer, setTimer] = useState<number>();
|
|
const [userSolutions, setUserSolutions] = useState<UserSolution[]>([]);
|
|
|
|
useEffect(() => {
|
|
setTimer(exam.minTimer * 60);
|
|
const timerInterval = setInterval(() => setTimer((prev) => prev && prev - 1), 1000);
|
|
|
|
return () => {
|
|
clearInterval(timerInterval);
|
|
};
|
|
}, [exam.minTimer]);
|
|
|
|
const nextExercise = (solution?: UserSolution) => {
|
|
if (solution) {
|
|
setUserSolutions((prev) => [...prev.filter((x) => x.id !== solution.id), solution]);
|
|
}
|
|
|
|
if (exerciseIndex + 1 < exam.exercises.length) {
|
|
setExerciseIndex((prev) => prev + 1);
|
|
return;
|
|
}
|
|
|
|
if (solution) {
|
|
onFinish([...userSolutions.filter((x) => x.id !== solution.id), solution].map((x) => ({...x, module: "speaking", exam: exam.id})));
|
|
} else {
|
|
onFinish(userSolutions.map((x) => ({...x, module: "speaking", exam: exam.id})));
|
|
}
|
|
};
|
|
|
|
const previousExercise = () => {
|
|
setExerciseIndex((prev) => prev - 1);
|
|
};
|
|
|
|
return (
|
|
<div className="w-full h-full relative flex flex-col gap-8 items-center justify-center p-8 px-16 overflow-hidden">
|
|
{timer && (
|
|
<div className="absolute w-24 top-8 text-center right-8 font-semibold text-lg border-ielts-speaking border-2 p-2 rounded-xl bg-ielts-speaking-transparent text-white">
|
|
{Math.floor(timer / 60) < 10 ? "0" : ""}
|
|
{Math.floor(timer / 60)}:{timer % 60 < 10 ? "0" : ""}
|
|
{timer % 60}
|
|
</div>
|
|
)}
|
|
{exerciseIndex > -1 &&
|
|
exerciseIndex < exam.exercises.length &&
|
|
!showSolutions &&
|
|
renderExercise(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
|
|
{exerciseIndex > -1 &&
|
|
exerciseIndex < exam.exercises.length &&
|
|
showSolutions &&
|
|
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
|
|
{exerciseIndex === -1 && (
|
|
<button className={clsx("btn btn-wide gap-4 relative text-white self-end", infoButtonStyle)} onClick={() => nextExercise()}>
|
|
Next
|
|
<div className="absolute right-4">
|
|
<Icon path={mdiArrowRight} color="white" size={1} />
|
|
</div>
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|