|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>StudyGen - Générateur de Flashcards & Quiz</title> |
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> |
|
<style> |
|
* { |
|
margin: 0; |
|
padding: 0; |
|
box-sizing: border-box; |
|
} |
|
|
|
:root { |
|
--primary-blue: #2563eb; |
|
--light-blue: #3b82f6; |
|
--extra-light-blue: #eff6ff; |
|
--white: #ffffff; |
|
--gray-50: #f9fafb; |
|
--gray-100: #f3f4f6; |
|
--gray-300: #d1d5db; |
|
--gray-600: #4b5563; |
|
--gray-800: #1f2937; |
|
--success: #10b981; |
|
--error: #ef4444; |
|
--shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); |
|
--shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.25); |
|
} |
|
|
|
body { |
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
|
background: linear-gradient(135deg, var(--extra-light-blue) 0%, var(--white) 50%, var(--gray-50) 100%); |
|
min-height: 100vh; |
|
padding: 0; |
|
color: var(--gray-800); |
|
line-height: 1.6; |
|
} |
|
|
|
.container { |
|
max-width: 100%; |
|
padding: 20px 16px; |
|
min-height: 100vh; |
|
} |
|
|
|
.header { |
|
text-align: center; |
|
margin-bottom: 32px; |
|
padding: 20px 0; |
|
} |
|
|
|
.header h1 { |
|
font-size: 2.5rem; |
|
font-weight: 800; |
|
background: linear-gradient(135deg, var(--primary-blue), var(--light-blue)); |
|
-webkit-background-clip: text; |
|
-webkit-text-fill-color: transparent; |
|
background-clip: text; |
|
margin-bottom: 8px; |
|
} |
|
|
|
.header p { |
|
color: var(--gray-600); |
|
font-size: 1.1rem; |
|
font-weight: 500; |
|
} |
|
|
|
.main-card { |
|
background: var(--white); |
|
border-radius: 24px; |
|
padding: 24px; |
|
box-shadow: var(--shadow); |
|
backdrop-filter: blur(10px); |
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
margin-bottom: 20px; |
|
transform: translateY(0); |
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
} |
|
|
|
.main-card:hover { |
|
transform: translateY(-2px); |
|
box-shadow: var(--shadow-lg); |
|
} |
|
|
|
.form-group { |
|
margin-bottom: 24px; |
|
} |
|
|
|
label { |
|
display: block; |
|
margin-bottom: 8px; |
|
font-weight: 600; |
|
color: var(--gray-800); |
|
font-size: 0.95rem; |
|
} |
|
|
|
input[type="text"] { |
|
width: 100%; |
|
padding: 16px 20px; |
|
border: 2px solid var(--gray-100); |
|
border-radius: 16px; |
|
font-size: 1rem; |
|
transition: all 0.3s ease; |
|
background: var(--gray-50); |
|
color: var(--gray-800); |
|
outline: none; |
|
} |
|
|
|
input[type="text"]:focus { |
|
border-color: var(--primary-blue); |
|
background: var(--white); |
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); |
|
transform: translateY(-1px); |
|
} |
|
|
|
.type-selector { |
|
display: flex; |
|
gap: 12px; |
|
margin-bottom: 24px; |
|
} |
|
|
|
.type-option { |
|
flex: 1; |
|
position: relative; |
|
} |
|
|
|
.type-option input[type="radio"] { |
|
display: none; |
|
} |
|
|
|
.type-option label { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
padding: 20px 16px; |
|
border: 2px solid var(--gray-200); |
|
border-radius: 16px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
background: var(--white); |
|
text-align: center; |
|
margin-bottom: 0; |
|
} |
|
|
|
.type-option label:hover { |
|
border-color: var(--light-blue); |
|
transform: translateY(-2px); |
|
} |
|
|
|
.type-option input[type="radio"]:checked + label { |
|
border-color: var(--primary-blue); |
|
background: linear-gradient(135deg, var(--primary-blue), var(--light-blue)); |
|
color: var(--white); |
|
box-shadow: var(--shadow); |
|
} |
|
|
|
.type-option i { |
|
font-size: 2rem; |
|
margin-bottom: 8px; |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.type-option input[type="radio"]:checked + label i { |
|
transform: scale(1.1); |
|
} |
|
|
|
.type-option span { |
|
font-weight: 600; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.generate-btn { |
|
width: 100%; |
|
background: linear-gradient(135deg, var(--primary-blue), var(--light-blue)); |
|
color: var(--white); |
|
border: none; |
|
padding: 18px 24px; |
|
border-radius: 16px; |
|
font-size: 1.1rem; |
|
font-weight: 600; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.generate-btn:hover { |
|
transform: translateY(-2px); |
|
box-shadow: var(--shadow-lg); |
|
} |
|
|
|
.generate-btn:active { |
|
transform: translateY(0); |
|
} |
|
|
|
.generate-btn:disabled { |
|
opacity: 0.7; |
|
cursor: not-allowed; |
|
transform: none; |
|
} |
|
|
|
.generate-btn .btn-text { |
|
transition: opacity 0.3s ease; |
|
} |
|
|
|
.generate-btn .spinner { |
|
position: absolute; |
|
top: 50%; |
|
left: 50%; |
|
transform: translate(-50%, -50%); |
|
opacity: 0; |
|
transition: opacity 0.3s ease; |
|
} |
|
|
|
.generate-btn.loading .btn-text { |
|
opacity: 0; |
|
} |
|
|
|
.generate-btn.loading .spinner { |
|
opacity: 1; |
|
} |
|
|
|
.spinner { |
|
width: 24px; |
|
height: 24px; |
|
border: 3px solid rgba(255, 255, 255, 0.3); |
|
border-radius: 50%; |
|
border-top-color: var(--white); |
|
animation: spin 1s ease-in-out infinite; |
|
} |
|
|
|
@keyframes spin { |
|
to { transform: translate(-50%, -50%) rotate(360deg); } |
|
} |
|
|
|
.results-container { |
|
margin-top: 24px; |
|
} |
|
|
|
|
|
.flashcard-container { |
|
perspective: 1000px; |
|
margin-bottom: 16px; |
|
} |
|
|
|
.flashcard { |
|
position: relative; |
|
min-height: 150px; |
|
transform-style: preserve-3d; |
|
transition: transform 0.6s; |
|
cursor: pointer; |
|
} |
|
|
|
.flashcard.is-flipped { |
|
transform: rotateY(180deg); |
|
} |
|
|
|
.flashcard-front, .flashcard-back { |
|
position: absolute; |
|
width: 100%; |
|
height: 100%; |
|
-webkit-backface-visibility: hidden; |
|
backface-visibility: hidden; |
|
display: flex; |
|
flex-direction: column; |
|
justify-content: center; |
|
align-items: center; |
|
padding: 24px; |
|
border-radius: 20px; |
|
background: var(--white); |
|
box-shadow: var(--shadow); |
|
border-left: 4px solid var(--primary-blue); |
|
} |
|
|
|
.flashcard-back { |
|
transform: rotateY(180deg); |
|
border-left-color: var(--success); |
|
} |
|
|
|
.flashcard h3 { |
|
color: var(--primary-blue); |
|
margin-bottom: 12px; |
|
font-size: 1.1rem; |
|
font-weight: 600; |
|
text-align: center; |
|
} |
|
|
|
.flashcard .answer { |
|
color: var(--gray-600); |
|
text-align: center; |
|
} |
|
|
|
.flashcard-front::after { |
|
content: 'Cliquez pour révéler'; |
|
position: absolute; |
|
bottom: 15px; |
|
font-size: 0.8rem; |
|
color: var(--gray-300); |
|
font-style: italic; |
|
} |
|
|
|
.quiz-question { |
|
background: var(--white); |
|
border-radius: 20px; |
|
padding: 24px; |
|
margin-bottom: 16px; |
|
box-shadow: var(--shadow); |
|
border-left: 4px solid var(--primary-blue); |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.quiz-question:hover { |
|
transform: translateX(4px); |
|
} |
|
|
|
.quiz-question h3 { |
|
color: var(--primary-blue); |
|
margin-bottom: 12px; |
|
font-size: 1.1rem; |
|
font-weight: 600; |
|
} |
|
|
|
.quiz-options { |
|
margin: 16px 0; |
|
} |
|
|
|
.quiz-option { |
|
padding: 12px 16px; |
|
margin: 8px 0; |
|
border: 2px solid var(--gray-100); |
|
border-radius: 12px; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
background: var(--white); |
|
} |
|
|
|
|
|
.quiz-question.answered .quiz-option { |
|
pointer-events: none; |
|
opacity: 0.8; |
|
} |
|
|
|
.quiz-option:hover { |
|
border-color: var(--light-blue); |
|
background: var(--extra-light-blue); |
|
} |
|
|
|
.quiz-option.correct { |
|
border-color: var(--success); |
|
background: #f0fdf4; |
|
color: #059669; |
|
font-weight: bold; |
|
} |
|
|
|
.quiz-option.incorrect { |
|
border-color: var(--error); |
|
background: #fef2f2; |
|
color: #dc2626; |
|
} |
|
|
|
.quiz-question.answered .quiz-option.correct { |
|
opacity: 1; |
|
} |
|
|
|
.quiz-explanation { |
|
margin-top: 16px; |
|
padding: 16px; |
|
background: var(--extra-light-blue); |
|
border-radius: 12px; |
|
border-left: 4px solid var(--primary-blue); |
|
} |
|
|
|
.error-message { |
|
background: #fef2f2; |
|
color: var(--error); |
|
padding: 16px 20px; |
|
border-radius: 12px; |
|
border: 1px solid #fecaca; |
|
margin-top: 16px; |
|
text-align: center; |
|
font-weight: 500; |
|
} |
|
|
|
.success-message { |
|
background: #f0fdf4; |
|
color: var(--success); |
|
padding: 16px 20px; |
|
border-radius: 12px; |
|
border: 1px solid #bbf7d0; |
|
margin-top: 16px; |
|
text-align: center; |
|
font-weight: 500; |
|
} |
|
|
|
.floating-elements { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
pointer-events: none; |
|
z-index: -1; |
|
} |
|
|
|
.floating-circle { |
|
position: absolute; |
|
border-radius: 50%; |
|
background: linear-gradient(135deg, rgba(37, 99, 235, 0.1), rgba(59, 130, 246, 0.05)); |
|
animation: float 6s ease-in-out infinite; |
|
} |
|
|
|
.floating-circle:nth-child(1) { |
|
width: 80px; |
|
height: 80px; |
|
top: 10%; |
|
left: 10%; |
|
animation-delay: 0s; |
|
} |
|
|
|
.floating-circle:nth-child(2) { |
|
width: 120px; |
|
height: 120px; |
|
top: 60%; |
|
right: 10%; |
|
animation-delay: 2s; |
|
} |
|
|
|
.floating-circle:nth-child(3) { |
|
width: 100px; |
|
height: 100px; |
|
bottom: 20%; |
|
left: 20%; |
|
animation-delay: 4s; |
|
} |
|
|
|
@keyframes float { |
|
0%, 100% { transform: translateY(0px) rotate(0deg); } |
|
50% { transform: translateY(-20px) rotate(180deg); } |
|
} |
|
|
|
|
|
.fade-in { |
|
animation: fadeIn 0.5s ease-out forwards; |
|
} |
|
|
|
@keyframes fadeIn { |
|
from { |
|
opacity: 0; |
|
transform: translateY(20px); |
|
} |
|
to { |
|
opacity: 1; |
|
transform: translateY(0); |
|
} |
|
} |
|
|
|
|
|
@media (min-width: 640px) { |
|
.container { |
|
max-width: 640px; |
|
margin: 0 auto; |
|
padding: 40px 24px; |
|
} |
|
|
|
.type-selector { |
|
gap: 16px; |
|
} |
|
|
|
.main-card { |
|
padding: 32px; |
|
} |
|
} |
|
|
|
|
|
@media (prefers-reduced-motion: reduce) { |
|
*, *::before, *::after { |
|
animation-duration: 0.01ms !important; |
|
animation-iteration-count: 1 !important; |
|
transition-duration: 0.01ms !important; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<div class="floating-elements"> |
|
<div class="floating-circle"></div> |
|
<div class="floating-circle"></div> |
|
<div class="floating-circle"></div> |
|
</div> |
|
|
|
<div class="container"> |
|
<div class="header"> |
|
<h1><i class="fas fa-graduation-cap"></i> StudyGen</h1> |
|
<p>Créez des flashcards et quiz personnalisés en quelques secondes</p> |
|
</div> |
|
|
|
<div class="main-card"> |
|
<form id="generateForm"> |
|
<div class="form-group"> |
|
<label for="topic"> |
|
<i class="fas fa-lightbulb"></i> Sujet d'étude |
|
</label> |
|
<input |
|
type="text" |
|
id="topic" |
|
name="topic" |
|
placeholder="Ex: Les révolutions françaises, Python, Anatomie..." |
|
required |
|
> |
|
</div> |
|
|
|
<div class="form-group"> |
|
<label> |
|
<i class="fas fa-tools"></i> Type de contenu |
|
</label> |
|
<div class="type-selector"> |
|
<div class="type-option"> |
|
<input type="radio" id="flashcards" name="type" value="flashcards" checked> |
|
<label for="flashcards"> |
|
<i class="fas fa-clone"></i> |
|
<span>Flashcards</span> |
|
</label> |
|
</div> |
|
<div class="type-option"> |
|
<input type="radio" id="quiz" name="type" value="quiz"> |
|
<label for="quiz"> |
|
<i class="fas fa-question-circle"></i> |
|
<span>Quiz</span> |
|
</label> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<button type="submit" class="generate-btn" id="generateBtn"> |
|
<span class="btn-text"> |
|
<i class="fas fa-magic"></i> Générer le contenu |
|
</span> |
|
<div class="spinner"></div> |
|
</button> |
|
</form> |
|
</div> |
|
|
|
<div id="results" class="results-container"></div> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
const form = document.getElementById('generateForm'); |
|
const generateBtn = document.getElementById('generateBtn'); |
|
const resultsContainer = document.getElementById('results'); |
|
|
|
form.addEventListener('submit', async function(e) { |
|
e.preventDefault(); |
|
|
|
const topic = document.getElementById('topic').value.trim(); |
|
const type = document.querySelector('input[name="type"]:checked').value; |
|
|
|
if (!topic) { |
|
showMessage('Veuillez entrer un sujet d\'étude.', 'error'); |
|
return; |
|
} |
|
|
|
generateBtn.classList.add('loading'); |
|
generateBtn.disabled = true; |
|
resultsContainer.innerHTML = ''; |
|
|
|
try { |
|
|
|
|
|
const response = await fetch('/generate', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ topic, type }) |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
if (data.success) { |
|
displayResults(data[type], type); |
|
showMessage(`${type === 'flashcards' ? 'Flashcards' : 'Quiz'} généré avec succès !`, 'success'); |
|
} else { |
|
showMessage(data.error || 'Une erreur est survenue lors de la génération.', 'error'); |
|
} |
|
} catch (error) { |
|
console.error('Erreur:', error); |
|
showMessage('Erreur de connexion. Veuillez réessayer.', 'error'); |
|
} finally { |
|
generateBtn.classList.remove('loading'); |
|
generateBtn.disabled = false; |
|
} |
|
}); |
|
|
|
function displayResults(data, type) { |
|
resultsContainer.innerHTML = ''; |
|
|
|
if (type === 'flashcards') { |
|
|
|
data.forEach((item, index) => { |
|
const flashcardContainer = document.createElement('div'); |
|
flashcardContainer.className = 'flashcard-container fade-in'; |
|
flashcardContainer.style.animationDelay = `${index * 0.1}s`; |
|
|
|
flashcardContainer.innerHTML = ` |
|
<div class="flashcard" onclick="this.classList.toggle('is-flipped')"> |
|
<div class="flashcard-front"> |
|
<h3><i class="fas fa-question"></i> ${item.question}</h3> |
|
</div> |
|
<div class="flashcard-back"> |
|
<div class="answer"> |
|
<i class="fas fa-lightbulb"></i> ${item.answer} |
|
</div> |
|
</div> |
|
</div> |
|
`; |
|
resultsContainer.appendChild(flashcardContainer); |
|
}); |
|
} else { |
|
|
|
data.forEach((item, index) => { |
|
const quizEl = document.createElement('div'); |
|
quizEl.className = 'quiz-question fade-in'; |
|
quizEl.style.animationDelay = `${index * 0.1}s`; |
|
|
|
quizEl.dataset.correctAnswer = item.correctAnswer; |
|
|
|
const optionsHtml = item.options.map(option => ` |
|
<div class="quiz-option" onclick="checkAnswer(this)"> |
|
${option} |
|
</div> |
|
`).join(''); |
|
|
|
quizEl.innerHTML = ` |
|
<h3><i class="fas fa-question-circle"></i> ${item.question}</h3> |
|
<div class="quiz-options">${optionsHtml}</div> |
|
<div class="quiz-explanation" style="display: none;"> |
|
<i class="fas fa-info-circle"></i> <strong>Explication :</strong> ${item.explanation} |
|
</div> |
|
`; |
|
resultsContainer.appendChild(quizEl); |
|
}); |
|
} |
|
} |
|
|
|
function showMessage(message, type) { |
|
const messageEl = document.createElement('div'); |
|
messageEl.className = `${type}-message fade-in`; |
|
messageEl.innerHTML = ` |
|
<i class="fas ${type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'}"></i> |
|
${message} |
|
`; |
|
|
|
const oldMessages = document.querySelectorAll('.error-message, .success-message'); |
|
oldMessages.forEach(msg => msg.remove()); |
|
|
|
resultsContainer.insertBefore(messageEl, resultsContainer.firstChild); |
|
|
|
setTimeout(() => { |
|
messageEl.remove(); |
|
}, 5000); |
|
} |
|
|
|
|
|
window.checkAnswer = function(optionEl) { |
|
const quizQuestion = optionEl.closest('.quiz-question'); |
|
if (quizQuestion.classList.contains('answered')) return; |
|
|
|
quizQuestion.classList.add('answered'); |
|
const correctAnswer = quizQuestion.dataset.correctAnswer; |
|
const selectedAnswer = optionEl.textContent.trim(); |
|
const explanation = quizQuestion.querySelector('.quiz-explanation'); |
|
const allOptions = quizQuestion.querySelectorAll('.quiz-option'); |
|
|
|
if (selectedAnswer === correctAnswer) { |
|
optionEl.classList.add('correct'); |
|
} else { |
|
optionEl.classList.add('incorrect'); |
|
} |
|
|
|
allOptions.forEach(opt => { |
|
const optText = opt.textContent.trim(); |
|
if (optText === correctAnswer) { |
|
opt.classList.add('correct'); |
|
opt.innerHTML = `<i class="fas fa-check"></i> ${optText}`; |
|
} else if (opt.classList.contains('incorrect')) { |
|
opt.innerHTML = `<i class="fas fa-times"></i> ${optText}`; |
|
} |
|
}); |
|
|
|
explanation.style.display = 'block'; |
|
explanation.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); |
|
}; |
|
}); |
|
</script> |
|
</body> |
|
</html> |