ENCOA-311

This commit is contained in:
Carlos-Mesquita
2025-01-13 01:18:19 +00:00
parent 715a841483
commit ccbbf30058
33 changed files with 824 additions and 194 deletions

View File

@@ -1,5 +1,5 @@
import { FillBlanksExercise, ReadingPart } from "@/interfaces/exam";
import { useEffect, useReducer, useState } from "react";
import { Difficulty, FillBlanksExercise, ReadingPart } from "@/interfaces/exam";
import { useCallback, useEffect, useReducer, useState } from "react";
import BlanksEditor from "..";
import { Card, CardContent } from "@/components/ui/card";
import { MdEdit, MdEditOff } from "react-icons/md";
@@ -21,6 +21,7 @@ interface Word {
const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; 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)!
);
@@ -249,12 +250,24 @@ const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: num
setEditingAlert(editing, setAlerts);
}, [editing])
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 (
<div className="space-y-4">
<BlanksEditor
alerts={alerts}
editing={editing}
state={blanksState}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
blanksDispatcher={blanksDispatcher}
description="Place blanks and assign words from the word bank"
initialText={local.text}

View File

@@ -1,5 +1,5 @@
import { FillBlanksExercise, FillBlanksMCOption, ReadingPart } from "@/interfaces/exam";
import { useEffect, useReducer, useState } from "react";
import { Difficulty, FillBlanksExercise, FillBlanksMCOption, ReadingPart } from "@/interfaces/exam";
import { useCallback, useEffect, useReducer, useState } from "react";
import BlanksEditor from "..";
import { Card, CardContent } from "@/components/ui/card";
import useExamEditorStore from "@/stores/examEditor";
@@ -15,6 +15,7 @@ import MCOption from "./MCOption";
const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; 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)!
);
@@ -257,12 +258,24 @@ const FillBlanksMC: React.FC<{ exercise: FillBlanksExercise; sectionId: number }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [blanksState.blanks]);
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 (
<div className="space-y-4">
<BlanksEditor
alerts={alerts}
editing={editing}
state={blanksState}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
blanksDispatcher={blanksDispatcher}
description="Place blanks and select the correct answer from multiple choice options"
initialText={local.text}

View File

@@ -1,8 +1,8 @@
import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit";
import { Card, CardContent } from "@/components/ui/card";
import { WriteBlanksExercise, ReadingPart } from "@/interfaces/exam";
import { WriteBlanksExercise, ReadingPart, Difficulty } from "@/interfaces/exam";
import useExamEditorStore from "@/stores/examEditor";
import { useState, useReducer, useEffect } from "react";
import { useState, useReducer, useEffect, useCallback } from "react";
import { toast } from "react-toastify";
import BlanksEditor from "..";
import { AlertItem } from "../../Shared/Alert";
@@ -13,6 +13,7 @@ import AlternativeSolutions from "./AlternativeSolutions";
const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; 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)!
);
@@ -161,6 +162,16 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
setEditingAlert(editing, setAlerts);
}, [editing]);
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 (
<div className="space-y-4">
<BlanksEditor
@@ -171,6 +182,8 @@ const WriteBlanksFill: React.FC<{ exercise: WriteBlanksExercise; sectionId: numb
blanksDispatcher={blanksDispatcher}
description={local.prompt}
initialText={local.text}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
module={currentModule}
showBlankBank={true}
onBlankSelect={(blankId) => setSelectedBlankId(blankId?.toString() || null)}

View File

@@ -20,12 +20,15 @@ import { Card, CardContent } from "@/components/ui/card";
import { Blank, DropZone } from "./DragNDrop";
import { getTextSegments, BlankState, BlanksState, BlanksAction, BlankToken } from "./BlanksReducer";
import PromptEdit from "../Shared/PromptEdit";
import { Difficulty } from "@/interfaces/exam";
interface Props {
title?: string;
initialText: string;
description: string;
difficulty?: Difficulty;
saveDifficulty: (difficulty: Difficulty) => void;
state: BlanksState;
module: string;
editing: boolean;
@@ -49,6 +52,8 @@ const BlanksEditor: React.FC<Props> = ({
title = "Fill Blanks",
initialText,
description,
difficulty,
saveDifficulty,
state,
editing,
module,
@@ -169,6 +174,8 @@ const BlanksEditor: React.FC<Props> = ({
title={title}
description={description}
editing={editing}
difficulty={difficulty}
saveDifficulty={saveDifficulty}
handleSave={onSave}
handleDelete={onDelete}
handleDiscard={onDiscard}

View File

@@ -1,10 +1,10 @@
import React, { useState, useMemo, useEffect } from 'react';
import React, { useState, useMemo, useEffect, useCallback } from 'react';
import {
MdAdd,
MdVisibility,
MdVisibilityOff
} from 'react-icons/md';
import { MatchSentencesExercise, ReadingPart } from '@/interfaces/exam';
import { Difficulty, MatchSentencesExercise, ReadingPart } from '@/interfaces/exam';
import Alert, { AlertItem } from '../Shared/Alert';
import ReferenceViewer from './ParagraphViewer';
import Header from '../../Shared/Header';
@@ -21,6 +21,7 @@ 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)!
);
@@ -147,12 +148,24 @@ const MatchSentences: React.FC<{ exercise: MatchSentencesExercise, sectionId: nu
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 (
<div className="flex flex-col mx-auto p-2">
<Header
title={exercise.variant && exercise.variant == "ideaMatch" ? "Idea Match" : "Paragraph Match"}
description={`Edit ${exercise.variant && exercise.variant == "ideaMatch" ? "ideas/opinions" : "headings"} and their matches`}
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDelete={handleDelete}
handleDiscard={handleDiscard}

View File

@@ -5,9 +5,9 @@ import UnderlineQuestion from "./UnderlineQuestion";
import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit";
import { toast } from "react-toastify";
import setEditingAlert from "../../Shared/setEditingAlert";
import { LevelPart, ListeningPart, MultipleChoiceExercise, MultipleChoiceQuestion, ReadingPart } from "@/interfaces/exam";
import { Difficulty, LevelPart, ListeningPart, MultipleChoiceExercise, MultipleChoiceQuestion, ReadingPart } from "@/interfaces/exam";
import useExamEditorStore from "@/stores/examEditor";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { MdAdd } from "react-icons/md";
import Alert, { AlertItem } from "../../Shared/Alert";
import PromptEdit from "../../Shared/PromptEdit";
@@ -18,6 +18,7 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
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)!
);
@@ -113,12 +114,24 @@ const UnderlineMultipleChoice: React.FC<{exercise: MultipleChoiceExercise, secti
}
});
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 (
<div className="p-4">
<Header
title='Underline Multiple Choice Exercise'
description="Edit questions with 4 underline options each"
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDelete={handleDelete}
handlePractice={handlePractice}

View File

@@ -1,11 +1,11 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import {
MdAdd,
MdEdit,
MdEditOff,
} from 'react-icons/md';
import { ReadingPart, MultipleChoiceExercise, MultipleChoiceQuestion, LevelPart, ListeningPart } from '@/interfaces/exam';
import { ReadingPart, MultipleChoiceExercise, MultipleChoiceQuestion, LevelPart, ListeningPart, Difficulty } from '@/interfaces/exam';
import clsx from 'clsx';
import useExamEditorStore from '@/stores/examEditor';
import { toast } from 'react-toastify';
@@ -74,7 +74,8 @@ const validateMultipleChoiceQuestions = (
};
const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, optionsQuantity }) => {
const { currentModule, dispatch} = useExamEditorStore();
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)!
);
@@ -202,12 +203,24 @@ const MultipleChoice: React.FC<MultipleChoiceProps> = ({ exercise, sectionId, op
setLocal(handleMultipleChoiceReorder(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 (
<div className="p-4">
<Header
title='Multiple Choice Exercise'
description={`Edit questions with ${optionsQuantity} options each`}
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDelete={handleDelete}
handleDiscard={handleDiscard}

View File

@@ -5,10 +5,10 @@ import { AiOutlineUnorderedList, AiOutlinePlus, AiOutlineDelete } from 'react-ic
import { Tooltip } from "react-tooltip";
import Header from "../../Shared/Header";
import GenLoader from "../Shared/GenLoader";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import useSectionEdit from "../../Hooks/useSectionEdit";
import useExamEditorStore from "@/stores/examEditor";
import { InteractiveSpeakingExercise, LevelPart } from "@/interfaces/exam";
import { Difficulty, InteractiveSpeakingExercise, LevelPart } from "@/interfaces/exam";
import { BsFileText } from "react-icons/bs";
import { FaChevronLeft, FaChevronRight } from "react-icons/fa6";
import { RiVideoLine } from "react-icons/ri";
@@ -21,7 +21,8 @@ interface Props {
}
const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
const { dispatch } = useExamEditorStore();
const { currentModule, dispatch } = useExamEditorStore();
const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
const [local, setLocal] = useState(exercise);
const [currentVideoIndex, setCurrentVideoIndex] = useState(0);
@@ -105,13 +106,17 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "s
useEffect(() => {
if (genResult && generating === "speakingScript") {
if (!difficulty.includes(genResult.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, genResult.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
title: genResult.result[0].title,
prompts: genResult.result[0].prompts.map((item: any) => ({
text: item || "",
video_url: ""
}))
})),
difficulty: genResult.result[0].difficulty
};
setEditing(true);
setLocal(updatedLocal);
@@ -158,13 +163,17 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "s
const isGenerating = levelGenerating?.includes(`${local.id}-speakingScript`);
if (speakingScript && isGenerating) {
if (!difficulty.includes(speakingScript.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, speakingScript.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
title: speakingScript.result[0].title,
prompts: speakingScript.result[0].prompts.map((item: any) => ({
text: item || "",
video_url: ""
}))
})),
difficulty: speakingScript.result[0].difficulty
};
setEditing(true);
setLocal(updatedLocal);
@@ -264,6 +273,21 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "s
);
};
const saveDifficulty = useCallback((diff: Difficulty)=> {
if (!difficulty.includes(diff)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } });
}
if (module !== "level") {
const updatedExercise = { ...exercise, difficulty: diff };
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: currentModule } });
} else {
const updatedExercise = { ...exercise, difficulty: diff };
const newState = { ...state as LevelPart };
newState.exercises = (newState as LevelPart).exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex );
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
}
}, [currentModule, difficulty, dispatch, exercise, module, sectionId, state]);
return (
<>
<div className='relative pb-4'>
@@ -271,6 +295,8 @@ const InteractiveSpeaking: React.FC<Props> = ({ sectionId, exercise, module = "s
title={`Interactive Speaking Script`}
description='Generate or write the scripts for the videos.'
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleEdit={handleEdit}
handleDiscard={handleDiscard}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import { Card, CardContent } from "@/components/ui/card";
import { AiOutlineUnorderedList, AiOutlinePlus, AiOutlineDelete } from 'react-icons/ai';
@@ -6,7 +6,7 @@ import Header from "../../Shared/Header";
import GenLoader from "../Shared/GenLoader";
import useSectionEdit from "../../Hooks/useSectionEdit";
import useExamEditorStore from "@/stores/examEditor";
import { InteractiveSpeakingExercise, LevelPart } from "@/interfaces/exam";
import { Difficulty, InteractiveSpeakingExercise, LevelPart } from "@/interfaces/exam";
import { BsFileText } from "react-icons/bs";
import { RiVideoLine } from 'react-icons/ri';
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa6';
@@ -19,7 +19,8 @@ interface Props {
}
const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
const { dispatch } = useExamEditorStore();
const { currentModule, dispatch } = useExamEditorStore();
const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
const [local, setLocal] = useState(() => {
const defaultPrompts = [
{ text: "Hello my name is {avatar}, what is yours?", video_url: "" },
@@ -118,6 +119,9 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
useEffect(() => {
if (genResult && generating === "speakingScript") {
if (!difficulty.includes(genResult.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, genResult.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
first_title: genResult.result[0].first_topic,
@@ -129,7 +133,8 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
text: item,
video_url: ""
}))
]
],
difficulty: genResult.result[0].difficulty
};
setEditing(true);
setLocal(updatedLocal);
@@ -176,10 +181,14 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
const isGenerating = levelGenerating?.includes(`${local.id}-speakingScript`);
if (speakingScript && isGenerating) {
if (!difficulty.includes(speakingScript.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, speakingScript.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
first_title: speakingScript.result[0].first_topic,
second_title: speakingScript.result[0].second_topic,
difficulty: speakingScript.result[0].difficulty,
prompts: [
local.prompts[0],
local.prompts[1],
@@ -300,6 +309,21 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
);
};
const saveDifficulty = useCallback((diff: Difficulty)=> {
if (!difficulty.includes(diff)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } });
}
if (module !== "level") {
const updatedExercise = { ...exercise, difficulty: diff };
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: currentModule } });
} else {
const updatedExercise = { ...exercise, difficulty: diff };
const newState = { ...state as LevelPart };
newState.exercises = (newState as LevelPart).exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex );
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
}
}, [currentModule, difficulty, dispatch, exercise, module, sectionId, state]);
return (
<>
<div className='relative pb-4'>
@@ -307,6 +331,8 @@ const Speaking1: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
title={`Speaking 1 Script`}
description='Generate or write the scripts for the videos.'
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleEdit={handleEdit}
handleDiscard={handleDiscard}

View File

@@ -1,9 +1,9 @@
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import { Card, CardContent } from "@/components/ui/card";
import { AiOutlinePlus, AiOutlineDelete } from 'react-icons/ai';
import { LevelPart, SpeakingExercise } from "@/interfaces/exam";
import { Difficulty, LevelPart, SpeakingExercise } from "@/interfaces/exam";
import useExamEditorStore from "@/stores/examEditor";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import useSectionEdit from "../../Hooks/useSectionEdit";
import Header from "../../Shared/Header";
import { Tooltip } from "react-tooltip";
@@ -21,7 +21,8 @@ interface Props {
}
const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }) => {
const { dispatch } = useExamEditorStore();
const { currentModule, dispatch } = useExamEditorStore();
const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
const [local, setLocal] = useState(exercise);
const { sections } = useExamEditorStore((store) => store.modules[module]);
@@ -104,11 +105,15 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
useEffect(() => {
if (genResult && generating === "speakingScript") {
if (!difficulty.includes(genResult.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, genResult.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
title: genResult.result[0].topic,
text: genResult.result[0].question,
prompts: genResult.result[0].prompts
prompts: genResult.result[0].prompts,
difficulty: genResult.result[0].difficulty
};
setEditing(true);
setLocal(updatedLocal);
@@ -153,11 +158,15 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
const speakingScript = levelGenResults.find((res) => res.generating === `${local.id}-speakingScript`);
const generating = levelGenerating.find((res) => res === `${local.id}-speakingScript`);
if (speakingScript && generating) {
if (!difficulty.includes(speakingScript.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, speakingScript.result[0].difficulty]} } });
}
const updatedLocal = {
...local,
title: speakingScript.result[0].topic,
text: speakingScript.result[0].question,
prompts: speakingScript.result[0].prompts
prompts: speakingScript.result[0].prompts,
difficulty: speakingScript.result[0].difficulty
};
setEditing(true);
setLocal(updatedLocal);
@@ -244,6 +253,21 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
</div>
`;
const saveDifficulty = useCallback((diff: Difficulty)=> {
if (!difficulty.includes(diff)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } });
}
if (module !== "level") {
const updatedExercise = { ...exercise, difficulty: diff };
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: currentModule } });
} else {
const updatedExercise = { ...exercise, difficulty: diff };
const newState = { ...state as LevelPart };
newState.exercises = (newState as LevelPart).exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex );
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
}
}, [currentModule, difficulty, dispatch, exercise, module, sectionId, state]);
return (
<>
<div className='relative pb-4'>
@@ -251,6 +275,8 @@ const Speaking2: React.FC<Props> = ({ sectionId, exercise, module = "speaking" }
title={`Speaking ${module === "level" ? local.sectionId : sectionId} Script`}
description='Generate or write the script for the video.'
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleEdit={handleEdit}
handleDiscard={handleDiscard}

View File

@@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import {
MdAdd,
} from 'react-icons/md';
import Alert, { AlertItem } from '../Shared/Alert';
import { ReadingPart, TrueFalseExercise } from '@/interfaces/exam';
import { Difficulty, ReadingPart, TrueFalseExercise } from '@/interfaces/exam';
import QuestionsList from '../Shared/QuestionsList';
import Header from '../../Shared/Header';
import SortableQuestion from '../Shared/SortableQuestion';
@@ -19,6 +19,7 @@ import PromptEdit from '../Shared/PromptEdit';
const TrueFalse: React.FC<{ exercise: TrueFalseExercise, 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)!
);
@@ -131,12 +132,24 @@ const TrueFalse: React.FC<{ exercise: TrueFalseExercise, sectionId: number }> =
setLocal(handleTrueFalseReorder(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 (
<div className="p-4">
<Header
title='True/False/Not Given Exercise'
description='Edit questions and their solutions'
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDelete={handleDelete}
handleDiscard={handleDiscard}

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import {
MdAdd,
@@ -13,7 +13,7 @@ import Header from '../../Shared/Header';
import clsx from 'clsx';
import Alert, { AlertItem } from '../Shared/Alert';
import AutoExpandingTextArea from '@/components/Low/AutoExpandingTextarea';
import { ReadingPart, WriteBlanksExercise } from '@/interfaces/exam';
import { Difficulty, ReadingPart, WriteBlanksExercise } from '@/interfaces/exam';
import useExamEditorStore from '@/stores/examEditor';
import useSectionEdit from '../../Hooks/useSectionEdit';
import setEditingAlert from '../Shared/setEditingAlert';
@@ -25,8 +25,8 @@ import PromptEdit from '../Shared/PromptEdit';
const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise; }> = ({ sectionId, exercise }) => {
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)!
);
@@ -231,12 +231,24 @@ const WriteBlanks: React.FC<{ sectionId: number; exercise: WriteBlanksExercise;
}, [local.solutions]);
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 (
<div className="p-4">
<Header
title={"Write Blanks: Questions"}
description="Edit questions and their solutions"
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDiscard={handleDiscard}
handleDelete={handleDelete}

View File

@@ -1,10 +1,10 @@
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import { Card, CardContent } from "@/components/ui/card";
import { WriteBlanksExercise, ReadingPart } from "@/interfaces/exam";
import { WriteBlanksExercise, ReadingPart, Difficulty } from "@/interfaces/exam";
import useExamEditorStore from "@/stores/examEditor";
import { DragEndEvent } from "@dnd-kit/core";
import { arrayMove } from "@dnd-kit/sortable";
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { MdEditOff, MdEdit, MdDelete, MdAdd } from "react-icons/md";
import { toast } from "react-toastify";
import useSectionEdit from "../../Hooks/useSectionEdit";
@@ -21,6 +21,7 @@ import PromptEdit from "../Shared/PromptEdit";
const WriteBlanksForm: React.FC<{ sectionId: number; exercise: WriteBlanksExercise }> = ({ sectionId, exercise }) => {
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)!
);
@@ -208,12 +209,24 @@ const WriteBlanksForm: React.FC<{ sectionId: number; exercise: WriteBlanksExerci
updateLocal({ ...local, text: newText });
};
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 (
<div className="p-4">
<Header
title="Write Blanks: Form Exercise"
description="Edit questions and their solutions"
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDiscard={handleDiscard}
handleDelete={handleDelete}

View File

@@ -2,7 +2,7 @@ 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 { LevelPart, WritingExercise } from "@/interfaces/exam";
import { Difficulty, LevelPart, WritingExercise } from "@/interfaces/exam";
import Header from "../../Shared/Header";
import Alert, { AlertItem } from "../Shared/Alert";
import clsx from "clsx";
@@ -20,6 +20,7 @@ interface Props {
const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
const { currentModule, dispatch } = useExamEditorStore();
const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
const { type, academic_url } = useExamEditorStore(
(state) => state.modules[currentModule]
);
@@ -39,6 +40,7 @@ const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
onSave: () => {
const newExercise = { ...local } as WritingExercise;
newExercise.prompt = prompt;
newExercise.difficulty = exercise.difficulty;
setAlerts([]);
setEditing(false);
if (!level) {
@@ -86,6 +88,12 @@ const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
if (genResult) {
setEditing(true);
setPrompt(genResult.result[0].prompt);
if (!difficulty.includes(genResult.result[0].difficulty)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, genResult.result[0].difficulty]} } });
}
const updatedExercise = { ...exercise, difficulty: genResult.result[0].difficulty };
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: currentModule } });
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "generating", value: undefined } })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -95,6 +103,21 @@ const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
setEditingAlert(prompt !== local.prompt, setAlerts);
}, [prompt, local.prompt]);
const saveDifficulty = useCallback((diff: Difficulty)=> {
if (!difficulty.includes(diff)) {
dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } });
}
if (!level) {
const updatedExercise = { ...exercise, difficulty: diff };
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: updatedExercise, module: currentModule } });
} else {
const updatedExercise = { ...exercise, difficulty: diff };
const newState = { ...state as LevelPart };
newState.exercises = (newState as LevelPart).exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex );
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
}
}, [currentModule, difficulty, dispatch, exercise, level, sectionId, state]);
return (
<>
<div className={clsx('relative', level ? "px-4 mt-2" : "pb-2")}>
@@ -102,6 +125,8 @@ const Writing: React.FC<Props> = ({ sectionId, exercise, module, index }) => {
title={`${sectionId === 1 ? (type === "academic" ? "Visual Information" : "Letter") : "Essay"} Instructions`}
description='Generate or edit the instructions for the task'
editing={editing}
difficulty={exercise.difficulty}
saveDifficulty={saveDifficulty}
handleSave={handleSave}
handleDelete={module == "level" ? handleDelete : undefined}
handleEdit={handleEdit}