Navigation rework, added prompt edit to components that were missing
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { WritingExercise } from "@/interfaces/exam";
|
||||
import { CommonProps } from ".";
|
||||
import React, { Fragment, useEffect, useRef, useState } from "react";
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { Dialog, DialogPanel, Transition, TransitionChild } from "@headlessui/react";
|
||||
import useExamStore, { usePersistentExamStore } from "@/stores/exam";
|
||||
import { CommonProps } from "./types";
|
||||
import { toast } from "react-toastify";
|
||||
import Button from "../Low/Button";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import useExamStore from "@/stores/examStore";
|
||||
|
||||
export default function Writing({
|
||||
const Writing: React.FC<WritingExercise & CommonProps> = ({
|
||||
id,
|
||||
prompt,
|
||||
prefix,
|
||||
@@ -17,17 +16,24 @@ export default function Writing({
|
||||
attachment,
|
||||
userSolutions,
|
||||
isPractice = false,
|
||||
onNext,
|
||||
onBack,
|
||||
enableNavigation = false
|
||||
}: WritingExercise & CommonProps) {
|
||||
registerSolution,
|
||||
headerButtons,
|
||||
footerButtons,
|
||||
preview,
|
||||
}) => {
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [inputText, setInputText] = useState(userSolutions.length === 1 ? userSolutions[0].solution : "");
|
||||
const [isSubmitEnabled, setIsSubmitEnabled] = useState(false);
|
||||
const [saveTimer, setSaveTimer] = useState(0);
|
||||
|
||||
const { userSolutions: storeUserSolutions, setUserSolutions } = useExamStore((state) => state);
|
||||
const hasExamEnded = useExamStore((state) => state.hasExamEnded);
|
||||
const examState = useExamStore((state) => state);
|
||||
const persistentExamState = usePersistentExamStore((state) => state);
|
||||
|
||||
const { userSolutions: storeUserSolutions, setUserSolutions, setNavigation } = !preview ? examState : persistentExamState;
|
||||
|
||||
useEffect(() => {
|
||||
if (!preview) setNavigation({ nextDisabled: true });
|
||||
}, [setNavigation, preview]);
|
||||
|
||||
useEffect(() => {
|
||||
const saveTimerInterval = setInterval(() => {
|
||||
@@ -43,7 +49,7 @@ export default function Writing({
|
||||
if (inputText.length > 0 && saveTimer % 10 === 0) {
|
||||
setUserSolutions([
|
||||
...storeUserSolutions.filter((x) => x.exercise !== id),
|
||||
{ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing" },
|
||||
{ exercise: id, solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing" },
|
||||
]);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -65,59 +71,39 @@ export default function Writing({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExamEnded)
|
||||
onNext({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, module: "writing", isPractice });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasExamEnded]);
|
||||
|
||||
useEffect(() => {
|
||||
const words = inputText.split(" ").filter((x) => x !== "");
|
||||
|
||||
if (wordCounter.type === "min") {
|
||||
setIsSubmitEnabled(wordCounter.limit <= words.length || enableNavigation);
|
||||
setNavigation({ nextDisabled: !(wordCounter.limit <= words.length) });
|
||||
} else {
|
||||
setIsSubmitEnabled(true);
|
||||
setNavigation({ nextDisabled: false });
|
||||
|
||||
if (wordCounter.limit < words.length) {
|
||||
toast.warning(`You have reached your word limit of ${wordCounter.limit} words!`, { toastId: "word-limit" });
|
||||
setInputText(words.slice(0, words.length - 1).join(" "));
|
||||
}
|
||||
|
||||
}
|
||||
}, [enableNavigation, inputText, wordCounter]);
|
||||
}, [inputText, setNavigation, wordCounter]);
|
||||
|
||||
useEffect(() => {
|
||||
registerSolution(() => ({
|
||||
exercise: id,
|
||||
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
isPractice
|
||||
}));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id, type, isPractice, inputText]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 mt-4">
|
||||
<div className="flex justify-between w-full gap-8">
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
color="purple"
|
||||
disabled={!isSubmitEnabled}
|
||||
onClick={() =>
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
module: "writing", isPractice
|
||||
})
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{headerButtons}
|
||||
{attachment && (
|
||||
<Transition show={isModalOpen} as={Fragment}>
|
||||
<Dialog onClose={() => setIsModalOpen(false)} className="relative z-50">
|
||||
<Transition.Child
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@@ -126,9 +112,9 @@ export default function Writing({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0">
|
||||
<div className="fixed inset-0 bg-black/30" />
|
||||
</Transition.Child>
|
||||
</TransitionChild>
|
||||
|
||||
<Transition.Child
|
||||
<TransitionChild
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
@@ -137,11 +123,11 @@ export default function Writing({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95">
|
||||
<div className="fixed inset-0 flex items-center justify-center p-4">
|
||||
<Dialog.Panel className="w-fit h-fit rounded-xl bg-white">
|
||||
<DialogPanel className="w-fit h-fit rounded-xl bg-white">
|
||||
<img src={attachment.url} alt={attachment.description} className="max-w-4xl w-full self-center rounded-xl p-4" />
|
||||
</Dialog.Panel>
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</TransitionChild>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
)}
|
||||
@@ -172,33 +158,9 @@ export default function Writing({
|
||||
<span className="text-base self-end text-mti-gray-cool">Word Count: {inputText.split(" ").filter((x) => x !== "").length}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
|
||||
<Button
|
||||
color="purple"
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
onBack({ exercise: id, solutions: [{ id, solution: inputText }], score: { correct: 100, total: 100, missing: 0 }, type, isPractice })
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
color="purple"
|
||||
disabled={!isSubmitEnabled}
|
||||
onClick={() =>
|
||||
onNext({
|
||||
exercise: id,
|
||||
solutions: [{ id, solution: inputText.replaceAll(/\s{2,}/g, " ") }],
|
||||
score: { correct: 100, total: 100, missing: 0 },
|
||||
type,
|
||||
module: "writing", isPractice
|
||||
})
|
||||
}
|
||||
className="max-w-[200px] self-end w-full">
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
{footerButtons}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Writing;
|
||||
|
||||
Reference in New Issue
Block a user