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:
Carlos-Mesquita
2024-11-12 14:17:54 +00:00
parent 696c968ebc
commit fdf411d133
66 changed files with 2546 additions and 1635 deletions

View File

@@ -0,0 +1,92 @@
import { Exercise } from "@/interfaces/exam";
import ExerciseItem, { isExerciseItem } from "./types";
import MultipleChoice from "../../Exercises/MultipleChoice";
import ExerciseLabel from "../../Shared/ExerciseLabel";
import writeBlanks from "./writeBlanks";
import TrueFalse from "../../Exercises/TrueFalse";
import fillBlanks from "./fillBlanks";
import MatchSentences from "../../Exercises/MatchSentences";
import Writing from "../../Exercises/Writing";
const getExerciseItems = (exercises: Exercise[], sectionId: number): ExerciseItem[] => {
const items: ExerciseItem[] = exercises.map((exercise, index) => {
let firstQuestionId, lastQuestionId;
switch (exercise.type) {
case "multipleChoice":
firstQuestionId = exercise.questions[0].id;
lastQuestionId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Multiple Choice Questions'
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <MultipleChoice exercise={exercise} sectionId={sectionId} />
};
case "trueFalse":
firstQuestionId = exercise.questions[0].id
lastQuestionId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='True/False/Not Given'
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <TrueFalse exercise={exercise} sectionId={sectionId} />
};
case "matchSentences":
firstQuestionId = exercise.sentences[0].id;
lastQuestionId = exercise.sentences[exercise.sentences.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type={exercise.variant == "ideaMatch" ? "Idea Match" : "Paragraph Match"}
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <MatchSentences exercise={exercise} sectionId={sectionId} />
};
case "fillBlanks":
return fillBlanks(exercise, index, sectionId);
case "writeBlanks":
return writeBlanks(exercise, index, sectionId);
case "writing":
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type={`Writing Task: ${exercise.variant === "letter" ? "Letter" : "Essay"}`}
firstId={exercise.sectionId!.toString()}
lastId={exercise.sectionId!.toString()}
prompt={exercise.prompt}
/>
),
content: <Writing key={exercise.id} exercise={exercise} sectionId={sectionId} index={index} module="level" />
};
default:
return {} as unknown as ExerciseItem;
}
}).filter(isExerciseItem);
/*return mappedItems.filter((item): item is ExerciseItem =>
item !== null && isExerciseItem(item)
);*/
return items;
};
export default getExerciseItems;

View File

@@ -0,0 +1,78 @@
import { FillBlanksExercise, FillBlanksMCOption } from "@/interfaces/exam";
import ExerciseItem from "./types";
import ExerciseLabel from "../../Shared/ExerciseLabel";
import FillBlanksLetters from "../../Exercises/Blanks/Letters";
import FillBlanksMC from "../../Exercises/Blanks/MultipleChoice";
interface LetterWord {
letter: string;
word: string;
}
function isLetterWordArray(words: (string | LetterWord | FillBlanksMCOption)[]): words is LetterWord[] {
return words.length > 0 &&
words.every(item =>
typeof item === 'object' &&
'letter' in item &&
'word' in item &&
!('options' in item)
);
}
function isFillBlanksMCOptionArray(words: (string | LetterWord | FillBlanksMCOption)[]): words is FillBlanksMCOption[] {
return words.length > 0 &&
words.every(item =>
typeof item === 'object' &&
'id' in item &&
'options' in item &&
typeof (item as FillBlanksMCOption).options === 'object' &&
'A' in (item as FillBlanksMCOption).options &&
'B' in (item as FillBlanksMCOption).options &&
'C' in (item as FillBlanksMCOption).options &&
'D' in (item as FillBlanksMCOption).options
);
}
const fillBlanks = (exercise: FillBlanksExercise, index: number, sectionId: number): ExerciseItem => {
const firstWordId = exercise.solutions[0].id;
const lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
if (isLetterWordArray(exercise.words)) {
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Fill Blanks Question'
firstId={firstWordId}
lastId={lastWordId}
prompt={exercise.prompt}
/>
),
content: <FillBlanksLetters exercise={exercise} sectionId={sectionId} />
};
}
if (isFillBlanksMCOptionArray(exercise.words)) {
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Fill Blanks: MC Question'
firstId={firstWordId}
lastId={lastWordId}
prompt={exercise.prompt}
/>
),
content: <FillBlanksMC exercise={exercise} sectionId={sectionId} />
};
}
// Don't know where the fillBlanks with words as string fits
throw new Error(`Unsupported Exercise`);
}
export default fillBlanks;

View File

@@ -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 >
);
}

View File

@@ -1,75 +0,0 @@
import { Exercise } from "@/interfaces/exam";
import ExerciseItem, { isExerciseItem } from "./types";
import ExerciseLabel from "../../Shared/ExerciseLabel";
import MultipleChoice from "../../Exercises/MultipleChoice";
import FillBlanksMC from "../../Exercises/Blanks/MultipleChoice";
import Passage from "../../Shared/Passage";
const getLevelQuestionItems = (exercises: Exercise[], sectionId: number): ExerciseItem[] => {
const previewLabel = (text: string) => {
return text !== undefined ? text.replaceAll('\\n', ' ').split(' ').slice(0, 15).join(' ') : "";
}
const items: ExerciseItem[] = exercises.map((exercise, index) => {
let firstWordId, lastWordId;
switch (exercise.type) {
case "multipleChoice":
let content = <MultipleChoice exercise={exercise} sectionId={sectionId} />;
const isReadingPassage = exercise.mcVariant && exercise.mcVariant === "passageUtas";
if (isReadingPassage) {
content = (<>
<div className="p-4">
<Passage
title={exercise.passage?.title!}
content={exercise.passage?.content!}
/>
</div>
<MultipleChoice exercise={exercise} sectionId={sectionId} /></>
);
}
firstWordId = exercise.questions[0].id;
lastWordId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={isReadingPassage ? `Reading Passage: MC Questions #${firstWordId} - #${lastWordId}` : `Multiple Choice Questions #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content
};
case "fillBlanks":
firstWordId = exercise.solutions[0].id;
lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Fill Blanks: MC Question #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <FillBlanksMC exercise={exercise} sectionId={sectionId} />
};
default:
return {} as unknown as ExerciseItem;
}
}).filter(isExerciseItem);
return items;
};
export default getLevelQuestionItems;

View File

@@ -1,127 +0,0 @@
import ExerciseItem, { isExerciseItem } from './types';
import ExerciseLabel from '../../Shared/ExerciseLabel';
import FillBlanksLetters from '../../Exercises/Blanks/Letters';
import { Exercise, WriteBlanksExercise } from '@/interfaces/exam';
import MultipleChoice from '../../Exercises/MultipleChoice';
import WriteBlanksForm from '../../Exercises/WriteBlanksForm';
import WriteBlanksFill from '../../Exercises/Blanks/WriteBlankFill';
import WriteBlanks from '../../Exercises/WriteBlanks';
const writeBlanks = (exercise: WriteBlanksExercise, index: number, sectionId: number, previewLabel: (text: string) => string): ExerciseItem => {
const firstWordId = exercise.solutions[0].id;
const lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
switch (exercise.variant) {
case 'form':
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Write Blanks: Form #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <WriteBlanksForm exercise={exercise} sectionId={sectionId} />
};
case 'fill':
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Write Blanks: Fill #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <WriteBlanksFill exercise={exercise} sectionId={sectionId} />
};
case 'questions':
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Write Blanks: Questions #${firstWordId} ${firstWordId === lastWordId ? '' : `- #${lastWordId}`}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <WriteBlanks exercise={exercise} sectionId={sectionId} title='Write Blanks: Questions' />
};
}
throw new Error(`Just so that typescript doesnt complain`);
};
const getListeningItems = (exercises: Exercise[], sectionId: number): ExerciseItem[] => {
const previewLabel = (text: string) => {
return text !== undefined ? text.replaceAll('\\n', ' ').split(' ').slice(0, 15).join(' ') : "";
};
const mappedItems = exercises.map((exercise, index): ExerciseItem | null => {
let firstWordId, lastWordId;
switch (exercise.type) {
case "fillBlanks":
firstWordId = exercise.solutions[0].id;
lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Fill Blanks Question #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <FillBlanksLetters exercise={exercise} sectionId={sectionId} />
};
case "writeBlanks":
return writeBlanks(exercise, index, sectionId, previewLabel);
case "multipleChoice":
firstWordId = exercise.questions[0].id;
lastWordId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Multiple Choice Questions #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <MultipleChoice exercise={exercise} sectionId={sectionId} />
};
default:
return null;
}
});
return mappedItems.filter((item): item is ExerciseItem =>
item !== null && isExerciseItem(item)
);
};
export default getListeningItems;

View File

@@ -1,117 +0,0 @@
import ExerciseItem, { isExerciseItem, ReadingExercise } from './types';
import WriteBlanks from "@/editor/Exercises/WriteBlanks";
import ExerciseLabel from '../../Shared/ExerciseLabel';
import MatchSentences from '../../Exercises/MatchSentences';
import TrueFalse from '../../Exercises/TrueFalse';
import FillBlanksLetters from '../../Exercises/Blanks/Letters';
import MultipleChoice from '../../Exercises/MultipleChoice';
const getExerciseItems = (exercises: ReadingExercise[], sectionId: number): ExerciseItem[] => {
const previewLabel = (text: string) => {
return text !== undefined ? text.replaceAll('\\n', ' ').split(' ').slice(0, 15).join(' ') : ""
}
const items: ExerciseItem[] = exercises.map((exercise, index) => {
let firstWordId, lastWordId;
switch (exercise.type) {
case "fillBlanks":
firstWordId = exercise.solutions[0].id;
lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Fill Blanks Question #${firstWordId} ${firstWordId === lastWordId ? '' : `- #${lastWordId}`}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <FillBlanksLetters exercise={exercise} sectionId={sectionId} />
};
case "writeBlanks":
firstWordId = exercise.solutions[0].id;
lastWordId = exercise.solutions[exercise.solutions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Write Blanks Question #${firstWordId} ${firstWordId === lastWordId ? '' : `- #${lastWordId}`}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <WriteBlanks exercise={exercise} sectionId={sectionId} />
};
case "matchSentences":
firstWordId = exercise.sentences[0].id;
lastWordId = exercise.sentences[exercise.sentences.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`${exercise.variant == "ideaMatch" ? "Idea Match" : "Paragraph Match"} #${firstWordId} ${firstWordId === lastWordId ? '' : `- #${lastWordId}`}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <MatchSentences exercise={exercise} sectionId={sectionId}/>
};
case "trueFalse":
firstWordId = exercise.questions[0].id
lastWordId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`True/False/Not Given #${firstWordId} ${firstWordId === lastWordId ? '' : `- #${lastWordId}`}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <TrueFalse exercise={exercise} sectionId={sectionId} />
};
case "multipleChoice":
firstWordId = exercise.questions[0].id;
lastWordId = exercise.questions[exercise.questions.length - 1].id;
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
label={`Multiple Choice Questions #${firstWordId} - #${lastWordId}`}
preview={
<>
&quot;{previewLabel(exercise.prompt)}...&quot;
</>
}
/>
),
content: <MultipleChoice exercise={exercise} sectionId={sectionId} />
};
}
}).filter(isExerciseItem);
return items;
};
export default getExerciseItems;

View File

@@ -1,5 +1,3 @@
import { FillBlanksExercise, MatchSentencesExercise, MultipleChoiceExercise, TrueFalseExercise, WriteBlanksExercise } from "@/interfaces/exam";
export default interface ExerciseItem {
id: string;
sectionId: number;
@@ -7,8 +5,6 @@ export default interface ExerciseItem {
content: React.ReactNode;
}
export type ReadingExercise = FillBlanksExercise | TrueFalseExercise | MatchSentencesExercise | WriteBlanksExercise | MultipleChoiceExercise;
export function isExerciseItem(item: unknown): item is ExerciseItem {
return item !== undefined &&
item !== null &&

View File

@@ -0,0 +1,58 @@
import { WriteBlanksExercise } from "@/interfaces/exam";
import ExerciseLabel from "../../Shared/ExerciseLabel";
import WriteBlanksForm from "../../Exercises/WriteBlanksForm";
import WriteBlanksFill from "../../Exercises/Blanks/WriteBlankFill";
import WriteBlanks from "../../Exercises/WriteBlanks";
import ExerciseItem from "./types";
const writeBlanks = (exercise: WriteBlanksExercise, index: number, sectionId: number): ExerciseItem => {
const firstQuestionId = exercise.solutions[0].id;
const lastQuestionId = exercise.solutions[exercise.solutions.length - 1].id;
switch (exercise.variant) {
case 'form':
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Write Blanks: Form'
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <WriteBlanksForm exercise={exercise} sectionId={sectionId} />
};
case 'fill':
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Write Blanks: Fill'
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <WriteBlanksFill exercise={exercise} sectionId={sectionId} />
};
default:
return {
id: index.toString(),
sectionId,
label: (
<ExerciseLabel
type='Write Blanks: Questions'
firstId={firstQuestionId}
lastId={lastQuestionId}
prompt={exercise.prompt}
/>
),
content: <WriteBlanks exercise={exercise} sectionId={sectionId} />
};
}
};
export default writeBlanks;