ENCOA-228 Now when user navigates between modules the generation items persist. Reading, listening and writing added to level module
This commit is contained in:
@@ -47,7 +47,7 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
|
||||
setEditing,
|
||||
});
|
||||
|
||||
const { handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
editing,
|
||||
setEditing,
|
||||
@@ -74,7 +74,7 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
|
||||
ex.id === exercise.id ? updatedExercise : ex
|
||||
);
|
||||
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setSelectedBlankId(null);
|
||||
@@ -96,12 +96,23 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
|
||||
blanksDispatcher({ type: "SET_BLANKS", payload: initialBlanks });
|
||||
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -252,8 +263,10 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
|
||||
onBlankRemove={handleBlankRemove}
|
||||
onSave={handleSave}
|
||||
onDiscard={handleDiscard}
|
||||
onDelete={modeHandle}
|
||||
onDelete={handleDelete}
|
||||
setEditing={setEditing}
|
||||
onPractice={handlePractice}
|
||||
isEvaluationEnabled={true}//local.isPractice}
|
||||
>
|
||||
<>
|
||||
{!blanksState.textMode && <Card className="p-4">
|
||||
|
||||
@@ -45,7 +45,7 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
|
||||
setEditing,
|
||||
});
|
||||
|
||||
const { handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
editing,
|
||||
setEditing,
|
||||
@@ -72,7 +72,7 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
|
||||
ex.id === exercise.id ? updatedExercise : ex
|
||||
);
|
||||
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setSelectedBlankId(null);
|
||||
@@ -92,12 +92,23 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
|
||||
}, [] as BlankState[]);
|
||||
blanksDispatcher({ type: "SET_BLANKS", payload: initialBlanks });
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -251,9 +262,11 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
|
||||
onBlankSelect={(blankId) => setSelectedBlankId(blankId?.toString() || null)}
|
||||
onSave={handleSave}
|
||||
onDiscard={handleDiscard}
|
||||
onDelete={modeHandle}
|
||||
onDelete={handleDelete}
|
||||
onPractice={handlePractice}
|
||||
setEditing={setEditing}
|
||||
onBlankRemove={handleBlankRemove}
|
||||
isEvaluationEnabled={true}
|
||||
>
|
||||
{!blanksState.textMode && selectedBlankId && (
|
||||
<Card className="p-4">
|
||||
|
||||
@@ -10,7 +10,6 @@ import setEditingAlert from "../../Shared/setEditingAlert";
|
||||
import { blanksReducer } from "../BlanksReducer";
|
||||
import { validateWriteBlanks } from "./validation";
|
||||
import AlternativeSolutions from "./AlternativeSolutions";
|
||||
import clsx from "clsx";
|
||||
|
||||
const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: number }> = ({ exercise, sectionId }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
@@ -34,7 +33,7 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
|
||||
setEditing,
|
||||
});
|
||||
|
||||
const { handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
editing,
|
||||
setEditing,
|
||||
@@ -57,19 +56,30 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
|
||||
ex.id === exercise.id ? updatedExercise : ex
|
||||
);
|
||||
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState } });
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setSelectedBlankId(null);
|
||||
setLocal(exercise);
|
||||
blanksDispatcher({ type: "RESET", payload: { text: exercise.text } });
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
isPractice: true,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -160,8 +170,10 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
|
||||
onBlankRemove={handleBlankRemove}
|
||||
onSave={handleSave}
|
||||
onDiscard={handleDiscard}
|
||||
onDelete={modeHandle}
|
||||
onDelete={handleDelete}
|
||||
onPractice={handlePractice}
|
||||
setEditing={setEditing}
|
||||
isEvaluationEnabled={true}
|
||||
>
|
||||
{!blanksState.textMode && (
|
||||
<Card>
|
||||
|
||||
@@ -37,6 +37,8 @@ interface Props {
|
||||
onSave: () => void;
|
||||
onDiscard: () => void;
|
||||
onDelete: () => void;
|
||||
onPractice: () => void;
|
||||
isEvaluationEnabled: boolean;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
@@ -56,6 +58,8 @@ const BlanksEditor: React.FC<Props> = ({
|
||||
onSave,
|
||||
onDiscard,
|
||||
onDelete,
|
||||
onPractice,
|
||||
isEvaluationEnabled,
|
||||
setEditing
|
||||
}) => {
|
||||
|
||||
@@ -161,8 +165,10 @@ const BlanksEditor: React.FC<Props> = ({
|
||||
description={description}
|
||||
editing={editing}
|
||||
handleSave={onSave}
|
||||
modeHandle={onDelete}
|
||||
handleDelete={onDelete}
|
||||
handleDiscard={onDiscard}
|
||||
handlePractice={onPractice}
|
||||
isEvaluationEnabled={isEvaluationEnabled}
|
||||
/>
|
||||
{alerts.length > 0 && <Alert alerts={alerts} />}
|
||||
<Card>
|
||||
|
||||
@@ -37,7 +37,7 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
|
||||
setEditing(true);
|
||||
};
|
||||
|
||||
const { editing, setEditing, handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { editing, setEditing, handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
onSave: () => {
|
||||
|
||||
@@ -53,19 +53,30 @@ 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: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
setSelectedParagraph(null);
|
||||
setShowReference(false);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -142,8 +153,10 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
|
||||
description={`Edit ${exercise.variant && exercise.variant == "ideaMatch" ? "ideas/opinions" : "headings"} and their matches`}
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handleDiscard={handleDiscard}
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowReference(!showReference)}
|
||||
@@ -210,7 +223,7 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
|
||||
</SortableQuestion>
|
||||
))}
|
||||
</QuestionsList>
|
||||
{(section.text.content.split("\n\n").length - 1) === local.sentences.length && (
|
||||
{(section.text !== undefined && 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"
|
||||
|
||||
@@ -73,9 +73,8 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
|
||||
updateLocal({ ...local, questions: newQuestions });
|
||||
};
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
setEditing(false);
|
||||
setAlerts([]);
|
||||
@@ -85,20 +84,31 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
|
||||
ex.id === local.id ? local : ex
|
||||
)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setAlerts([]);
|
||||
setLocal(exercise);
|
||||
setEditing(false);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -108,8 +118,10 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
|
||||
description="Edit questions with 4 underline options each"
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handlePractice={handlePractice}
|
||||
handleDiscard={handleDiscard}
|
||||
isEvaluationEnabled={true}
|
||||
/>
|
||||
{alerts.length > 0 && <Alert className="mb-6" alerts={alerts} />}
|
||||
|
||||
|
||||
@@ -143,9 +143,8 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
|
||||
updateLocal({ ...local, questions: updatedQuestions });
|
||||
};
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
const isValid = validateMultipleChoiceQuestions(
|
||||
local.questions,
|
||||
@@ -164,20 +163,31 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
|
||||
...section,
|
||||
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setEditing(false);
|
||||
setAlerts([]);
|
||||
setLocal(exercise);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -197,8 +207,10 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
|
||||
description={`Edit questions with ${optionsQuantity} options each`}
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handleDiscard={handleDiscard}
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
/>
|
||||
{alerts.length > 0 && <Alert className="mb-6" alerts={alerts} />}
|
||||
<Card className="mb-6">
|
||||
|
||||
@@ -41,7 +41,6 @@ const colorOptions = [
|
||||
];
|
||||
|
||||
const ScriptEditor: React.FC<Props> = ({ section, editing = false, local, setLocal }) => {
|
||||
|
||||
const isConversation = [1, 3].includes(section);
|
||||
const speakerCount = section === 1 ? 2 : 4;
|
||||
|
||||
|
||||
@@ -12,53 +12,70 @@ import { InteractiveSpeakingExercise } from "@/interfaces/exam";
|
||||
import { BsFileText } from "react-icons/bs";
|
||||
import { FaChevronLeft, FaChevronRight } from "react-icons/fa6";
|
||||
import { RiVideoLine } from "react-icons/ri";
|
||||
import { Module } from "@/interfaces";
|
||||
|
||||
interface Props {
|
||||
sectionId: number;
|
||||
exercise: InteractiveSpeakingExercise;
|
||||
module?: Module;
|
||||
}
|
||||
|
||||
const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
|
||||
const { dispatch } = useExamEditorStore();
|
||||
const [local, setLocal] = useState(exercise);
|
||||
|
||||
const [currentVideoIndex, setCurrentVideoIndex] = useState(0);
|
||||
|
||||
const { generating, genResult } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
|
||||
const { generating, genResult, state } = useExamEditorStore(
|
||||
(state) => state.modules[module].sections.find((section) => section.sectionId === sectionId)!
|
||||
);
|
||||
|
||||
const { editing, setEditing, handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { editing, setEditing, handleSave, handleDiscard, handleEdit, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
setEditing(false);
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local, module: module } });
|
||||
if (genResult) {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: module,
|
||||
field: "genResult",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
},
|
||||
onMode: () => { },
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...state,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: module } });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "context") {
|
||||
if (genResult && generating === "speakingScript") {
|
||||
setEditing(true);
|
||||
setLocal({
|
||||
...local,
|
||||
title: genResult[0].title,
|
||||
prompts: genResult[0].prompts.map((item: any) => ({
|
||||
title: genResult.result[0].title,
|
||||
prompts: genResult.result[0].prompts.map((item: any) => ({
|
||||
text: item || "",
|
||||
video_url: ""
|
||||
}))
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "genResult",
|
||||
module: module,
|
||||
field: "generating",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
@@ -91,27 +108,18 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
const isUnedited = local.prompts.length === 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "media") {
|
||||
setLocal({ ...local, prompts: genResult[0].prompts });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: { ...local, prompts: genResult[0].prompts } } });
|
||||
if (genResult && generating === "video") {
|
||||
setLocal({ ...local, prompts: genResult.result[0].prompts });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: { ...local, prompts: genResult.result[0].prompts }, module: module } });
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
module: module,
|
||||
field: "generating",
|
||||
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, generating]);
|
||||
@@ -121,7 +129,7 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
};
|
||||
|
||||
const handleNextVideo = () => {
|
||||
setCurrentVideoIndex((prev) =>
|
||||
setCurrentVideoIndex((prev) =>
|
||||
(prev < local.prompts.length - 1 ? prev + 1 : prev)
|
||||
);
|
||||
};
|
||||
@@ -134,14 +142,15 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
description='Generate or write the scripts for the videos.'
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleEdit={handleEdit}
|
||||
handleDiscard={handleDiscard}
|
||||
mode="edit"
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
module="speaking"
|
||||
/>
|
||||
</div>
|
||||
{generating && generating === "context" ? (
|
||||
<GenLoader module={currentModule} />
|
||||
{generating && generating === "speakingScript" ? (
|
||||
<GenLoader module={module} />
|
||||
) : (
|
||||
<>
|
||||
{editing ? (
|
||||
@@ -160,8 +169,8 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
onClick={handlePrevVideo}
|
||||
disabled={currentVideoIndex === 0}
|
||||
className={`p-2 rounded-full ${currentVideoIndex === 0
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<FaChevronLeft className="w-4 h-4" />
|
||||
@@ -173,8 +182,8 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
onClick={handleNextVideo}
|
||||
disabled={currentVideoIndex === local.prompts.length - 1}
|
||||
className={`p-2 rounded-full ${currentVideoIndex === local.prompts.length - 1
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-gray-600 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<FaChevronRight className="w-4 h-4" />
|
||||
@@ -196,8 +205,8 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
{generating && generating === "media" &&
|
||||
<GenLoader module={currentModule} custom="Generating the videos ... This may take a while ..." />
|
||||
{generating && generating === "video" &&
|
||||
<GenLoader module={module} custom="Generating the videos ... This may take a while ..." />
|
||||
}
|
||||
<Card>
|
||||
<CardContent>
|
||||
|
||||
@@ -10,14 +10,16 @@ import { InteractiveSpeakingExercise } from "@/interfaces/exam";
|
||||
import { BsFileText } from "react-icons/bs";
|
||||
import { RiVideoLine } from 'react-icons/ri';
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6';
|
||||
import { Module } from '@/interfaces';
|
||||
|
||||
interface Props {
|
||||
sectionId: number;
|
||||
exercise: InteractiveSpeakingExercise;
|
||||
module?: Module;
|
||||
}
|
||||
|
||||
const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
|
||||
const {dispatch} = useExamEditorStore();
|
||||
const [local, setLocal] = useState(() => {
|
||||
const defaultPrompts = [
|
||||
{ text: "Hello my name is {avatar}, what is yours?", video_url: "" },
|
||||
@@ -29,16 +31,26 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
|
||||
const [currentVideoIndex, setCurrentVideoIndex] = useState(0);
|
||||
|
||||
const { generating, genResult } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
|
||||
const { generating, genResult , state} = useExamEditorStore(
|
||||
(state) => state.modules[module].sections.find((section) => section.sectionId === sectionId)!
|
||||
);
|
||||
|
||||
const { editing, setEditing, handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { editing, setEditing, handleSave, handleDiscard, handleEdit, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
setEditing(false);
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local, module } });
|
||||
if (genResult) {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: module,
|
||||
field: "genResult",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal({
|
||||
@@ -50,32 +62,37 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
]
|
||||
});
|
||||
},
|
||||
onMode: () => { },
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...state,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: module } });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "context") {
|
||||
if (genResult && generating === "speakingScript") {
|
||||
setEditing(true);
|
||||
setLocal(prev => ({
|
||||
...prev,
|
||||
first_title: genResult[0].first_topic,
|
||||
second_title: genResult[0].second_topic,
|
||||
first_title: genResult.result[0].first_topic,
|
||||
second_title: genResult.result[0].second_topic,
|
||||
prompts: [
|
||||
prev.prompts[0],
|
||||
prev.prompts[1],
|
||||
...genResult[0].prompts.map((item: any) => ({
|
||||
...genResult.result[0].prompts.map((item: any) => ({
|
||||
text: item,
|
||||
video_url: ""
|
||||
}))
|
||||
]
|
||||
}));
|
||||
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "genResult",
|
||||
module: module,
|
||||
field: "generating",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
@@ -111,15 +128,14 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "media") {
|
||||
console.log(genResult[0].prompts);
|
||||
setLocal({ ...local, prompts: genResult[0].prompts });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: { ...local, prompts: genResult[0].prompts } } });
|
||||
if (genResult && generating === "video") {
|
||||
setLocal({ ...local, prompts: genResult.result[0].prompts });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: { ...local, prompts: genResult.result[0].prompts }, module } });
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
module: module,
|
||||
field: "generating",
|
||||
value: undefined
|
||||
}
|
||||
@@ -128,7 +144,7 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
module: module,
|
||||
field: "genResult",
|
||||
value: undefined
|
||||
}
|
||||
@@ -155,14 +171,15 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
description='Generate or write the scripts for the videos.'
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleEdit={handleEdit}
|
||||
handleDiscard={handleDiscard}
|
||||
mode="edit"
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
module="speaking"
|
||||
/>
|
||||
</div>
|
||||
{generating && generating === "context" ? (
|
||||
<GenLoader module={currentModule} />
|
||||
{generating && generating === "speakingScript" ? (
|
||||
<GenLoader module={module} />
|
||||
) : (
|
||||
<>
|
||||
{editing ? (
|
||||
@@ -305,8 +322,8 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
{generating && generating === "media" &&
|
||||
<GenLoader module={currentModule} custom="Generating the videos ... This may take a while ..." />
|
||||
{generating && generating === "video" &&
|
||||
<GenLoader module={module} custom="Generating the videos ... This may take a while ..." />
|
||||
}
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
|
||||
@@ -12,52 +12,69 @@ import { AiOutlineUnorderedList } from 'react-icons/ai';
|
||||
import { BiQuestionMark, BiMessageRoundedDetail } from "react-icons/bi";
|
||||
import GenLoader from "../Shared/GenLoader";
|
||||
import { RiVideoLine } from 'react-icons/ri';
|
||||
import { Module } from "@/interfaces";
|
||||
|
||||
interface Props {
|
||||
sectionId: number;
|
||||
exercise: SpeakingExercise;
|
||||
module?: Module;
|
||||
}
|
||||
|
||||
|
||||
const Speaking2: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
|
||||
const { dispatch } = useExamEditorStore();
|
||||
const [local, setLocal] = useState(exercise);
|
||||
|
||||
const { generating, genResult } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
|
||||
const { generating, genResult, state } = useExamEditorStore(
|
||||
(state) => state.modules[module].sections.find((section) => section.sectionId === sectionId)!
|
||||
);
|
||||
|
||||
const { editing, setEditing, handleSave, handleDiscard, modeHandle } = useSectionEdit({
|
||||
const { editing, setEditing, handleSave, handleDiscard, handleEdit, handlePractice } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
setEditing(false);
|
||||
console.log(local);
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: local , module} });
|
||||
if (genResult) {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: module,
|
||||
field: "genResult",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
},
|
||||
onMode: () => { },
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...state,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: module } });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "context") {
|
||||
if (genResult && generating === "speakingScript") {
|
||||
setEditing(true);
|
||||
setLocal({
|
||||
...local,
|
||||
title: genResult[0].topic,
|
||||
text: genResult[0].question,
|
||||
prompts: genResult[0].prompts
|
||||
title: genResult.result[0].topic,
|
||||
text: genResult.result[0].question,
|
||||
prompts: genResult.result[0].prompts
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "genResult",
|
||||
module: module,
|
||||
field: "generating",
|
||||
value: undefined
|
||||
}
|
||||
});
|
||||
@@ -66,27 +83,18 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
}, [genResult, generating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult && generating === "media") {
|
||||
setLocal({...local, video_url: genResult[0].video_url});
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: {...local, video_url: genResult[0].video_url} } });
|
||||
if (genResult && generating === "video") {
|
||||
setLocal({...local, video_url: genResult.result[0].video_url});
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: {...local, video_url: genResult.result[0].video_url} , module} });
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
module: module,
|
||||
field: "generating",
|
||||
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, generating]);
|
||||
@@ -138,14 +146,15 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
description='Generate or write the script for the video.'
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleEdit={handleEdit}
|
||||
handleDiscard={handleDiscard}
|
||||
mode="edit"
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
module="speaking"
|
||||
/>
|
||||
</div>
|
||||
{generating && generating === "context" ? (
|
||||
<GenLoader module={currentModule} />
|
||||
{generating && generating === "speakingScript" ? (
|
||||
<GenLoader module={module} />
|
||||
) : (
|
||||
<>
|
||||
{editing ? (
|
||||
@@ -260,8 +269,8 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
</CardContent>
|
||||
</Card>
|
||||
}
|
||||
{generating && generating === "media" &&
|
||||
<GenLoader module={currentModule} custom="Generating the video ... This may take a while ..." />
|
||||
{generating && generating === "video" &&
|
||||
<GenLoader module={module} custom="Generating the video ... This may take a while ..." />
|
||||
}
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import { ModuleState } from "@/stores/examEditor/types";
|
||||
import { SpeakingExercise, InteractiveSpeakingExercise } from "@/interfaces/exam";
|
||||
import useSectionEdit from "../../Hooks/useSectionEdit";
|
||||
import Header from "../../Shared/Header";
|
||||
import GenLoader from "../Shared/GenLoader";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
|
||||
import Speaking2 from "./Speaking2";
|
||||
import InteractiveSpeaking from "./InteractiveSpeaking";
|
||||
import Speaking1 from "./Speaking1";
|
||||
|
||||
@@ -72,9 +72,8 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
|
||||
updateLocal({ ...local, questions: updatedQuestions });
|
||||
};
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
const isValid = validateTrueFalseQuestions(
|
||||
local.questions,
|
||||
@@ -93,18 +92,29 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
|
||||
...section,
|
||||
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -127,8 +137,9 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
|
||||
description='Edit questions and their solutions'
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handleDiscard={handleDiscard}
|
||||
handlePractice={handlePractice}
|
||||
/>
|
||||
{alerts.length > 0 && <Alert className="mb-6" alerts={alerts} />}
|
||||
<PromptEdit
|
||||
|
||||
@@ -23,7 +23,7 @@ import { handleWriteBlanksReorder } from '@/stores/examEditor/reorder/local';
|
||||
import { ParsedQuestion, parseText, reconstructText } from './parsing';
|
||||
|
||||
|
||||
const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise; title?: string; }> = ({ sectionId, exercise, title = "Write Blanks Exercise" }) => {
|
||||
const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise; }> = ({ sectionId, exercise }) => {
|
||||
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const { state } = useExamEditorStore(
|
||||
@@ -38,9 +38,8 @@ const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise;
|
||||
const [errors, setErrors] = useState<{ [key: string]: string[] }>({});
|
||||
const [parsedQuestions, setParsedQuestions] = useState<ParsedQuestion[]>([]);
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
const isQuestionTextValid = validateQuestionText(
|
||||
parsedQuestions,
|
||||
@@ -65,18 +64,29 @@ const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise;
|
||||
...section,
|
||||
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -222,12 +232,14 @@ const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise;
|
||||
return (
|
||||
<div className="p-4">
|
||||
<Header
|
||||
title={title}
|
||||
title={"Write Blanks: Questions"}
|
||||
description="Edit questions and their solutions"
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
handleDiscard={handleDiscard}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
/>
|
||||
<div className="space-y-4">
|
||||
{alerts.length > 0 && <Alert alerts={alerts} />}
|
||||
|
||||
@@ -31,9 +31,8 @@ const WriteBlanksForm: React.FC<{ sectionId: number; exercise: WriteBlanksExerci
|
||||
const [editingPrompt, setEditingPrompt] = useState(false);
|
||||
const [parsedQuestions, setParsedQuestions] = useState<ParsedQuestion[]>([]);
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
mode: "edit",
|
||||
onSave: () => {
|
||||
const isQuestionsValid = validateQuestions(parsedQuestions, setAlerts);
|
||||
const isSolutionsValid = validateEmptySolutions(local.solutions, setAlerts);
|
||||
@@ -50,19 +49,30 @@ const WriteBlanksForm: React.FC<{ sectionId: number; exercise: WriteBlanksExerci
|
||||
...section,
|
||||
exercises: section.exercises.map((ex) => ex.id === local.id ? local : ex)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onDiscard: () => {
|
||||
setLocal(exercise);
|
||||
setParsedQuestions([]);
|
||||
},
|
||||
onMode: () => {
|
||||
onDelete: () => {
|
||||
const newSection = {
|
||||
...section,
|
||||
exercises: section.exercises.filter((ex) => ex.id !== local.id)
|
||||
};
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection } });
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
|
||||
},
|
||||
onPractice: () => {
|
||||
const updatedExercise = {
|
||||
...local,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
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 } });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -204,7 +214,8 @@ const WriteBlanksForm: React.FC<{ sectionId: number; exercise: WriteBlanksExerci
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
handleDiscard={handleDiscard}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handlePractice={handlePractice}
|
||||
/>
|
||||
<div className="space-y-4">
|
||||
{alerts.length > 0 && <Alert alerts={alerts} />}
|
||||
|
||||
@@ -2,67 +2,88 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import ExamEditorStore, { ModuleState } from "@/stores/examEditor/types";
|
||||
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
|
||||
import { WritingExercise } from "@/interfaces/exam";
|
||||
import { LevelPart, WritingExercise } from "@/interfaces/exam";
|
||||
import Header from "../../Shared/Header";
|
||||
import Alert, { AlertItem } from "../Shared/Alert";
|
||||
import clsx from "clsx";
|
||||
import useSectionEdit from "../../Hooks/useSectionEdit";
|
||||
import GenLoader from "../Shared/GenLoader";
|
||||
import setEditingAlert from "../Shared/setEditingAlert";
|
||||
import { Module } from "@/interfaces";
|
||||
|
||||
interface Props {
|
||||
sectionId: number;
|
||||
exercise: WritingExercise;
|
||||
module: Module;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
const Writing: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const { edit } = useExamEditorStore((store) => store.modules[currentModule]);
|
||||
const { generating, genResult, state } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
|
||||
);
|
||||
|
||||
|
||||
const [local, setLocal] = useState(exercise);
|
||||
const [prompt, setPrompt] = useState(exercise.prompt);
|
||||
const [loading, setLoading] = useState(generating && generating == "exercises");
|
||||
const [alerts, setAlerts] = useState<AlertItem[]>([]);
|
||||
|
||||
const updateModule = useCallback((updates: Partial<ModuleState>) => {
|
||||
dispatch({ type: 'UPDATE_MODULE', payload: { updates } });
|
||||
}, [dispatch]);
|
||||
const level = module === "level";
|
||||
|
||||
const { editing, handleSave, handleDiscard, modeHandle, setEditing } = useSectionEdit({
|
||||
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, handleEdit, setEditing } = useSectionEdit({
|
||||
sectionId,
|
||||
onSave: () => {
|
||||
const newExercise = { ...local } as WritingExercise;
|
||||
newExercise.prompt = prompt;
|
||||
setAlerts([]);
|
||||
setEditing(false);
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: newExercise } });
|
||||
if (!level) {
|
||||
dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId: sectionId, update: newExercise, module } });
|
||||
}
|
||||
if (genResult) {
|
||||
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: undefined } })
|
||||
}
|
||||
},
|
||||
onDiscard: () => {
|
||||
setEditing(false);
|
||||
setLocal(exercise);
|
||||
setPrompt(exercise.prompt);
|
||||
},
|
||||
onDelete: () => {
|
||||
if (level) {
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_STATE", payload: {
|
||||
sectionId: sectionId,
|
||||
update: {
|
||||
exercises: (state as LevelPart).exercises.filter((_, i) => i !== index)
|
||||
},
|
||||
module
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onEdit: ()=> {},
|
||||
onPractice: () => {
|
||||
const newState = {
|
||||
...state,
|
||||
//isPractice: !isPractice,
|
||||
};
|
||||
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loading = generating && generating == "context";
|
||||
const loading = generating && generating == "writing";
|
||||
setLoading(loading);
|
||||
|
||||
if (loading) {
|
||||
updateModule({ edit: Array.from(new Set(edit).add(sectionId)) });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [generating, updateModule]);
|
||||
}, [generating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult !== undefined && generating === "context") {
|
||||
if (genResult) {
|
||||
setEditing(true);
|
||||
setPrompt(genResult[0].prompt);
|
||||
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: undefined } })
|
||||
setPrompt(genResult.result[0].prompt);
|
||||
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "generating", value: undefined } })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [genResult, dispatch, sectionId, setEditing, currentModule]);
|
||||
@@ -73,20 +94,22 @@ const Writing: React.FC<Props> = ({ sectionId, exercise }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='relative pb-4'>
|
||||
<div className={clsx('relative', level ? "px-4 mt-2" : "pb-2")}>
|
||||
<Header
|
||||
title={`Task ${sectionId} Instructions`}
|
||||
title={`${sectionId === 1 ? "Letter" : "Essay"} Instructions`}
|
||||
description='Generate or edit the instructions for the task'
|
||||
editing={editing}
|
||||
handleSave={handleSave}
|
||||
modeHandle={modeHandle}
|
||||
handleDelete={handleDelete}
|
||||
handleEdit={handleEdit}
|
||||
handleDiscard={handleDiscard}
|
||||
mode="edit"
|
||||
handlePractice={handlePractice}
|
||||
isEvaluationEnabled={true}
|
||||
module={"writing"}
|
||||
/>
|
||||
{alerts.length !== 0 && <Alert alerts={alerts} />}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<div className={clsx(level ? "mt-2 px-4" : "mt-4")}>
|
||||
{loading ?
|
||||
<GenLoader module={currentModule} /> :
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user