Files
Puechberty Arthur 49fd31f4db first commit
2026-03-30 23:07:36 +02:00

336 lines
13 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useLocale } from './LocaleProvider';
import { ts } from '@/lib/i18n';
interface QuizQuestion {
question: Record<string, string>;
options: Record<string, string[]>;
correctIndex: number;
explanation: Record<string, string>;
}
const QUESTIONS: QuizQuestion[] = [
{
question: {
en: 'How long is one complete lunar cycle (synodic month)?',
fr: 'Quelle est la durée d\'un cycle lunaire complet (mois synodique) ?',
},
options: {
en: ['27.32 days', '28 days', '29.53 days', '30 days'],
fr: ['27,32 jours', '28 jours', '29,53 jours', '30 jours'],
},
correctIndex: 2,
explanation: {
en: 'The synodic month lasts 29.53 days — the time between two identical phases (e.g., full moon to full moon).',
fr: 'Le mois synodique dure 29,53 jours — le temps entre deux phases identiques.',
},
},
{
question: {
en: 'What is the traditional name for the September full moon?',
fr: 'Quel est le nom traditionnel de la pleine lune de septembre ?',
},
options: {
en: ['Wolf Moon', 'Harvest Moon', 'Hunter\'s Moon', 'Blood Moon'],
fr: ['Lune du Loup', 'Lune des Moissons', 'Lune du Chasseur', 'Lune de Sang'],
},
correctIndex: 1,
explanation: {
en: 'The Harvest Moon rises near sunset during harvest season, providing extra light for farmers.',
fr: 'La Lune des Moissons se lève près du coucher du soleil, offrant de la lumière aux agriculteurs.',
},
},
{
question: {
en: 'What causes the tides on Earth?',
fr: 'Qu\'est-ce qui cause les marées sur Terre ?',
},
options: {
en: ['Wind', 'Earth\'s rotation only', 'Gravitational pull of Moon and Sun', 'Ocean currents'],
fr: ['Le vent', 'La rotation terrestre seule', 'L\'attraction gravitationnelle de la Lune et du Soleil', 'Les courants océaniques'],
},
correctIndex: 2,
explanation: {
en: 'Tides are primarily caused by the gravitational pull of the Moon and, to a lesser extent, the Sun.',
fr: 'Les marées sont principalement causées par l\'attraction gravitationnelle de la Lune et du Soleil.',
},
},
{
question: {
en: 'During which moon phase do we see the most light?',
fr: 'Pendant quelle phase voit-on le plus de lumière ?',
},
options: {
en: ['New Moon', 'First Quarter', 'Full Moon', 'Waning Crescent'],
fr: ['Nouvelle Lune', 'Premier Quartier', 'Pleine Lune', 'Croissant Décroissant'],
},
correctIndex: 2,
explanation: {
en: 'The full moon is 100% illuminated, reflecting maximum sunlight toward Earth.',
fr: 'La pleine lune est illuminée à 100%, reflétant le maximum de lumière solaire.',
},
},
{
question: {
en: 'What is a "supermoon"?',
fr: 'Qu\'est-ce qu\'une "super lune" ?',
},
options: {
en: ['A moon larger than usual', 'A full moon at its closest point to Earth (perigee)', 'Two full moons in one month', 'A full moon during an eclipse'],
fr: ['Une lune plus grande que d\'habitude', 'Une pleine lune au point le plus proche de la Terre (périgée)', 'Deux pleines lunes dans un mois', 'Une pleine lune pendant une éclipse'],
},
correctIndex: 1,
explanation: {
en: 'A supermoon occurs when the full moon coincides with perigee, appearing about 14% bigger and 30% brighter.',
fr: 'Une super lune se produit quand la pleine lune coïncide avec le périgée, paraissant ~14% plus grande.',
},
},
{
question: {
en: 'How much of the Moon\'s surface can we see from Earth?',
fr: 'Quelle proportion de la surface lunaire peut-on voir depuis la Terre ?',
},
options: {
en: ['50%', '41%', '59%', '100%'],
fr: ['50%', '41%', '59%', '100%'],
},
correctIndex: 2,
explanation: {
en: 'Due to libration (slight wobble), we can see about 59% of the Moon\'s surface over time, though only 50% at any given moment.',
fr: 'Grâce à la libration, on peut voir environ 59% de la surface lunaire au fil du temps.',
},
},
{
question: {
en: 'What are "spring tides"?',
fr: 'Que sont les "vives-eaux" ?',
},
options: {
en: ['Tides in springtime', 'Extra high tides during full and new moons', 'Tides caused by storms', 'Low tides only'],
fr: ['Les marées au printemps', 'Marées extra hautes pendant pleine et nouvelle lune', 'Marées causées par les tempêtes', 'Marées basses uniquement'],
},
correctIndex: 1,
explanation: {
en: 'Spring tides occur when Moon and Sun align (full and new moon), creating the highest and lowest tides.',
fr: 'Les vives-eaux se produisent quand Lune et Soleil sont alignés, créant les marées les plus extrêmes.',
},
},
{
question: {
en: 'Why does the Moon always show the same face to Earth?',
fr: 'Pourquoi la Lune montre-t-elle toujours la même face à la Terre ?',
},
options: {
en: ['It doesn\'t rotate', 'Tidal locking — rotation period equals orbital period', 'It\'s a coincidence', 'Earth\'s magnetic field keeps it locked'],
fr: ['Elle ne tourne pas', 'Verrouillage gravitationnel — période de rotation = période orbitale', 'C\'est une coïncidence', 'Le champ magnétique terrestre la maintient'],
},
correctIndex: 1,
explanation: {
en: 'Tidal locking means the Moon rotates on its axis in the same time it takes to orbit Earth (~27.3 days).',
fr: 'Le verrouillage gravitationnel fait que la Lune tourne sur elle-même en même temps qu\'elle orbite la Terre.',
},
},
{
question: {
en: 'What is a "blue moon"?',
fr: 'Qu\'est-ce qu\'une "lune bleue" ?',
},
options: {
en: ['A moon that appears blue', 'The second full moon in a calendar month', 'A lunar eclipse', 'A new moon visible during daytime'],
fr: ['Une lune de couleur bleue', 'La deuxième pleine lune dans un mois', 'Une éclipse lunaire', 'Une nouvelle lune visible le jour'],
},
correctIndex: 1,
explanation: {
en: 'A blue moon is the second full moon in a single calendar month, occurring roughly every 2.7 years.',
fr: 'Une lune bleue est la deuxième pleine lune d\'un même mois, survenant environ tous les 2,7 ans.',
},
},
{
question: {
en: 'What is the Moon\'s average distance from Earth?',
fr: 'Quelle est la distance moyenne Terre-Lune ?',
},
options: {
en: ['238,900 miles (384,400 km)', '150,000 miles (241,000 km)', '500,000 miles (800,000 km)', '93 million miles (150 million km)'],
fr: ['384 400 km', '241 000 km', '800 000 km', '150 millions km'],
},
correctIndex: 0,
explanation: {
en: 'The Moon orbits at an average distance of 384,400 km (238,900 miles) from Earth.',
fr: 'La Lune orbite à une distance moyenne de 384 400 km de la Terre.',
},
},
];
export default function Quiz() {
const { locale } = useLocale();
const lang = (locale === 'fr') ? 'fr' : 'en';
const [started, setStarted] = useState(false);
const [currentQ, setCurrentQ] = useState(0);
const [selected, setSelected] = useState<number | null>(null);
const [score, setScore] = useState(0);
const [finished, setFinished] = useState(false);
const [answered, setAnswered] = useState(false);
const question = QUESTIONS[currentQ];
const handleSelect = (index: number) => {
if (answered) return;
setSelected(index);
setAnswered(true);
if (index === question.correctIndex) {
setScore(s => s + 1);
}
};
const handleNext = () => {
if (currentQ + 1 >= QUESTIONS.length) {
setFinished(true);
} else {
setCurrentQ(q => q + 1);
setSelected(null);
setAnswered(false);
}
};
const restart = () => {
setStarted(true);
setCurrentQ(0);
setSelected(null);
setScore(0);
setFinished(false);
setAnswered(false);
};
const scorePercentage = Math.round((score / QUESTIONS.length) * 100);
const getScoreMessage = () => {
if (scorePercentage >= 90) return locale === 'fr' ? '🏆 Expert lunaire !' : '🏆 Lunar Expert!';
if (scorePercentage >= 70) return locale === 'fr' ? '🌟 Très bien !' : '🌟 Great job!';
if (scorePercentage >= 50) return locale === 'fr' ? '👍 Pas mal !' : '👍 Not bad!';
return locale === 'fr' ? '📚 À réviser !' : '📚 Keep learning!';
};
return (
<section id="quiz" aria-label="Moon Knowledge Quiz" className="section-container">
<div className="text-center mb-10">
<h2 className="section-title">{ts('quiz_title', locale)}</h2>
<p className="section-subtitle mx-auto">{ts('quiz_subtitle', locale)}</p>
</div>
<div className="glass-card max-w-2xl mx-auto p-4 sm:p-6 md:p-8">
{!started ? (
<div className="text-center">
<p className="text-6xl mb-6">🌙</p>
<p className="text-lg text-white/60 mb-8">
{QUESTIONS.length} {locale === 'fr' ? 'questions sur la lune et ses mystères' : 'questions about the moon and its mysteries'}
</p>
<button onClick={() => setStarted(true)} className="glow-btn text-lg px-8 py-3">
{ts('quiz_start', locale)}
</button>
</div>
) : finished ? (
<div className="text-center">
<p className="text-6xl mb-4">{scorePercentage >= 70 ? '🎉' : '🌙'}</p>
<h3 className="text-2xl font-bold mb-2">{ts('quiz_results', locale)}</h3>
<p className="text-4xl font-bold text-indigo-300 mb-2">{score}/{QUESTIONS.length}</p>
<p className="text-lg text-white/60 mb-2">{scorePercentage}%</p>
<p className="text-xl mb-8">{getScoreMessage()}</p>
{/* Score bar */}
<div className="w-full h-3 bg-white/10 rounded-full mb-8 max-w-xs mx-auto">
<div
className={`h-full rounded-full transition-all duration-1000 ${
scorePercentage >= 70 ? 'bg-green-400' : scorePercentage >= 50 ? 'bg-yellow-400' : 'bg-red-400'
}`}
style={{ width: `${scorePercentage}%` }}
/>
</div>
<button onClick={restart} className="glow-btn">
{ts('quiz_restart', locale)}
</button>
</div>
) : (
<>
{/* Progress */}
<div className="flex items-center justify-between mb-6">
<span className="text-sm text-white/40">
{currentQ + 1}/{QUESTIONS.length}
</span>
<div className="flex-1 mx-4 h-1.5 bg-white/10 rounded-full">
<div
className="h-full bg-indigo-400 rounded-full transition-all duration-300"
style={{ width: `${((currentQ + 1) / QUESTIONS.length) * 100}%` }}
/>
</div>
<span className="text-sm text-indigo-300">{ts('quiz_score', locale)}: {score}</span>
</div>
{/* Question */}
<h3 className="text-xl font-semibold mb-6">
{question.question[lang] || question.question.en}
</h3>
{/* Options */}
<div className="space-y-3 mb-6">
{(question.options[lang] || question.options.en).map((option, i) => (
<button
key={i}
onClick={() => handleSelect(i)}
className={`quiz-option w-full text-left ${
answered
? i === question.correctIndex
? 'correct'
: i === selected
? 'wrong'
: ''
: ''
}`}
>
<span className="flex items-center gap-3">
<span className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold border ${
answered && i === question.correctIndex
? 'bg-green-500/20 border-green-400 text-green-300'
: answered && i === selected
? 'bg-red-500/20 border-red-400 text-red-300'
: 'border-white/20 text-white/50'
}`}>
{String.fromCharCode(65 + i)}
</span>
{option}
</span>
</button>
))}
</div>
{/* Explanation */}
{answered && (
<div className={`p-4 rounded-xl mb-6 ${
selected === question.correctIndex
? 'bg-green-500/10 border border-green-500/20'
: 'bg-red-500/10 border border-red-500/20'
}`}>
<p className="font-semibold mb-1">
{selected === question.correctIndex ? ts('quiz_correct', locale) : ts('quiz_wrong', locale)}
</p>
<p className="text-sm text-white/70">
{question.explanation[lang] || question.explanation.en}
</p>
</div>
)}
{answered && (
<button onClick={handleNext} className="glow-btn w-full">
{currentQ + 1 >= QUESTIONS.length ? ts('quiz_results', locale) : ts('quiz_next', locale)}
</button>
)}
</>
)}
</div>
</section>
);
}