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"; import Button from "../Low/Button"; import ResetModule from "./Standalone/ResetModule"; import ListeningInstructions from "./Standalone/ListeningInstructions"; import {EntityWithRoles} from "@/interfaces/entity"; const DIFFICULTIES: Difficulty[] = ["A1", "A2", "B1", "B2", "C1", "C2"]; const ExamEditor: React.FC<{levelParts?: number; entitiesAllowEditPrivacy: EntityWithRoles[]}> = ({ levelParts = 0, entitiesAllowEditPrivacy = [], }) => { 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); const [isResetModuleOpen, setIsResetModuleOpen] = useState(false); // For exam edits 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 ((currentModule === "level" && currentSections.length !== currentLabels.length) || numberOfLevelParts !== currentSections.length) { const newSections = [...currentSections]; const newLabels = [...currentLabels]; for (let i = currentLabels.length; i < numberOfLevelParts; i++) { if (currentSections.length !== numberOfLevelParts) 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); const updateLevelParts = (parts: number) => { setNumberOfLevelParts(parts); }; return ( <> {showImport ? ( ) : ( <> {isResetModuleOpen && ( )}
updateModule({ minTimer: parseInt(e) < 15 ? 15 : parseInt(e), }) } value={minTimer} className="max-w-[300px]" />
setNumberOfLevelParts(parseInt(v))} value={numberOfLevelParts} />
)}
updateModule({isPrivate: checked})} disabled={entitiesAllowEditPrivacy.length === 0}> Privacy (Only available for Assignments)
updateModule({examLabel: text})} roundness="xl" value={examLabel} required />
{currentModule === "listening" && }
)} ); }; export default ExamEditor;