import { useEffect, useState } from "react" import { useNavigate, useParams } from "react-router-dom" import { ArrowLeft, Plus, X } from "lucide-react" import { toast } from "sonner" import { Button } from "../../components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card" import { Input } from "../../components/ui/input" import { Textarea } from "../../components/ui/textarea" import { Select } from "../../components/ui/select" import { createQuestion, getQuestionById, updateQuestion } from "../../api/courses.api" type QuestionType = "MCQ" | "TRUE_FALSE" | "SHORT_ANSWER" | "AUDIO" type Difficulty = "EASY" | "MEDIUM" | "HARD" type QuestionStatus = "DRAFT" | "PUBLISHED" | "INACTIVE" interface Question { id?: number question: string type: QuestionType options: string[] correctAnswer: string points: number difficulty: Difficulty status: QuestionStatus tips: string explanation: string voicePrompt: string sampleAnswerVoicePrompt: string audioCorrectAnswerText: string } const initialForm: Question = { question: "", type: "MCQ", options: ["", "", "", ""], correctAnswer: "", points: 1, difficulty: "EASY", status: "PUBLISHED", tips: "", explanation: "", voicePrompt: "", sampleAnswerVoicePrompt: "", audioCorrectAnswerText: "", } export function AddQuestionPage() { const navigate = useNavigate() const { id } = useParams<{ id?: string }>() const isEditing = !!id const [formData, setFormData] = useState(initialForm) const [loading, setLoading] = useState(false) const [submitting, setSubmitting] = useState(false) useEffect(() => { const loadQuestion = async () => { if (!isEditing || !id) return setLoading(true) try { const res = await getQuestionById(Number(id)) const q = res.data.data const mappedType: QuestionType = q.question_type === "MCQ" || q.question_type === "TRUE_FALSE" || q.question_type === "SHORT_ANSWER" || q.question_type === "AUDIO" ? q.question_type : "MCQ" const shortAnswer = Array.isArray(q.short_answers) && q.short_answers.length > 0 ? typeof q.short_answers[0] === "string" ? String(q.short_answers[0] || "") : String((q.short_answers[0] as { acceptable_answer?: string }).acceptable_answer || "") : "" setFormData({ id: q.id, question: q.question_text || "", type: mappedType, options: (q.options ?? []) .slice() .sort((a, b) => a.option_order - b.option_order) .map((o) => o.option_text) || ["", "", "", ""], correctAnswer: mappedType === "SHORT_ANSWER" ? shortAnswer : mappedType === "AUDIO" ? q.audio_correct_answer_text || "" : (q.options ?? []).find((o) => o.is_correct)?.option_text || "", points: q.points ?? 1, difficulty: q.difficulty_level === "EASY" || q.difficulty_level === "MEDIUM" || q.difficulty_level === "HARD" ? q.difficulty_level : "EASY", status: q.status === "DRAFT" || q.status === "PUBLISHED" || q.status === "INACTIVE" ? q.status : "PUBLISHED", tips: q.tips || "", explanation: q.explanation || "", voicePrompt: q.voice_prompt || "", sampleAnswerVoicePrompt: q.sample_answer_voice_prompt || "", audioCorrectAnswerText: q.audio_correct_answer_text || "", }) } catch (error) { console.error("Failed to load question:", error) toast.error("Failed to load question details") } finally { setLoading(false) } } loadQuestion() }, [isEditing, id]) const handleTypeChange = (type: QuestionType) => { setFormData((prev) => { if (type === "TRUE_FALSE") { return { ...prev, type, options: ["True", "False"], correctAnswer: prev.correctAnswer === "True" || prev.correctAnswer === "False" ? prev.correctAnswer : "", } } else if (type === "SHORT_ANSWER" || type === "AUDIO") { return { ...prev, type, options: [], correctAnswer: type === "AUDIO" ? prev.audioCorrectAnswerText : prev.correctAnswer, } } else { return { ...prev, type, options: prev.options.length > 0 ? prev.options : ["", "", "", ""], } } }) } const handleOptionChange = (index: number, value: string) => { setFormData((prev) => { const newOptions = [...prev.options] newOptions[index] = value return { ...prev, options: newOptions } }) } const addOption = () => { setFormData((prev) => ({ ...prev, options: [...prev.options, ""], })) } const removeOption = (index: number) => { setFormData((prev) => ({ ...prev, options: prev.options.filter((_, i) => i !== index), })) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() // Validation if (!formData.question.trim()) { toast.error("Missing question", { description: "Please enter a question before saving.", }) return } if (formData.type === "MCQ" || formData.type === "TRUE_FALSE") { if (!formData.correctAnswer) { toast.error("Missing correct answer", { description: "Select the correct answer for this question.", }) return } if (formData.type === "MCQ") { const hasEmptyOptions = formData.options.some((opt) => !opt.trim()) if (hasEmptyOptions) { toast.error("Incomplete options", { description: "Fill in all answer options for this multiple choice question.", }) return } } } else if (formData.type === "SHORT_ANSWER") { if (!formData.correctAnswer.trim()) { toast.error("Missing correct answer", { description: "Enter the expected correct answer.", }) return } } else if (formData.type === "AUDIO") { if (!formData.voicePrompt.trim() || !formData.sampleAnswerVoicePrompt.trim() || !formData.audioCorrectAnswerText.trim()) { toast.error("Missing audio fields", { description: "Voice prompt, sample answer voice prompt, and audio correct answer text are required for AUDIO questions.", }) return } } setSubmitting(true) try { const optionsPayload = formData.type === "MCQ" || formData.type === "TRUE_FALSE" ? formData.options .filter((o) => o.trim()) .map((optionText, index) => ({ option_text: optionText.trim(), option_order: index + 1, is_correct: optionText === formData.correctAnswer, })) : undefined const shortAnswersPayload = formData.type === "SHORT_ANSWER" ? [ { acceptable_answer: formData.correctAnswer.trim(), match_type: "EXACT" as const }, { acceptable_answer: formData.correctAnswer.trim(), match_type: "CASE_INSENSITIVE" as const }, ] : undefined const payload = { question_text: formData.question, question_type: formData.type, status: formData.status, difficulty_level: formData.difficulty, points: formData.points, tips: formData.tips || undefined, explanation: formData.explanation || undefined, options: optionsPayload, short_answers: shortAnswersPayload, voice_prompt: formData.type === "AUDIO" ? formData.voicePrompt : formData.voicePrompt || undefined, sample_answer_voice_prompt: formData.type === "AUDIO" ? formData.sampleAnswerVoicePrompt : formData.sampleAnswerVoicePrompt || undefined, audio_correct_answer_text: formData.type === "AUDIO" ? formData.audioCorrectAnswerText : undefined, } if (isEditing && id) { await updateQuestion(Number(id), payload) } else { await createQuestion(payload) } toast.success(isEditing ? "Question updated" : "Question created", { description: isEditing ? "The question has been updated successfully." : "Your new question has been created.", }) navigate("/content/questions") } catch (error) { console.error("Failed to save question:", error) toast.error("Failed to save question") } finally { setSubmitting(false) } } return (
{/* Page Header */}

{isEditing ? "Edit Question" : "Add New Question"}

{isEditing ? "Update the question details below" : "Fill in the details to create a new question"}

{loading && ( Loading question details... )}
Question Details {/* Question Type */}

{/* Question Text */}