| | <!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> |