'use client'; import React, { useState, useRef } from 'react'; import { Plus, Minus, Upload, Image, Video, Music, File, X, Eye, Settings, Puzzle, Gamepad2, Brain, Timer, Star, Heart, Zap, MousePointer, Shuffle, Target, CheckCircle, AlertCircle, HelpCircle, Play, Pause } from 'lucide-react'; import { MultimediaManager, MultimediaFile } from './MultimediaManager'; export interface InteractiveQuestionOption { id: string; text: string; isCorrect: boolean; multimedia?: MultimediaFile; explanation?: string; weight?: number; // for weighted scoring } export interface InteractiveQuestionData { id?: string; title: string; type: 'multiple_choice' | 'essay' | 'true_false' | 'enhanced_mcq' | 'puzzle' | 'scenario' | 'puzzle_gambar_icon' | 'true_false_cepat' | 'mini_simulation_rpg' | 'mini_survey_reflection'; category: string; difficulty: 'easy' | 'medium' | 'hard'; points: number; explanation?: string; status: 'draft' | 'active'; // Standard question data options?: InteractiveQuestionOption[]; essayAnswer?: string; trueFalseAnswer?: boolean; // Interactive features multimedia?: MultimediaFile[]; interactiveConfig?: { // Enhanced MCQ allowMultipleAnswers?: boolean; enableHints?: boolean; hints?: string[]; // Puzzle features puzzleType?: 'drag_drop' | 'sequencing' | 'matching' | 'visual_matching'; puzzleItems?: Array<{ id: string; content: string; position?: { x: number; y: number }; matchTarget?: string; multimedia?: MultimediaFile; }>; // Scenario simulation scenarioStages?: Array<{ id: string; title: string; description: string; choices: Array<{ id: string; text: string; consequence: string; nextStage?: string; }>; multimedia?: MultimediaFile; }>; // RPG simulation rpgConfig?: { character?: { name: string; attributes: { [key: string]: number }; }; storyline?: Array<{ id: string; text: string; choices: Array<{ id: string; text: string; attributeEffects?: { [key: string]: number }; nextScene?: string; }>; }>; }; // Reflection survey reflectionConfig?: { emojiScale?: { min: number; max: number; labels: string[]; }; textPrompts?: string[]; allowSkip?: boolean; }; // Timing and behavior timeLimit?: number; showTimer?: boolean; trackBehavior?: boolean; allowReview?: boolean; }; } interface EnhancedQuestionBuilderProps { initialData?: Partial; onSave: (questionData: InteractiveQuestionData) => void; onCancel: () => void; categories: string[]; className?: string; } export const EnhancedQuestionBuilder: React.FC = ({ initialData, onSave, onCancel, categories, className = '' }) => { const [questionData, setQuestionData] = useState({ title: '', type: 'multiple_choice', category: '', difficulty: 'medium', points: 10, explanation: '', status: 'draft', options: [ { id: '1', text: '', isCorrect: false }, { id: '2', text: '', isCorrect: false }, { id: '3', text: '', isCorrect: false }, { id: '4', text: '', isCorrect: false } ], multimedia: [], interactiveConfig: { allowMultipleAnswers: false, enableHints: false, hints: [], timeLimit: 0, showTimer: false, trackBehavior: false, allowReview: true }, ...initialData }); const [activeTab, setActiveTab] = useState<'basic' | 'content' | 'interactive' | 'multimedia' | 'preview'>('basic'); const [showMultimediaManager, setShowMultimediaManager] = useState(false); const [previewMode, setPreviewMode] = useState(false); const questionTypes = [ { value: 'multiple_choice', label: 'Pilihan Ganda', icon: CheckCircle, description: 'Soal pilihan ganda standar' }, { value: 'enhanced_mcq', label: 'Pilihan Ganda Plus', icon: Star, description: 'Pilihan ganda dengan fitur tambahan' }, { value: 'essay', label: 'Essay', icon: File, description: 'Soal essay dengan jawaban terbuka' }, { value: 'true_false', label: 'Benar/Salah', icon: Target, description: 'Soal benar atau salah' }, { value: 'true_false_cepat', label: 'Benar/Salah Cepat', icon: Zap, description: 'Benar/salah dengan tracking waktu reaksi' }, { value: 'puzzle', label: 'Puzzle', icon: Puzzle, description: 'Soal puzzle interaktif' }, { value: 'puzzle_gambar_icon', label: 'Puzzle Visual', icon: Image, description: 'Puzzle dengan gambar dan ikon' }, { value: 'scenario', label: 'Simulasi Skenario', icon: Brain, description: 'Simulasi skenario dengan pilihan bertingkat' }, { value: 'mini_simulation_rpg', label: 'Mini RPG', icon: Gamepad2, description: 'Simulasi RPG mini dengan karakter' }, { value: 'mini_survey_reflection', label: 'Refleksi Survey', icon: Heart, description: 'Survey refleksi dengan skala emoji' } ]; const handleInputChange = (field: keyof InteractiveQuestionData, value: any) => { setQuestionData(prev => ({ ...prev, [field]: value })); }; const handleInteractiveConfigChange = (field: string, value: any) => { setQuestionData(prev => ({ ...prev, interactiveConfig: { ...prev.interactiveConfig, [field]: value } })); }; const handleOptionChange = (optionId: string, field: keyof InteractiveQuestionOption, value: any) => { setQuestionData(prev => ({ ...prev, options: prev.options?.map(option => option.id === optionId ? { ...option, [field]: value } : option ) })); }; const addOption = () => { const newId = ((questionData.options?.length || 0) + 1).toString(); setQuestionData(prev => ({ ...prev, options: [...(prev.options || []), { id: newId, text: '', isCorrect: false }] })); }; const removeOption = (optionId: string) => { if ((questionData.options?.length || 0) > 2) { setQuestionData(prev => ({ ...prev, options: prev.options?.filter(option => option.id !== optionId) })); } }; const addHint = () => { const hints = questionData.interactiveConfig?.hints || []; handleInteractiveConfigChange('hints', [...hints, '']); }; const updateHint = (index: number, value: string) => { const hints = questionData.interactiveConfig?.hints || []; const newHints = [...hints]; newHints[index] = value; handleInteractiveConfigChange('hints', newHints); }; const removeHint = (index: number) => { const hints = questionData.interactiveConfig?.hints || []; handleInteractiveConfigChange('hints', hints.filter((_, i) => i !== index)); }; const handleMultimediaUpload = async (files: File[]): Promise => { // Simulate file upload - in real implementation, this would upload to server const uploadedFiles: MultimediaFile[] = files.map(file => ({ id: Math.random().toString(36).substr(2, 9), name: file.name, type: file.type.startsWith('image/') ? 'image' : file.type.startsWith('video/') ? 'video' : file.type.startsWith('audio/') ? 'audio' : 'document', url: URL.createObjectURL(file), size: file.size, uploadedAt: new Date() })); setQuestionData(prev => ({ ...prev, multimedia: [...(prev.multimedia || []), ...uploadedFiles] })); return uploadedFiles; }; const handleMultimediaDelete = async (fileId: string) => { setQuestionData(prev => ({ ...prev, multimedia: prev.multimedia?.filter(file => file.id !== fileId) })); }; const validateQuestion = (): string[] => { const errors: string[] = []; if (!questionData.title.trim()) { errors.push('Judul soal harus diisi'); } if (!questionData.category) { errors.push('Kategori harus dipilih'); } if (['multiple_choice', 'enhanced_mcq'].includes(questionData.type)) { const hasCorrectAnswer = questionData.options?.some(option => option.isCorrect); const hasEmptyOption = questionData.options?.some(option => !option.text.trim()); if (hasEmptyOption) { errors.push('Semua pilihan jawaban harus diisi'); } if (!hasCorrectAnswer) { errors.push('Pilih jawaban yang benar'); } } if (questionData.type === 'essay' && !questionData.essayAnswer?.trim()) { errors.push('Kunci jawaban essay harus diisi'); } return errors; }; const handleSave = () => { const errors = validateQuestion(); if (errors.length > 0) { alert('Terdapat kesalahan:\n' + errors.join('\n')); return; } onSave(questionData); }; const renderBasicTab = () => (