Files
encoach_ui_odoo19/src/components/ExamEditor/Shared/ExercisePicker/index.tsx

181 lines
7.4 KiB
TypeScript

import EXERCISES from "./exercises";
import clsx from "clsx";
import { ExerciseGen, GeneratedExercises, GeneratorState } from "./generatedExercises";
import Modal from "@/components/Modal";
import { useState } from "react";
import ExerciseWizard, { ExerciseConfig } from "./ExerciseWizard";
import { generate } from "../../SettingsEditor/Shared/Generate";
import { Module } from "@/interfaces";
import useExamEditorStore from "@/stores/examEditor";
import { ListeningPart, Message, ReadingPart } from "@/interfaces/exam";
import { BsArrowRepeat } from "react-icons/bs";
interface ExercisePickerProps {
module: string;
sectionId: number;
difficulty: string;
extraArgs?: Record<string, any>;
}
const ExercisePicker: React.FC<ExercisePickerProps> = ({
module,
sectionId,
extraArgs = undefined,
}) => {
const { currentModule, dispatch } = useExamEditorStore();
const { difficulty} = useExamEditorStore((store) => store.modules[currentModule]);
const section = useExamEditorStore((store) => store.modules[currentModule].sections.find((s) => s.sectionId == sectionId)!);
const { state, selectedExercises } = section;
const [pickerOpen, setPickerOpen] = useState(false);
const getFullExerciseType = (exercise: ExerciseGen): string => {
if (exercise.extra && exercise.extra.length > 0) {
const extraValue = exercise.extra.find(e => e.param === 'name')?.value;
return extraValue ? `${exercise.type}/?name=${extraValue}` : exercise.type;
}
return exercise.type;
};
const handleChange = (exercise: ExerciseGen) => {
const fullType = getFullExerciseType(exercise);
const newSelected = selectedExercises.includes(fullType)
? selectedExercises.filter(type => type !== fullType)
: [...selectedExercises, fullType];
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { module: currentModule, sectionId, field: "selectedExercises", value: newSelected } })
};
const moduleExercises = module === 'level' ? EXERCISES : (sectionId ? EXERCISES.filter((ex) => ex.module === module && ex.sectionId == sectionId) : EXERCISES.filter((ex) => ex.module === module));
const onModuleSpecific = (configurations: ExerciseConfig[]) => {
const exercises = configurations.map(config => {
const exerciseType = config.type.split('name=')[1];
return {
type: exerciseType,
quantity: Number(config.params.quantity || 1),
...(config.params.num_random_words !== undefined && {
num_random_words: Number(config.params.num_random_words)
}),
...(config.params.max_words !== undefined && {
max_words: Number(config.params.max_words)
})
};
});
let context, moduleState;
switch (module) {
case 'reading':
moduleState = state as ReadingPart;
context = {
text: moduleState.text.content
}
break;
case 'listening':
moduleState = state as ListeningPart;
let script = moduleState.script;
let text, dialog;
switch (sectionId) {
case 1:
case 3:
dialog = script as Message[];
text = dialog.map((d) => `${d.name}: ${d.text}`).join("\n");
context = { text: text }
break;
case 2:
case 4:
text = script as string;
context = { text: text }
break;
}
break;
default:
context = {}
}
generate(
sectionId,
module as Module,
"exercises",
{
method: 'POST',
body: {
...context,
exercises: exercises,
difficulty: difficulty
}
},
(data: any) => [{
exercises: data.exercises
}]
);
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "selectedExercises", value: [] } })
setPickerOpen(false);
};
return (
<>
<Modal isOpen={pickerOpen} onClose={() => setPickerOpen(false)} title="Exercise Wizard">
<ExerciseWizard
sectionId={sectionId}
exercises={moduleExercises}
onSubmit={onModuleSpecific}
onDiscard={() => setPickerOpen(false)}
extraArgs={extraArgs}
/>
</Modal>
<div className="flex flex-col gap-4 px-4" key={sectionId}>
<div className="space-y-2">
{moduleExercises.map((exercise) => {
const fullType = getFullExerciseType(exercise);
return (
<label
key={fullType}
className={`flex items-center space-x-3 text-white font-semibold cursor-pointer p-2 hover:bg-ielts-${exercise.module}/70 rounded bg-ielts-${exercise.module}/90`}
>
<input
type="checkbox"
name="exercise"
value={fullType}
checked={selectedExercises.includes(fullType)}
onChange={() => handleChange(exercise)}
className="h-5 w-5"
/>
<div className="flex items-center space-x-2">
<exercise.icon className="h-5 w-5 text-white" />
<span>{exercise.label}</span>
</div>
</label>
);
})}
</div>
<div className="flex flex-row justify-center">
<button
className={
clsx("flex items-center justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300 disabled:cursor-not-allowed",
`bg-ielts-${module}/70 border border-ielts-${module} hover:bg-ielts-${module} disabled:bg-ielts-${module}/40 `,
)
}
onClick={() => setPickerOpen(true)}
disabled={selectedExercises.length == 0}
>
{section.generating === "exercises" ? (
<div key={`section-${sectionId}`} className="flex items-center justify-center">
<BsArrowRepeat className="text-white animate-spin" size={25} />
</div>
) : (
<>Set Up Exercises ({selectedExercises.length}) </>
)}
</button>
</div>
</div>
</>
);
};
export default ExercisePicker;