import React, { useState, useEffect } from 'react'; import { getNonce } from '../../Helpers'; import QuizQuestionEditor from './QuizQuestionEditor'; import QuizSettings from './QuizSettings'; import NoticeModal from '../NoticeModal/NoticeModal'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import SaveIcon from '@mui/icons-material/Save'; import AddIcon from '@mui/icons-material/Add'; import PreviewIcon from '@mui/icons-material/Preview'; import SettingsIcon from '@mui/icons-material/Settings'; import QuizIcon from '@mui/icons-material/Quiz'; import '../../styles/_quizzes.scss'; // Declare wp global for WordPress declare const wp: any; interface Question { id: string; question: string; description?: string; image_url?: string; answers: Answer[]; } interface Answer { id: string; text: string; image_url?: string; is_correct: boolean; } interface QuizSettingsData { passing_score: number; show_results: boolean; show_correct_answers: boolean; reward_type: 'none' | 'coupon' | 'redirect'; reward_value: string; redirect_url: string; randomize_questions: boolean; randomize_answers: boolean; time_limit: number; collect_name: boolean; collect_email: boolean; display_mode: 'all_at_once' | 'step_by_step'; questions_per_step: number; step_navigation: 'numbers' | 'custom' | 'progress_bar'; custom_step_labels: string[]; require_login: boolean; allow_retake: boolean; quiz_tracking: 'ip_address' | 'browser_storage' | 'hybrid'; } interface QuizBuilderProps { quizId?: number; onBack: () => void; onSave: () => void; } const QuizBuilder: React.FC = ({ quizId, onBack, onSave }) => { const [quizName, setQuizName] = useState(''); const [description, setDescription] = useState(''); const [questions, setQuestions] = useState([]); const [settings, setSettings] = useState({ passing_score: 70, show_results: true, show_correct_answers: true, reward_type: 'none', reward_value: '', redirect_url: '', randomize_questions: false, randomize_answers: false, time_limit: 0, collect_name: false, collect_email: false, display_mode: 'all_at_once', questions_per_step: 1, step_navigation: 'numbers', custom_step_labels: [], require_login: false, allow_retake: true, quiz_tracking: 'hybrid', }); const [activeTab, setActiveTab] = useState<'questions' | 'settings'>('questions'); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [previewUrl, setPreviewUrl] = useState(null); const [modalConfig, setModalConfig] = useState({ isOpen: false, type: 'confirmation', title: '', message: '', onConfirm: () => { }, confirmText: 'Confirm', declineText: 'Cancel', mode: 'success', position: 'center' as 'center' | 'top-right' }); const closeModal = () => { setModalConfig(prev => ({ ...prev, isOpen: false })); }; const handlePreview = () => { if (!quizId) { setModalConfig({ isOpen: true, type: 'toast', title: 'Info', message: 'Please save the quiz first before previewing.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } wp.ajax.send('simpleform_get_quiz_preview_url', { data: { nonce: getNonce(), quiz_id: quizId, }, success(response: any) { if (response.preview_url) { setPreviewUrl(response.preview_url); } }, error(error: any) { console.error('Error getting preview URL:', error); setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Failed to generate preview URL.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); }, }); }; const closePreview = () => { setPreviewUrl(null); }; useEffect(() => { if (quizId) { loadQuiz(); } else { // Add first question for new quiz addQuestion(); } }, [quizId]); const loadQuiz = () => { if (!quizId) return; setLoading(true); (wp as any).ajax.send('simpleform_get_quiz', { data: { nonce: getNonce(), quiz_id: quizId, }, success(response: any) { if (response.quiz) { const quiz = response.quiz; setQuizName(quiz.quiz_name); setDescription(quiz.description || ''); setQuestions(quiz.questions || []); // Convert string booleans to actual booleans const loadedSettings = quiz.settings || {}; const normalizedSettings = { ...settings, ...loadedSettings, show_results: loadedSettings.show_results === 'true' || loadedSettings.show_results === true, show_correct_answers: loadedSettings.show_correct_answers === 'true' || loadedSettings.show_correct_answers === true, randomize_questions: loadedSettings.randomize_questions === 'true' || loadedSettings.randomize_questions === true, randomize_answers: loadedSettings.randomize_answers === 'true' || loadedSettings.randomize_answers === true, collect_name: loadedSettings.collect_name === 'true' || loadedSettings.collect_name === true, collect_email: loadedSettings.collect_email === 'true' || loadedSettings.collect_email === true, require_login: loadedSettings.require_login === 'true' || loadedSettings.require_login === true, allow_retake: loadedSettings.allow_retake === 'true' || loadedSettings.allow_retake === true, passing_score: parseInt(loadedSettings.passing_score) || 70, time_limit: parseInt(loadedSettings.time_limit) || 0, display_mode: loadedSettings.display_mode || 'all_at_once', questions_per_step: parseInt(loadedSettings.questions_per_step) || 1, step_navigation: loadedSettings.step_navigation || 'numbers', custom_step_labels: loadedSettings.custom_step_labels || [], quiz_tracking: loadedSettings.quiz_tracking || 'hybrid', }; setSettings(normalizedSettings); } setLoading(false); }, error(error: any) { console.error('Error loading quiz:', error); setLoading(false); }, }); }; const generateId = () => { return 'id_' + Math.random().toString(36).substr(2, 9); }; const addQuestion = () => { const newQuestion: Question = { id: generateId(), question: '', description: '', image_url: '', answers: [ { id: generateId(), text: '', image_url: '', is_correct: false }, { id: generateId(), text: '', image_url: '', is_correct: false }, ], }; setQuestions(prev => [...prev, newQuestion]); }; const updateQuestion = (questionId: string, updatedQuestion: Question) => { setQuestions(prev => prev.map(q => q.id === questionId ? updatedQuestion : q) ); }; const deleteQuestion = (questionId: string) => { if (questions.length <= 1) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'A quiz must have at least one question.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } setQuestions(prev => prev.filter(q => q.id !== questionId)); }; const moveQuestion = (index: number, direction: 'up' | 'down') => { const newQuestions = [...questions]; const targetIndex = direction === 'up' ? index - 1 : index + 1; if (targetIndex < 0 || targetIndex >= newQuestions.length) { return; } [newQuestions[index], newQuestions[targetIndex]] = [newQuestions[targetIndex], newQuestions[index]]; setQuestions(newQuestions); }; const handleSave = () => { if (!quizName.trim()) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Please enter a quiz name.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } if (questions.length === 0) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Please add at least one question.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } // Validate questions for (const question of questions) { if (!question.question.trim()) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Please fill in all question texts.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } if (question.answers.length < 2) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Each question must have at least 2 answers.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } const hasCorrectAnswer = question.answers.some(answer => answer.is_correct); if (!hasCorrectAnswer) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Each question must have at least one correct answer.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } for (const answer of question.answers) { if (!answer.text.trim()) { setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: 'Please fill in all answer texts.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); return; } } } setSaving(true); const quizData = { quiz_name: quizName, description, questions, settings, }; const action = quizId ? 'simpleform_update_quiz' : 'simpleform_create_quiz'; const data: any = { nonce: getNonce(), quiz_data: quizData, }; if (quizId) { data.quiz_id = quizId; } (wp as any).ajax.send(action, { data, success(response: any) { setSaving(false); setModalConfig({ isOpen: true, type: 'toast', title: 'Success', message: response.message || 'Quiz saved successfully!', position: 'top-right', mode: 'success', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); // Don't redirect after save - stay on the same page }, error(error: any) { console.error('Error saving quiz:', error); setSaving(false); setModalConfig({ isOpen: true, type: 'toast', title: 'Error', message: error.message || 'Failed to save quiz. Please try again.', position: 'top-right', mode: 'error', onConfirm: () => {}, confirmText: 'OK', declineText: 'Cancel', }); setTimeout(closeModal, 3000); }, }); }; if (loading) { return (

Loading quiz...

); } return (

{quizId ? 'Edit Quiz' : 'Create Quiz'}

setQuizName(e.target.value)} placeholder="Enter quiz name" />