import clsx from "clsx"; import SectionRenderer from "./SectionRenderer"; import Checkbox from "../Low/Checkbox"; import Input from "../Low/Input"; import Select from "../Low/Select"; import { capitalize } from "lodash"; import { Difficulty } from "@/interfaces/exam"; import { useCallback, useEffect, useMemo, useState } from "react"; import { toast } from "react-toastify"; import { ModuleState, SectionState } from "@/stores/examEditor/types"; import { Module } from "@/interfaces"; import useExamEditorStore from "@/stores/examEditor"; import WritingSettings from "./SettingsEditor/writing"; import ReadingSettings from "./SettingsEditor/reading"; import LevelSettings from "./SettingsEditor/level"; import ListeningSettings from "./SettingsEditor/listening"; import SpeakingSettings from "./SettingsEditor/speaking"; import ImportOrStartFromScratch from "./ImportExam/ImportOrFromScratch"; import { defaultSectionSettings } from "@/stores/examEditor/defaults"; const DIFFICULTIES: Difficulty[] = ["easy", "medium", "hard"]; const ExamEditor: React.FC<{ levelParts?: number }> = ({ levelParts = 0 }) => { const { currentModule, dispatch } = useExamEditorStore(); const { sections, minTimer, expandedSections, examLabel, isPrivate, difficulty, sectionLabels, importModule } = useExamEditorStore(state => state.modules[currentModule]); const [numberOfLevelParts, setNumberOfLevelParts] = useState(levelParts !== 0 ? levelParts : 1); useEffect(() => { if (levelParts !== 0) { setNumberOfLevelParts(levelParts); dispatch({ type: 'UPDATE_MODULE', payload: { updates: { sectionLabels: Array.from({ length: levelParts }).map((_, i) => ({ id: i + 1, label: `Part ${i + 1}` })) }, module: "level" } }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [levelParts]) useEffect(() => { const currentSections = sections; const currentLabels = sectionLabels; let updatedSections: SectionState[]; let updatedLabels: any; if (numberOfLevelParts > currentSections.length) { const newSections = [...currentSections]; const newLabels = [...currentLabels]; for (let i = currentSections.length; i < numberOfLevelParts; i++) { newSections.push(defaultSectionSettings(currentModule, i + 1)); newLabels.push({ id: i + 1, label: `Part ${i + 1}` }); } updatedSections = newSections; updatedLabels = newLabels; } else if (numberOfLevelParts < currentSections.length) { updatedSections = currentSections.slice(0, numberOfLevelParts); updatedLabels = currentLabels.slice(0, numberOfLevelParts); } else { return; } const updatedExpandedSections = expandedSections.filter( sectionId => updatedSections.some(section => section.sectionId === sectionId) ); dispatch({ type: 'UPDATE_MODULE', payload: { updates: { sections: updatedSections, sectionLabels: updatedLabels, expandedSections: updatedExpandedSections } } }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [numberOfLevelParts]); const sectionIds = sections.map((section) => section.sectionId) const updateModule = useCallback((updates: Partial) => { dispatch({ type: 'UPDATE_MODULE', payload: { updates } }); }, [dispatch]); const toggleSection = (sectionId: number) => { if (expandedSections.length === 1 && sectionIds.includes(sectionId)) { toast.error("Include at least one section!"); return; } dispatch({ type: 'TOGGLE_SECTION', payload: { sectionId } }); }; const ModuleSettings: Record = { reading: ReadingSettings, writing: WritingSettings, speaking: SpeakingSettings, listening: ListeningSettings, level: LevelSettings }; const Settings = ModuleSettings[currentModule]; const showImport = importModule && ["reading", "listening", "level"].includes(currentModule); return ( <> {showImport ? : ( <>
updateModule({ minTimer: parseInt(e) < 15 ? 15 : parseInt(e) })} value={minTimer} className="max-w-[300px]" />
setNumberOfLevelParts(parseInt(v))} value={numberOfLevelParts} />
)}
updateModule({ isPrivate: checked })}> Privacy (Only available for Assignments)
updateModule({ examLabel: text })} roundness="xl" value={examLabel} required />
)} ); }; export default ExamEditor;