import React, { useState, useMemo, useEffect, useCallback } from 'react'; import { MdAdd, MdVisibility, MdVisibilityOff } from 'react-icons/md'; import { Difficulty, MatchSentencesExercise, ReadingPart } from '@/interfaces/exam'; import Alert, { AlertItem } from '../Shared/Alert'; import ReferenceViewer from './ParagraphViewer'; import Header from '../../Shared/Header'; import SortableQuestion from '../Shared/SortableQuestion'; import QuestionsList from '../Shared/QuestionsList'; import useExamEditorStore from '@/stores/examEditor'; import useSectionEdit from '../../Hooks/useSectionEdit'; import validateMatchSentences from './validation'; import setEditingAlert from '../Shared/setEditingAlert'; import { toast } from 'react-toastify'; import { DragEndEvent } from '@dnd-kit/core'; import { handleMatchSentencesReorder } from '@/stores/examEditor/reorder/local'; import PromptEdit from '../Shared/PromptEdit'; const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: number }> = ({ exercise, sectionId }) => { const { currentModule, dispatch } = useExamEditorStore(); const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty); const { state } = useExamEditorStore( (state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)! ); const section = state as ReadingPart; const [local, setLocal] = useState(exercise); const [selectedParagraph, setSelectedParagraph] = useState(null); const [showReference, setShowReference] = useState(false); const [alerts, setAlerts] = useState([]); const updateLocal = (exercise: MatchSentencesExercise) => { setLocal(exercise); setEditing(true); }; const { editing, setEditing, handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({ sectionId, onSave: () => { const isValid = validateMatchSentences(local.sentences, setAlerts); if (!isValid) { toast.error("Please fix the errors before saving!"); return; } setEditing(false); setAlerts([]); const newState = { ...section }; newState.exercises = newState.exercises.map((ex) => ex.id === exercise.id ? local : ex); dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } }); }, onDiscard: () => { setLocal(exercise); setSelectedParagraph(null); setShowReference(false); }, 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 } }); } }); const usedOptions = useMemo(() => { return local.sentences.reduce((acc, sentence) => { if (sentence.solution) { acc.add(sentence.solution); } return acc; }, new Set()); }, [local.sentences]); const addHeading = () => { const newId = (parseInt(local.sentences[local.sentences.length - 1].id) + 1).toString(); updateLocal({ ...local, sentences: [ ...local.sentences, { id: newId, sentence: "", solution: "" } ] }); }; const updateHeading = (index: number, field: string, value: string) => { const newSentences = [...local.sentences]; if (field === 'solution') { const oldSolution = newSentences[index].solution; if (oldSolution) { usedOptions.delete(oldSolution); } } newSentences[index] = { ...newSentences[index], [field]: value }; updateLocal({ ...local, sentences: newSentences }); }; const deleteHeading = (index: number) => { if (local.sentences.length <= 1) { toast.error(`There needs to be at least one ${exercise.variant && exercise.variant == "ideaMatch" ? "idea/opinion" : "heading"}!`); return; } const deletedSolution = local.sentences[index].solution; if (deletedSolution) { usedOptions.delete(deletedSolution); } const newSentences = local.sentences.filter((_, i) => i !== index); updateLocal({ ...local, sentences: newSentences }); }; useEffect(() => { validateMatchSentences(local.sentences, setAlerts); }, [local.sentences]); useEffect(() => { setEditingAlert(editing, setAlerts); }, [editing]); const handleDragEnd = (event: DragEndEvent) => { updateLocal(handleMatchSentencesReorder(event, local)); } const saveDifficulty = useCallback((diff: Difficulty) => { if (!difficulty.includes(diff)) { dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } }); } const updatedExercise = { ...exercise, difficulty: diff }; 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 } }); }, [currentModule, difficulty, dispatch, exercise, section, sectionId]); return (
{alerts.length > 0 && } updateLocal({ ...local, prompt: text })} /> s.id)} handleDragEnd={handleDragEnd} > {local.sentences.map((sentence, index) => ( deleteHeading(index)} onFocus={() => setSelectedParagraph(sentence.solution)} > <> updateHeading(index, 'sentence', e.target.value)} className="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:outline-none text-mti-gray-dim" placeholder={`Enter ${exercise.variant && exercise.variant == "ideaMatch" ? "idea/opinion" : "heading"} ...`} />
))}
{(section.text !== undefined && section.text.content.split("\n\n").length - 1) === local.sentences.length && ( )}
); }; export default MatchSentences;