Blanks Exercises were removing the blanks on the editor and text but not on solutions, added submit and preview to writing and reading

This commit is contained in:
Carlos-Mesquita
2024-11-05 17:53:55 +00:00
parent 15c9c4d4bd
commit ffa2045a2d
28 changed files with 519 additions and 264 deletions

View File

@@ -6,7 +6,7 @@ import { MdEdit, MdEditOff } from "react-icons/md";
import FillBlanksWord from "./FillBlanksWord";
import { FaPlus } from "react-icons/fa";
import useExamEditorStore from "@/stores/examEditor";
import { blanksReducer, BlankState, getTextSegments } from "../FillBlanksReducer";
import { blanksReducer, BlankState, getTextSegments } from "../BlanksReducer";
import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit";
import { AlertItem } from "../../Shared/Alert";
import validateBlanks from "../validateBlanks";
@@ -75,7 +75,6 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
);
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
dispatch({ type: "REORDER_EXERCISES" });
},
onDiscard: () => {
setSelectedBlankId(null);
@@ -103,7 +102,6 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
}
});
@@ -211,11 +209,30 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
});
};
const handleBlankRemove = (blankId: number) => {
if (!editing) setEditing(true);
const newAnswers = new Map(answers);
newAnswers.delete(blankId.toString());
setAnswers(newAnswers);
setLocal(prev => ({
...prev,
solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
id,
solution
}))
}));
blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId });
};
useEffect(() => {
validateBlanks(blanksState.blanks, answers, alerts, setAlerts);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, blanksState.blanks, blanksState.textMode])
useEffect(()=> {
setEditingAlert(editing, setAlerts);
}, [editing])
@@ -232,6 +249,7 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
module={currentModule}
showBlankBank={true}
onBlankSelect={(blankId) => setSelectedBlankId(blankId?.toString() || null)}
onBlankRemove={handleBlankRemove}
onSave={handleSave}
onDiscard={handleDiscard}
onDelete={modeHandle}

View File

@@ -3,7 +3,7 @@ import { useEffect, useReducer, useState } from "react";
import BlanksEditor from "..";
import { Card, CardContent } from "@/components/ui/card";
import useExamEditorStore from "@/stores/examEditor";
import { blanksReducer, BlankState, getTextSegments } from "../FillBlanksReducer";
import { blanksReducer, BlankState, getTextSegments } from "../BlanksReducer";
import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit";
import { AlertItem } from "../../Shared/Alert";
import validateBlanks from "../validateBlanks";
@@ -73,7 +73,6 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
);
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
dispatch({ type: "REORDER_EXERCISES" });
},
onDiscard: () => {
setSelectedBlankId(null);
@@ -99,7 +98,6 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
}
});
@@ -191,6 +189,7 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
useEffect(() => {
validateBlanks(blanksState.blanks, answers, alerts, setAlerts);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [answers, blanksState.blanks, blanksState.textMode]);
useEffect(() => {
@@ -216,8 +215,28 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
solution
])
));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleBlankRemove = (blankId: number) => {
if (!editing) setEditing(true);
const newAnswers = new Map(answers);
newAnswers.delete(blankId.toString());
setAnswers(newAnswers);
setLocal(prev => ({
...prev,
words: (prev.words as FillBlanksMCOption[]).filter(w => w.id !== blankId.toString()),
solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
id,
solution
}))
}));
blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId });
};
return (
<div className="space-y-4">
<BlanksEditor
@@ -234,6 +253,7 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
onDiscard={handleDiscard}
onDelete={modeHandle}
setEditing={setEditing}
onBlankRemove={handleBlankRemove}
>
{!blanksState.textMode && selectedBlankId && (
<Card className="p-4">

View File

@@ -7,7 +7,7 @@ import { toast } from "react-toastify";
import BlanksEditor from "..";
import { AlertItem } from "../../Shared/Alert";
import setEditingAlert from "../../Shared/setEditingAlert";
import { blanksReducer } from "../FillBlanksReducer";
import { blanksReducer } from "../BlanksReducer";
import { validateWriteBlanks } from "./validation";
import AlternativeSolutions from "./AlternativeSolutions";
import clsx from "clsx";
@@ -126,6 +126,16 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
}));
};
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]);
@@ -147,6 +157,7 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
module={currentModule}
showBlankBank={true}
onBlankSelect={(blankId) => setSelectedBlankId(blankId?.toString() || null)}
onBlankRemove={handleBlankRemove}
onSave={handleSave}
onDiscard={handleDiscard}
onDelete={modeHandle}

View File

@@ -1,5 +1,5 @@
import { AlertItem } from "../../Shared/Alert";
import { BlankState } from "../FillBlanksReducer";
import { BlankState } from "../BlanksReducer";
export const validateWriteBlanks = (

View File

@@ -18,7 +18,7 @@ import Alert, { AlertItem } from "../Shared/Alert";
import clsx from "clsx";
import { Card, CardContent } from "@/components/ui/card";
import { Blank, DropZone } from "./DragNDrop";
import { getTextSegments, BlankState, BlanksState, BlanksAction } from "./FillBlanksReducer";
import { getTextSegments, BlankState, BlanksState, BlanksAction } from "./BlanksReducer";
interface Props {
@@ -33,6 +33,7 @@ interface Props {
setEditing: React.Dispatch<React.SetStateAction<boolean>>;
blanksDispatcher: React.Dispatch<BlanksAction>
onBlankSelect?: (blankId: number | null) => void;
onBlankRemove: (blankId: number) => void;
onSave: () => void;
onDiscard: () => void;
onDelete: () => void;
@@ -51,6 +52,7 @@ const BlanksEditor: React.FC<Props> = ({
alerts,
blanksDispatcher,
onBlankSelect,
onBlankRemove,
onSave,
onDiscard,
onDelete,
@@ -99,9 +101,24 @@ const BlanksEditor: React.FC<Props> = ({
const handleTextChange = useCallback(
(newText: string) => {
const processedText = newText.replace(/\[(\d+)\]/g, "{{$1}}");
const existingBlankIds = getTextSegments(state.text)
.filter(token => token.type === 'blank')
.map(token => token.id);
const newBlankIds = getTextSegments(processedText)
.filter(token => token.type === 'blank')
.map(token => token.id);
const removedBlankIds = existingBlankIds.filter(id => !newBlankIds.includes(id));
removedBlankIds.forEach(id => {
onBlankRemove(id);
});
blanksDispatcher({ type: "SET_TEXT", payload: processedText });
},
[blanksDispatcher]
[blanksDispatcher, state.text, onBlankRemove]
);
useEffect(() => {
@@ -116,8 +133,9 @@ const BlanksEditor: React.FC<Props> = ({
};
const handleBlankRemove = useCallback((blankId: number) => {
onBlankRemove(blankId);
blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId });
}, [blanksDispatcher]);
}, [blanksDispatcher, onBlankRemove]);
const sensors = useSensors(
useSensor(PointerSensor, {

View File

@@ -1,5 +1,5 @@
import { AlertItem } from "../Shared/Alert";
import { BlankState } from "./FillBlanksReducer";
import { BlankState } from "./BlanksReducer";
const validateBlanks = (

View File

@@ -48,7 +48,6 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
const newState = { ...section };
newState.exercises = newState.exercises.map((ex) => ex.id === exercise.id ? local : ex);
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
dispatch({ type: "REORDER_EXERCISES" });
},
onDiscard: () => {
setLocal(exercise);
@@ -61,7 +60,6 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
}
});
@@ -206,16 +204,16 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
</SortableQuestion>
))}
</QuestionsList>
<button
onClick={addHeading}
className="w-full p-4 border-2 border-dashed border-gray-300 rounded-lg hover:border-blue-500 hover:bg-blue-50 transition-colors flex items-center justify-center gap-2 text-gray-600 hover:text-blue-600"
>
<MdAdd size={18} />
Add New Match
</button>
{(section.text.content.split("\n\n").length - 1) === local.sentences.length && (
<button
onClick={addHeading}
className="w-full p-4 border-2 border-dashed border-gray-300 rounded-lg hover:border-blue-500 hover:bg-blue-50 transition-colors flex items-center justify-center gap-2 text-gray-600 hover:text-blue-600"
>
<MdAdd size={18} />
Add New Match
</button>
)}
</div>
<ReferenceViewer
headings={exercise.variant !== "ideaMatch"}
showReference={showReference}

View File

@@ -86,7 +86,6 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
},
onDiscard: () => {
setLocal(exercise);
@@ -98,7 +97,6 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
},
});

View File

@@ -165,7 +165,6 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
},
onDiscard: () => {
setLocal(exercise);
@@ -176,7 +175,6 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" });
},
});

View File

@@ -97,7 +97,6 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" })
},
onDiscard: () => {
setLocal(exercise);
@@ -108,7 +107,6 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
exercises: section.exercises.filter((ex) => ex.id !== local.id)
};
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
dispatch({ type: "REORDER_EXERCISES" })
},
});

View File

@@ -198,6 +198,7 @@ const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise }
const handleDragEnd = (event: DragEndEvent) => {
setEditing(true);
console.log("ASOJNFOAI+SHJOIPFAS");
setLocal(handleWriteBlanksReorder(event, local));
}

View File

@@ -17,7 +17,7 @@ interface Props {
const Writing: React.FC<Props> = ({ sectionId }) => {
const { currentModule, dispatch } = useExamEditorStore();
const {edit } = useExamEditorStore((store) => store.modules[currentModule]);
const { edit } = useExamEditorStore((store) => store.modules[currentModule]);
const { generating, genResult, state } = useExamEditorStore(
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
);
@@ -62,8 +62,9 @@ const Writing: React.FC<Props> = ({ sectionId }) => {
if (genResult !== undefined && generating === "context") {
setEditing(true);
setPrompt(genResult[0].prompt);
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: undefined }})
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: undefined } })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [genResult, dispatch, sectionId, setEditing, currentModule]);
useEffect(() => {