Reverted Level to only utas placement test exercises, Speaking, bug fixes, placeholder

This commit is contained in:
Carlos-Mesquita
2024-11-10 04:24:23 +00:00
parent c507eae507
commit 322d7905c3
39 changed files with 1251 additions and 279 deletions

View File

@@ -1,31 +1,56 @@
import {renderExercise} from "@/components/Exercises";
import { renderExercise } from "@/components/Exercises";
import ModuleTitle from "@/components/Medium/ModuleTitle";
import {renderSolution} from "@/components/Solutions";
import {infoButtonStyle} from "@/constants/buttonStyles";
import {UserSolution, SpeakingExam, SpeakingExercise, InteractiveSpeakingExercise} from "@/interfaces/exam";
import useExamStore from "@/stores/examStore";
import {defaultUserSolutions} from "@/utils/exams";
import {countExercises} from "@/utils/moduleUtils";
import {convertCamelCaseToReadable} from "@/utils/string";
import {mdiArrowRight} from "@mdi/js";
import { renderSolution } from "@/components/Solutions";
import { infoButtonStyle } from "@/constants/buttonStyles";
import { UserSolution, SpeakingExam, SpeakingExercise, InteractiveSpeakingExercise } from "@/interfaces/exam";
import useExamStore, { usePersistentExamStore } from "@/stores/examStore";
import { defaultUserSolutions } from "@/utils/exams";
import { countExercises } from "@/utils/moduleUtils";
import { convertCamelCaseToReadable } from "@/utils/string";
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";
import { Fragment, useEffect, useState } from "react";
import { toast } from "react-toastify";
import PartDivider from "./Navigation/SectionDivider";
interface Props {
exam: SpeakingExam;
showSolutions?: boolean;
onFinish: (userSolutions: UserSolution[]) => void;
preview?: boolean;
}
export default function Speaking({exam, showSolutions = false, onFinish}: Props) {
const [speakingPromptsDone, setSpeakingPromptsDone] = useState<{id: string; amount: number}[]>([]);
export default function Speaking({ exam, showSolutions = false, onFinish, preview = false }: Props) {
const [speakingPromptsDone, setSpeakingPromptsDone] = useState<{ id: string; amount: number }[]>([]);
const {userSolutions, setUserSolutions} = useExamStore((state) => state);
const {questionIndex, setQuestionIndex} = useExamStore((state) => state);
const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
const speakingBgColor = "bg-ielts-speaking-light";
const examState = useExamStore((state) => state);
const persistentExamState = usePersistentExamStore((state) => state);
const {
userSolutions,
questionIndex,
exerciseIndex,
hasExamEnded,
setBgColor,
setUserSolutions,
setHasExamEnded,
setQuestionIndex,
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 (!showSolutions && exam.exercises[exerciseIndex]?.intro !== undefined && exam.exercises[exerciseIndex]?.intro !== "" && !seenParts.has(exerciseIndex)) {
setShowPartDivider(true);
setBgColor(speakingBgColor);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [exerciseIndex]);
useEffect(() => {
if (hasExamEnded && exerciseIndex === -1) {
@@ -38,12 +63,12 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
const nextExercise = (solution?: UserSolution) => {
scrollToTop();
if (solution) {
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "speaking", exam: exam.id}]);
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "speaking", exam: exam.id }]);
}
if (questionIndex > 0) {
const exercise = getExercise();
setSpeakingPromptsDone((prev) => [...prev.filter((x) => x.id !== exercise.id), {id: exercise.id, amount: questionIndex}]);
setSpeakingPromptsDone((prev) => [...prev.filter((x) => x.id !== exercise.id), { id: exercise.id, amount: questionIndex }]);
}
setQuestionIndex(0);
@@ -57,7 +82,7 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
setHasExamEnded(false);
if (solution) {
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "speaking", exam: exam.id}]);
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "speaking", exam: exam.id }]);
} else {
onFinish(userSolutions);
}
@@ -66,7 +91,7 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
const previousExercise = (solution?: UserSolution) => {
scrollToTop();
if (solution) {
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "speaking", exam: exam.id}]);
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "speaking", exam: exam.id }]);
}
if (exerciseIndex > 0) {
@@ -85,24 +110,34 @@ export default function Speaking({exam, showSolutions = false, onFinish}: Props)
return (
<>
<div className="flex flex-col h-full w-full gap-8 items-center">
<ModuleTitle
label={convertCamelCaseToReadable(exam.exercises[exerciseIndex].type)}
minTimer={exam.minTimer}
exerciseIndex={exerciseIndex + 1 + questionIndex + speakingPromptsDone.reduce((acc, curr) => acc + curr.amount, 0)}
{(showPartDivider) ?
<PartDivider
module="speaking"
totalExercises={countExercises(exam.exercises)}
disableTimer={showSolutions}
/>
{exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length &&
!showSolutions &&
renderExercise(getExercise(), exam.id, nextExercise, previousExercise)}
{exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length &&
showSolutions &&
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
</div>
sectionLabel="Speaking"
defaultTitle="Speaking 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
label={convertCamelCaseToReadable(exam.exercises[exerciseIndex].type)}
minTimer={exam.minTimer}
exerciseIndex={exerciseIndex + 1 + questionIndex + speakingPromptsDone.reduce((acc, curr) => acc + curr.amount, 0)}
module="speaking"
totalExercises={countExercises(exam.exercises)}
disableTimer={showSolutions || preview}
/>
{exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length &&
!showSolutions &&
renderExercise(getExercise(), exam.id, nextExercise, previousExercise, undefined, preview)}
{exerciseIndex > -1 &&
exerciseIndex < exam.exercises.length &&
showSolutions &&
renderSolution(exam.exercises[exerciseIndex], nextExercise, previousExercise)}
</div>
)}
</>
);
}