import { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Card, CardHeader } from '../../ui/Card'; import { Button } from '../../ui/Button'; import { CheckCircle, XCircle, ArrowRight, Lightbulb } from 'lucide-react'; export interface QuizOption { id: string; text: string; isCorrect: boolean; } export interface QuizHint { text: string; } export interface QuizExerciseProps { question: string; questionNumber?: number; totalQuestions?: number; options: QuizOption[]; hints?: QuizHint[]; explanation: string; onComplete?: (result: QuizResult) => void; exerciseId?: string; maxAttempts?: number; } export interface QuizResult { correct: boolean; attempts: number; score: number; maxScore: number; usedHint: boolean; } const MAX_SCORE_PER_QUESTION = 100; const HINT_PENALTY = 20; const ATTEMPTS_BEFORE_HINT = 2; export function QuizExercise({ question, questionNumber, totalQuestions, options, hints = [], explanation, onComplete, exerciseId: _exerciseId, maxAttempts = 3, }: QuizExerciseProps) { const [selectedOption, setSelectedOption] = useState(null); const [showFeedback, setShowFeedback] = useState(false); const [attempts, setAttempts] = useState(0); const [showHint, setShowHint] = useState(false); const [hintIndex, setHintIndex] = useState(0); const [currentHint, setCurrentHint] = useState(null); const [isComplete, setIsComplete] = useState(false); const [score, setScore] = useState(0); const [usedHint, setUsedHint] = useState(false); const correctOption = options.find((opt) => opt.isCorrect); const isCorrect = selectedOption === correctOption?.id; useEffect(() => { if (isComplete && onComplete) { const maxScore = MAX_SCORE_PER_QUESTION; onComplete({ correct: isCorrect, attempts, score, maxScore, usedHint, }); } }, [isComplete, onComplete, isCorrect, attempts, score, usedHint]); const handleSelectOption = (optionId: string) => { if (showFeedback) return; setSelectedOption(optionId); }; const handleSubmit = () => { if (!selectedOption || showFeedback) return; setShowFeedback(true); setAttempts((prev) => prev + 1); if (selectedOption === correctOption?.id) { // Calculate score based on attempts let earnedScore = MAX_SCORE_PER_QUESTION; if (attempts > 0) { // Reduce score for each attempt (except first) earnedScore = Math.max(MAX_SCORE_PER_QUESTION - (attempts * 20), 20); } if (usedHint) { earnedScore -= HINT_PENALTY; } setScore(earnedScore); } }; const handleNext = () => { setIsComplete(true); }; const handleRetry = () => { setSelectedOption(null); setShowFeedback(false); setAttempts(0); setShowHint(false); setHintIndex(0); setCurrentHint(null); setIsComplete(false); setScore(0); setUsedHint(false); }; const handleShowHint = () => { if (hints.length === 0) return; setUsedHint(true); setShowHint(true); if (hintIndex < hints.length) { setCurrentHint(hints[hintIndex].text); setHintIndex((prev) => prev + 1); } }; const canShowHint = hints.length > 0 && attempts >= ATTEMPTS_BEFORE_HINT && !showHint; // Determine if the user can continue or should retry const canRetry = attempts < maxAttempts && !isCorrect; if (isComplete) { return (
{isCorrect ? ( ) : ( )}

{isCorrect ? '¡Correcto!' : 'Incorrecto'}

Explicación:

{explanation}

Intentos

{attempts}

Puntuación

{score}/{MAX_SCORE_PER_QUESTION}

{canRetry && !isCorrect && ( )} {!canRetry && !isCorrect && (

Has agotado tus intentos. La respuesta correcta era:{' '} {correctOption?.text}

)}
); } return ( {/* Progress bar for multi-question exercises */} {questionNumber && totalQuestions && (
Progreso {Math.round((questionNumber / totalQuestions) * 100)}%
)}
{options.map((option, index) => { const isSelected = selectedOption === option.id; const showCorrect = showFeedback && option.isCorrect; const showIncorrect = showFeedback && isSelected && !option.isCorrect; return ( handleSelectOption(option.id)} disabled={showFeedback} whileHover={!showFeedback ? { scale: 1.02 } : {}} whileTap={!showFeedback ? { scale: 0.98 } : {}} className={`w-full p-4 rounded-lg border-2 text-left transition-all ${ showCorrect ? 'border-green-500 bg-green-50' : showIncorrect ? 'border-red-500 bg-red-50' : isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-blue-300' }`} >
{String.fromCharCode(65 + index)} {option.text}
{showCorrect && } {showIncorrect && }
); })}
{/* Hint section */} {showHint && currentHint && (
Pista

{currentHint}

)}
{/* Feedback section */} {showFeedback && (
{isCorrect ? ( ) : ( )} {isCorrect ? '¡Correcto!' : 'Incorrecto'}

{explanation}

)}
Intentos: {attempts}/{maxAttempts} {canShowHint && ( )}
{!showFeedback ? ( ) : ( )}
); } export default QuizExercise;