/* eslint-disable @next/next/no-img-element */ import Head from "next/head"; import { withIronSessionSsr } from "iron-session/next"; import { sessionOptions } from "@/lib/session"; import { ToastContainer } from "react-toastify"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; import { Radio, RadioGroup } from "@headlessui/react"; import clsx from "clsx"; import { MODULE_ARRAY } from "@/utils/moduleUtils"; import { capitalize } from "lodash"; import Input from "@/components/Low/Input"; import { findAllowedEntities } from "@/utils/permissions"; import { User } from "@/interfaces/user"; import useExamEditorStore from "@/stores/examEditor"; import ExamEditorStore from "@/stores/examEditor/types"; import ExamEditor from "@/components/ExamEditor"; import { mapBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; import { Module } from "@/interfaces"; import { getExam, } from "@/utils/exams.be"; import { Exam, Exercise, InteractiveSpeakingExercise, ListeningPart, SpeakingExercise } from "@/interfaces/exam"; import { useEffect, useState } from "react"; import { getEntitiesWithRoles } from "@/utils/entities.be"; import { isAdmin } from "@/utils/users"; import axios from "axios"; type Permission = { [key in Module]: boolean } export const getServerSideProps = withIronSessionSsr(async ({ req, res, query }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") if (shouldRedirectHome(user)) return redirect("/") const entityIDs = mapBy(user.entities, 'id') const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDs) const permissions: Permission = { reading: findAllowedEntities(user, entities, `generate_reading`).length > 0, listening: findAllowedEntities(user, entities, `generate_listening`).length > 0, writing: findAllowedEntities(user, entities, `generate_writing`).length > 0, speaking: findAllowedEntities(user, entities, `generate_speaking`).length > 0, level: findAllowedEntities(user, entities, `generate_level`).length > 0, } if (Object.keys(permissions).every(p => !permissions[p as Module])) return redirect("/") const { id, module: examModule } = query as { id?: string, module?: Module } if (!id || !examModule) return { props: serialize({ user, permissions }) }; //if (!permissions[module]) return redirect("/generation") const exam = await getExam(examModule, id) if (!exam) return redirect("/generation") return { props: serialize({ id, user, exam, examModule, permissions }), }; }, sessionOptions); export default function Generation({ id, user, exam, examModule, permissions }: { id: string, user: User; exam?: Exam, examModule?: Module, permissions: Permission }) { const { title, currentModule, modules, dispatch } = useExamEditorStore(); const [examLevelParts, setExamLevelParts] = useState(undefined); const updateRoot = (updates: Partial) => { dispatch({ type: 'UPDATE_ROOT', payload: { updates } }); }; useEffect(() => { if (id && exam && examModule) { if (examModule === "level" && exam.module === "level") { setExamLevelParts(exam.parts.length); } updateRoot({currentModule: examModule}) dispatch({ type: "INIT_EXAM_EDIT", payload: { exam, id, examModule } }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [id, exam, module]) useEffect(() => { const fetchAvatars = async () => { const response = await axios.get("/api/exam/avatars"); updateRoot({ speakingAvatars: response.data }); }; fetchAvatars(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // media cleanup on unmount useEffect(() => { return () => { const state = modules; if (state.writing.academic_url) { URL.revokeObjectURL(state.writing.academic_url); } state.listening.sections.forEach(section => { const listeningPart = section.state as ListeningPart; if (listeningPart.audio?.source) { URL.revokeObjectURL(listeningPart.audio.source); dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId: section.sectionId, module: "listening", field: "state", value: { ...listeningPart, audio: undefined } } }) } }); if (state.listening.instructionsState.customInstructionsURL.startsWith('blob:')) { URL.revokeObjectURL(state.listening.instructionsState.customInstructionsURL); } state.speaking.sections.forEach(section => { const sectionState = section.state as Exercise; if (sectionState.type === 'speaking') { const speakingExercise = sectionState as SpeakingExercise; URL.revokeObjectURL(speakingExercise.video_url); dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId: section.sectionId, module: "listening", field: "state", value: { ...speakingExercise, video_url: undefined } } }) } if (sectionState.type === 'interactiveSpeaking') { const interactiveSpeaking = sectionState as InteractiveSpeakingExercise; interactiveSpeaking.prompts.forEach(prompt => { URL.revokeObjectURL(prompt.video_url); }); dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId: section.sectionId, module: "listening", field: "state", value: { ...interactiveSpeaking, prompts: interactiveSpeaking.prompts.map((p) => ({ ...p, video_url: undefined })) } } }) } }); dispatch({ type: 'FULL_RESET' }); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> Exam Generation | EnCoach {user && ( <>

Exam Editor

updateRoot({ title })} roundness="xl" value={title} defaultValue={title} required /> updateRoot({ currentModule })} className="flex flex-row flex-wrap w-full gap-4 -md:justify-center justify-between"> {[...MODULE_ARRAY].filter(m => permissions[m]).map((x) => ( {({ checked }) => ( {capitalize(x)} )} ))}
)} ); }