import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit"; import { Card, CardContent } from "@/components/ui/card"; import { WriteBlanksExercise, ReadingPart } from "@/interfaces/exam"; import useExamEditorStore from "@/stores/examEditor"; import { useState, useReducer, useEffect } from "react"; import { toast } from "react-toastify"; import BlanksEditor from ".."; import { AlertItem } from "../../Shared/Alert"; import setEditingAlert from "../../Shared/setEditingAlert"; import { blanksReducer } from "../BlanksReducer"; import { validateWriteBlanks } from "./validation"; import AlternativeSolutions from "./AlternativeSolutions"; const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: number }> = ({ exercise, sectionId }) => { const { currentModule, dispatch } = useExamEditorStore(); const { state } = useExamEditorStore( (state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)! ); const section = state as ReadingPart; const [alerts, setAlerts] = useState([]); const [local, setLocal] = useState(exercise); const [selectedBlankId, setSelectedBlankId] = useState(null); const [editing, setEditing] = useState(false); const updateLocal = (exercise: WriteBlanksExercise) => { setLocal(exercise); setEditingAlert(true, setAlerts); setEditing(true); }; const [blanksState, blanksDispatcher] = useReducer(blanksReducer, { text: exercise.text || "", blanks: [], selectedBlankId: null, draggedItemId: null, textMode: false, setEditing, }); const { handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({ sectionId, editing, setEditing, onSave: () => { if (!validateWriteBlanks(local.solutions, local.maxWords, setAlerts)) { toast.error("Please fix the errors before saving!"); return; } setEditing(false); setAlerts([]); const updatedExercise = { ...local, text: blanksState.text, }; const newState = { ...section }; newState.exercises = newState.exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex ); dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } }); }, onDiscard: () => { setSelectedBlankId(null); setLocal(exercise); blanksDispatcher({ type: "RESET", payload: { text: exercise.text } }); }, onDelete: () => { const newSection = { ...section, exercises: section.exercises.filter((ex) => ex.id !== local.id) }; dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } }); }, onPractice: () => { const updatedExercise = { ...local, isPractice: !local.isPractice }; const newState = { ...section }; newState.exercises = newState.exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex ); setLocal((prev) => ({ ...prev, isPractice: !local.isPractice })) dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } }); } }); useEffect(() => { if (!editing) { setLocal(exercise); } }, [exercise, editing]); const handleAddSolution = (blankId: string) => { if (!editing) setEditing(true); setLocal(prev => ({ ...prev, solutions: prev.solutions.map(s => s.id === blankId ? { ...s, solution: [...s.solution, ""] } : s ) })); }; const handleRemoveSolution = (blankId: string, index: number) => { if (!editing) setEditing(true); const solutions = local.solutions.find(s => s.id === blankId); if (solutions && solutions.solution.length <= 1) { toast.error("Each blank must have at least one solution!"); return; } setLocal(prev => ({ ...prev, solutions: prev.solutions.map(s => s.id === blankId ? { ...s, solution: s.solution.filter((_, i) => i !== index) } : s ) })); }; const handleEditSolution = (blankId: string, index: number, value: string) => { if (!editing) setEditing(true); setLocal(prev => ({ ...prev, solutions: prev.solutions.map(s => s.id === blankId ? { ...s, solution: s.solution.map((sol, i) => i === index ? value : sol) } : s ) })); }; const handleBlankRemove = (blankId: number) => { if (!editing) setEditing(true); setLocal(prev => ({ ...prev, solutions: prev.solutions.filter(s => s.id !== blankId.toString()) })); blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId }); }; useEffect(() => { validateWriteBlanks(local.solutions, local.maxWords, setAlerts); }, [local.solutions, local.maxWords]); useEffect(() => { setEditingAlert(editing, setAlerts); }, [editing]); return (
setSelectedBlankId(blankId?.toString() || null)} onBlankRemove={handleBlankRemove} onSave={handleSave} onDiscard={handleDiscard} onDelete={handleDelete} onPractice={handlePractice} setEditing={setEditing} isEvaluationEnabled={!local.isPractice} prompt={local.prompt} updatePrompt={(prompt: string) => updateLocal({ ...local, prompt })} > {!blanksState.textMode && (
{selectedBlankId ? `Solutions for Blank ${selectedBlankId}` : "Click a blank to edit its solutions"} {selectedBlankId && ( Max words per solution: {local.maxWords} )}
{selectedBlankId && ( s.id === selectedBlankId)?.solution || []} onAdd={() => handleAddSolution(selectedBlankId)} onRemove={(index: number) => handleRemoveSolution(selectedBlankId, index)} onEdit={(index: number, value: string) => handleEditSolution(selectedBlankId, index, value)} /> )}
)}
); }; export default WriteBlanksFill;