import { Trash2, Plus, ArrowRight } from "lucide-react"; import { Button } from "../../../../components/ui/button"; import { Card } from "../../../../components/ui/card"; import { Input } from "../../../../components/ui/input"; import { DynamicSchemaSlotField } from "../../../../components/content-management/DynamicSchemaSlotField"; import type { QuestionTypeDefinition } from "../../../../types/questionTypeDefinition.types"; import { questionTypeDefinitionListLabel } from "../../../../api/questionTypeDefinitions.api"; import { definitionUsesDynamicPayload, emptyDynamicFieldValuesForDefinition, legacyQuestionTypeFromDefinition, } from "../../../../lib/learnEnglishDefinitionQuestion"; function defaultMcqOptions() { return [ { text: "", isCorrect: true }, { text: "", isCorrect: false }, { text: "", isCorrect: false }, { text: "", isCorrect: false }, ]; } function createEmptyQuestionRow(id: string) { return { id, questionTypeDefinitionId: null as number | null, text: "", dynamicFieldValues: {} as Record, mcqOptions: defaultMcqOptions(), trueFalseCorrect: true, shortAnswers: [""], }; } interface QuestionsStepProps { formData: any; setFormData: (data: any) => void; nextStep: () => void; prevStep: () => void; typeDefinitions: QuestionTypeDefinition[]; definitionsLoading: boolean; definitionsError: string | null; } export function QuestionsStep({ formData, setFormData, nextStep, prevStep, typeDefinitions, definitionsLoading, definitionsError, }: QuestionsStepProps) { const applyDefinitionToQuestion = ( index: number, definitionId: number, defs: QuestionTypeDefinition[], ) => { const def = defs.find((d) => d.id === definitionId); const newQuestions = [...formData.questions]; const row = { ...newQuestions[index], questionTypeDefinitionId: definitionId }; if (def) { row.dynamicFieldValues = emptyDynamicFieldValuesForDefinition(def); } newQuestions[index] = row; setFormData({ ...formData, questions: newQuestions }); }; const setDynamicValue = (qIndex: number, key: string, value: string) => { const newQuestions = [...formData.questions]; newQuestions[qIndex] = { ...newQuestions[qIndex], dynamicFieldValues: { ...(newQuestions[qIndex].dynamicFieldValues ?? {}), [key]: value, }, }; setFormData({ ...formData, questions: newQuestions }); }; const addQuestion = () => { const id = `q${Date.now()}`; const row = createEmptyQuestionRow(id); if (typeDefinitions[0]) { row.questionTypeDefinitionId = typeDefinitions[0].id; row.dynamicFieldValues = emptyDynamicFieldValuesForDefinition( typeDefinitions[0], ); } setFormData({ ...formData, questions: [...formData.questions, row], }); }; const renderTypeSpecificFields = (q: any, i: number, def: QuestionTypeDefinition) => { if (definitionUsesDynamicPayload(def)) { return (

Image / Audio slots use upload or URL import ( POST /files/upload ). Others: URL, text, or JSON.

{def.stimulus_schema.length > 0 ? (

Stimulus

{def.stimulus_schema.map((row) => (
setDynamicValue(i, `stimulus:${row.id}`, next) } />
))}
) : null} {def.response_schema.length > 0 ? (

Response

{def.response_schema.map((row) => (
setDynamicValue(i, `response:${row.id}`, next) } />
))}
) : null}
); } const legacy = legacyQuestionTypeFromDefinition(def); if (legacy === "MCQ") { return (
{(q.mcqOptions ?? defaultMcqOptions()).map( (opt: { text: string; isCorrect: boolean }, j: number) => (
{ const newQuestions = [...formData.questions]; const opts = [ ...(newQuestions[i].mcqOptions ?? defaultMcqOptions()), ]; opts[j] = { ...opts[j], text: e.target.value }; newQuestions[i].mcqOptions = opts; setFormData({ ...formData, questions: newQuestions }); }} className="min-w-0 flex-1 rounded-lg border-grayScale-200" placeholder={`Option ${j + 1}`} />
), )}
); } if (legacy === "TRUE_FALSE") { return (
Correct answer
); } if (legacy === "SHORT_ANSWER") { return (
{(q.shortAnswers ?? [""]).map((line: string, j: number) => (
{ const newQuestions = [...formData.questions]; const lines = [...(newQuestions[i].shortAnswers ?? [""])]; lines[j] = e.target.value; newQuestions[i].shortAnswers = lines; setFormData({ ...formData, questions: newQuestions }); }} className="rounded-lg border-grayScale-200" placeholder="Acceptable wording" />
))}
); } return (

This definition has no schema rows and is not mapped to MCQ / True‑False / Short answer. It will be submitted as{" "} DYNAMIC with an empty payload.

); }; return (

Questions

Question types are loaded from{" "} GET /questions/type-definitions . Pick a type per row, then fill the fields required for that definition.

{definitionsError ? (
{definitionsError}
) : null} {definitionsLoading ? (

Loading question types…

) : null}
{formData.questions.map((q: any, i: number) => { const def = typeDefinitions.find( (d) => d.id === q.questionTypeDefinitionId, ); return (
Question {i + 1}
{def?.description ? (

{def.description}

) : null}
{ const newQuestions = [...formData.questions]; newQuestions[i].text = e.target.value; setFormData({ ...formData, questions: newQuestions }); }} className="min-h-[52px] rounded-xl border-grayScale-200 px-4 py-3 text-base font-medium text-grayScale-700" placeholder="Question prompt for learners" />
{def ? renderTypeSpecificFields(q, i, def) : null}
); })}
); }