ENCOA-311
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user