import { useState } from "react" import { Link, useParams, useNavigate } from "react-router-dom" import { ArrowLeft, ArrowRight, ChevronDown, Grid3X3, Check, Plus, Trash2, GripVertical, X, Edit, Rocket } from "lucide-react" import { Card } from "../../components/ui/card" import { Button } from "../../components/ui/button" import { Input } from "../../components/ui/input" import { createQuestionSet, createQuestion, addQuestionToSet } from "../../api/courses.api" import { Select } from "../../components/ui/select" import type { QuestionOption } from "../../types/course.types" type Step = 1 | 2 | 3 | 4 | 5 type ResultStatus = "success" | "error" type QuestionType = "MCQ" | "TRUE_FALSE" | "SHORT" type DifficultyLevel = "EASY" | "MEDIUM" | "HARD" interface Persona { id: string name: string avatar: string } interface MCQOption { text: string isCorrect: boolean } interface Question { id: string questionText: string questionType: QuestionType difficultyLevel: DifficultyLevel points: number tips: string explanation: string options: MCQOption[] voicePrompt: string sampleAnswerVoicePrompt: string shortAnswers: string[] } const PERSONAS: Persona[] = [ { id: "1", name: "Dawit", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Dawit" }, { id: "2", name: "Mahlet", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Mahlet" }, { id: "3", name: "Amanuel", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Amanuel" }, { id: "4", name: "Bethel", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Bethel" }, { id: "5", name: "Liya", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Liya" }, { id: "6", name: "Aseffa", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Aseffa" }, { id: "7", name: "Hana", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Hana" }, { id: "8", name: "Nahom", avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Nahom" }, ] const STEPS = [ { number: 1, label: "Context" }, { number: 2, label: "Persona" }, { number: 3, label: "Questions" }, { number: 4, label: "Review" }, ] function createEmptyQuestion(id: string): Question { return { id, questionText: "", questionType: "MCQ", difficultyLevel: "EASY", points: 1, tips: "", explanation: "", options: [ { text: "", isCorrect: true }, { text: "", isCorrect: false }, { text: "", isCorrect: false }, { text: "", isCorrect: false }, ], voicePrompt: "", sampleAnswerVoicePrompt: "", shortAnswers: [], } } export function AddNewPracticePage() { const { categoryId, courseId, subCourseId } = useParams() const navigate = useNavigate() const [currentStep, setCurrentStep] = useState(1) const [saving, setSaving] = useState(false) // Step 1: Context const [selectedProgram] = useState("Intermediate") const [selectedCourse] = useState("B2") const [practiceTitle, setPracticeTitle] = useState("") const [practiceDescription, setPracticeDescription] = useState("") const [shuffleQuestions, setShuffleQuestions] = useState(false) const [passingScore, setPassingScore] = useState(50) const [timeLimitMinutes, setTimeLimitMinutes] = useState(60) const [saveError, setSaveError] = useState(null) const [resultStatus, setResultStatus] = useState(null) const [resultMessage, setResultMessage] = useState("") // Step 2: Persona const [selectedPersona, setSelectedPersona] = useState(null) // Step 3: Questions const [questions, setQuestions] = useState([ createEmptyQuestion("1"), ]) const handleNext = () => { if (currentStep < 4) { setCurrentStep((currentStep + 1) as Step) } } const handleBack = () => { if (currentStep > 1) { setCurrentStep((currentStep - 1) as Step) } } const handleCancel = () => { navigate(`/content/category/${categoryId}/courses/${courseId}/sub-courses/${subCourseId}`) } const addQuestion = () => { setQuestions([...questions, createEmptyQuestion(String(Date.now()))]) } const removeQuestion = (id: string) => { if (questions.length > 1) { setQuestions(questions.filter(q => q.id !== id)) } } const updateQuestion = (id: string, updates: Partial) => { setQuestions(questions.map(q => q.id === id ? { ...q, ...updates } : q)) } const updateOption = (questionId: string, optionIndex: number, updates: Partial) => { setQuestions(questions.map(q => { if (q.id !== questionId) return q const newOptions = q.options.map((opt, i) => i === optionIndex ? { ...opt, ...updates } : opt) return { ...q, options: newOptions } })) } const addOption = (questionId: string) => { setQuestions(questions.map(q => { if (q.id !== questionId) return q return { ...q, options: [...q.options, { text: "", isCorrect: false }] } })) } const removeOption = (questionId: string, optionIndex: number) => { setQuestions(questions.map(q => { if (q.id !== questionId) return q return { ...q, options: q.options.filter((_, i) => i !== optionIndex) } })) } const setCorrectOption = (questionId: string, optionIndex: number) => { setQuestions(questions.map(q => { if (q.id !== questionId) return q return { ...q, options: q.options.map((opt, i) => ({ ...opt, isCorrect: i === optionIndex })) } })) } const saveQuestionSet = async (status: "DRAFT" | "PUBLISHED") => { setSaving(true) setSaveError(null) try { const persona = PERSONAS.find(p => p.id === selectedPersona) const setRes = await createQuestionSet({ title: practiceTitle || "Untitled Practice", description: practiceDescription, set_type: "PRACTICE", owner_type: "SUB_COURSE", owner_id: Number(subCourseId), persona: persona?.name, shuffle_questions: shuffleQuestions, status, passing_score: passingScore, time_limit_minutes: timeLimitMinutes, }) const questionSetId = setRes.data?.data?.id if (questionSetId) { for (let i = 0; i < questions.length; i++) { const q = questions[i] if (!q.questionText.trim()) continue const options: QuestionOption[] = q.questionType === "MCQ" ? q.options.map((opt, idx) => ({ option_order: idx + 1, option_text: opt.text, is_correct: opt.isCorrect, })) : [] const qRes = await createQuestion({ question_text: q.questionText, question_type: q.questionType, difficulty_level: q.difficultyLevel, points: q.points, tips: q.tips || undefined, explanation: q.explanation || undefined, status: "PUBLISHED", options: options.length > 0 ? options : undefined, voice_prompt: q.voicePrompt || undefined, sample_answer_voice_prompt: q.sampleAnswerVoicePrompt || undefined, short_answers: q.shortAnswers.length > 0 ? q.shortAnswers : undefined, }) const questionId = qRes.data?.data?.id if (questionId) { await addQuestionToSet(questionSetId, { display_order: i + 1, question_id: questionId, }) } } } setResultStatus("success") setResultMessage( status === "PUBLISHED" ? "Your speaking practice is now active." : "Your practice has been saved as a draft." ) setCurrentStep(5) } catch (err: unknown) { console.error("Failed to save practice:", err) const errorMsg = err instanceof Error ? err.message : "An unexpected error occurred." setResultStatus("error") setResultMessage(errorMsg) setCurrentStep(5) } finally { setSaving(false) } } const handleSaveAsDraft = () => saveQuestionSet("DRAFT") const handlePublish = () => saveQuestionSet("PUBLISHED") const getNextButtonLabel = () => { switch (currentStep) { case 1: return "Next: Persona" case 2: return "Next: Questions" case 3: return "Next: Review" default: return "Next" } } return (
{currentStep !== 5 && ( <> {/* Back Link */} Back to Sub-course {/* Header */}

Add New Practice

Create a new immersive practice session for students.

)} {/* Step Tracker */} {currentStep !== 5 && (
{STEPS.map((step, index) => (
step.number ? "bg-brand-500 text-white" : "border-2 border-grayScale-300 text-grayScale-400" }`} > {currentStep > step.number ? : step.number}
{step.label}
{index < STEPS.length - 1 && (
step.number ? "bg-brand-500" : "bg-grayScale-200" }`} /> )}
))}
)} {/* Step Content */} {currentStep === 1 && (

Step 1: Context Definition

Define the educational level and curriculum module for this practice.

{/* Practice Title */}
setPracticeTitle(e.target.value)} placeholder="Enter practice title" />
{/* Practice Description */}