import Input from "@/components/Low/Input"; import Select from "@/components/Low/Select"; import {Difficulty, Exercise, ReadingExam, ReadingPart} from "@/interfaces/exam"; import useExamStore from "@/stores/examStore"; import {getExamById} from "@/utils/exams"; import {playSound} from "@/utils/sound"; import {convertCamelCaseToReadable} from "@/utils/string"; import {generate} from "random-words"; import {Tab} from "@headlessui/react"; import axios from "axios"; import clsx from "clsx"; import {capitalize, sample} from "lodash"; import {useRouter} from "next/router"; import {useEffect, useState, Dispatch, SetStateAction} from "react"; import {BsArrowRepeat, BsCheck} from "react-icons/bs"; import {toast} from "react-toastify"; import {v4} from "uuid"; import FillBlanksEdit from "@/components/Generation/fill.blanks.edit"; import TrueFalseEdit from "@/components/Generation/true.false.edit"; import WriteBlanksEdit from "@/components/Generation/write.blanks.edit"; import MatchSentencesEdit from "@/components/Generation/match.sentences.edit"; import MultipleChoiceEdit from "@/components/Generation/multiple.choice.edit"; const DIFFICULTIES: Difficulty[] = ["easy", "medium", "hard"]; const availableTypes = [ {type: "fillBlanks", label: "Fill the Blanks"}, {type: "trueFalse", label: "True or False"}, {type: "writeBlanks", label: "Write the Blanks"}, {type: "paragraphMatch", label: "Match Sentences"}, ]; const PartTab = ({ part, difficulty, index, setPart, updatePart, }: { part?: ReadingPart; index: number; difficulty: Difficulty; setPart: (part?: ReadingPart) => void; updatePart: Dispatch>; }) => { const [topic, setTopic] = useState(""); const [isLoading, setIsLoading] = useState(false); const [types, setTypes] = useState([]); const toggleType = (type: string) => setTypes((prev) => (prev.includes(type) ? [...prev.filter((x) => x !== type)] : [...prev, type])); const generate = () => { const url = new URLSearchParams(); url.append("difficulty", difficulty); if (topic) url.append("topic", topic); if (types) types.forEach((t) => url.append("exercises", t)); setPart(undefined); setIsLoading(true); axios .get(`/api/exam/reading/generate/reading_passage_${index}${topic || types ? `?${url.toString()}` : ""}`) .then((result) => { console.log(result.data); playSound(typeof result.data === "string" ? "error" : "check"); if (typeof result.data === "string") return toast.error("Something went wrong, please try to generate again."); setPart(result.data); }) .catch((error) => { console.log(error); toast.error("Something went wrong!"); }) .finally(() => setIsLoading(false)); }; const renderExercises = () => { return part?.exercises.map((exercise) => { switch (exercise.type) { case "fillBlanks": return ( <>

Exercise: Fill Blanks

updatePart((part?: ReadingPart) => { if (part) { const exercises = part.exercises.map((x) => (x.id === exercise.id ? {...x, ...data} : x)) as Exercise[]; const updatedPart = {...part, exercises} as ReadingPart; return updatedPart; } return part; }) } /> ); case "trueFalse": return ( <>

Exercise: True or False

{ updatePart((part?: ReadingPart) => { if (part) { return { ...part, exercises: part.exercises.map((x) => (x.id === exercise.id ? {...x, ...data} : x)), } as ReadingPart; } return part; }); }} /> ); case "multipleChoice": return ( <>

Exercise: True or False

{ updatePart((part?: ReadingPart) => { if (part) { return { ...part, exercises: part.exercises.map((x) => (x.id === exercise.id ? {...x, ...data} : x)), } as ReadingPart; } return part; }); }} /> ); case "writeBlanks": return ( <>

Exercise: Write Blanks

{ updatePart((part?: ReadingPart) => { if (part) { return { ...part, exercises: part.exercises.map((x) => (x.id === exercise.id ? {...x, ...data} : x)), } as ReadingPart; } return part; }); }} /> ); case "matchSentences": return ( <>

Exercise: Match Sentences

{ updatePart((part?: ReadingPart) => { if (part) { return { ...part, exercises: part.exercises.map((x) => (x.id === exercise.id ? {...x, ...data} : x)), } as ReadingPart; } return part; }); }} /> ); default: return null; } }); }; return (
{[...availableTypes, ...(index === 3 ? [{type: "ideaMatch", label: "Idea Match"}] : [])].map((x) => ( toggleType(x.type)} key={x.type} className={clsx( "px-6 py-4 w-64 flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer", "transition duration-300 ease-in-out", !types.includes(x.type) ? "bg-white border-mti-gray-platinum" : "bg-ielts-reading/70 border-ielts-reading text-white", )}> {x.label} ))}
{isLoading && (
Generating...
)} {part && ( <>
{part.exercises.map((x) => ( {x.type && convertCamelCaseToReadable(x.type)} ))}

{part.text.title}

{part.text.content}
{renderExercises()} )}
); }; const ReadingGeneration = () => { const [part1, setPart1] = useState(); const [part2, setPart2] = useState(); const [part3, setPart3] = useState(); const [minTimer, setMinTimer] = useState(60); const [isLoading, setIsLoading] = useState(false); const [resultingExam, setResultingExam] = useState(); const [difficulty, setDifficulty] = useState(sample(DIFFICULTIES)!); useEffect(() => { const parts = [part1, part2, part3].filter((x) => !!x); setMinTimer(parts.length === 0 ? 60 : parts.length * 20); }, [part1, part2, part3]); const router = useRouter(); const setExams = useExamStore((state) => state.setExams); const setSelectedModules = useExamStore((state) => state.setSelectedModules); const loadExam = async (examId: string) => { const exam = await getExamById("reading", 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(["reading"]); router.push("/exercises"); }; const submitExam = () => { const parts = [part1, part2, part3].filter((x) => !!x) as ReadingPart[]; if (parts.length === 0) { toast.error("Please generate at least one passage before submitting"); return; } setIsLoading(true); const exam: ReadingExam = { parts, isDiagnostic: false, minTimer, module: "reading", id: generate({minLength: 4, maxLength: 8, min: 3, max: 5, join: " ", formatter: capitalize}), type: "academic", variant: parts.length === 3 ? "full" : "partial", difficulty, }; axios .post(`/api/exam/reading`, exam) .then((result) => { playSound("sent"); console.log(`Generated Exam ID: ${result.data.id}`); toast.success(`Generated Exam ID: ${result.data.id}`); setResultingExam(result.data); setPart1(undefined); setPart2(undefined); setPart3(undefined); setDifficulty(sample(DIFFICULTIES)!); setMinTimer(60); }) .catch((error) => { console.log(error); toast.error("Something went wrong while generating, please try again later."); }) .finally(() => setIsLoading(false)); }; return ( <>
setMinTimer(parseInt(e) < 15 ? 15 : parseInt(e))} value={minTimer} className="max-w-[300px]" />