YOUSEF2434's picture
Upload 96 files
a566fb0 verified
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { createPageUrl } from '@/utils';
import { motion, AnimatePresence } from 'framer-motion';
import { Brain, CheckCircle2, XCircle, ArrowLeft, Trophy, RotateCcw, Sparkles, Target } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import confetti from 'canvas-confetti';
const questions = [
{
id: 1,
question: "In YOLO (You Only Look Once), what makes it faster than traditional object detection methods?",
options: [
"It uses smaller images",
"It processes the entire image in a single pass instead of multiple regions",
"It only detects one object at a time",
"It uses black and white images"
],
correct: 1,
topic: "Object Detection"
},
{
id: 2,
question: "What does a confidence score of 95% mean in object detection?",
options: [
"The object is 95% visible in the image",
"The AI is 95% certain about its prediction",
"95% of the object is inside the bounding box",
"The detection took 95% of the processing time"
],
correct: 1,
topic: "Object Detection"
},
{
id: 3,
question: "How many keypoints does a standard pose estimation model typically track on the human body?",
options: [
"7 keypoints",
"10 keypoints",
"17 keypoints",
"25 keypoints"
],
correct: 2,
topic: "Pose Estimation"
},
{
id: 4,
question: "What are 'skeleton lines' in pose estimation?",
options: [
"Lines that show bones in an X-ray",
"Connections between keypoints showing body structure",
"Errors in the detection algorithm",
"Grid lines dividing the image"
],
correct: 1,
topic: "Pose Estimation"
},
{
id: 5,
question: "Which of these is NOT one of the 7 basic emotions in FER (Facial Emotion Recognition)?",
options: [
"Fear",
"Disgust",
"Confused",
"Surprise"
],
correct: 2,
topic: "Emotion Recognition"
},
{
id: 6,
question: "Why do facial emotion recognition models use 48×48 pixel images?",
options: [
"Larger images don't work with AI",
"Smaller size allows faster processing while retaining key facial features",
"48×48 is the size of human faces",
"It's the only size cameras can capture"
],
correct: 1,
topic: "Emotion Recognition"
},
{
id: 7,
question: "Which technology would be BEST for analyzing a dancer's movements in real-time?",
options: [
"Object Detection",
"Emotion Recognition",
"Pose Estimation",
"Voice Recognition"
],
correct: 2,
topic: "Pose Estimation"
},
{
id: 8,
question: "In object detection, what happens during 'non-max suppression'?",
options: [
"The AI stops detecting objects",
"Duplicate or overlapping bounding boxes are removed",
"The confidence threshold is lowered",
"The image brightness is reduced"
],
correct: 1,
topic: "Object Detection"
},
{
id: 9,
question: "Why is consent important before using emotion recognition technology on someone?",
options: [
"It makes the AI more accurate",
"People have a right to privacy and to know when they're being analyzed",
"It's required by all cameras",
"Consent improves the lighting in photos"
],
correct: 1,
topic: "Emotion Recognition"
},
{
id: 10,
question: "What is the main advantage of using grayscale images in emotion recognition instead of color?",
options: [
"Grayscale images look more professional",
"Color doesn't exist in emotions",
"It reduces processing complexity and focuses on facial structure rather than skin tone",
"Grayscale cameras are cheaper"
],
correct: 2,
topic: "Emotion Recognition"
},
{
id: 11,
question: "If pose estimation detects 2 people in a video, how many total keypoints might it track?",
options: [
"17 keypoints total",
"34 keypoints (17 per person)",
"25 keypoints total",
"10 keypoints total"
],
correct: 1,
topic: "Pose Estimation"
},
{
id: 12,
question: "What is a potential bias concern with emotion recognition AI?",
options: [
"It works too slowly",
"It might perform differently across different cultures, ages, or demographics",
"It can only detect happy emotions",
"It requires too much computer power"
],
correct: 1,
topic: "Emotion Recognition"
}
];
// 60-minute TTL logic (same as Lessons.js)
const COMPLETED_KEY = 'completedChapters_v1';
const TTL_MS = 60 * 60 * 1000;
function checkLessonsCompleted() {
try {
const raw = localStorage.getItem(COMPLETED_KEY);
if (!raw) return false;
const parsed = JSON.parse(raw);
const savedAt = parsed?.savedAt;
const value = parsed?.value;
if (!Array.isArray(value) || typeof savedAt !== 'number') return false;
if (Date.now() - savedAt > TTL_MS) {
localStorage.removeItem(COMPLETED_KEY);
return false;
}
return value.length === 3;
} catch {
return false;
}
}
export default function Quiz() {
const [started, setStarted] = useState(false);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [selectedAnswer, setSelectedAnswer] = useState(null);
const [showResult, setShowResult] = useState(false);
const [score, setScore] = useState(0);
const [answers, setAnswers] = useState([]);
const [finished, setFinished] = useState(false);
const [allLessonsCompleted, setAllLessonsCompleted] = useState(checkLessonsCompleted);
// Trigger confetti when finished with perfect score
useEffect(() => {
if (finished && score === questions.length) {
triggerConfetti();
}
}, [finished, score]);
const triggerConfetti = () => {
const duration = 5 * 1000;
const animationEnd = Date.now() + duration;
const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 };
const random = (min, max) => Math.random() * (max - min) + min;
const interval = setInterval(function() {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
return clearInterval(interval);
}
const particleCount = 50 * (timeLeft / duration);
// Since particles fall down, start a bit higher than random
confetti({
...defaults,
particleCount,
origin: { x: random(0.1, 0.3), y: Math.random() - 0.2 }
});
confetti({
...defaults,
particleCount,
origin: { x: random(0.7, 0.9), y: Math.random() - 0.2 }
});
}, 250);
};
const handleStart = () => {
setStarted(true);
setCurrentQuestion(0);
setScore(0);
setAnswers([]);
setFinished(false);
};
const handleAnswer = (index) => {
if (showResult) return;
setSelectedAnswer(index);
setShowResult(true);
const isCorrect = index === questions[currentQuestion].correct;
if (isCorrect) {
setScore(score + 1);
}
setAnswers([...answers, { questionId: currentQuestion, selected: index, correct: isCorrect }]);
};
const handleNext = () => {
if (currentQuestion < questions.length - 1) {
setCurrentQuestion(currentQuestion + 1);
setSelectedAnswer(null);
setShowResult(false);
} else {
setFinished(true);
}
};
const getScoreMessage = () => {
const percentage = (score / questions.length) * 100;
if (percentage === 100) return { emoji: "🏆", message: "PERFECT SCORE! You're an AI Vision Master!" };
if (percentage >= 80) return { emoji: "🌟", message: "Amazing! You really know your AI stuff!" };
if (percentage >= 60) return { emoji: "👍", message: "Good job! Keep learning and you'll be an expert!" };
if (percentage >= 40) return { emoji: "📚", message: "Nice try! Review the lessons and try again!" };
return { emoji: "💪", message: "Don't give up! Go through the lessons and come back stronger!" };
};
// Start Screen
if (!started) {
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 pt-24 pb-12 px-6">
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-20 left-10 w-72 h-72 bg-yellow-500/20 rounded-full blur-3xl animate-pulse" />
<div className="absolute bottom-20 right-10 w-96 h-96 bg-orange-500/20 rounded-full blur-3xl animate-pulse" />
</div>
<div className="max-w-2xl mx-auto relative text-center">
<Link to={createPageUrl('Home')}>
<Button className="text-white/70 hover:text-white mb-8 bg-transparent border-0 hover:bg-white/5">
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Home
</Button>
</Link>
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: "spring" }}
className="w-32 h-32 mx-auto rounded-3xl bg-gradient-to-br from-yellow-500 to-orange-500 flex items-center justify-center shadow-2xl shadow-yellow-500/30 mb-8"
>
<Target className="w-16 h-16 text-white" />
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-5xl font-black text-white mb-4"
>
AI Vision Quiz
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="text-xl text-white/70 mb-8"
>
Test your knowledge with {questions.length} questions about Object Detection, Pose Estimation, and Emotion Recognition!
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="bg-white/5 backdrop-blur-xl rounded-3xl p-8 border border-white/10 mb-8"
>
<div className="grid grid-cols-3 gap-4 text-center">
<div>
<div className="text-3xl font-bold text-cyan-400">{questions.length}</div>
<div className="text-white/60">Questions</div>
</div>
<div>
<div className="text-3xl font-bold text-purple-400">3</div>
<div className="text-white/60">Topics</div>
</div>
<div>
<div className="text-3xl font-bold text-emerald-400"></div>
<div className="text-white/60">Retries</div>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
>
{allLessonsCompleted ? (
<Button
onClick={handleStart}
size="lg"
className="text-xl px-12 py-8 bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-600 hover:to-orange-600 rounded-2xl shadow-2xl shadow-orange-500/30 text-white font-bold border-0 transition-all hover:scale-105"
>
<Sparkles className="w-6 h-6 mr-2" />
Start Quiz
</Button>
) : (
<div>
<Button
disabled
size="lg"
className="text-xl px-12 py-8 bg-slate-700 text-slate-400 rounded-2xl font-bold border-0 cursor-not-allowed opacity-60 mb-4"
>
🔒 Quiz Locked
</Button>
<p className="text-white/60">Complete all 3 lessons to unlock the quiz!</p>
<Link to={createPageUrl('Lessons')} className="inline-block mt-4">
<Button className="bg-blue-600 hover:bg-blue-700 text-white border-0 rounded-xl px-6 py-3">
Go to Lessons
</Button>
</Link>
</div>
)}
</motion.div>
</div>
</div>
);
}
// Results Screen
if (finished) {
const { emoji, message } = getScoreMessage();
const percentage = Math.round((score / questions.length) * 100);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 pt-24 pb-12 px-6">
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-20 left-10 w-72 h-72 bg-yellow-500/20 rounded-full blur-3xl animate-pulse" />
<div className="absolute bottom-20 right-10 w-96 h-96 bg-orange-500/20 rounded-full blur-3xl animate-pulse" />
</div>
<div className="max-w-2xl mx-auto relative">
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: "spring" }}
className="text-center"
>
<div className="text-8xl mb-6">{emoji}</div>
<h1 className="text-4xl md:text-5xl font-black text-white mb-4">
Quiz Complete!
</h1>
<div className="bg-white/5 backdrop-blur-xl rounded-3xl p-8 border border-white/10 mb-8">
<div className="text-6xl font-black text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-orange-400 mb-2">
{score}/{questions.length}
</div>
<div className="text-xl text-white/70 mb-6">
{percentage}% Correct
</div>
<Progress value={percentage} className="h-4 bg-white/10" />
<p className="text-white/80 mt-6 text-lg">{message}</p>
</div>
{/* Answer Summary */}
<div className="bg-white/5 backdrop-blur-xl rounded-3xl p-6 border border-white/10 mb-8">
<h3 className="text-lg font-bold text-white mb-4">Your Answers</h3>
<div className="grid grid-cols-5 gap-2">
{answers.map((answer, i) => (
<motion.div
key={i}
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: i * 0.05 }}
className={`w-12 h-12 rounded-xl flex items-center justify-center font-bold text-white ${
answer.correct
? 'bg-gradient-to-br from-emerald-500 to-teal-500'
: 'bg-gradient-to-br from-red-500 to-rose-500'
}`}
>
{i + 1}
</motion.div>
))}
</div>
</div>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-12">
<Button
onClick={handleStart}
size="lg"
className="bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-600 hover:to-orange-600 rounded-xl text-white font-bold border-0 px-8 py-6 text-lg shadow-xl shadow-orange-500/25 transition-all hover:scale-105"
>
<RotateCcw className="w-5 h-5 mr-2" />
Try Again
</Button>
<Link to={createPageUrl('Lessons')}>
<Button
size="lg"
className="border-2 border-white/30 text-white bg-white/5 hover:bg-white/10 rounded-xl w-full px-8 py-6 text-lg font-bold transition-all"
>
Review Lessons
</Button>
</Link>
</div>
{/* Test It Yourself Section */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="bg-gradient-to-br from-blue-500/10 to-purple-500/10 rounded-3xl p-10 border border-blue-500/20"
>
<div className="text-center mb-6">
<div className="text-5xl mb-4">🚀</div>
<h2 className="text-3xl font-black text-white mb-3">Ready to Test It Yourself?</h2>
<p className="text-white/70 text-lg mb-8">
Now that you've mastered the concepts, try building your own AI vision projects!
</p>
</div>
<div className="grid md:grid-cols-3 gap-4 mb-8">
<div className="bg-white/5 rounded-2xl p-6 border border-white/10">
<div className="text-3xl mb-3">👁️</div>
<h3 className="text-white font-bold mb-2">Object Detection</h3>
<p className="text-white/60 text-sm">Build your own detector</p>
</div>
<div className="bg-white/5 rounded-2xl p-6 border border-white/10">
<div className="text-3xl mb-3">🏃</div>
<h3 className="text-white font-bold mb-2">Pose Tracking</h3>
<p className="text-white/60 text-sm">Create motion games</p>
</div>
<div className="bg-white/5 rounded-2xl p-6 border border-white/10">
<div className="text-3xl mb-3">😊</div>
<h3 className="text-white font-bold mb-2">Emotion AI</h3>
<p className="text-white/60 text-sm">Analyze expressions</p>
</div>
</div>
<Button
size="lg"
onClick={() => {
window.location.href = "/Lab-Selection.html";
}}
className="w-full bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 hover:from-blue-600 hover:via-purple-600 hover:to-pink-600 text-white font-bold text-xl py-8 rounded-2xl border-0 shadow-2xl transition-all hover:scale-105"
>
<Sparkles className="w-6 h-6 mr-3" />
Launch AI Playground
</Button>
</motion.div>
</motion.div>
</div>
</div>
);
}
// Quiz Question Screen
const question = questions[currentQuestion];
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 pt-24 pb-12 px-6">
<div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-20 left-10 w-72 h-72 bg-yellow-500/20 rounded-full blur-3xl animate-pulse" />
<div className="absolute bottom-20 right-10 w-96 h-96 bg-orange-500/20 rounded-full blur-3xl animate-pulse" />
</div>
<div className="max-w-2xl mx-auto relative">
{/* Progress */}
<div className="mb-8">
<div className="flex items-center justify-between text-white/60 mb-2">
<span>Question {currentQuestion + 1} of {questions.length}</span>
<span className="px-3 py-1 rounded-full bg-white/10 text-sm">{question.topic}</span>
</div>
<Progress
value={((currentQuestion + 1) / questions.length) * 100}
className="h-2 bg-white/10"
/>
</div>
{/* Question */}
<AnimatePresence mode="wait">
<motion.div
key={currentQuestion}
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -50 }}
>
<div className="bg-white/5 backdrop-blur-xl rounded-3xl p-8 border border-white/10 mb-6">
<div className="flex items-start gap-4 mb-6">
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-yellow-500 to-orange-500 flex items-center justify-center flex-shrink-0">
<Brain className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-bold text-white leading-relaxed">
{question.question}
</h2>
</div>
{/* Options */}
<div className="space-y-3">
{question.options.map((option, index) => {
const isSelected = selectedAnswer === index;
const isCorrect = index === question.correct;
const showCorrect = showResult && isCorrect;
const showWrong = showResult && isSelected && !isCorrect;
return (
<motion.button
key={index}
onClick={() => handleAnswer(index)}
disabled={showResult}
className={`w-full p-4 rounded-2xl text-left transition-all border-2 ${
showCorrect
? 'bg-emerald-500/20 border-emerald-500 text-white'
: showWrong
? 'bg-red-500/20 border-red-500 text-white'
: isSelected
? 'bg-white/20 border-white/40 text-white'
: 'bg-white/5 border-white/10 text-white/80 hover:bg-white/10 hover:border-white/20'
}`}
whileHover={!showResult ? { scale: 1.02 } : {}}
whileTap={!showResult ? { scale: 0.98 } : {}}
>
<div className="flex items-center gap-4">
<div className={`w-10 h-10 rounded-xl flex items-center justify-center font-bold ${
showCorrect
? 'bg-emerald-500 text-white'
: showWrong
? 'bg-red-500 text-white'
: 'bg-white/10 text-white/60'
}`}>
{showCorrect ? (
<CheckCircle2 className="w-5 h-5" />
) : showWrong ? (
<XCircle className="w-5 h-5" />
) : (
String.fromCharCode(65 + index)
)}
</div>
<span className="text-lg">{option}</span>
</div>
</motion.button>
);
})}
</div>
</div>
{/* Feedback */}
<AnimatePresence>
{showResult && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className={`rounded-2xl p-6 mb-6 ${
selectedAnswer === question.correct
? 'bg-gradient-to-br from-emerald-500/20 to-teal-500/20 border border-emerald-500/30'
: 'bg-gradient-to-br from-red-500/20 to-rose-500/20 border border-red-500/30'
}`}
>
<div className="flex items-center gap-3">
{selectedAnswer === question.correct ? (
<>
<CheckCircle2 className="w-8 h-8 text-emerald-400" />
<div>
<h3 className="text-xl font-bold text-white">Correct! 🎉</h3>
<p className="text-white/70">Great job! You got this one right.</p>
</div>
</>
) : (
<>
<XCircle className="w-8 h-8 text-red-400" />
<div>
<h3 className="text-xl font-bold text-white">Not quite! 🤔</h3>
<p className="text-white/70">
The correct answer was: {question.options[question.correct]}
</p>
</div>
</>
)}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Next Button */}
{showResult && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-center"
>
<Button
onClick={handleNext}
size="lg"
className="bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 rounded-2xl px-12 py-6 text-lg font-bold text-white border-0 shadow-xl shadow-purple-500/30 transition-all hover:scale-105"
>
{currentQuestion < questions.length - 1 ? 'Next Question →' : '✨ See Results'}
</Button>
</motion.div>
)}
</motion.div>
</AnimatePresence>
</div>
</div>
);
}