179 lines
8.0 KiB
TypeScript
179 lines
8.0 KiB
TypeScript
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 { Difficulty, 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, module, index }) => {
|
|
const { currentModule, dispatch } = useExamEditorStore();
|
|
const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
|
|
const { type, academic_url } = useExamEditorStore(
|
|
(state) => state.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 level = module === "level";
|
|
|
|
const { editing, handleSave, handleDiscard, handleDelete, handlePractice, handleEdit, setEditing } = useSectionEdit({
|
|
sectionId,
|
|
onSave: () => {
|
|
const newExercise = { ...local } as WritingExercise;
|
|
newExercise.prompt = prompt;
|
|
newExercise.difficulty = exercise.difficulty;
|
|
setAlerts([]);
|
|
setEditing(false);
|
|
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
|
|
}
|
|
});
|
|
}
|
|
},
|
|
onPractice: () => {
|
|
const newState = {
|
|
...state,
|
|
isPractice: !local.isPractice
|
|
};
|
|
setLocal((prev) => ({ ...prev, isPractice: !local.isPractice }))
|
|
dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
|
|
}
|
|
});
|
|
|
|
useEffect(() => {
|
|
const loading = generating && generating == "writing";
|
|
setLoading(loading);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [generating]);
|
|
|
|
useEffect(() => {
|
|
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
|
|
}, [genResult, dispatch, sectionId, setEditing, currentModule]);
|
|
|
|
useEffect(() => {
|
|
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")}>
|
|
<Header
|
|
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}
|
|
handleDiscard={handleDiscard}
|
|
handlePractice={handlePractice}
|
|
isEvaluationEnabled={!local.isPractice}
|
|
module={"writing"}
|
|
/>
|
|
{alerts.length !== 0 && <Alert alerts={alerts} />}
|
|
</div>
|
|
<div className={clsx(level ? "mt-2 px-4" : "mt-4")}>
|
|
{loading ?
|
|
<GenLoader module={currentModule} /> :
|
|
<>
|
|
{
|
|
editing ? (
|
|
<div className="text-gray-600 p-4">
|
|
<AutoExpandingTextArea
|
|
value={prompt}
|
|
onChange={(text) => setPrompt(text)}
|
|
placeholder="Instructions ..."
|
|
/>
|
|
</div>
|
|
) : (
|
|
<p className={
|
|
clsx("w-full px-7 py-8 border-2 bg-white rounded-3xl whitespace-pre-line",
|
|
prompt === "" ? "text-gray-600/50" : "text-gray-600"
|
|
)
|
|
}>
|
|
{prompt === "" ? "Instructions ..." : prompt}
|
|
</p>
|
|
)
|
|
}
|
|
{academic_url && sectionId == 1 && (
|
|
<div className="flex items-center justify-center mt-8">
|
|
<div className="max-w-lg self-center rounded-xl cursor-pointer">
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img src={academic_url} alt="Visual Information" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
}
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Writing;
|