import Input from "@/components/Low/Input"; import Select from "@/components/Low/Select"; import {Difficulty, Exercise, InteractiveSpeakingExercise, SpeakingExam, SpeakingExercise} from "@/interfaces/exam"; import {AVATARS} from "@/resources/speakingAvatars"; import useExamStore from "@/stores/examStore"; import {getExamById} from "@/utils/exams"; import {playSound} from "@/utils/sound"; import {convertCamelCaseToReadable} from "@/utils/string"; import {Tab} from "@headlessui/react"; import axios from "axios"; import clsx from "clsx"; import {capitalize, sample, uniq} from "lodash"; import moment from "moment"; import {useRouter} from "next/router"; import {generate} from "random-words"; import {useEffect, useState, Dispatch, SetStateAction} 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 PartTab = ({ part, index, difficulty, setPart, updatePart, }: { part?: SpeakingPart; difficulty: Difficulty; index: number; setPart: (part?: SpeakingPart) => void; updatePart: Dispatch>; }) => { const [gender, setGender] = useState<"male" | "female">("male"); const [isLoading, setIsLoading] = useState(false); const generate = () => { setPart(undefined); setIsLoading(true); const url = new URLSearchParams(); url.append("difficulty", difficulty); axios .get(`/api/exam/speaking/generate/speaking_task_${index}?${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."); console.log(result.data); setPart(result.data); }) .catch((error) => { console.log(error); toast.error("Something went wrong!"); }) .finally(() => setIsLoading(false)); }; const generateVideo = async () => { if (!part) return toast.error("Please generate the first part before generating the video!"); toast.info("This will take quite a while, please do not leave this page or close the tab/window."); const avatar = sample(AVATARS.filter((x) => x.gender === gender)); setIsLoading(true); const initialTime = moment(); axios .post(`/api/exam/speaking/generate/speaking/generate_video_${index}`, { ...part, avatar: avatar?.id, }) .then((result) => { const isError = typeof result.data === "string" || moment().diff(initialTime, "seconds") < 60; playSound(isError ? "error" : "check"); console.log(result.data); if (isError) return toast.error("Something went wrong, please try to generate the video again."); setPart({ ...part, result: {...result.data, topic: part?.topic}, gender, avatar, }); }) .catch((e) => { toast.error("Something went wrong!"); console.log(e); }) .finally(() => setIsLoading(false)); }; return (
updatePart((part?: SpeakingPart) => { if (part) { return { ...part, questions: part.questions?.map((x, xIndex) => (xIndex === index ? value : x)), } as SpeakingPart; } return part; }) } /> ))}
)}
); }; interface SpeakingPart { prompts?: string[]; question?: string; questions?: string[]; topic: string; first_topic?: string; second_topic?: string; result?: SpeakingExercise | InteractiveSpeakingExercise; gender?: "male" | "female"; avatar?: (typeof AVATARS)[number]; } interface Props { id: string; } const SpeakingGeneration = ({ id } : Props) => { const [part1, setPart1] = useState(); const [part2, setPart2] = useState(); const [part3, setPart3] = useState(); const [minTimer, setMinTimer] = useState(14); 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 ? 5 : parts.length * 5); }, [part1, part2, part3]); const router = useRouter(); const setExams = useExamStore((state) => state.setExams); const setSelectedModules = useExamStore((state) => state.setSelectedModules); const submitExam = () => { if (!part1?.result && !part2?.result && !part3?.result) return toast.error("Please generate at least one task!"); if(!id) { toast.error("Please insert a title before submitting"); return; } setIsLoading(true); const genders = [part1?.gender, part2?.gender, part3?.gender].filter((x) => !!x); const exercises = [part1?.result, part2?.result, part3?.result] .filter((x) => !!x) .map((x) => ({ ...x, first_title: x?.type === "interactiveSpeaking" ? x.first_topic : undefined, second_title: x?.type === "interactiveSpeaking" ? x.second_topic : undefined, })); const exam: SpeakingExam = { id, isDiagnostic: false, exercises: exercises as (SpeakingExercise | InteractiveSpeakingExercise)[], minTimer, variant: minTimer >= 14 ? "full" : "partial", module: "speaking", instructorGender: genders.every((x) => x === "male") ? "male" : genders.every((x) => x === "female") ? "female" : "varied", }; axios .post(`/api/exam/speaking`, 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(14); }) .catch((error) => { console.log(error); toast.error("Something went wrong while generating, please try again later."); }) .finally(() => setIsLoading(false)); }; const loadExam = async (examId: string) => { const exam = await getExamById("speaking", 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(["speaking"]); router.push("/exercises"); }; return ( <>
setMinTimer(parseInt(e) < 5 ? 5 : parseInt(e))} value={minTimer} className="max-w-[300px]" />