Navigation rework, added prompt edit to components that were missing
This commit is contained in:
@@ -1,162 +1,66 @@
|
||||
import { MultipleChoiceExercise, ReadingExam, ReadingPart, UserSolution } from "@/interfaces/exam";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { Exercise, ReadingExam, UserSolution } from "@/interfaces/exam";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import { convertCamelCaseToReadable } from "@/utils/string";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { renderExercise } from "@/components/Exercises";
|
||||
import { renderSolution } from "@/components/Solutions";
|
||||
import { BsChevronDown, BsChevronUp } from "react-icons/bs";
|
||||
import ModuleTitle from "@/components/Medium/ModuleTitle";
|
||||
import Button from "@/components/Low/Button";
|
||||
import BlankQuestionsModal from "@/components/QuestionsModal";
|
||||
import useExamStore, { usePersistentExamStore } from "@/stores/examStore";
|
||||
import { countExercises } from "@/utils/moduleUtils";
|
||||
import PartDivider from "./Navigation/SectionDivider";
|
||||
import ReadingPassage from "./components/ReadingPassage";
|
||||
//import ReadingPassageModal from "./components/ReadingPassageModal";
|
||||
import {calculateExerciseIndex} from "./utils/calculateExerciseIndex";
|
||||
import useExamNavigation from "./Navigation/useExamNavigation";
|
||||
import { ExamProps } from "./types";
|
||||
import useExamStore, { usePersistentExamStore } from "@/stores/exam";
|
||||
import useExamTimer from "@/hooks/useExamTimer";
|
||||
import SectionNavbar from "./Navigation/SectionNavbar";
|
||||
import ProgressButtons from "./components/ProgressButtons";
|
||||
|
||||
interface Props {
|
||||
exam: ReadingExam;
|
||||
showSolutions?: boolean;
|
||||
preview?: boolean;
|
||||
onFinish: (userSolutions: UserSolution[]) => void;
|
||||
}
|
||||
const Reading: React.FC<ExamProps<ReadingExam>> = ({ exam, showSolutions = false, preview = false }) => {
|
||||
const updateTimers = useExamTimer(exam.module, preview || showSolutions);
|
||||
const userSolutionRef = useRef<(() => UserSolution) | null>(null);
|
||||
const [solutionWasUpdated, setSolutionWasUpdated] = useState(false);
|
||||
|
||||
const numberToLetter = (number: number) => (number + 9).toString(36).toUpperCase();
|
||||
|
||||
function TextModal({ isOpen, title, content, onClose }: { isOpen: boolean; title: string; content: string; onClose: () => void }) {
|
||||
return (
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0">
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95">
|
||||
<Dialog.Panel className="w-full relative max-w-4xl transform rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2 overflow-auto mb-28">
|
||||
<p className="text-sm">
|
||||
{content.split("\\n").map((line, index) => (
|
||||
<Fragment key={index}>
|
||||
{line}
|
||||
<br />
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-8 right-8 max-w-[200px] self-end w-full">
|
||||
<Button color="purple" variant="outline" className="max-w-[200px] self-end w-full" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
||||
function TextComponent({ part, exerciseType }: { part: ReadingPart; exerciseType: string }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full">
|
||||
<h3 className="text-xl font-semibold">{part.text.title}</h3>
|
||||
<div className="border border-mti-gray-dim w-full rounded-full opacity-10" />
|
||||
{part.text.content
|
||||
.split(/\n|(\\n)/g)
|
||||
.filter((x) => x && x.length > 0 && x !== "\\n")
|
||||
.map((line, index) => (
|
||||
<Fragment key={index}>
|
||||
{exerciseType === "matchSentences" && (
|
||||
<div className="flex gap-3 border border-transparent hover:border-mti-purple-light rounded-lg transition ease-in-out duration-300 p-2 px-3 cursor-pointer">
|
||||
<span className="font-bold text-mti-purple-dark">{numberToLetter(index + 1)}</span>
|
||||
<p>{line}</p>
|
||||
</div>
|
||||
)}
|
||||
{exerciseType !== "matchSentences" && <p key={index}>{line}</p>}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Reading({ exam, showSolutions = false, preview = false, onFinish }: Props) {
|
||||
const readingBgColor = "bg-ielts-reading-light";
|
||||
|
||||
const [showTextModal, setShowTextModal] = useState(false);
|
||||
const [showBlankModal, setShowBlankModal] = useState(false);
|
||||
const [multipleChoicesDone, setMultipleChoicesDone] = useState<{ id: string; amount: number }[]>([]);
|
||||
//const [showTextModal, setShowTextModal] = useState(false);
|
||||
const [isTextMinimized, setIsTextMinimzed] = useState(false);
|
||||
const [exerciseType, setExerciseType] = useState("");
|
||||
|
||||
const [seenParts, setSeenParts] = useState<Set<number>>(new Set(showSolutions ? exam.parts.map((_, index) => index) : []));
|
||||
const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.parts[0].intro === "string" && exam.parts[0].intro !== "");
|
||||
|
||||
const examState = useExamStore((state) => state);
|
||||
const persistentExamState = usePersistentExamStore((state) => state);
|
||||
|
||||
const {
|
||||
hasExamEnded,
|
||||
userSolutions,
|
||||
exerciseIndex,
|
||||
partIndex,
|
||||
questionIndex: storeQuestionIndex,
|
||||
setBgColor,
|
||||
setUserSolutions,
|
||||
setHasExamEnded,
|
||||
setExerciseIndex,
|
||||
setPartIndex,
|
||||
setQuestionIndex: setStoreQuestionIndex
|
||||
exerciseIndex, partIndex, questionIndex,
|
||||
userSolutions, flags, timeSpentCurrentModule,
|
||||
setBgColor, setUserSolutions, setTimeIsUp,
|
||||
dispatch
|
||||
} = !preview ? examState : persistentExamState;
|
||||
|
||||
|
||||
const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));
|
||||
const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60);
|
||||
|
||||
const { finalizeModule, timeIsUp } = flags;
|
||||
const [isFirstTimeRender, setIsFirstTimeRender] = useState(partIndex === 0 && exerciseIndex == 0 && !showSolutions);
|
||||
|
||||
const {
|
||||
nextExercise, previousExercise,
|
||||
showPartDivider, setShowPartDivider,
|
||||
seenParts, setSeenParts,
|
||||
isBetweenParts, setIsBetweenParts
|
||||
} = useExamNavigation({ exam, module: "reading", showBlankModal, setShowBlankModal, showSolutions, preview, disableBetweenParts: showSolutions });
|
||||
|
||||
useEffect(() => {
|
||||
if (!showSolutions && exam.parts[partIndex]?.intro !== undefined && exam.parts[partIndex]?.intro !== "" && !seenParts.has(exerciseIndex)) {
|
||||
setShowPartDivider(true);
|
||||
setBgColor(readingBgColor);
|
||||
if (finalizeModule || timeIsUp) {
|
||||
updateTimers();
|
||||
|
||||
if (timeIsUp) {
|
||||
setTimeIsUp(false);
|
||||
}
|
||||
dispatch({ type: "FINALIZE_MODULE", payload: { updateTimers: false } })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [partIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showSolutions) setExerciseIndex(-1);
|
||||
}, [setExerciseIndex, showSolutions]);
|
||||
|
||||
useEffect(() => {
|
||||
const previousParts = exam.parts.filter((_, index) => index < partIndex);
|
||||
let previousMultipleChoice = previousParts.flatMap((x) => x.exercises).filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[];
|
||||
|
||||
if (partIndex > -1 && exerciseIndex > -1) {
|
||||
const previousPartExercises = exam.parts[partIndex].exercises.filter((_, index) => index < exerciseIndex);
|
||||
const partMultipleChoice = previousPartExercises.filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[];
|
||||
|
||||
previousMultipleChoice = [...previousMultipleChoice, ...partMultipleChoice];
|
||||
}
|
||||
|
||||
setMultipleChoicesDone(previousMultipleChoice.map((x) => ({ id: x.id, amount: x.questions.length - 1 })));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [finalizeModule, timeIsUp])
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (e: KeyboardEvent) => {
|
||||
@@ -164,144 +68,70 @@ export default function Reading({ exam, showSolutions = false, preview = false,
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", listener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", listener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const registerSolution = useCallback((updateSolution: () => UserSolution) => {
|
||||
userSolutionRef.current = updateSolution;
|
||||
setSolutionWasUpdated(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded && exerciseIndex === -1) {
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
}
|
||||
}, [hasExamEnded, exerciseIndex, setExerciseIndex]);
|
||||
|
||||
const confirmFinishModule = (keepGoing?: boolean) => {
|
||||
if (!keepGoing) {
|
||||
setShowBlankModal(false);
|
||||
return;
|
||||
}
|
||||
|
||||
onFinish(userSolutions);
|
||||
};
|
||||
|
||||
const nextExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
if (solutionWasUpdated && userSolutionRef.current) {
|
||||
const solution = userSolutionRef.current();
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "reading", exam: exam.id }]);
|
||||
setSolutionWasUpdated(false);
|
||||
}
|
||||
if (storeQuestionIndex > 0) {
|
||||
const exercise = getExercise();
|
||||
setExerciseType(exercise.type);
|
||||
setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== exercise.id), { id: exercise.id, amount: storeQuestionIndex }]);
|
||||
}
|
||||
setStoreQuestionIndex(0);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [solutionWasUpdated])
|
||||
|
||||
if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
|
||||
setExerciseIndex(exerciseIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
|
||||
setPartIndex(partIndex + 1);
|
||||
setExerciseIndex(showSolutions ? 0 : -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
solution &&
|
||||
![...userSolutions.filter((x) => x.exercise !== solution?.exercise).map((x) => x.score.missing), solution?.score.missing].every(
|
||||
(x) => x === 0,
|
||||
) &&
|
||||
!showSolutions &&
|
||||
!preview &&
|
||||
!hasExamEnded
|
||||
) {
|
||||
setShowBlankModal(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setHasExamEnded(false);
|
||||
|
||||
if (solution) {
|
||||
onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "reading", exam: exam.id }]);
|
||||
} else {
|
||||
onFinish(userSolutions);
|
||||
}
|
||||
};
|
||||
|
||||
const previousExercise = (solution?: UserSolution) => {
|
||||
scrollToTop();
|
||||
if (solution) {
|
||||
setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "reading", exam: exam.id }]);
|
||||
}
|
||||
setStoreQuestionIndex(0);
|
||||
|
||||
setExerciseIndex(exerciseIndex - 1);
|
||||
};
|
||||
|
||||
const getExercise = () => {
|
||||
const currentExercise = useMemo<Exercise>(() => {
|
||||
const exercise = exam.parts[partIndex].exercises[exerciseIndex];
|
||||
return {
|
||||
...exercise,
|
||||
userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [partIndex, exerciseIndex]);
|
||||
|
||||
|
||||
const confirmFinishModule = (keepGoing?: boolean) => {
|
||||
if (!keepGoing) {
|
||||
setShowBlankModal(false);
|
||||
return;
|
||||
} else {
|
||||
nextExercise(true);
|
||||
setShowBlankModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const memoizedExerciseIndex = useMemo(() =>
|
||||
calculateExerciseIndex(exam, partIndex, exerciseIndex, questionIndex)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
, [partIndex, exerciseIndex, questionIndex]
|
||||
);
|
||||
|
||||
const handlePartDividerClick = () => {
|
||||
setShowPartDivider(false);
|
||||
setBgColor("bg-white");
|
||||
setSeenParts((prev) => new Set(prev).add(partIndex));
|
||||
if (isFirstTimeRender) setIsFirstTimeRender(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (partIndex > -1 && exerciseIndex > -1) {
|
||||
const exercise = getExercise();
|
||||
setExerciseType(exercise.type);
|
||||
setMultipleChoicesDone((prev) => prev.filter((x) => x.id !== exercise.id));
|
||||
if (partIndex !== 0 && !showSolutions) {
|
||||
setIsBetweenParts(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [exerciseIndex, partIndex]);
|
||||
}, [partIndex, setIsBetweenParts, showSolutions])
|
||||
|
||||
const calculateExerciseIndex = () => {
|
||||
if (partIndex === -1) return 0;
|
||||
if (partIndex === 0)
|
||||
return (
|
||||
(exerciseIndex === -1 ? 0 : exerciseIndex + 1) + storeQuestionIndex + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0)
|
||||
);
|
||||
|
||||
const exercisesPerPart = exam.parts.map((x) => x.exercises.length);
|
||||
const exercisesDone = exercisesPerPart.filter((_, index) => index < partIndex).reduce((acc, curr) => curr + acc, 0);
|
||||
return (
|
||||
exercisesDone +
|
||||
(exerciseIndex === -1 ? 0 : exerciseIndex + 1) +
|
||||
storeQuestionIndex +
|
||||
multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0)
|
||||
);
|
||||
};
|
||||
|
||||
const renderText = () => (
|
||||
<div className={clsx("flex flex-col gap-6 w-full bg-mti-gray-seasalt rounded-xl mt-4 relative", isTextMinimized ? "p-2 px-8" : "py-8 px-16")}>
|
||||
<button
|
||||
data-tip={isTextMinimized ? "Maximise" : "Minimize"}
|
||||
className={clsx("absolute right-8 tooltip", isTextMinimized ? "top-1/2 -translate-y-1/2" : "top-8")}
|
||||
onClick={() => setIsTextMinimzed((prev) => !prev)}>
|
||||
{isTextMinimized ? (
|
||||
<BsChevronDown className="text-mti-purple-dark text-lg" />
|
||||
) : (
|
||||
<BsChevronUp className="text-mti-purple-dark text-lg" />
|
||||
)}
|
||||
</button>
|
||||
{!isTextMinimized && (
|
||||
<>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<h4 className="text-xl font-semibold">
|
||||
Please read the following excerpt attentively, you will then be asked questions about the text you've read.
|
||||
</h4>
|
||||
<span className="text-base">You will be allowed to read the text while doing the exercises</span>
|
||||
</div>
|
||||
<TextComponent part={exam.parts[partIndex]} exerciseType={exerciseType} />
|
||||
</>
|
||||
)}
|
||||
{isTextMinimized && <span className="font-semibold">Reading Passage</span>}
|
||||
</div>
|
||||
);
|
||||
const progressButtons = useMemo(() =>
|
||||
// Do not remove the ()=> in handle next
|
||||
<ProgressButtons handlePrevious={previousExercise} handleNext={() => nextExercise()} />
|
||||
, [nextExercise, previousExercise]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -313,42 +143,47 @@ export default function Reading({ exam, showSolutions = false, preview = false,
|
||||
defaultTitle="Reading exam"
|
||||
section={exam.parts[partIndex]}
|
||||
sectionIndex={partIndex}
|
||||
onNext={() => { setShowPartDivider(false); setBgColor("bg-white"); setSeenParts((prev) => new Set(prev).add(exerciseIndex)) }}
|
||||
onNext={() => handlePartDividerClick()}
|
||||
/>
|
||||
</div> : (
|
||||
<>
|
||||
<div className="flex flex-col h-full w-full gap-8">
|
||||
<BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} />
|
||||
{partIndex > -1 && <TextModal {...exam.parts[partIndex].text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} />}
|
||||
{/*<ReadingPassageModal text={exam.parts[partIndex].text} isOpen={showTextModal} onClose={() => setShowTextModal(false)} />*/}
|
||||
{exam.parts.length > 1 && <SectionNavbar
|
||||
module="reading"
|
||||
sectionLabel="Part"
|
||||
seenParts={seenParts}
|
||||
setShowPartDivider={setShowPartDivider}
|
||||
setSeenParts={setSeenParts}
|
||||
setIsBetweenParts={setIsBetweenParts}
|
||||
preview={preview}
|
||||
/>}
|
||||
<ModuleTitle
|
||||
minTimer={exam.minTimer}
|
||||
exerciseIndex={calculateExerciseIndex()}
|
||||
minTimer={timer.current}
|
||||
exerciseIndex={memoizedExerciseIndex}
|
||||
module="reading"
|
||||
totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
|
||||
disableTimer={showSolutions || preview}
|
||||
label={exerciseIndex === -1 ? undefined : convertCamelCaseToReadable(exam.parts[partIndex].exercises[exerciseIndex].type)}
|
||||
label={convertCamelCaseToReadable(exam.parts[partIndex].exercises[exerciseIndex].type)}
|
||||
preview={preview}
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
"mb-20 w-full",
|
||||
exerciseIndex > -1 && !isTextMinimized && "grid grid-cols-2 gap-4",
|
||||
exerciseIndex > -1 && isTextMinimized && "flex flex-col gap-2",
|
||||
((isFirstTimeRender || isBetweenParts) && !showSolutions) ? "flex flex-col gap-2" : "grid grid-cols-2 gap-4",
|
||||
)}>
|
||||
{partIndex > -1 && renderText()}
|
||||
|
||||
{exerciseIndex > -1 &&
|
||||
partIndex > -1 &&
|
||||
exerciseIndex < exam.parts[partIndex].exercises.length &&
|
||||
!showSolutions &&
|
||||
renderExercise(getExercise(), exam.id, nextExercise, previousExercise, undefined, preview)}
|
||||
|
||||
{exerciseIndex > -1 &&
|
||||
partIndex > -1 &&
|
||||
exerciseIndex < exam.parts[partIndex].exercises.length &&
|
||||
showSolutions &&
|
||||
renderSolution(exam.parts[partIndex].exercises[exerciseIndex], nextExercise, previousExercise)}
|
||||
<ReadingPassage
|
||||
exam={exam}
|
||||
partIndex={partIndex}
|
||||
exerciseType={currentExercise.type}
|
||||
isTextMinimized={isTextMinimized}
|
||||
setIsTextMinimized={setIsTextMinimzed}
|
||||
/>
|
||||
{!isFirstTimeRender && !showPartDivider && !showSolutions && !isBetweenParts && renderExercise(currentExercise, exam.id, registerSolution, preview, progressButtons, progressButtons)}
|
||||
{showSolutions && renderSolution(currentExercise, progressButtons, progressButtons)}
|
||||
</div>
|
||||
{exerciseIndex > -1 && partIndex > -1 && exerciseIndex < exam.parts[partIndex].exercises.length && (
|
||||
{/*exerciseIndex > -1 && partIndex > -1 && exerciseIndex < exam.parts[partIndex].exercises.length && (
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
@@ -356,33 +191,19 @@ export default function Reading({ exam, showSolutions = false, preview = false,
|
||||
className="max-w-[200px] self-end w-full absolute bottom-[31px] right-64">
|
||||
Read text
|
||||
</Button>
|
||||
)}
|
||||
)*/}
|
||||
</div>
|
||||
{exerciseIndex === -1 && partIndex > 0 && (
|
||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
|
||||
setPartIndex(partIndex - 1);
|
||||
}}
|
||||
className="max-w-[200px] w-full">
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{exerciseIndex === -1 && partIndex === 0 && (
|
||||
<Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full">
|
||||
Start now
|
||||
</Button>
|
||||
)}
|
||||
{((isFirstTimeRender || isBetweenParts) && !showPartDivider && !showSolutions) &&
|
||||
<ProgressButtons
|
||||
hidePrevious={partIndex == 0 && isBetweenParts || isFirstTimeRender}
|
||||
nextLabel={isFirstTimeRender ? "Start now" : "Next Page"}
|
||||
handlePrevious={previousExercise}
|
||||
handleNext={() => isFirstTimeRender ? setIsFirstTimeRender(false) : nextExercise()} />
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Reading;
|
||||
|
||||
Reference in New Issue
Block a user