import Input from "@/components/Low/Input"; import Select from "@/components/Low/Select"; import {Difficulty, WritingExam, WritingExercise} from "@/interfaces/exam"; import useExamStore from "@/stores/examStore"; import {getExamById} from "@/utils/exams"; import {playSound} from "@/utils/sound"; import {Tab} from "@headlessui/react"; import axios from "axios"; import clsx from "clsx"; import {capitalize, sample} from "lodash"; import {useRouter} from "next/router"; import {generate} from "random-words"; import {useEffect, useState} from "react"; import {BsArrowRepeat, BsCheck} from "react-icons/bs"; import {toast} from "react-toastify"; import {v4} from "uuid"; const DIFFICULTIES: Difficulty[] = ["easy", "medium", "hard"]; const TaskTab = ({task, index, difficulty, setTask}: {task?: string; difficulty: Difficulty; index: number; setTask: (task: string) => void}) => { const [isLoading, setIsLoading] = useState(false); const generate = () => { setIsLoading(true); const url = new URLSearchParams(); url.append("difficulty", difficulty); axios .get(`/api/exam/writing/generate/writing_task${index}_general?${url.toString()}`) .then((result) => { playSound(typeof result.data === "string" ? "error" : "check"); if (typeof result.data === "string") return toast.error("Something went wrong, please try to generate again."); setTask(result.data.question); }) .catch((error) => { console.log(error); toast.error("Something went wrong!"); }) .finally(() => setIsLoading(false)); }; return ( {isLoading ? ( ) : ( "Generate" )} {isLoading && ( Generating... )} {task && ( {task} )} ); }; const WritingGeneration = () => { const [task1, setTask1] = useState(); const [task2, setTask2] = useState(); const [minTimer, setMinTimer] = useState(60); const [isLoading, setIsLoading] = useState(false); const [resultingExam, setResultingExam] = useState(); const [difficulty, setDifficulty] = useState(sample(DIFFICULTIES)!); useEffect(() => { const task1Timer = task1 ? 20 : 0; const task2Timer = task2 ? 40 : 0; setMinTimer(task1Timer > 0 || task2Timer > 0 ? task1Timer + task2Timer : 20); }, [task1, task2]); const router = useRouter(); const setExams = useExamStore((state) => state.setExams); const setSelectedModules = useExamStore((state) => state.setSelectedModules); const loadExam = async (examId: string) => { const exam = await getExamById("writing", examId.trim()); if (!exam) { toast.error("Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID", { toastId: "invalid-exam-id", }); return; } setExams([exam]); setSelectedModules(["writing"]); router.push("/exercises"); }; const submitExam = () => { if (!task1 && !task2) { toast.error("Please generate a task before submitting"); return; } const exercise1 = task1 ? ({ id: v4(), type: "writing", prefix: `You should spend about 20 minutes on this task.`, prompt: task1, userSolutions: [], suffix: "You should write at least 150 words.", wordCounter: { limit: 150, type: "min", }, } as WritingExercise) : undefined; const exercise2 = task2 ? ({ id: v4(), type: "writing", prefix: `You should spend about 40 minutes on this task.`, prompt: task2, userSolutions: [], suffix: "You should write at least 250 words.", wordCounter: { limit: 250, type: "min", }, } as WritingExercise) : undefined; setIsLoading(true); const exam: WritingExam = { isDiagnostic: false, minTimer, module: "writing", exercises: [...(exercise1 ? [exercise1] : []), ...(exercise2 ? [exercise2] : [])], id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}), variant: exercise1 && exercise2 ? "full" : "partial", difficulty, }; axios .post(`/api/exam/writing`, exam) .then((result) => { console.log(`Generated Exam ID: ${result.data.id}`); toast.success(`Generated Exam ID: ${result.data.id}`); playSound("sent"); toast.success("This new exam has been generated successfully! Check the ID in our browser's console."); setResultingExam(result.data); setTask1(undefined); setTask2(undefined); setDifficulty(sample(DIFFICULTIES)!); }) .catch((error) => { console.log(error); toast.error("Something went wrong while generating, please try again later."); }) .finally(() => setIsLoading(false)); }; return ( <> Timer setMinTimer(parseInt(e) < 15 ? 15 : parseInt(e))} value={minTimer} className="max-w-[300px]" /> Difficulty ({value: x, label: capitalize(x)}))} onChange={(value) => (value ? setDifficulty(value.value as Difficulty) : null)} value={{value: difficulty, label: capitalize(difficulty)}} disabled={!!task1 || !!task2} /> clsx( "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-writing/70 flex gap-2 items-center justify-center", "ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-writing focus:outline-none focus:ring-2", "transition duration-300 ease-in-out", selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-writing", ) }> Task 1 {task1 && } clsx( "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-writing/70 flex gap-2 items-center justify-center", "ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-writing focus:outline-none focus:ring-2", "transition duration-300 ease-in-out", selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-writing", ) }> Task 2 {task2 && } {[ {task: task1, setTask: setTask1}, {task: task2, setTask: setTask2}, ].map(({task, setTask}, index) => ( ))} {resultingExam && ( loadExam(resultingExam.id)} className={clsx( "bg-white border border-ielts-writing text-ielts-writing w-full max-w-[200px] rounded-xl h-[70px] self-end", "hover:bg-ielts-writing hover:text-white disabled:bg-ielts-writing/40 disabled:cursor-not-allowed", "transition ease-in-out duration-300", )}> Perform Exam )} {isLoading ? ( ) : ( "Submit" )} > ); }; export default WritingGeneration;