|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Générateur de Flashcards et Quiz</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> |
|
<script> |
|
tailwind.config = { |
|
theme: { |
|
extend: { |
|
colors: { |
|
primary: { |
|
50: '#f0f9ff', |
|
100: '#e0f2fe', |
|
200: '#bae6fd', |
|
300: '#7dd3fc', |
|
400: '#38bdf8', |
|
500: '#0ea5e9', |
|
600: '#0284c7', |
|
700: '#0369a1', |
|
800: '#075985', |
|
900: '#0c4a6e', |
|
} |
|
}, |
|
animation: { |
|
'bounce-slow': 'bounce 3s linear infinite', |
|
} |
|
} |
|
} |
|
} |
|
</script> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); |
|
|
|
body { |
|
font-family: 'Poppins', sans-serif; |
|
background-color: #f8fafc; |
|
} |
|
|
|
.flashcard { |
|
perspective: 1000px; |
|
height: 220px; |
|
} |
|
|
|
.flashcard-inner { |
|
position: relative; |
|
width: 100%; |
|
height: 100%; |
|
transition: transform 0.8s; |
|
transform-style: preserve-3d; |
|
} |
|
|
|
.flashcard.flipped .flashcard-inner { |
|
transform: rotateY(180deg); |
|
} |
|
|
|
.flashcard-front, .flashcard-back { |
|
position: absolute; |
|
width: 100%; |
|
height: 100%; |
|
-webkit-backface-visibility: hidden; |
|
backface-visibility: hidden; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
border-radius: 0.5rem; |
|
padding: 1.5rem; |
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); |
|
} |
|
|
|
.flashcard-front { |
|
background-color: #ffffff; |
|
color: #1e293b; |
|
border-left: 5px solid #0ea5e9; |
|
} |
|
|
|
.flashcard-back { |
|
background-color: #0ea5e9; |
|
color: white; |
|
transform: rotateY(180deg); |
|
} |
|
|
|
.quiz-option { |
|
position: relative; |
|
padding-left: 2.5rem; |
|
} |
|
|
|
.quiz-option input[type="radio"] { |
|
position: absolute; |
|
opacity: 0; |
|
} |
|
|
|
.quiz-option label { |
|
display: block; |
|
position: relative; |
|
padding: 1rem 1.5rem 1rem 2.5rem; |
|
cursor: pointer; |
|
border: 1px solid #e2e8f0; |
|
border-radius: 0.5rem; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.quiz-option label:before { |
|
content: ''; |
|
position: absolute; |
|
left: 1rem; |
|
top: 50%; |
|
transform: translateY(-50%); |
|
width: 1.25rem; |
|
height: 1.25rem; |
|
border-radius: 50%; |
|
border: 2px solid #cbd5e1; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.quiz-option input[type="radio"]:checked + label { |
|
background-color: #e0f2fe; |
|
border-color: #0ea5e9; |
|
} |
|
|
|
.quiz-option input[type="radio"]:checked + label:before { |
|
border-color: #0ea5e9; |
|
background-color: #0ea5e9; |
|
box-shadow: inset 0 0 0 4px #e0f2fe; |
|
} |
|
|
|
.quiz-option.correct input[type="radio"]:checked + label { |
|
background-color: #d1fae5; |
|
border-color: #10b981; |
|
} |
|
|
|
.quiz-option.correct input[type="radio"]:checked + label:before { |
|
border-color: #10b981; |
|
background-color: #10b981; |
|
box-shadow: inset 0 0 0 4px #d1fae5; |
|
} |
|
|
|
.quiz-option.incorrect input[type="radio"]:checked + label { |
|
background-color: #fee2e2; |
|
border-color: #ef4444; |
|
} |
|
|
|
.quiz-option.incorrect input[type="radio"]:checked + label:before { |
|
border-color: #ef4444; |
|
background-color: #ef4444; |
|
box-shadow: inset 0 0 0 4px #fee2e2; |
|
} |
|
|
|
.progress-bar-container { |
|
width: 100%; |
|
height: 8px; |
|
background-color: #e2e8f0; |
|
border-radius: 4px; |
|
overflow: hidden; |
|
} |
|
|
|
.progress-bar { |
|
height: 100%; |
|
background-color: #0ea5e9; |
|
transition: width 0.5s ease; |
|
} |
|
|
|
.floating-label { |
|
position: absolute; |
|
top: -10px; |
|
left: 10px; |
|
padding: 0 5px; |
|
background-color: white; |
|
transition: all 0.3s ease; |
|
pointer-events: none; |
|
} |
|
|
|
.pulse-animation { |
|
animation: pulse 2s infinite; |
|
} |
|
|
|
@keyframes pulse { |
|
0% { |
|
box-shadow: 0 0 0 0 rgba(14, 165, 233, 0.4); |
|
} |
|
70% { |
|
box-shadow: 0 0 0 10px rgba(14, 165, 233, 0); |
|
} |
|
100% { |
|
box-shadow: 0 0 0 0 rgba(14, 165, 233, 0); |
|
} |
|
} |
|
|
|
.confetti { |
|
position: fixed; |
|
width: 10px; |
|
height: 10px; |
|
background-color: #f00; |
|
opacity: 0; |
|
animation: confetti-fall 3s linear forwards; |
|
} |
|
|
|
@keyframes confetti-fall { |
|
0% { |
|
transform: translateY(0) rotate(0deg); |
|
opacity: 1; |
|
} |
|
100% { |
|
transform: translateY(100vh) rotate(360deg); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
.btn-primary { |
|
@apply bg-primary-600 text-white px-6 py-3 rounded-lg shadow-md hover:bg-primary-700 transition duration-300 ease-in-out transform hover:-translate-y-1 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-opacity-50; |
|
} |
|
|
|
.btn-secondary { |
|
@apply bg-white text-primary-600 border border-primary-600 px-6 py-3 rounded-lg shadow-md hover:bg-primary-50 transition duration-300 ease-in-out transform hover:-translate-y-1 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-opacity-50; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="min-h-screen bg-gradient-to-b from-primary-50 to-white"> |
|
<header class="bg-white shadow-sm py-6"> |
|
<div class="container mx-auto px-4"> |
|
<div class="flex items-center justify-between"> |
|
<a href="#" class="text-3xl font-bold text-primary-700 flex items-center"> |
|
<i class="fas fa-home mr-3 text-primary-500"></i> |
|
Mariam Quizz |
|
</a> |
|
<div class="flex space-x-2"> |
|
<button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100"> |
|
<i class="fas fa-moon text-gray-600"></i> |
|
</button> |
|
<button class="p-2 rounded-full hover:bg-gray-100"> |
|
<i class="fas fa-question-circle text-gray-600"></i> |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
</header> |
|
|
|
<main class="container mx-auto px-4 py-8"> |
|
<div class="max-w-3xl mx-auto"> |
|
|
|
<div class="text-center mb-8"> |
|
<h2 class="text-4xl font-bold text-gray-800 mb-4">Apprenez plus efficacement</h2> |
|
<p class="text-lg text-gray-600 mb-6">Créez des flashcards ou des quiz pour mémoriser n'importe quel sujet</p> |
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-xl shadow-lg p-8 mb-10 relative overflow-hidden"> |
|
<div class="absolute top-0 right-0 w-40 h-40 bg-primary-100 rounded-full -mr-20 -mt-20 z-0"></div> |
|
<div class="absolute bottom-0 left-0 w-24 h-24 bg-primary-100 rounded-full -ml-12 -mb-12 z-0"></div> |
|
|
|
<div class="relative z-10"> |
|
<h3 class="text-2xl font-semibold text-gray-800 mb-6">Que souhaitez-vous apprendre aujourd'hui ?</h3> |
|
|
|
<div class="space-y-6"> |
|
<div class="relative"> |
|
<input type="text" id="topic" class="w-full px-4 py-3 border-2 border-gray-300 rounded-lg focus:border-primary-500 focus:ring focus:ring-primary-200 focus:ring-opacity-50 transition-all duration-300 pl-10" placeholder="Entrez un sujet..."> |
|
<i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> |
|
<div class="text-xs text-gray-500 mt-1 ml-2">Exemples: "Capitales du monde", "Photosynthèse", "Verbes irréguliers en anglais"</div> |
|
</div> |
|
|
|
<div> |
|
<p class="text-gray-700 font-medium mb-3">Choisissez votre méthode d'apprentissage :</p> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
|
<div class="relative"> |
|
<input type="radio" id="typeFlashcards" name="contentType" value="flashcards" checked class="peer absolute opacity-0"> |
|
<label for="typeFlashcards" class="flex flex-col items-center justify-center p-4 border-2 border-gray-200 rounded-lg cursor-pointer transition-all duration-300 hover:bg-gray-50 peer-checked:border-primary-500 peer-checked:bg-primary-50"> |
|
<div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3"> |
|
<i class="fas fa-clone text-xl text-primary-600"></i> |
|
</div> |
|
<h4 class="font-semibold text-gray-800">Flashcards</h4> |
|
<p class="text-sm text-gray-600 text-center mt-2">Pour mémoriser des informations par répétition</p> |
|
</label> |
|
</div> |
|
|
|
<div class="relative"> |
|
<input type="radio" id="typeQuiz" name="contentType" value="quiz" class="peer absolute opacity-0"> |
|
<label for="typeQuiz" class="flex flex-col items-center justify-center p-4 border-2 border-gray-200 rounded-lg cursor-pointer transition-all duration-300 hover:bg-gray-50 peer-checked:border-primary-500 peer-checked:bg-primary-50"> |
|
<div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3"> |
|
<i class="fas fa-question text-xl text-primary-600"></i> |
|
</div> |
|
<h4 class="font-semibold text-gray-800">Quiz</h4> |
|
<p class="text-sm text-gray-600 text-center mt-2">Pour tester vos connaissances avec des QCM</p> |
|
</label> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="flex items-center justify-between pt-2"> |
|
<div class="text-sm text-gray-500 italic"> |
|
<i class="fas fa-info-circle mr-1"></i> La génération peut prendre jusqu'à 1 minute |
|
</div> |
|
<button id="generateBtn" class="btn-primary flex items-center"> |
|
<span class="mr-2">Générer</span> |
|
<i class="fas fa-chevron-right"></i> |
|
</button> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="loading" class="hidden"> |
|
<div class="bg-white rounded-xl shadow-lg p-8 text-center"> |
|
<div class="flex flex-col items-center justify-center space-y-4"> |
|
<div class="relative w-24 h-24"> |
|
<div class="absolute inset-0 border-4 border-primary-100 border-t-primary-500 rounded-full animate-spin"></div> |
|
<div class="absolute inset-0 flex items-center justify-center"> |
|
<i class="fas fa-lightbulb text-2xl text-primary-500 animate-pulse"></i> |
|
</div> |
|
</div> |
|
<h3 class="text-xl font-semibold text-gray-800">Création en cours...</h3> |
|
<p class="text-gray-600">Nous élaborons votre contenu d'apprentissage personnalisé</p> |
|
|
|
<div class="w-full max-w-md mt-4"> |
|
<div class="progress-bar-container"> |
|
<div id="progressBar" class="progress-bar" style="width: 0%"></div> |
|
</div> |
|
<div id="loadingSteps" class="mt-8 text-left"> |
|
<div class="flex items-center mb-3"> |
|
<div class="w-6 h-6 rounded-full bg-primary-500 flex items-center justify-center mr-3"> |
|
<i class="fas fa-check text-white text-xs"></i> |
|
</div> |
|
<span class="text-sm text-gray-700">Analyse du sujet</span> |
|
</div> |
|
<div class="flex items-center mb-3"> |
|
<div id="step2" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3"> |
|
<i class="fas fa-spinner text-white text-xs animate-spin"></i> |
|
</div> |
|
<span class="text-sm text-gray-500">Recherche d'informations</span> |
|
</div> |
|
<div class="flex items-center mb-3"> |
|
<div id="step3" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3"> |
|
<i class="fas fa-hourglass text-white text-xs"></i> |
|
</div> |
|
<span class="text-sm text-gray-500">Création du contenu</span> |
|
</div> |
|
<div class="flex items-center"> |
|
<div id="step4" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3"> |
|
<i class="fas fa-hourglass text-white text-xs"></i> |
|
</div> |
|
<span class="text-sm text-gray-500">Finalisation</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="flashcardsContainer" class="mt-8 hidden"> |
|
|
|
</div> |
|
|
|
|
|
<div id="quizContainer" class="mt-8 hidden"> |
|
|
|
</div> |
|
</div> |
|
</main> |
|
|
|
|
|
<footer class="hidden md:block bg-gray-900 text-white py-8 mt-20"> |
|
<div class="container mx-auto px-4"> |
|
<div class="flex flex-col md:flex-row justify-between items-center"> |
|
<div class="mb-4 md:mb-0"> |
|
<h2 class="text-xl font-bold flex items-center"> |
|
<i class="fas fa-brain mr-2"></i> Mémorisation Facile |
|
</h2> |
|
<p class="text-gray-400 text-sm mt-2">Votre outil d'apprentissage intelligent</p> |
|
</div> |
|
<div class="flex space-x-4"> |
|
<a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-twitter"></i></a> |
|
<a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-facebook"></i></a> |
|
<a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-instagram"></i></a> |
|
<a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-youtube"></i></a> |
|
</div> |
|
</div> |
|
<div class="mt-8 border-t border-gray-800 pt-6 text-sm text-gray-400 text-center"> |
|
© 2025 Mémorisation Facile. Tous droits réservés. |
|
</div> |
|
</div> |
|
</footer> |
|
</div> |
|
|
|
<script> |
|
const generateBtn = document.getElementById('generateBtn'); |
|
const loadingIndicator = document.getElementById('loading'); |
|
const flashcardsContainer = document.getElementById('flashcardsContainer'); |
|
const quizContainer = document.getElementById('quizContainer'); |
|
const topicInput = document.getElementById('topic'); |
|
const progressBar = document.getElementById('progressBar'); |
|
const themeToggle = document.getElementById('themeToggle'); |
|
const step2 = document.getElementById('step2'); |
|
const step3 = document.getElementById('step3'); |
|
const step4 = document.getElementById('step4'); |
|
|
|
|
|
themeToggle.addEventListener('click', function() { |
|
document.body.classList.toggle('dark-mode'); |
|
const icon = this.querySelector('i'); |
|
if (icon.classList.contains('fa-moon')) { |
|
icon.classList.remove('fa-moon'); |
|
icon.classList.add('fa-sun'); |
|
} else { |
|
icon.classList.remove('fa-sun'); |
|
icon.classList.add('fa-moon'); |
|
} |
|
|
|
if (document.body.classList.contains('dark-mode')) { |
|
document.body.style.backgroundColor = '#1f2937'; |
|
document.body.style.color = '#f3f4f6'; |
|
} else { |
|
document.body.style.backgroundColor = '#f8fafc'; |
|
document.body.style.color = 'initial'; |
|
} |
|
}); |
|
|
|
|
|
function simulateLoading() { |
|
loadingIndicator.classList.remove('hidden'); |
|
progressBar.style.width = '0%'; |
|
|
|
[step2, step3, step4].forEach(step => { |
|
step.className = 'w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3'; |
|
step.innerHTML = '<i class="fas fa-hourglass text-white text-xs"></i>'; |
|
}); |
|
|
|
const step1IconContainer = loadingSteps.children[0].querySelector('div'); |
|
step1IconContainer.className = 'w-6 h-6 rounded-full bg-primary-500 flex items-center justify-center mr-3'; |
|
step1IconContainer.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; |
|
step2.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>'; |
|
|
|
let progress = 0; |
|
const interval = setInterval(() => { |
|
progress += 1; |
|
progressBar.style.width = `${Math.min(progress, 100)}%`; |
|
|
|
if (progress === 25) { |
|
step2.classList.remove('bg-gray-200'); |
|
step2.classList.add('bg-primary-500'); |
|
step2.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; |
|
step3.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>'; |
|
} else if (progress === 60) { |
|
step3.classList.remove('bg-gray-200'); |
|
step3.classList.add('bg-primary-500'); |
|
step3.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; |
|
step4.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>'; |
|
} else if (progress === 95) { |
|
step4.classList.remove('bg-gray-200'); |
|
step4.classList.add('bg-primary-500'); |
|
step4.innerHTML = '<i class="fas fa-check text-white text-xs"></i>'; |
|
} |
|
|
|
if (progress >= 100) { |
|
clearInterval(interval); |
|
|
|
} |
|
}, 50); |
|
|
|
return interval; |
|
} |
|
|
|
|
|
function createConfetti() { |
|
const confettiCount = 100; |
|
const colors = ['#0ea5e9', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']; |
|
|
|
for (let i = 0; i < confettiCount; i++) { |
|
const confetti = document.createElement('div'); |
|
confetti.className = 'confetti fixed top-0 pointer-events-none z-50'; |
|
confetti.style.left = `${Math.random() * 100}vw`; |
|
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; |
|
confetti.style.width = `${Math.random() * 10 + 5}px`; |
|
confetti.style.height = `${Math.random() * 10 + 5}px`; |
|
|
|
confetti.style.animation = `confetti-fall ${Math.random() * 3 + 2}s linear forwards`; |
|
confetti.style.transform = `translateY(-20px) rotate(${Math.random() * 360}deg)`; |
|
confetti.style.opacity = '1'; |
|
|
|
document.body.appendChild(confetti); |
|
|
|
setTimeout(() => { |
|
confetti.remove(); |
|
}, 5000); |
|
} |
|
} |
|
|
|
|
|
|
|
function flipCard(card) { |
|
card.classList.toggle('flipped'); |
|
} |
|
|
|
generateBtn.addEventListener('click', function() { |
|
const topic = topicInput.value.trim(); |
|
|
|
const existingError = topicInput.parentElement.querySelector('.text-red-500'); |
|
if (existingError) { |
|
existingError.remove(); |
|
} |
|
|
|
document.querySelectorAll('.error-container-global').forEach(el => el.remove()); |
|
|
|
|
|
if (!topic) { |
|
|
|
topicInput.classList.add('border-red-500'); |
|
topicInput.classList.add('animate-bounce'); |
|
setTimeout(() => { |
|
topicInput.classList.remove('animate-bounce'); |
|
topicInput.classList.remove('border-red-500'); |
|
}, 1000); |
|
|
|
|
|
const errorMsg = document.createElement('div'); |
|
errorMsg.className = 'text-red-500 text-sm mt-1 ml-2'; |
|
errorMsg.textContent = 'Veuillez entrer un sujet'; |
|
topicInput.parentElement.appendChild(errorMsg); |
|
|
|
return; |
|
} |
|
|
|
const contentType = document.querySelector('input[name="contentType"]:checked').value; |
|
|
|
|
|
flashcardsContainer.classList.add('hidden'); |
|
quizContainer.classList.add('hidden'); |
|
flashcardsContainer.innerHTML = ''; |
|
quizContainer.innerHTML = ''; |
|
loadingIndicator.classList.remove('hidden'); |
|
|
|
|
|
const loadingInterval = simulateLoading(); |
|
const minLoadingTime = 3000; |
|
const startTime = Date.now(); |
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
const elapsedTime = Date.now() - startTime; |
|
const remainingTime = Math.max(0, minLoadingTime - elapsedTime); |
|
|
|
|
|
let fakeData; |
|
if (contentType === 'flashcards') { |
|
fakeData = { |
|
flashcards: [ |
|
{ question: `Question 1 sur ${topic}?`, answer: `Réponse 1 sur ${topic}` }, |
|
{ question: `Question 2 sur ${topic}?`, answer: `Réponse 2 sur ${topic}` }, |
|
{ question: `Question 3 sur ${topic}?`, answer: `Réponse 3 sur ${topic}` }, |
|
{ question: `Question 4 sur ${topic}?`, answer: `Réponse 4 sur ${topic}` }, |
|
{ question: `Question 5 sur ${topic}?`, answer: `Réponse 5 sur ${topic}` } |
|
] |
|
}; |
|
} else { |
|
const options = [`Option A pour ${topic}`, `Option B pour ${topic}`, `Option C pour ${topic}`, `Option D pour ${topic}`]; |
|
fakeData = { |
|
quiz: [ |
|
{ question: `Quiz 1: Quelle est la capitale de ${topic}?`, options: [...options].sort(() => Math.random() - 0.5), correctAnswer: options[0], explanation: `Explication pour quiz 1 sur ${topic}.` }, |
|
{ question: `Quiz 2: Comment fonctionne ${topic}?`, options: [...options].sort(() => Math.random() - 0.5), correctAnswer: options[1], explanation: `Explication pour quiz 2 sur ${topic}.` }, |
|
{ question: `Quiz 3: Pourquoi ${topic} est important?`, options: [...options].sort(() => Math.random() - 0.5), correctAnswer: options[2], explanation: `Explication pour quiz 3 sur ${topic}.` } |
|
] |
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
clearInterval(loadingInterval); |
|
progressBar.style.width = '100%'; |
|
|
|
|
|
setTimeout(() => { |
|
loadingIndicator.classList.add('hidden'); |
|
|
|
if (fakeData.error) { |
|
displayGlobalError(fakeData.error); |
|
} else if (contentType === 'flashcards' && fakeData.flashcards) { |
|
flashcardsContainer.classList.remove('hidden'); |
|
displayFlashcards(fakeData.flashcards); |
|
createConfetti(); |
|
} else if (contentType === 'quiz' && fakeData.quiz) { |
|
quizContainer.classList.remove('hidden'); |
|
displayQuiz(fakeData.quiz); |
|
createConfetti(); |
|
} else { |
|
displayGlobalError("Aucune donnée reçue ou format incorrect."); |
|
} |
|
}, 300); |
|
}, remainingTime); |
|
|
|
}, 1500); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
function displayGlobalError(message) { |
|
const errorContainer = document.createElement('div'); |
|
errorContainer.className = 'error-container-global bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-md mt-6 mb-6'; |
|
errorContainer.innerHTML = ` |
|
<div class="flex"> |
|
<div class="flex-shrink-0"> |
|
<i class="fas fa-exclamation-triangle text-red-500 fa-lg"></i> |
|
</div> |
|
<div class="ml-3"> |
|
<h3 class="text-sm font-medium">Une erreur est survenue</h3> |
|
<p class="text-sm mt-1"> |
|
${message} |
|
</p> |
|
</div> |
|
</div> |
|
`; |
|
|
|
const configSection = document.querySelector('.bg-white.rounded-xl.shadow-lg.p-8.mb-10'); |
|
if (configSection) { |
|
configSection.parentNode.insertBefore(errorContainer, configSection.nextSibling); |
|
} else { |
|
|
|
document.querySelector('main .max-w-3xl').appendChild(errorContainer); |
|
} |
|
} |
|
|
|
|
|
function displayFlashcards(flashcards) { |
|
flashcardsContainer.innerHTML = ''; |
|
|
|
const header = document.createElement('div'); |
|
header.className = 'mb-8 text-center'; |
|
header.innerHTML = ` |
|
<h2 class="text-3xl font-bold text-gray-800 mb-2">Vos Flashcards (${flashcards.length})</h2> |
|
<p class="text-gray-600">Cliquez sur une carte pour la retourner et voir la réponse.</p> |
|
<p class="text-gray-500 text-sm mt-1"> |
|
<i class="far fa-lightbulb mr-1"></i> Astuce : Essayez de répondre mentalement avant de retourner la carte. |
|
</p> |
|
`; |
|
flashcardsContainer.appendChild(header); |
|
|
|
const flashcardsGrid = document.createElement('div'); |
|
flashcardsGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6'; |
|
flashcardsContainer.appendChild(flashcardsGrid); |
|
|
|
flashcards.forEach((card, index) => { |
|
const cardElement = document.createElement('div'); |
|
|
|
cardElement.style.animation = `fadeInUp 0.5s ease-out ${index * 0.1}s forwards`; |
|
cardElement.style.opacity = '0'; |
|
cardElement.className = 'flashcard cursor-pointer'; |
|
cardElement.onclick = function() { flipCard(this); }; |
|
cardElement.innerHTML = ` |
|
<div class="flashcard-inner"> |
|
<div class="flashcard-front"> |
|
<div class="w-full flex flex-col justify-between h-full"> |
|
<div class="text-left text-xs text-gray-400">#${index + 1}</div> |
|
<h3 class="text-lg font-semibold text-center flex-grow flex items-center justify-center px-2">${card.question}</h3> |
|
<div class="text-center mt-auto"> |
|
<span class="text-xs text-gray-500">Cliquez pour voir la réponse</span> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="flashcard-back"> |
|
<div class="w-full flex flex-col justify-between h-full"> |
|
<div class="text-right text-xs text-white opacity-70">#${index + 1}</div> |
|
<div class="text-center flex-grow flex items-center justify-center px-2"> |
|
<p class="font-medium">${card.answer}</p> |
|
</div> |
|
<div class="text-right mt-auto"> |
|
<span class="text-xs text-white opacity-70">Cliquez pour retourner</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
`; |
|
flashcardsGrid.appendChild(cardElement); |
|
}); |
|
|
|
|
|
const controls = document.createElement('div'); |
|
controls.className = 'mt-10 flex flex-col items-center justify-center space-y-4'; |
|
controls.innerHTML = ` |
|
<div class="flex items-center space-x-4"> |
|
<button id="newTopicBtn" class="btn-primary"> |
|
<i class="fas fa-plus mr-2"></i> Nouveau sujet |
|
</button> |
|
<button id="restartFlashcardsBtn" class="btn-secondary"> |
|
<i class="fas fa-redo-alt mr-2"></i> Recommencer (retourner tout) |
|
</button> |
|
</div> |
|
<!-- Ajouter d'autres boutons si nécessaire --> |
|
`; |
|
flashcardsContainer.appendChild(controls); |
|
|
|
|
|
document.getElementById('newTopicBtn').addEventListener('click', function() { |
|
|
|
flashcardsContainer.classList.add('hidden'); |
|
flashcardsContainer.innerHTML = ''; |
|
quizContainer.classList.add('hidden'); |
|
quizContainer.innerHTML = ''; |
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' }); |
|
topicInput.value = ''; |
|
topicInput.focus(); |
|
}); |
|
|
|
document.getElementById('restartFlashcardsBtn').addEventListener('click', function() { |
|
const allCards = flashcardsContainer.querySelectorAll('.flashcard.flipped'); |
|
allCards.forEach(card => card.classList.remove('flipped')); |
|
|
|
const firstCard = flashcardsContainer.querySelector('.flashcard'); |
|
if(firstCard) { |
|
firstCard.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
|
} |
|
}); |
|
} |
|
|
|
function displayQuiz(quizQuestions) { |
|
quizContainer.innerHTML = ''; |
|
|
|
const header = document.createElement('div'); |
|
header.className = 'mb-8 text-center'; |
|
header.innerHTML = ` |
|
<h2 class="text-3xl font-bold text-gray-800 mb-2">Votre Quiz (${quizQuestions.length} questions)</h2> |
|
<p class="text-gray-600">Testez vos connaissances en répondant aux questions</p> |
|
<div class="flex items-center justify-center mt-6"> |
|
<div class="bg-white px-4 py-2 rounded-full shadow-sm flex items-center border border-gray-200"> |
|
<span class="text-primary-700 font-medium">Score: </span> |
|
<span id="score" class="ml-1 text-primary-700 font-bold">0</span> |
|
<span class="mx-1 text-gray-400">/</span> |
|
<span class="text-gray-600">${quizQuestions.length}</span> |
|
</div> |
|
</div> |
|
`; |
|
quizContainer.appendChild(header); |
|
|
|
const questionsContainer = document.createElement('div'); |
|
questionsContainer.className = 'space-y-8'; |
|
quizContainer.appendChild(questionsContainer); |
|
|
|
let currentScore = 0; |
|
let answeredQuestions = 0; |
|
|
|
quizQuestions.forEach((question, qIndex) => { |
|
const questionElement = document.createElement('div'); |
|
questionElement.className = 'bg-white rounded-xl shadow-md p-6 transition-all duration-300 question-card'; |
|
questionElement.setAttribute('id', `question-${qIndex}`); |
|
|
|
questionElement.style.animation = `fadeInUp 0.5s ease-out ${qIndex * 0.15}s forwards`; |
|
questionElement.style.opacity = '0'; |
|
|
|
let optionsHtml = ''; |
|
|
|
const safeCorrectAnswer = String(question.correctAnswer).replace(/"/g, '"'); |
|
|
|
|
|
const shuffledOptions = [...question.options].sort(() => Math.random() - 0.5); |
|
|
|
shuffledOptions.forEach((option, oIndex) => { |
|
const safeOption = String(option).replace(/"/g, '"'); |
|
optionsHtml += ` |
|
<div class="quiz-option mb-3" id="option-${qIndex}-${oIndex}"> |
|
<input type="radio" id="q${qIndex}-o${oIndex}" name="question-${qIndex}" value="${safeOption}" data-correct="${safeCorrectAnswer}"> |
|
<label for="q${qIndex}-o${oIndex}" class="group flex items-center"> |
|
<span class="flex-grow">${option}</span> |
|
<span class="ml-2 hidden success-icon text-green-500 text-lg"> |
|
<i class="fas fa-check-circle"></i> |
|
</span> |
|
<span class="ml-2 hidden error-icon text-red-500 text-lg"> |
|
<i class="fas fa-times-circle"></i> |
|
</span> |
|
</label> |
|
</div> |
|
`; |
|
}); |
|
|
|
questionElement.innerHTML = ` |
|
<div class="flex items-center justify-between mb-4"> |
|
<span class="bg-primary-100 text-primary-800 text-xs font-medium px-2.5 py-0.5 rounded-full">Question ${qIndex + 1}/${quizQuestions.length}</span> |
|
<!-- On pourrait ajouter une icône d'info/aide ici si pertinent --> |
|
</div> |
|
<h3 class="text-lg md:text-xl font-medium text-gray-800 mb-5">${question.question}</h3> |
|
<div class="options mt-5 space-y-3"> <!-- Ajout space-y pour l'espacement --> |
|
${optionsHtml} |
|
</div> |
|
<div class="explanation hidden mt-6 p-4 bg-sky-50 border border-sky-200 rounded-lg" id="explanation-${qIndex}"> |
|
<div class="flex items-start"> |
|
<div class="flex-shrink-0 mt-0.5"> |
|
<i class="fas fa-info-circle text-sky-500"></i> |
|
</div> |
|
<div class="ml-3"> |
|
<h4 class="text-sm font-semibold text-sky-800">Explication</h4> |
|
<div class="mt-1 text-sm text-sky-700">${question.explanation || "Pas d'explication fournie."}</div> |
|
</div> |
|
</div> |
|
</div> |
|
`; |
|
questionsContainer.appendChild(questionElement); |
|
|
|
const optionsInputs = questionElement.querySelectorAll('input[type="radio"]'); |
|
optionsInputs.forEach((optionInput) => { |
|
optionInput.addEventListener('change', function() { |
|
|
|
if (questionElement.dataset.answered === 'true') return; |
|
questionElement.dataset.answered = 'true'; |
|
answeredQuestions++; |
|
|
|
|
|
const qIdx = qIndex; |
|
const selectedValue = this.value; |
|
const correctAnswer = this.getAttribute('data-correct'); |
|
const parentOptionDiv = this.parentElement; |
|
const explanationElement = document.getElementById(`explanation-${qIdx}`); |
|
|
|
|
|
optionsInputs.forEach(opt => opt.disabled = true); |
|
|
|
let isCorrect = false; |
|
if (selectedValue === correctAnswer) { |
|
|
|
parentOptionDiv.classList.add('correct'); |
|
parentOptionDiv.querySelector('.success-icon').classList.remove('hidden'); |
|
currentScore++; |
|
document.getElementById('score').textContent = currentScore; |
|
isCorrect = true; |
|
} else { |
|
|
|
parentOptionDiv.classList.add('incorrect'); |
|
parentOptionDiv.querySelector('.error-icon').classList.remove('hidden'); |
|
|
|
|
|
optionsInputs.forEach(opt => { |
|
if (opt.value === correctAnswer) { |
|
const correctParentDiv = opt.parentElement; |
|
correctParentDiv.classList.add('correct'); |
|
correctParentDiv.querySelector('.success-icon').classList.remove('hidden'); |
|
} |
|
}); |
|
} |
|
|
|
|
|
explanationElement.classList.remove('hidden'); |
|
explanationElement.style.animation = 'fadeIn 0.5s ease-in'; |
|
|
|
|
|
if (answeredQuestions === quizQuestions.length) { |
|
|
|
setTimeout(() => { |
|
displayQuizResults(currentScore, quizQuestions.length); |
|
}, isCorrect ? 1500 : 2500); |
|
} else { |
|
|
|
const nextQuestion = document.getElementById(`question-${qIdx + 1}`); |
|
if (nextQuestion) { |
|
setTimeout(() => { |
|
nextQuestion.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
|
}, isCorrect ? 1000 : 2000); |
|
} |
|
} |
|
}); |
|
}); |
|
}); |
|
} |
|
|
|
|
|
function displayQuizResults(score, total) { |
|
|
|
const existingResults = quizContainer.querySelector('.quiz-results'); |
|
if (existingResults) existingResults.remove(); |
|
|
|
const percentage = total > 0 ? Math.round((score / total) * 100) : 0; |
|
let resultClass, resultIcon, resultMessage, colorClass; |
|
|
|
if (percentage >= 80) { |
|
resultClass = 'bg-green-50 border-green-500 text-green-800'; |
|
resultIcon = '<i class="fas fa-trophy text-4xl text-yellow-400 mb-4"></i>'; |
|
resultMessage = 'Excellent ! Vous maîtrisez ce sujet.'; |
|
colorClass = 'bg-green-500'; |
|
} else if (percentage >= 50) { |
|
resultClass = 'bg-blue-50 border-blue-500 text-blue-800'; |
|
resultIcon = '<i class="fas fa-award text-4xl text-blue-500 mb-4"></i>'; |
|
resultMessage = 'Bon travail ! Continuez vos efforts.'; |
|
colorClass = 'bg-blue-500'; |
|
} else { |
|
resultClass = 'bg-red-50 border-red-500 text-red-800'; |
|
resultIcon = '<i class="fas fa-book-reader text-4xl text-red-500 mb-4"></i>'; |
|
resultMessage = 'Continuez à étudier pour améliorer vos connaissances.'; |
|
colorClass = 'bg-red-500'; |
|
} |
|
|
|
const resultsElement = document.createElement('div'); |
|
resultsElement.className = `quiz-results mt-12 p-6 rounded-xl shadow-lg border-t-4 ${resultClass} text-center`; |
|
resultsElement.style.animation = 'fadeInUp 0.6s ease-out'; |
|
resultsElement.innerHTML = ` |
|
<div class="flex flex-col items-center"> |
|
${resultIcon} |
|
<h3 class="text-2xl font-bold mb-2">Résultat final</h3> |
|
<p class="text-3xl font-bold mb-3">${score} / ${total}</p> |
|
<div class="w-full max-w-xs bg-gray-200 rounded-full h-5 mb-4 overflow-hidden border border-gray-300"> |
|
<div class="h-full rounded-full ${colorClass} text-xs font-medium text-white text-center p-0.5 leading-none flex items-center justify-center" style="width: ${percentage}%"> |
|
${percentage}% |
|
</div> |
|
</div> |
|
<p class="mb-6">${resultMessage}</p> |
|
<div class="flex flex-col sm:flex-row space-y-3 sm:space-y-0 sm:space-x-4 mt-4"> |
|
<button id="retryQuizBtn" class="btn-secondary"> |
|
<i class="fas fa-redo mr-2"></i> Réessayer le Quiz |
|
</button> |
|
<button id="newQuizBtnResults" class="btn-primary"> |
|
<i class="fas fa-plus mr-2"></i> Nouveau Sujet |
|
</button> |
|
</div> |
|
</div> |
|
`; |
|
quizContainer.appendChild(resultsElement); |
|
|
|
|
|
if (percentage >= 70) { |
|
createConfetti(); |
|
} |
|
|
|
|
|
resultsElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); |
|
|
|
|
|
|
|
document.getElementById('retryQuizBtn').addEventListener('click', function() { |
|
|
|
resultsElement.remove(); |
|
|
|
document.getElementById('score').textContent = '0'; |
|
|
|
const allQuestions = quizContainer.querySelectorAll('.question-card'); |
|
allQuestions.forEach(qElement => { |
|
qElement.dataset.answered = 'false'; |
|
const radios = qElement.querySelectorAll('input[type="radio"]'); |
|
radios.forEach(radio => { |
|
radio.checked = false; |
|
radio.disabled = false; |
|
}); |
|
const optionsDivs = qElement.querySelectorAll('.quiz-option'); |
|
optionsDivs.forEach(div => { |
|
div.classList.remove('correct', 'incorrect'); |
|
div.querySelectorAll('.success-icon, .error-icon').forEach(icon => icon.classList.add('hidden')); |
|
}); |
|
const explanation = qElement.querySelector('.explanation'); |
|
if (explanation) explanation.classList.add('hidden'); |
|
}); |
|
|
|
|
|
const firstQuestion = document.getElementById('question-0'); |
|
if (firstQuestion) { |
|
firstQuestion.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
} |
|
|
|
currentScore = 0; |
|
answeredQuestions = 0; |
|
}); |
|
|
|
document.getElementById('newQuizBtnResults').addEventListener('click', function() { |
|
|
|
quizContainer.classList.add('hidden'); |
|
quizContainer.innerHTML = ''; |
|
flashcardsContainer.classList.add('hidden'); |
|
flashcardsContainer.innerHTML = ''; |
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' }); |
|
topicInput.value = ''; |
|
topicInput.focus(); |
|
}); |
|
} |
|
|
|
|
|
const styleSheet = document.createElement("style"); |
|
styleSheet.type = "text/css"; |
|
styleSheet.innerText = ` |
|
@keyframes fadeInUp { |
|
from { |
|
opacity: 0; |
|
transform: translateY(20px); |
|
} |
|
to { |
|
opacity: 1; |
|
transform: translateY(0); |
|
} |
|
} |
|
@keyframes fadeIn { |
|
from { opacity: 0; } |
|
to { opacity: 1; } |
|
} |
|
/* Style pour le dark mode (très basique) */ |
|
body.dark-mode { background-color: #111827; color: #d1d5db; } |
|
body.dark-mode .bg-white { background-color: #1f2937; border-color: #374151; } |
|
body.dark-mode .bg-gray-50 { background-color: #374151; } |
|
body.dark-mode .text-gray-800 { color: #f9fafb; } |
|
body.dark-mode .text-gray-600 { color: #9ca3af; } |
|
body.dark-mode .text-gray-700 { color: #d1d5db; } |
|
body.dark-mode .text-gray-500 { color: #6b7280; } |
|
body.dark-mode .text-gray-400 { color: #4b5563; } |
|
body.dark-mode .border-gray-200 { border-color: #374151; } |
|
body.dark-mode .border-gray-300 { border-color: #4b5563; } |
|
body.dark-mode .shadow-sm { box-shadow: 0 1px 2px 0 rgba(255, 255, 255, 0.05); } |
|
body.dark-mode .shadow-lg { box-shadow: 0 10px 15px -3px rgba(255, 255, 255, 0.05), 0 4px 6px -2px rgba(255, 255, 255, 0.03); } |
|
body.dark-mode .shadow-md { box-shadow: 0 4px 6px -1px rgba(255, 255, 255, 0.06), 0 2px 4px -1px rgba(255, 255, 255, 0.04); } |
|
body.dark-mode input[type="text"] { background-color: #374151; border-color: #4b5563; color: #f3f4f6; } |
|
body.dark-mode input[type="text"]::placeholder { color: #6b7280; } |
|
body.dark-mode .quiz-option label { border-color: #374151; } |
|
body.dark-mode .quiz-option label:hover { background-color: #4b5563; } |
|
body.dark-mode .quiz-option input[type="radio"]:checked + label { background-color: #0c4a6e; border-color: #0ea5e9; color: white; } /* Adjust primary dark check */ |
|
body.dark-mode .quiz-option input[type="radio"]:checked + label:before { border-color: #0ea5e9; background-color: #0ea5e9; box-shadow: inset 0 0 0 4px #0c4a6e; } |
|
body.dark-mode .flashcard-front { background-color: #1f2937; color: #f3f4f6; border-left-color: #0ea5e9; } |
|
/* Ajuster les couleurs primaires pour le dark mode si nécessaire */ |
|
body.dark-mode .bg-primary-50 { background-color: #0c4a6e; } |
|
body.dark-mode .bg-primary-100 { background-color: #075985; } |
|
body.dark-mode .text-primary-600 { color: #38bdf8; } |
|
body.dark-mode .text-primary-700 { color: #7dd3fc; } |
|
body.dark-mode .text-primary-800 { color: #bae6fd; } |
|
body.dark-mode .border-primary-500 { border-color: #38bdf8; } |
|
body.dark-mode .peer-checked\\:bg-primary-50:checked ~ label { background-color: #0c4a6e; } |
|
body.dark-mode .peer-checked\\:border-primary-500:checked ~ label { border-color: #38bdf8; } |
|
body.dark-mode .btn-primary { background-color: #0284c7; hover:bg-primary-700; } /* Exemple d'ajustement bouton */ |
|
body.dark-mode .btn-secondary { background-color: #374151; color:#38bdf8; border-color:#38bdf8; hover:bg-gray-600 } /* Exemple d'ajustement bouton */ |
|
body.dark-mode #themeToggle i { color: #e5e7eb; } /* Icône du thème */ |
|
body.dark-mode .progress-bar-container { background-color: #374151; } |
|
body.dark-mode .progress-bar { background-color: #0ea5e9; } |
|
body.dark-mode .bg-gradient-to-b { background-image: linear-gradient(to bottom, #0c4a6e, #111827); } /* Gradient dark */ |
|
body.dark-mode header { background-color: #1f2937; } /* Header dark */ |
|
`; |
|
document.head.appendChild(styleSheet); |
|
|
|
|
|
window.addEventListener('load', () => { |
|
topicInput.focus(); |
|
}); |
|
</script> |
|
</body> |
|
</html> |