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:
@@ -1,8 +1,7 @@
|
||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import SortableSection from "../../Shared/SortableSection";
|
||||
import getReadingQuestions from '../SectionExercises/reading';
|
||||
import { Exercise, LevelPart, ListeningPart, ReadingPart, SpeakingExercise, WritingExercise } from "@/interfaces/exam";
|
||||
import ExerciseItem, { ReadingExercise } from "./types";
|
||||
import ExerciseItem from "./types";
|
||||
import Dropdown from "@/components/Dropdown";
|
||||
import useExamEditorStore from "@/stores/examEditor";
|
||||
import Writing from "../../Exercises/Writing";
|
||||
@@ -13,15 +12,14 @@ import {
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragEndEvent,
|
||||
closestCenter,
|
||||
UniqueIdentifier,
|
||||
} from '@dnd-kit/core';
|
||||
import GenLoader from "../../Exercises/Shared/GenLoader";
|
||||
import { ExamPart } from "@/stores/examEditor/types";
|
||||
import getListeningItems from "./listening";
|
||||
import getLevelQuestionItems from "./level";
|
||||
import { ExamPart, Generating } from "@/stores/examEditor/types";
|
||||
import React from "react";
|
||||
import getExerciseItems from "./exercises";
|
||||
import { Action } from "@/stores/examEditor/reducers";
|
||||
import { writingTask } from "@/stores/examEditor/sections";
|
||||
|
||||
|
||||
interface QuestionItemsResult {
|
||||
@@ -30,31 +28,133 @@ interface QuestionItemsResult {
|
||||
}
|
||||
|
||||
const SectionExercises: React.FC<{ sectionId: number; }> = ({ sectionId }) => {
|
||||
const { currentModule, dispatch } = useExamEditorStore();
|
||||
const { sections, expandedSections } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule]
|
||||
);
|
||||
|
||||
const { genResult, generating, state } = useExamEditorStore(
|
||||
(state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
|
||||
const dispatch = useExamEditorStore(state => state.dispatch);
|
||||
const currentModule = useExamEditorStore(state => state.currentModule);
|
||||
|
||||
const sections = useExamEditorStore(state => state.modules[currentModule].sections);
|
||||
const expandedSections = useExamEditorStore(state => state.modules[currentModule].expandedSections);
|
||||
|
||||
const section = useExamEditorStore(
|
||||
state => state.modules[currentModule].sections.find(
|
||||
section => section.sectionId === sectionId
|
||||
)
|
||||
);
|
||||
|
||||
const genResult = section?.genResult;
|
||||
const generating = section?.generating;
|
||||
const levelGenResults = section?.levelGenResults
|
||||
const levelGenerating = section?.levelGenerating;
|
||||
const sectionState = section?.state;
|
||||
|
||||
useEffect(() => {
|
||||
if (genResult !== undefined && generating === "exercises") {
|
||||
const newExercises = genResult[0].exercises;
|
||||
if (genResult && genResult.generating === "exercises" && genResult.module === currentModule) {
|
||||
const newExercises = genResult.result[0].exercises;
|
||||
dispatch({
|
||||
type: "UPDATE_SECTION_STATE", payload: {
|
||||
sectionId, update: {
|
||||
exercises: [...(state as ExamPart).exercises, ...newExercises]
|
||||
sectionId,
|
||||
module: genResult.module,
|
||||
update: {
|
||||
exercises: [...(sectionState as ExamPart).exercises, ...newExercises]
|
||||
}
|
||||
}
|
||||
})
|
||||
dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, 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, dispatch, sectionId, currentModule]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (levelGenResults && levelGenResults.some(res => res.generating.startsWith("exercises"))) {
|
||||
const newExercises = levelGenResults
|
||||
.filter(res => res.generating.startsWith("exercises"))
|
||||
.map(res => res.result[0].exercises)
|
||||
.flat();
|
||||
|
||||
const updates = [
|
||||
{
|
||||
type: "UPDATE_SECTION_STATE",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: "level",
|
||||
update: {
|
||||
exercises: [...(sectionState as ExamPart).exercises, ...newExercises]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "levelGenerating",
|
||||
value: levelGenerating?.filter(g => !g?.startsWith("exercises"))
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "levelGenResults",
|
||||
value: levelGenResults.filter(res => !res.generating.startsWith("exercises"))
|
||||
}
|
||||
}
|
||||
] as Action[];
|
||||
|
||||
updates.forEach(update => dispatch(update));
|
||||
}
|
||||
}, [levelGenResults, sectionState, levelGenerating, sectionId, currentModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (levelGenResults && levelGenResults.some(res => res.generating === "writing_letter" || res.generating === "writing_2")) {
|
||||
const results = levelGenResults.filter(res => res.generating === "writing_letter" || res.generating === "writing_2");
|
||||
|
||||
const updates = [
|
||||
{
|
||||
type: "UPDATE_SECTION_STATE",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: "level",
|
||||
update: {
|
||||
exercises: [...(sectionState as ExamPart).exercises,
|
||||
...results.map((res)=> {
|
||||
return {
|
||||
...writingTask(res.generating === "writing_letter" ? 1 : 2),
|
||||
prompt: res.result[0].prompt,
|
||||
variant: res.generating === "writing_letter" ? "letter" : "essay"
|
||||
} as WritingExercise;
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "levelGenerating",
|
||||
value: levelGenerating?.filter(g => !results.flatMap(res => res.generating as Generating).includes(g))
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "UPDATE_SECTION_SINGLE_FIELD",
|
||||
payload: {
|
||||
sectionId,
|
||||
module: currentModule,
|
||||
field: "levelGenResults",
|
||||
value: levelGenResults.filter(res => !results.flatMap(res => res.generating as Generating).includes(res.generating))
|
||||
}
|
||||
}
|
||||
] as Action[];
|
||||
|
||||
updates.forEach(update => dispatch(update));
|
||||
}
|
||||
}, [levelGenResults, sectionState, levelGenerating, sectionId, currentModule]);
|
||||
|
||||
|
||||
const currentSection = sections.find((s) => s.sectionId === sectionId)!;
|
||||
|
||||
const sensors = useSensors(
|
||||
@@ -62,42 +162,12 @@ const SectionExercises: React.FC<{ sectionId: number; }> = ({ sectionId }) => {
|
||||
);
|
||||
|
||||
const questionItems = (): QuestionItemsResult => {
|
||||
let result: QuestionItemsResult = {
|
||||
ids: [],
|
||||
items: []
|
||||
};
|
||||
|
||||
switch (currentModule) {
|
||||
case "reading": {
|
||||
const items = getReadingQuestions(
|
||||
(currentSection.state as ReadingPart).exercises as ReadingExercise[],
|
||||
sectionId
|
||||
);
|
||||
result.items = items.filter((item): item is ExerciseItem => item !== undefined);
|
||||
result.ids = result.items.map(item => item.id);
|
||||
break;
|
||||
}
|
||||
case "listening": {
|
||||
const items = getListeningItems(
|
||||
(currentSection.state as ListeningPart).exercises as Exercise[],
|
||||
sectionId
|
||||
);
|
||||
result.items = items.filter((item): item is ExerciseItem => item !== undefined);
|
||||
result.ids = result.items.map(item => item.id);
|
||||
break;
|
||||
}
|
||||
case "level": {
|
||||
const items = getLevelQuestionItems(
|
||||
(currentSection.state as LevelPart).exercises as Exercise[],
|
||||
sectionId
|
||||
);
|
||||
result.items = items.filter((item): item is ExerciseItem => item !== undefined);
|
||||
result.ids = result.items.map(item => item.id);
|
||||
break;
|
||||
}
|
||||
const part = currentSection.state as ReadingPart | ListeningPart | LevelPart;
|
||||
const items = getExerciseItems(part.exercises, sectionId);
|
||||
return {
|
||||
items,
|
||||
ids: items.map(item => item.id)
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const background = (component: ReactNode) => {
|
||||
@@ -108,33 +178,33 @@ const SectionExercises: React.FC<{ sectionId: number; }> = ({ sectionId }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (currentModule == "writing") return background(<Writing sectionId={sectionId} exercise={currentSection.state as WritingExercise} />);
|
||||
if (currentModule == "writing") return background(<Writing sectionId={sectionId} exercise={currentSection.state as WritingExercise} module="writing" />);
|
||||
if (currentModule == "speaking") return background(<Speaking sectionId={sectionId} exercise={currentSection.state as SpeakingExercise} />);
|
||||
|
||||
const questions = questionItems();
|
||||
|
||||
// #############################################################################
|
||||
// Typescript checks so that the compiler and builder don't freak out
|
||||
const filteredIds = (questions.ids ?? []).filter(Boolean);
|
||||
|
||||
function isValidItem(item: ExerciseItem | undefined): item is ExerciseItem {
|
||||
return item !== undefined &&
|
||||
typeof item.id === 'string' &&
|
||||
typeof item.sectionId === 'number' &&
|
||||
React.isValidElement(item.label) &&
|
||||
return item !== undefined &&
|
||||
typeof item.id === 'string' &&
|
||||
typeof item.sectionId === 'number' &&
|
||||
React.isValidElement(item.label) &&
|
||||
React.isValidElement(item.content);
|
||||
}
|
||||
|
||||
const filteredItems = (questions.items ?? []).filter(isValidItem);
|
||||
// #############################################################################
|
||||
|
||||
console.log(levelGenerating);
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={(e) => dispatch({ type: "REORDER_EXERCISES", payload: { event: e, sectionId } })}
|
||||
onDragEnd={(e) => dispatch({ type: "REORDER_EXERCISES", payload: { event: e, sectionId, module: currentModule } })}
|
||||
>
|
||||
{(currentModule === "level" && questions.ids?.length === 0 && generating === undefined) ? (
|
||||
background(<span className="flex justify-center">Generated exercises will appear here!</span>)
|
||||
) : (
|
||||
expandedSections.includes(sectionId) &&
|
||||
{expandedSections.includes(sectionId) &&
|
||||
questions.items &&
|
||||
questions.items.length > 0 &&
|
||||
questions.ids &&
|
||||
@@ -160,9 +230,16 @@ const SectionExercises: React.FC<{ sectionId: number; }> = ({ sectionId }) => {
|
||||
</SortableContext>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
}
|
||||
{generating === "exercises" && <GenLoader module={currentModule} className="mt-4" />}
|
||||
{currentModule === "level" && (
|
||||
<>
|
||||
{
|
||||
questions.ids?.length === 0 && !levelGenerating?.some((g) => g?.startsWith("exercises") || g?.startsWith("writing")) && generating !== "exercises"
|
||||
&& background(<span className="flex justify-center">Generated exercises will appear here!</span>)}
|
||||
{levelGenerating?.some((g) => g?.startsWith("exercises") || g?.startsWith("writing")) && <GenLoader module={currentModule} className="mt-4" />}
|
||||
</>)
|
||||
}
|
||||
</DndContext >
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user