>
)}
{/* Step Tracker */}
{currentStep !== 5 && (
{STEPS.map((step, index) => (
step.number
? "bg-brand-500 text-white"
: "border-2 border-grayScale-300 bg-white text-grayScale-400"
}`}
>
{currentStep > step.number ? : step.number}
step.number
? "text-brand-500"
: "text-grayScale-400"
}`}
>
{step.label}
{index < STEPS.length - 1 && (
step.number ? "bg-brand-500" : "bg-grayScale-200"
}`}
/>
)}
))}
)}
{/* Step Content */}
{currentStep === 1 && (
Step 1: Context
Define details and rules for this practice. Curriculum context is shown on the right.
setPracticeTitle(e.target.value)}
placeholder="Enter practice title"
className="h-11"
/>
setIntroVideoUrl(e.target.value)}
onBlur={() => void handleImportIntroVideoFromUrl()}
placeholder="https://…"
type="url"
inputMode="url"
autoComplete="off"
className="h-11 font-mono text-[13px]"
/>
{introVideoUrl.trim() ? (
) : null}
{introVideoPreview ? (
Preview
{introVideoPreview.kind === "vimeo" ? (
) : (
)}
) : null}
Paste a link or upload from your computer; uploads go through the file service (optional, not tied to sub-course video rows).
)}
{currentStep === 2 && (
Step 2: Persona
Choose the character students will interact with in this practice.
{PERSONAS.map((persona) => (
))}
)}
{currentStep === 3 && (
Step 3: Questions
Add MCQ, True/False, Short Answer, or Audio items. Use the full width for stems and options.
{questions.map((question, index) => (
Question {index + 1}
{
updateQuestion(question.id, {
questionText: next.questionText,
questionType: next.questionType as QuestionType,
difficultyLevel: next.difficultyLevel as DifficultyLevel,
points: next.points,
tips: next.tips,
explanation: next.explanation,
options: next.options,
voicePrompt: next.voicePrompt,
sampleAnswerVoicePrompt: next.sampleAnswerVoicePrompt,
audioCorrectAnswerText: next.audioCorrectAnswerText,
shortAnswers: next.shortAnswer.trim() ? [next.shortAnswer.trim()] : [],
imageUrl: next.imageUrl,
})
}}
mediaBusy={saving}
/>
))}
)}
{currentStep === 4 && (
Step 4: Review & publish
Confirm context, persona, and questions before saving or publishing.
{/* Basic Information Card */}
Basic Information
Title
{practiceTitle || "Untitled Practice"}
Description
{descriptionPreviewHtml ? (
) : (
—
)}
Intro video URL
{introVideoUrl.trim() || "—"}
{introVideoPreview ? (
Intro video preview
{introVideoPreview.kind === "vimeo" ? (
) : (
)}
) : null}
Passing Score
{passingScore}%
Time Limit
{timeLimitMinutes} minutes
Shuffle Questions
{shuffleQuestions ? "Yes" : "No"}
Persona
{selectedPersona && (

p.id === selectedPersona)?.avatar}
alt="Persona"
className="h-full w-full object-cover"
/>
)}
{PERSONAS.find(p => p.id === selectedPersona)?.name || "None selected"}
{/* Questions Review */}
Questions
{questions.length}
{questions.map((question, index) => (
{index + 1}
{question.questionText}
{question.questionType === "MCQ"
? "Multiple Choice"
: question.questionType === "TRUE_FALSE"
? "True/False"
: question.questionType === "AUDIO"
? "Audio"
: "Short Answer"}
{question.difficultyLevel}
{question.points} pt{question.points !== 1 ? "s" : ""}
{question.questionType === "MCQ" && question.options.length > 0 && (
{question.options.map((opt, i) => (
{opt.isCorrect && }
{opt.text || `Option ${i + 1}`}
))}
)}
{question.tips && (
💡 Tip: {question.tips}
)}
{question.explanation && (
Explanation: {question.explanation}
)}
))}
{saveError && (
)}
)}
{/* Step 5: Result */}
{currentStep === 5 && resultStatus && (
{resultStatus === "success" ? (
<>
Practice Published Successfully!
{resultMessage}
>
) : (
<>
Publish Error!
{resultMessage}
>
)}
)}