Docfile commited on
Commit
741e703
·
verified ·
1 Parent(s): e190b23

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +802 -970
templates/index.html CHANGED
@@ -1,4 +1,6 @@
 
1
  <!DOCTYPE html>
 
2
  <html lang="fr">
3
  <head>
4
  <meta charset="UTF-8">
@@ -33,198 +35,199 @@
33
  </script>
34
  <style>
35
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
36
-
37
- body {
38
- font-family: 'Poppins', sans-serif;
39
- background-color: #f8fafc;
40
- }
41
-
42
- .flashcard {
43
- perspective: 1000px;
44
- height: 220px;
45
- }
46
-
47
- .flashcard-inner {
48
- position: relative;
49
- width: 100%;
50
- height: 100%;
51
- transition: transform 0.8s;
52
- transform-style: preserve-3d;
53
- }
54
-
55
- .flashcard.flipped .flashcard-inner {
56
- transform: rotateY(180deg);
57
- }
58
-
59
- .flashcard-front, .flashcard-back {
60
- position: absolute;
61
- width: 100%;
62
- height: 100%;
63
- -webkit-backface-visibility: hidden;
64
- backface-visibility: hidden;
65
- display: flex;
66
- align-items: center;
67
- justify-content: center;
68
- border-radius: 0.5rem;
69
- padding: 1.5rem;
70
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
71
- }
72
-
73
- .flashcard-front {
74
- background-color: #ffffff;
75
- color: #1e293b;
76
- border-left: 5px solid #0ea5e9;
77
- }
78
-
79
- .flashcard-back {
80
- background-color: #0ea5e9;
81
- color: white;
82
- transform: rotateY(180deg);
83
- }
84
-
85
- .quiz-option {
86
- position: relative;
87
- padding-left: 2.5rem;
88
- }
89
-
90
- .quiz-option input[type="radio"] {
91
- position: absolute;
92
- opacity: 0;
93
- }
94
-
95
- .quiz-option label {
96
- display: block;
97
- position: relative;
98
- padding: 1rem 1.5rem 1rem 2.5rem;
99
- cursor: pointer;
100
- border: 1px solid #e2e8f0;
101
- border-radius: 0.5rem;
102
- transition: all 0.3s ease;
103
- }
104
-
105
- .quiz-option label:before {
106
- content: '';
107
- position: absolute;
108
- left: 1rem;
109
- top: 50%;
110
- transform: translateY(-50%);
111
- width: 1.25rem;
112
- height: 1.25rem;
113
- border-radius: 50%;
114
- border: 2px solid #cbd5e1;
115
- transition: all 0.3s ease;
116
- }
117
-
118
- .quiz-option input[type="radio"]:checked + label {
119
- background-color: #e0f2fe;
120
- border-color: #0ea5e9;
121
- }
122
-
123
- .quiz-option input[type="radio"]:checked + label:before {
124
- border-color: #0ea5e9;
125
- background-color: #0ea5e9;
126
- box-shadow: inset 0 0 0 4px #e0f2fe;
127
- }
128
-
129
- .quiz-option.correct input[type="radio"]:checked + label {
130
- background-color: #d1fae5;
131
- border-color: #10b981;
132
- }
133
-
134
- .quiz-option.correct input[type="radio"]:checked + label:before {
135
- border-color: #10b981;
136
- background-color: #10b981;
137
- box-shadow: inset 0 0 0 4px #d1fae5;
138
- }
139
-
140
- .quiz-option.incorrect input[type="radio"]:checked + label {
141
- background-color: #fee2e2;
142
- border-color: #ef4444;
143
- }
144
-
145
- .quiz-option.incorrect input[type="radio"]:checked + label:before {
146
- border-color: #ef4444;
147
- background-color: #ef4444;
148
- box-shadow: inset 0 0 0 4px #fee2e2;
149
- }
150
-
151
- .progress-bar-container {
152
- width: 100%;
153
- height: 8px;
154
- background-color: #e2e8f0;
155
- border-radius: 4px;
156
- overflow: hidden;
157
- }
158
-
159
- .progress-bar {
160
- height: 100%;
161
- background-color: #0ea5e9;
162
- transition: width 0.5s ease;
163
- }
164
-
165
- .floating-label {
166
- position: absolute;
167
- top: -10px;
168
- left: 10px;
169
- padding: 0 5px;
170
- background-color: white;
171
- transition: all 0.3s ease;
172
- pointer-events: none;
173
- }
174
-
175
- .pulse-animation {
176
- animation: pulse 2s infinite;
177
- }
178
-
179
- @keyframes pulse {
180
- 0% {
181
- box-shadow: 0 0 0 0 rgba(14, 165, 233, 0.4);
182
- }
183
- 70% {
184
- box-shadow: 0 0 0 10px rgba(14, 165, 233, 0);
185
- }
186
- 100% {
187
- box-shadow: 0 0 0 0 rgba(14, 165, 233, 0);
188
- }
189
- }
190
-
191
- .confetti {
192
- position: fixed;
193
- width: 10px;
194
- height: 10px;
195
- background-color: #f00;
 
 
 
 
 
 
 
 
 
 
 
196
  opacity: 0;
197
- animation: confetti-fall 3s linear forwards;
198
  }
199
-
200
- @keyframes confetti-fall {
201
- 0% {
202
- transform: translateY(0) rotate(0deg);
203
- opacity: 1;
204
- }
205
- 100% {
206
- transform: translateY(100vh) rotate(360deg);
207
- opacity: 0;
208
- }
209
- }
210
-
211
- .btn-primary {
212
- @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;
213
- }
214
-
215
- .btn-secondary {
216
- @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;
217
- }
218
- </style>
219
  </head>
220
  <body>
221
  <div class="min-h-screen bg-gradient-to-b from-primary-50 to-white">
222
  <header class="bg-white shadow-sm py-6">
223
  <div class="container mx-auto px-4">
224
  <div class="flex items-center justify-between">
225
- <a href="#" class="text-3xl font-bold text-primary-700 flex items-center">
226
  <i class="fas fa-home mr-3 text-primary-500"></i>
227
- Mariam Quizz
228
  </a>
229
  <div class="flex space-x-2">
230
  <button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100">
@@ -238,863 +241,692 @@
238
  </div>
239
  </header>
240
 
241
- <main class="container mx-auto px-4 py-8">
242
- <div class="max-w-3xl mx-auto">
243
- <!-- Introduction -->
244
- <div class="text-center mb-8">
245
- <h2 class="text-4xl font-bold text-gray-800 mb-4">Apprenez plus efficacement</h2>
246
- <p class="text-lg text-gray-600 mb-6">Créez des flashcards ou des quiz pour mémoriser n'importe quel sujet</p>
247
- </div>
 
 
 
 
 
248
 
249
- <!-- Section de configuration -->
250
- <div class="bg-white rounded-xl shadow-lg p-8 mb-10 relative overflow-hidden">
251
- <div class="absolute top-0 right-0 w-40 h-40 bg-primary-100 rounded-full -mr-20 -mt-20 z-0"></div>
252
- <div class="absolute bottom-0 left-0 w-24 h-24 bg-primary-100 rounded-full -ml-12 -mb-12 z-0"></div>
253
 
254
- <div class="relative z-10">
255
- <h3 class="text-2xl font-semibold text-gray-800 mb-6">Que souhaitez-vous apprendre aujourd'hui ?</h3>
 
 
 
 
256
 
257
- <div class="space-y-6">
258
- <div class="relative">
259
- <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...">
260
- <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
261
- <div class="text-xs text-gray-500 mt-1 ml-2">Exemples: "Capitales du monde", "Photosynthèse", "Verbes irréguliers en anglais"</div>
262
- </div>
263
-
264
- <div>
265
- <p class="text-gray-700 font-medium mb-3">Choisissez votre méthode d'apprentissage :</p>
266
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
267
- <div class="relative">
268
- <input type="radio" id="typeFlashcards" name="contentType" value="flashcards" checked class="peer absolute opacity-0">
269
- <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">
270
- <div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3">
271
- <i class="fas fa-clone text-xl text-primary-600"></i>
272
- </div>
273
- <h4 class="font-semibold text-gray-800">Flashcards</h4>
274
- <p class="text-sm text-gray-600 text-center mt-2">Pour mémoriser des informations par répétition</p>
275
- </label>
276
- </div>
277
-
278
- <div class="relative">
279
- <input type="radio" id="typeQuiz" name="contentType" value="quiz" class="peer absolute opacity-0">
280
- <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">
281
- <div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3">
282
- <i class="fas fa-question text-xl text-primary-600"></i>
283
- </div>
284
- <h4 class="font-semibold text-gray-800">Quiz</h4>
285
- <p class="text-sm text-gray-600 text-center mt-2">Pour tester vos connaissances avec des QCM</p>
286
- </label>
287
- </div>
288
  </div>
289
- </div>
290
-
291
- <div class="flex items-center justify-between pt-2">
292
- <div class="text-sm text-gray-500 italic">
293
- <i class="fas fa-info-circle mr-1"></i> La génération peut prendre jusqu'à 1 minute
 
 
 
 
 
294
  </div>
295
- <button id="generateBtn" class="btn-primary flex items-center">
296
- <span class="mr-2">Générer</span>
297
- <i class="fas fa-chevron-right"></i>
298
- </button>
299
  </div>
300
  </div>
 
 
 
 
 
 
 
 
 
 
301
  </div>
302
  </div>
 
303
 
304
- <!-- Indicateur de chargement -->
305
- <div id="loading" class="hidden">
306
- <div class="bg-white rounded-xl shadow-lg p-8 text-center">
307
- <div class="flex flex-col items-center justify-center space-y-4">
308
- <div class="relative w-24 h-24">
309
- <div class="absolute inset-0 border-4 border-primary-100 border-t-primary-500 rounded-full animate-spin"></div>
310
- <div class="absolute inset-0 flex items-center justify-center">
311
- <i class="fas fa-lightbulb text-2xl text-primary-500 animate-pulse"></i>
312
- </div>
313
  </div>
314
- <h3 class="text-xl font-semibold text-gray-800">Création en cours...</h3>
315
- <p class="text-gray-600">Nous élaborons votre contenu d'apprentissage personnalisé</p>
316
-
317
- <div class="w-full max-w-md mt-4">
318
- <div class="progress-bar-container">
319
- <div id="progressBar" class="progress-bar" style="width: 0%"></div>
320
- </div>
321
- <div id="loadingSteps" class="mt-8 text-left">
322
- <div class="flex items-center mb-3">
323
- <div class="w-6 h-6 rounded-full bg-primary-500 flex items-center justify-center mr-3">
324
- <i class="fas fa-check text-white text-xs"></i>
325
- </div>
326
- <span class="text-sm text-gray-700">Analyse du sujet</span>
327
  </div>
328
- <div class="flex items-center mb-3">
329
- <div id="step2" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
330
- <i class="fas fa-spinner text-white text-xs animate-spin"></i>
331
- </div>
332
- <span class="text-sm text-gray-500">Recherche d'informations</span>
333
  </div>
334
- <div class="flex items-center mb-3">
335
- <div id="step3" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
336
- <i class="fas fa-hourglass text-white text-xs"></i>
337
- </div>
338
- <span class="text-sm text-gray-500">Création du contenu</span>
339
  </div>
340
- <div class="flex items-center">
341
- <div id="step4" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
342
- <i class="fas fa-hourglass text-white text-xs"></i>
343
- </div>
344
- <span class="text-sm text-gray-500">Finalisation</span>
345
  </div>
 
346
  </div>
347
  </div>
348
  </div>
349
  </div>
350
  </div>
 
351
 
352
- <!-- Conteneur pour les Flashcards -->
353
- <div id="flashcardsContainer" class="mt-8 hidden">
354
- <!-- Les flashcards seront injectées ici -->
355
- </div>
356
-
357
- <!-- Conteneur pour le Quiz -->
358
- <div id="quizContainer" class="mt-8 hidden">
359
- <!-- Les questions du quiz seront injectées ici -->
360
- </div>
361
  </div>
362
- </main>
363
 
364
- <!-- MODIFICATION ICI: Ajout de 'hidden md:block' -->
365
- <footer class="hidden md:block bg-gray-900 text-white py-8 mt-20">
366
- <div class="container mx-auto px-4">
367
- <div class="flex flex-col md:flex-row justify-between items-center">
368
- <div class="mb-4 md:mb-0">
369
- <h2 class="text-xl font-bold flex items-center">
370
- <i class="fas fa-brain mr-2"></i> Mémorisation Facile
371
- </h2>
372
- <p class="text-gray-400 text-sm mt-2">Votre outil d'apprentissage intelligent</p>
373
- </div>
374
- <div class="flex space-x-4">
375
- <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-twitter"></i></a>
376
- <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-facebook"></i></a>
377
- <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-instagram"></i></a>
378
- <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-youtube"></i></a>
379
- </div>
380
  </div>
381
- <div class="mt-8 border-t border-gray-800 pt-6 text-sm text-gray-400 text-center">
382
- © 2025 Mémorisation Facile. Tous droits réservés.
 
 
 
383
  </div>
384
  </div>
385
- </footer>
386
- </div>
387
-
388
- <script>
389
- const generateBtn = document.getElementById('generateBtn');
390
- const loadingIndicator = document.getElementById('loading');
391
- const flashcardsContainer = document.getElementById('flashcardsContainer');
392
- const quizContainer = document.getElementById('quizContainer');
393
- const topicInput = document.getElementById('topic');
394
- const progressBar = document.getElementById('progressBar');
395
- const themeToggle = document.getElementById('themeToggle');
396
- const step2 = document.getElementById('step2');
397
- const step3 = document.getElementById('step3');
398
- const step4 = document.getElementById('step4');
399
-
400
- // Gestion du thème clair/sombre
401
- themeToggle.addEventListener('click', function() {
402
- document.body.classList.toggle('dark-mode'); // Vous aurez besoin d'ajouter des styles pour .dark-mode si vous voulez un vrai thème sombre
403
- const icon = this.querySelector('i');
404
- if (icon.classList.contains('fa-moon')) {
405
- icon.classList.remove('fa-moon');
406
- icon.classList.add('fa-sun');
407
- } else {
408
- icon.classList.remove('fa-sun');
409
- icon.classList.add('fa-moon');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  }
411
- // Implémentation basique pour l'exemple: changer le fond du body
412
- if (document.body.classList.contains('dark-mode')) {
413
- document.body.style.backgroundColor = '#1f2937'; // Un gris sombre
414
- document.body.style.color = '#f3f4f6'; // Un gris clair pour le texte
415
- } else {
416
- document.body.style.backgroundColor = '#f8fafc'; // Retour au fond clair
417
- document.body.style.color = 'initial'; // Retour à la couleur de texte par défaut
418
  }
419
- });
420
 
421
- // Animation de chargement
422
- function simulateLoading() {
423
- loadingIndicator.classList.remove('hidden'); // Assurez-vous que le chargement est visible
424
- progressBar.style.width = '0%'; // Reset progress bar
425
- // Reset steps visual state
426
- [step2, step3, step4].forEach(step => {
427
- step.className = 'w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3';
428
- step.innerHTML = '<i class="fas fa-hourglass text-white text-xs"></i>';
429
- });
430
- // Start step 1 visual state
431
- const step1IconContainer = loadingSteps.children[0].querySelector('div');
432
- step1IconContainer.className = 'w-6 h-6 rounded-full bg-primary-500 flex items-center justify-center mr-3';
433
- step1IconContainer.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
434
- step2.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>'; // Start step 2 loading
435
-
436
- let progress = 0;
437
- const interval = setInterval(() => {
438
- progress += 1;
439
- progressBar.style.width = `${Math.min(progress, 100)}%`; // Ne pas dépasser 100%
440
-
441
- if (progress === 25) {
442
- step2.classList.remove('bg-gray-200');
443
- step2.classList.add('bg-primary-500');
444
- step2.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
445
- step3.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>';
446
- } else if (progress === 60) {
447
- step3.classList.remove('bg-gray-200');
448
- step3.classList.add('bg-primary-500');
449
- step3.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
450
- step4.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>';
451
- } else if (progress === 95) {
452
- step4.classList.remove('bg-gray-200');
453
- step4.classList.add('bg-primary-500');
454
- step4.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
455
- }
456
-
457
- if (progress >= 100) {
458
- clearInterval(interval);
459
- // Ne cachez pas l'indicateur ici, laissez la fonction principale le faire après le fetch
460
- }
461
- }, 50); // 50ms * 100 = 5 secondes pour l'animation complète
462
-
463
- return interval; // Retourne l'ID de l'intervalle pour pouvoir l'arrêter
464
- }
465
-
466
- // Fonction pour créer des confettis
467
- function createConfetti() {
468
- const confettiCount = 100;
469
- const colors = ['#0ea5e9', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];
470
-
471
- for (let i = 0; i < confettiCount; i++) {
472
- const confetti = document.createElement('div');
473
- confetti.className = 'confetti fixed top-0 pointer-events-none z-50'; // Assurez-vous qu'ils sont bien positionnés et visibles
474
- confetti.style.left = `${Math.random() * 100}vw`;
475
- confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
476
- confetti.style.width = `${Math.random() * 10 + 5}px`;
477
- confetti.style.height = `${Math.random() * 10 + 5}px`;
478
- // Ajuster l'animation pour partir du haut
479
- confetti.style.animation = `confetti-fall ${Math.random() * 3 + 2}s linear forwards`;
480
- confetti.style.transform = `translateY(-20px) rotate(${Math.random() * 360}deg)`; // Position initiale légèrement au-dessus
481
- confetti.style.opacity = '1'; // Doit être visible au début
482
-
483
- document.body.appendChild(confetti);
484
-
485
- setTimeout(() => {
486
- confetti.remove();
487
- }, 5000); // Durée max avant suppression
488
- }
489
- }
490
-
491
-
492
- // Gestion des flashcards
493
- function flipCard(card) {
494
- card.classList.toggle('flipped');
495
  }
496
 
497
- generateBtn.addEventListener('click', function() {
498
- const topic = topicInput.value.trim();
499
- // Retirer les anciens messages d'erreur s'il y en a
500
- const existingError = topicInput.parentElement.querySelector('.text-red-500');
501
- if (existingError) {
502
- existingError.remove();
503
- }
504
- // Retirer les anciens conteneurs d'erreur globaux
505
- document.querySelectorAll('.error-container-global').forEach(el => el.remove());
506
-
507
-
508
- if (!topic) {
509
- // Animation de secouement sur l'input
510
- topicInput.classList.add('border-red-500');
511
- topicInput.classList.add('animate-bounce'); // Attention, 'animate-bounce' fait rebondir, pas secouer. Il faudrait une animation custom pour secouer.
512
- setTimeout(() => {
513
- topicInput.classList.remove('animate-bounce');
514
- topicInput.classList.remove('border-red-500');
515
- }, 1000);
516
-
517
- // Afficher un message d'erreur sous l'input
518
- const errorMsg = document.createElement('div');
519
- errorMsg.className = 'text-red-500 text-sm mt-1 ml-2';
520
- errorMsg.textContent = 'Veuillez entrer un sujet';
521
- topicInput.parentElement.appendChild(errorMsg);
522
 
523
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  }
525
-
526
- const contentType = document.querySelector('input[name="contentType"]:checked').value;
527
-
528
- // Cacher les anciens résultats et afficher le chargement
529
- flashcardsContainer.classList.add('hidden');
530
- quizContainer.classList.add('hidden');
531
- flashcardsContainer.innerHTML = '';
532
- quizContainer.innerHTML = '';
533
- loadingIndicator.classList.remove('hidden');
534
-
535
- // Simuler l'animation de chargement
536
- const loadingInterval = simulateLoading();
537
- const minLoadingTime = 3000; // Temps minimum d'affichage du chargement (en ms)
538
- const startTime = Date.now();
539
-
540
-
541
- // ---- DEBUT BLOC SIMULATION (À REMPLACER PAR VOTRE VRAI FETCH) ----
542
- // Simule une réponse de l'API après un délai
543
  setTimeout(() => {
544
- const elapsedTime = Date.now() - startTime;
545
- const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
546
-
547
- // Simuler des données de réponse
548
- let fakeData;
549
- if (contentType === 'flashcards') {
550
- fakeData = {
551
- flashcards: [
552
- { question: `Question 1 sur ${topic}?`, answer: `Réponse 1 sur ${topic}` },
553
- { question: `Question 2 sur ${topic}?`, answer: `Réponse 2 sur ${topic}` },
554
- { question: `Question 3 sur ${topic}?`, answer: `Réponse 3 sur ${topic}` },
555
- { question: `Question 4 sur ${topic}?`, answer: `Réponse 4 sur ${topic}` },
556
- { question: `Question 5 sur ${topic}?`, answer: `Réponse 5 sur ${topic}` }
557
- ]
558
- };
559
- } else {
560
- const options = [`Option A pour ${topic}`, `Option B pour ${topic}`, `Option C pour ${topic}`, `Option D pour ${topic}`];
561
- fakeData = {
562
- quiz: [
563
- { 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}.` },
564
- { question: `Quiz 2: Comment fonctionne ${topic}?`, options: [...options].sort(() => Math.random() - 0.5), correctAnswer: options[1], explanation: `Explication pour quiz 2 sur ${topic}.` },
565
- { question: `Quiz 3: Pourquoi ${topic} est important?`, options: [...options].sort(() => Math.random() - 0.5), correctAnswer: options[2], explanation: `Explication pour quiz 3 sur ${topic}.` }
566
- ]
567
- };
568
- }
569
- // Simuler une erreur parfois
570
- // fakeData = { error: "Désolé, une erreur simulée s'est produite." };
571
-
572
- // Attendre la fin du temps minimum de chargement avant de traiter la réponse
573
  setTimeout(() => {
574
- clearInterval(loadingInterval); // Arrêter l'intervalle de la barre de progression
575
- progressBar.style.width = '100%'; // S'assurer que la barre est pleine
576
-
577
- // Cacher l'indicateur de chargement après un court délai pour montrer la fin
578
- setTimeout(() => {
579
- loadingIndicator.classList.add('hidden');
580
-
581
- if (fakeData.error) {
582
- displayGlobalError(fakeData.error);
583
- } else if (contentType === 'flashcards' && fakeData.flashcards) {
584
- flashcardsContainer.classList.remove('hidden');
585
- displayFlashcards(fakeData.flashcards);
586
- createConfetti();
587
- } else if (contentType === 'quiz' && fakeData.quiz) {
588
- quizContainer.classList.remove('hidden');
589
- displayQuiz(fakeData.quiz);
590
- createConfetti();
591
- } else {
592
- displayGlobalError("Aucune donnée reçue ou format incorrect.");
593
- }
594
- }, 300); // Court délai pour voir la barre à 100%
595
- }, remainingTime);
596
-
597
- }, 1500); // Délai simulé de réponse de l'API (1.5s)
598
- // ---- FIN BLOC SIMULATION ----
599
-
600
- /*
601
- // ---- VOTRE VRAI FETCH (DÉCOMMENTER ET ADAPTER) ----
602
- fetch('/generate', {
603
- method: 'POST',
604
- headers: {
605
- 'Content-Type': 'application/json',
606
- },
607
- body: JSON.stringify({ topic, type: contentType }),
608
- })
609
- .then(response => {
610
- if (!response.ok) {
611
- // Essayer de lire le message d'erreur du corps si possible
612
- return response.text().then(text => {
613
- throw new Error(`Erreur HTTP: ${response.status} - ${text || response.statusText}`);
614
- });
615
- }
616
- return response.json();
617
- })
618
- .then(data => {
619
- const elapsedTime = Date.now() - startTime;
620
- const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
621
-
622
- setTimeout(() => {
623
- clearInterval(loadingInterval);
624
- progressBar.style.width = '100%';
625
-
626
- setTimeout(() => {
627
- loadingIndicator.classList.add('hidden');
628
-
629
- if (data.error) {
630
- displayGlobalError(data.error);
631
- } else if (contentType === 'flashcards' && data.flashcards) {
632
- flashcardsContainer.classList.remove('hidden');
633
- displayFlashcards(data.flashcards);
634
- createConfetti();
635
- } else if (contentType === 'quiz' && data.quiz) {
636
- quizContainer.classList.remove('hidden');
637
- displayQuiz(data.quiz);
638
- createConfetti();
639
- } else {
640
- displayGlobalError("Aucune donnée reçue ou format incorrect.");
641
- }
642
- }, 300);
643
- }, remainingTime);
644
- })
645
- .catch(error => {
646
- const elapsedTime = Date.now() - startTime;
647
- const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
648
-
649
- setTimeout(() => {
650
- clearInterval(loadingInterval);
651
  loadingIndicator.classList.add('hidden');
652
- console.error('Erreur lors de la génération:', error);
653
- displayGlobalError(`Erreur lors de la génération: ${error.message}. Veuillez réessayer.`);
654
- }, remainingTime);
655
- });
656
- // ---- FIN VRAI FETCH ----
657
- */
658
- });
659
- // Fonction pour afficher une erreur globale
660
- function displayGlobalError(message) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  const errorContainer = document.createElement('div');
662
- 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'; // Ajout de marge
663
  errorContainer.innerHTML = `
664
  <div class="flex">
665
  <div class="flex-shrink-0">
666
- <i class="fas fa-exclamation-triangle text-red-500 fa-lg"></i>
667
  </div>
668
  <div class="ml-3">
669
- <h3 class="text-sm font-medium">Une erreur est survenue</h3>
670
- <p class="text-sm mt-1">
671
- ${message}
672
  </p>
673
  </div>
674
  </div>
675
  `;
676
- // Insérer après la section de configuration
677
- const configSection = document.querySelector('.bg-white.rounded-xl.shadow-lg.p-8.mb-10');
678
- if (configSection) {
679
- configSection.parentNode.insertBefore(errorContainer, configSection.nextSibling);
680
- } else {
681
- // Fallback si la section de config n'est pas trouvée
682
- document.querySelector('main .max-w-3xl').appendChild(errorContainer);
683
- }
684
- }
685
-
686
-
687
- function displayFlashcards(flashcards) {
688
- flashcardsContainer.innerHTML = ''; // Vider le conteneur
689
-
690
- const header = document.createElement('div');
691
- header.className = 'mb-8 text-center';
692
- header.innerHTML = `
693
- <h2 class="text-3xl font-bold text-gray-800 mb-2">Vos Flashcards (${flashcards.length})</h2>
694
- <p class="text-gray-600">Cliquez sur une carte pour la retourner et voir la réponse.</p>
695
- <p class="text-gray-500 text-sm mt-1">
696
- <i class="far fa-lightbulb mr-1"></i> Astuce : Essayez de répondre mentalement avant de retourner la carte.
697
- </p>
698
- `;
699
- flashcardsContainer.appendChild(header);
700
-
701
- const flashcardsGrid = document.createElement('div');
702
- flashcardsGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6';
703
- flashcardsContainer.appendChild(flashcardsGrid);
704
-
705
- flashcards.forEach((card, index) => {
706
- const cardElement = document.createElement('div');
707
- // Ajouter un délai d'animation basé sur l'index pour un effet d'apparition décalé
708
- cardElement.style.animation = `fadeInUp 0.5s ease-out ${index * 0.1}s forwards`;
709
- cardElement.style.opacity = '0'; // Commencer invisible pour l'animation
710
- cardElement.className = 'flashcard cursor-pointer';
711
- cardElement.onclick = function() { flipCard(this); };
712
- cardElement.innerHTML = `
713
- <div class="flashcard-inner">
714
- <div class="flashcard-front">
715
- <div class="w-full flex flex-col justify-between h-full">
716
- <div class="text-left text-xs text-gray-400">#${index + 1}</div>
717
- <h3 class="text-lg font-semibold text-center flex-grow flex items-center justify-center px-2">${card.question}</h3>
718
- <div class="text-center mt-auto">
719
- <span class="text-xs text-gray-500">Cliquez pour voir la réponse</span>
720
- </div>
721
  </div>
722
  </div>
723
- <div class="flashcard-back">
724
- <div class="w-full flex flex-col justify-between h-full">
725
- <div class="text-right text-xs text-white opacity-70">#${index + 1}</div>
726
- <div class="text-center flex-grow flex items-center justify-center px-2">
727
- <p class="font-medium">${card.answer}</p>
728
- </div>
729
- <div class="text-right mt-auto">
730
- <span class="text-xs text-white opacity-70">Cliquez pour retourner</span>
731
- </div>
 
732
  </div>
733
  </div>
734
  </div>
735
- `;
736
- flashcardsGrid.appendChild(cardElement);
737
- });
738
-
739
- // Ajouter les contrôles après les cartes
740
- const controls = document.createElement('div');
741
- controls.className = 'mt-10 flex flex-col items-center justify-center space-y-4';
742
- controls.innerHTML = `
743
- <div class="flex items-center space-x-4">
744
- <button id="newTopicBtn" class="btn-primary">
745
- <i class="fas fa-plus mr-2"></i> Nouveau sujet
746
- </button>
747
- <button id="restartFlashcardsBtn" class="btn-secondary">
748
- <i class="fas fa-redo-alt mr-2"></i> Recommencer (retourner tout)
749
- </button>
750
  </div>
751
- <!-- Ajouter d'autres boutons si nécessaire -->
752
- `;
753
- flashcardsContainer.appendChild(controls);
754
-
755
- // Ajouter des interactions aux boutons
756
- document.getElementById('newTopicBtn').addEventListener('click', function() {
757
- // Cacher les résultats actuels
758
- flashcardsContainer.classList.add('hidden');
759
- flashcardsContainer.innerHTML = ''; // Vider pour la prochaine génération
760
- quizContainer.classList.add('hidden');
761
- quizContainer.innerHTML = '';
762
-
763
- // Remonter en haut et focus
764
- window.scrollTo({ top: 0, behavior: 'smooth' });
765
- topicInput.value = ''; // Vider le champ sujet
766
- topicInput.focus();
767
- });
768
-
769
- document.getElementById('restartFlashcardsBtn').addEventListener('click', function() {
770
- const allCards = flashcardsContainer.querySelectorAll('.flashcard.flipped');
771
- allCards.forEach(card => card.classList.remove('flipped'));
772
- // Optionnel : Scroll vers la première carte
773
- const firstCard = flashcardsContainer.querySelector('.flashcard');
774
- if(firstCard) {
775
- firstCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
776
- }
 
 
 
 
 
 
 
 
 
 
 
777
  });
778
- }
779
-
780
- function displayQuiz(quizQuestions) {
781
- quizContainer.innerHTML = ''; // Vider le conteneur
782
-
783
- const header = document.createElement('div');
784
- header.className = 'mb-8 text-center';
785
- header.innerHTML = `
786
- <h2 class="text-3xl font-bold text-gray-800 mb-2">Votre Quiz (${quizQuestions.length} questions)</h2>
787
- <p class="text-gray-600">Testez vos connaissances en répondant aux questions</p>
788
- <div class="flex items-center justify-center mt-6">
789
- <div class="bg-white px-4 py-2 rounded-full shadow-sm flex items-center border border-gray-200">
790
- <span class="text-primary-700 font-medium">Score: </span>
791
- <span id="score" class="ml-1 text-primary-700 font-bold">0</span>
792
- <span class="mx-1 text-gray-400">/</span>
793
- <span class="text-gray-600">${quizQuestions.length}</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
794
  </div>
 
 
 
 
 
 
 
 
 
795
  </div>
796
- `;
797
- quizContainer.appendChild(header);
798
-
799
- const questionsContainer = document.createElement('div');
800
- questionsContainer.className = 'space-y-8';
801
- quizContainer.appendChild(questionsContainer);
802
-
803
- let currentScore = 0;
804
- let answeredQuestions = 0; // Compteur pour savoir quand afficher les résultats
805
-
806
- quizQuestions.forEach((question, qIndex) => {
807
- const questionElement = document.createElement('div');
808
- questionElement.className = 'bg-white rounded-xl shadow-md p-6 transition-all duration-300 question-card'; // Ajout de classe pour ciblage
809
- questionElement.setAttribute('id', `question-${qIndex}`);
810
- // Animation d'apparition
811
- questionElement.style.animation = `fadeInUp 0.5s ease-out ${qIndex * 0.15}s forwards`;
812
- questionElement.style.opacity = '0';
813
-
814
- let optionsHtml = '';
815
- // Assurez-vous que correctAnswer est une chaîne pour la comparaison
816
- const safeCorrectAnswer = String(question.correctAnswer).replace(/"/g, '"');
817
-
818
- // Mélanger les options une seule fois pour cette question
819
- const shuffledOptions = [...question.options].sort(() => Math.random() - 0.5);
820
-
821
- shuffledOptions.forEach((option, oIndex) => {
822
- const safeOption = String(option).replace(/"/g, '"'); // Sécuriser aussi les options
823
- optionsHtml += `
824
- <div class="quiz-option mb-3" id="option-${qIndex}-${oIndex}">
825
- <input type="radio" id="q${qIndex}-o${oIndex}" name="question-${qIndex}" value="${safeOption}" data-correct="${safeCorrectAnswer}">
826
- <label for="q${qIndex}-o${oIndex}" class="group flex items-center">
827
- <span class="flex-grow">${option}</span>
828
- <span class="ml-2 hidden success-icon text-green-500 text-lg">
829
- <i class="fas fa-check-circle"></i>
830
- </span>
831
- <span class="ml-2 hidden error-icon text-red-500 text-lg">
832
- <i class="fas fa-times-circle"></i>
833
- </span>
834
- </label>
835
  </div>
836
- `;
837
- });
838
-
839
- questionElement.innerHTML = `
840
- <div class="flex items-center justify-between mb-4">
841
- <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>
842
- <!-- On pourrait ajouter une icône d'info/aide ici si pertinent -->
843
- </div>
844
- <h3 class="text-lg md:text-xl font-medium text-gray-800 mb-5">${question.question}</h3>
845
- <div class="options mt-5 space-y-3"> <!-- Ajout space-y pour l'espacement -->
846
- ${optionsHtml}
847
- </div>
848
- <div class="explanation hidden mt-6 p-4 bg-sky-50 border border-sky-200 rounded-lg" id="explanation-${qIndex}">
849
- <div class="flex items-start">
850
- <div class="flex-shrink-0 mt-0.5">
851
- <i class="fas fa-info-circle text-sky-500"></i>
852
- </div>
853
- <div class="ml-3">
854
- <h4 class="text-sm font-semibold text-sky-800">Explication</h4>
855
- <div class="mt-1 text-sm text-sky-700">${question.explanation || "Pas d'explication fournie."}</div>
856
- </div>
857
  </div>
858
  </div>
859
- `;
860
- questionsContainer.appendChild(questionElement);
861
-
862
- const optionsInputs = questionElement.querySelectorAll('input[type="radio"]');
863
- optionsInputs.forEach((optionInput) => {
864
- optionInput.addEventListener('change', function() {
865
- // Empêcher de répondre à nouveau si déjà répondu
866
- if (questionElement.dataset.answered === 'true') return;
867
- questionElement.dataset.answered = 'true'; // Marquer comme répondu
868
- answeredQuestions++;
869
-
870
-
871
- const qIdx = qIndex; // Utiliser qIndex directement
872
- const selectedValue = this.value;
873
- const correctAnswer = this.getAttribute('data-correct');
874
- const parentOptionDiv = this.parentElement; // Le div .quiz-option
875
- const explanationElement = document.getElementById(`explanation-${qIdx}`);
876
-
877
- // Désactiver toutes les options pour cette question
878
- optionsInputs.forEach(opt => opt.disabled = true);
879
-
880
- let isCorrect = false;
881
- if (selectedValue === correctAnswer) {
882
- // Réponse correcte
883
- parentOptionDiv.classList.add('correct'); // Appliquer sur le div parent
884
- parentOptionDiv.querySelector('.success-icon').classList.remove('hidden');
885
- currentScore++;
886
- document.getElementById('score').textContent = currentScore;
887
- isCorrect = true;
888
- } else {
889
- // Réponse incorrecte
890
- parentOptionDiv.classList.add('incorrect');
891
- parentOptionDiv.querySelector('.error-icon').classList.remove('hidden');
 
 
 
 
 
 
 
 
 
 
 
892
 
893
- // Trouver et surligner la bonne réponse
894
- optionsInputs.forEach(opt => {
895
- if (opt.value === correctAnswer) {
896
- const correctParentDiv = opt.parentElement;
897
- correctParentDiv.classList.add('correct');
898
- correctParentDiv.querySelector('.success-icon').classList.remove('hidden');
899
- }
900
- });
901
  }
 
902
 
903
- // Afficher l'explication
904
- explanationElement.classList.remove('hidden');
905
- explanationElement.style.animation = 'fadeIn 0.5s ease-in'; // Petite animation pour l'explication
906
 
907
- // Si toutes les questions ont été répondues, afficher les résultats finaux
908
- if (answeredQuestions === quizQuestions.length) {
909
- // Délai avant d'afficher les résultats pour laisser le temps de voir la dernière explication
910
- setTimeout(() => {
911
- displayQuizResults(currentScore, quizQuestions.length);
912
- }, isCorrect ? 1500 : 2500); // Plus long si incorrect
 
 
913
  } else {
914
- // Optionnel: Scroll automatique vers la question suivante après un délai
915
- const nextQuestion = document.getElementById(`question-${qIdx + 1}`);
916
- if (nextQuestion) {
917
- setTimeout(() => {
918
- nextQuestion.scrollIntoView({ behavior: 'smooth', block: 'center' });
919
- }, isCorrect ? 1000 : 2000); // Délai plus court si correct
920
- }
921
  }
922
- });
923
  });
924
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
925
  }
926
-
927
-
928
- function displayQuizResults(score, total) {
929
- // Supprimer les anciens résultats s'il y en a
930
- const existingResults = quizContainer.querySelector('.quiz-results');
931
- if (existingResults) existingResults.remove();
932
-
933
- const percentage = total > 0 ? Math.round((score / total) * 100) : 0;
934
- let resultClass, resultIcon, resultMessage, colorClass;
935
-
936
- if (percentage >= 80) {
937
- resultClass = 'bg-green-50 border-green-500 text-green-800';
938
- resultIcon = '<i class="fas fa-trophy text-4xl text-yellow-400 mb-4"></i>';
939
- resultMessage = 'Excellent ! Vous maîtrisez ce sujet.';
940
- colorClass = 'bg-green-500';
941
- } else if (percentage >= 50) {
942
- resultClass = 'bg-blue-50 border-blue-500 text-blue-800';
943
- resultIcon = '<i class="fas fa-award text-4xl text-blue-500 mb-4"></i>';
944
- resultMessage = 'Bon travail ! Continuez vos efforts.';
945
- colorClass = 'bg-blue-500';
946
- } else {
947
- resultClass = 'bg-red-50 border-red-500 text-red-800';
948
- resultIcon = '<i class="fas fa-book-reader text-4xl text-red-500 mb-4"></i>';
949
- resultMessage = 'Continuez à étudier pour améliorer vos connaissances.';
950
- colorClass = 'bg-red-500';
951
- }
952
-
953
- const resultsElement = document.createElement('div');
954
- resultsElement.className = `quiz-results mt-12 p-6 rounded-xl shadow-lg border-t-4 ${resultClass} text-center`; // Border top
955
- resultsElement.style.animation = 'fadeInUp 0.6s ease-out';
956
- resultsElement.innerHTML = `
957
- <div class="flex flex-col items-center">
958
- ${resultIcon}
959
- <h3 class="text-2xl font-bold mb-2">Résultat final</h3>
960
- <p class="text-3xl font-bold mb-3">${score} / ${total}</p>
961
- <div class="w-full max-w-xs bg-gray-200 rounded-full h-5 mb-4 overflow-hidden border border-gray-300">
962
- <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}%">
963
- ${percentage}%
964
- </div>
965
- </div>
966
- <p class="mb-6">${resultMessage}</p>
967
- <div class="flex flex-col sm:flex-row space-y-3 sm:space-y-0 sm:space-x-4 mt-4">
968
- <button id="retryQuizBtn" class="btn-secondary">
969
- <i class="fas fa-redo mr-2"></i> Réessayer le Quiz
970
- </button>
971
- <button id="newQuizBtnResults" class="btn-primary">
972
- <i class="fas fa-plus mr-2"></i> Nouveau Sujet
973
- </button>
974
- </div>
975
  </div>
976
- `;
977
- quizContainer.appendChild(resultsElement);
978
-
979
- // Animation de confettis si le score est bon
980
- if (percentage >= 70) {
981
- createConfetti();
982
- }
983
-
984
- // Scroll vers les résultats
985
- resultsElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
986
-
987
-
988
- // Ajouter des interactions aux boutons
989
- document.getElementById('retryQuizBtn').addEventListener('click', function() {
990
- // Cacher les résultats
991
- resultsElement.remove();
992
- // Réinitialiser le score affiché
993
- document.getElementById('score').textContent = '0';
994
- // Réactiver et déselectionner toutes les options, cacher les icônes/explications
995
- const allQuestions = quizContainer.querySelectorAll('.question-card');
996
- allQuestions.forEach(qElement => {
997
- qElement.dataset.answered = 'false'; // Réinitialiser l'état "répondu"
998
- const radios = qElement.querySelectorAll('input[type="radio"]');
999
- radios.forEach(radio => {
1000
- radio.checked = false;
1001
- radio.disabled = false;
1002
- });
1003
- const optionsDivs = qElement.querySelectorAll('.quiz-option');
1004
- optionsDivs.forEach(div => {
1005
- div.classList.remove('correct', 'incorrect');
1006
- div.querySelectorAll('.success-icon, .error-icon').forEach(icon => icon.classList.add('hidden'));
1007
- });
1008
- const explanation = qElement.querySelector('.explanation');
1009
- if (explanation) explanation.classList.add('hidden');
1010
- });
1011
-
1012
- // Remonter à la première question
1013
- const firstQuestion = document.getElementById('question-0');
1014
- if (firstQuestion) {
1015
- firstQuestion.scrollIntoView({ behavior: 'smooth', block: 'start' });
1016
- }
1017
- // Réinitialiser les compteurs internes
1018
- currentScore = 0;
1019
- answeredQuestions = 0;
1020
  });
1021
-
1022
- document.getElementById('newQuizBtnResults').addEventListener('click', function() {
1023
- // Cacher les résultats actuels
1024
- quizContainer.classList.add('hidden');
1025
- quizContainer.innerHTML = ''; // Vider pour la prochaine génération
1026
- flashcardsContainer.classList.add('hidden');
1027
- flashcardsContainer.innerHTML = '';
1028
-
1029
- // Remonter en haut et focus
1030
- window.scrollTo({ top: 0, behavior: 'smooth' });
1031
- topicInput.value = ''; // Vider le champ sujet
1032
- topicInput.focus();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1033
  });
1034
- }
1035
-
1036
- // Ajouter des animations CSS simples
1037
- const styleSheet = document.createElement("style");
1038
- styleSheet.type = "text/css";
1039
- styleSheet.innerText = `
1040
- @keyframes fadeInUp {
1041
- from {
1042
- opacity: 0;
1043
- transform: translateY(20px);
1044
- }
1045
- to {
1046
- opacity: 1;
1047
- transform: translateY(0);
1048
- }
1049
- }
1050
- @keyframes fadeIn {
1051
- from { opacity: 0; }
1052
- to { opacity: 1; }
1053
- }
1054
- /* Style pour le dark mode (très basique) */
1055
- body.dark-mode { background-color: #111827; color: #d1d5db; }
1056
- body.dark-mode .bg-white { background-color: #1f2937; border-color: #374151; }
1057
- body.dark-mode .bg-gray-50 { background-color: #374151; }
1058
- body.dark-mode .text-gray-800 { color: #f9fafb; }
1059
- body.dark-mode .text-gray-600 { color: #9ca3af; }
1060
- body.dark-mode .text-gray-700 { color: #d1d5db; }
1061
- body.dark-mode .text-gray-500 { color: #6b7280; }
1062
- body.dark-mode .text-gray-400 { color: #4b5563; }
1063
- body.dark-mode .border-gray-200 { border-color: #374151; }
1064
- body.dark-mode .border-gray-300 { border-color: #4b5563; }
1065
- body.dark-mode .shadow-sm { box-shadow: 0 1px 2px 0 rgba(255, 255, 255, 0.05); }
1066
- 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); }
1067
- 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); }
1068
- body.dark-mode input[type="text"] { background-color: #374151; border-color: #4b5563; color: #f3f4f6; }
1069
- body.dark-mode input[type="text"]::placeholder { color: #6b7280; }
1070
- body.dark-mode .quiz-option label { border-color: #374151; }
1071
- body.dark-mode .quiz-option label:hover { background-color: #4b5563; }
1072
- body.dark-mode .quiz-option input[type="radio"]:checked + label { background-color: #0c4a6e; border-color: #0ea5e9; color: white; } /* Adjust primary dark check */
1073
- 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; }
1074
- body.dark-mode .flashcard-front { background-color: #1f2937; color: #f3f4f6; border-left-color: #0ea5e9; }
1075
- /* Ajuster les couleurs primaires pour le dark mode si nécessaire */
1076
- body.dark-mode .bg-primary-50 { background-color: #0c4a6e; }
1077
- body.dark-mode .bg-primary-100 { background-color: #075985; }
1078
- body.dark-mode .text-primary-600 { color: #38bdf8; }
1079
- body.dark-mode .text-primary-700 { color: #7dd3fc; }
1080
- body.dark-mode .text-primary-800 { color: #bae6fd; }
1081
- body.dark-mode .border-primary-500 { border-color: #38bdf8; }
1082
- body.dark-mode .peer-checked\\:bg-primary-50:checked ~ label { background-color: #0c4a6e; }
1083
- body.dark-mode .peer-checked\\:border-primary-500:checked ~ label { border-color: #38bdf8; }
1084
- body.dark-mode .btn-primary { background-color: #0284c7; hover:bg-primary-700; } /* Exemple d'ajustement bouton */
1085
- body.dark-mode .btn-secondary { background-color: #374151; color:#38bdf8; border-color:#38bdf8; hover:bg-gray-600 } /* Exemple d'ajustement bouton */
1086
- body.dark-mode #themeToggle i { color: #e5e7eb; } /* Icône du thème */
1087
- body.dark-mode .progress-bar-container { background-color: #374151; }
1088
- body.dark-mode .progress-bar { background-color: #0ea5e9; }
1089
- body.dark-mode .bg-gradient-to-b { background-image: linear-gradient(to bottom, #0c4a6e, #111827); } /* Gradient dark */
1090
- body.dark-mode header { background-color: #1f2937; } /* Header dark */
1091
- `;
1092
- document.head.appendChild(styleSheet);
1093
-
1094
- // Focus sur le champ de saisie au chargement
1095
- window.addEventListener('load', () => {
1096
  topicInput.focus();
1097
  });
1098
- </script>
 
 
 
 
 
 
 
1099
  </body>
1100
- </html>
 
1
+
2
  <!DOCTYPE html>
3
+
4
  <html lang="fr">
5
  <head>
6
  <meta charset="UTF-8">
 
35
  </script>
36
  <style>
37
  @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
38
+
39
+ body {
40
+ font-family: 'Poppins', sans-serif;
41
+ background-color: #f8fafc;
42
+ }
43
+
44
+ .flashcard {
45
+ perspective: 1000px;
46
+ height: 220px;
47
+ }
48
+
49
+ .flashcard-inner {
50
+ position: relative;
51
+ width: 100%;
52
+ height: 100%;
53
+ transition: transform 0.8s;
54
+ transform-style: preserve-3d;
55
+ }
56
+
57
+ .flashcard.flipped .flashcard-inner {
58
+ transform: rotateY(180deg);
59
+ }
60
+
61
+ .flashcard-front, .flashcard-back {
62
+ position: absolute;
63
+ width: 100%;
64
+ height: 100%;
65
+ -webkit-backface-visibility: hidden;
66
+ backface-visibility: hidden;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ border-radius: 0.5rem;
71
+ padding: 1.5rem;
72
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
73
+ }
74
+
75
+ .flashcard-front {
76
+ background-color: #ffffff;
77
+ color: #1e293b;
78
+ border-left: 5px solid #0ea5e9;
79
+ }
80
+
81
+ .flashcard-back {
82
+ background-color: #0ea5e9;
83
+ color: white;
84
+ transform: rotateY(180deg);
85
+ }
86
+
87
+ .quiz-option {
88
+ position: relative;
89
+ padding-left: 2.5rem;
90
+ }
91
+
92
+ .quiz-option input[type="radio"] {
93
+ position: absolute;
94
+ opacity: 0;
95
+ }
96
+
97
+ .quiz-option label {
98
+ display: block;
99
+ position: relative;
100
+ padding: 1rem 1.5rem 1rem 2.5rem;
101
+ cursor: pointer;
102
+ border: 1px solid #e2e8f0;
103
+ border-radius: 0.5rem;
104
+ transition: all 0.3s ease;
105
+ }
106
+
107
+ .quiz-option label:before {
108
+ content: '';
109
+ position: absolute;
110
+ left: 1rem;
111
+ top: 50%;
112
+ transform: translateY(-50%);
113
+ width: 1.25rem;
114
+ height: 1.25rem;
115
+ border-radius: 50%;
116
+ border: 2px solid #cbd5e1;
117
+ transition: all 0.3s ease;
118
+ }
119
+
120
+ .quiz-option input[type="radio"]:checked + label {
121
+ background-color: #e0f2fe;
122
+ border-color: #0ea5e9;
123
+ }
124
+
125
+ .quiz-option input[type="radio"]:checked + label:before {
126
+ border-color: #0ea5e9;
127
+ background-color: #0ea5e9;
128
+ box-shadow: inset 0 0 0 4px #e0f2fe;
129
+ }
130
+
131
+ .quiz-option.correct input[type="radio"]:checked + label {
132
+ background-color: #d1fae5;
133
+ border-color: #10b981;
134
+ }
135
+
136
+ .quiz-option.correct input[type="radio"]:checked + label:before {
137
+ border-color: #10b981;
138
+ background-color: #10b981;
139
+ box-shadow: inset 0 0 0 4px #d1fae5;
140
+ }
141
+
142
+ .quiz-option.incorrect input[type="radio"]:checked + label {
143
+ background-color: #fee2e2;
144
+ border-color: #ef4444;
145
+ }
146
+
147
+ .quiz-option.incorrect input[type="radio"]:checked + label:before {
148
+ border-color: #ef4444;
149
+ background-color: #ef4444;
150
+ box-shadow: inset 0 0 0 4px #fee2e2;
151
+ }
152
+
153
+ .progress-bar-container {
154
+ width: 100%;
155
+ height: 8px;
156
+ background-color: #e2e8f0;
157
+ border-radius: 4px;
158
+ overflow: hidden;
159
+ }
160
+
161
+ .progress-bar {
162
+ height: 100%;
163
+ background-color: #0ea5e9;
164
+ transition: width 0.5s ease;
165
+ }
166
+
167
+ .floating-label {
168
+ position: absolute;
169
+ top: -10px;
170
+ left: 10px;
171
+ padding: 0 5px;
172
+ background-color: white;
173
+ transition: all 0.3s ease;
174
+ pointer-events: none;
175
+ }
176
+
177
+ .pulse-animation {
178
+ animation: pulse 2s infinite;
179
+ }
180
+
181
+ @keyframes pulse {
182
+ 0% {
183
+ box-shadow: 0 0 0 0 rgba(14, 165, 233, 0.4);
184
+ }
185
+ 70% {
186
+ box-shadow: 0 0 0 10px rgba(14, 165, 233, 0);
187
+ }
188
+ 100% {
189
+ box-shadow: 0 0 0 0 rgba(14, 165, 233, 0);
190
+ }
191
+ }
192
+
193
+ .confetti {
194
+ position: fixed;
195
+ width: 10px;
196
+ height: 10px;
197
+ background-color: #f00;
198
+ opacity: 0;
199
+ animation: confetti-fall 3s linear forwards;
200
+ }
201
+
202
+ @keyframes confetti-fall {
203
+ 0% {
204
+ transform: translateY(0) rotate(0deg);
205
+ opacity: 1;
206
+ }
207
+ 100% {
208
+ transform: translateY(100vh) rotate(360deg);
209
  opacity: 0;
 
210
  }
211
+ }
212
+
213
+ .btn-primary {
214
+ @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;
215
+ }
216
+
217
+ .btn-secondary {
218
+ @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;
219
+ }
220
+ </style>
221
+
 
 
 
 
 
 
 
 
 
222
  </head>
223
  <body>
224
  <div class="min-h-screen bg-gradient-to-b from-primary-50 to-white">
225
  <header class="bg-white shadow-sm py-6">
226
  <div class="container mx-auto px-4">
227
  <div class="flex items-center justify-between">
228
+ <a href="https://mariam-241.vercel.app" class="text-3xl font-bold text-primary-700 flex items-center">
229
  <i class="fas fa-home mr-3 text-primary-500"></i>
230
+ Accueil
231
  </a>
232
  <div class="flex space-x-2">
233
  <button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100">
 
241
  </div>
242
  </header>
243
 
244
+ <main class="container mx-auto px-4 py-8">
245
+ <div class="max-w-3xl mx-auto">
246
+ <!-- Introduction -->
247
+ <div class="text-center mb-8">
248
+ <h2 class="text-4xl font-bold text-gray-800 mb-4">Apprenez plus efficacement</h2>
249
+ <p class="text-lg text-gray-600 mb-6">Créez des flashcards ou des quiz pour mémoriser n'importe quel sujet</p>
250
+ </div>
251
+
252
+ <!-- Section de configuration -->
253
+ <div class="bg-white rounded-xl shadow-lg p-8 mb-10 relative overflow-hidden">
254
+ <div class="absolute top-0 right-0 w-40 h-40 bg-primary-100 rounded-full -mr-20 -mt-20 z-0"></div>
255
+ <div class="absolute bottom-0 left-0 w-24 h-24 bg-primary-100 rounded-full -ml-12 -mb-12 z-0"></div>
256
 
257
+ <div class="relative z-10">
258
+ <h3 class="text-2xl font-semibold text-gray-800 mb-6">Que souhaitez-vous apprendre aujourd'hui ?</h3>
 
 
259
 
260
+ <div class="space-y-6">
261
+ <div class="relative">
262
+ <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...">
263
+ <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
264
+ <div class="text-xs text-gray-500 mt-1 ml-2">Exemples: "Capitales du monde", "Photosynthèse", "Verbes irréguliers en anglais"</div>
265
+ </div>
266
 
267
+ <div>
268
+ <p class="text-gray-700 font-medium mb-3">Choisissez votre méthode d'apprentissage :</p>
269
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
270
+ <div class="relative">
271
+ <input type="radio" id="typeFlashcards" name="contentType" value="flashcards" checked class="peer absolute opacity-0">
272
+ <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">
273
+ <div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3">
274
+ <i class="fas fa-clone text-xl text-primary-600"></i>
275
+ </div>
276
+ <h4 class="font-semibold text-gray-800">Flashcards</h4>
277
+ <p class="text-sm text-gray-600 text-center mt-2">Pour mémoriser des informations par répétition</p>
278
+ </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  </div>
280
+
281
+ <div class="relative">
282
+ <input type="radio" id="typeQuiz" name="contentType" value="quiz" class="peer absolute opacity-0">
283
+ <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">
284
+ <div class="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center mb-3">
285
+ <i class="fas fa-question text-xl text-primary-600"></i>
286
+ </div>
287
+ <h4 class="font-semibold text-gray-800">Quiz</h4>
288
+ <p class="text-sm text-gray-600 text-center mt-2">Pour tester vos connaissances avec des QCM</p>
289
+ </label>
290
  </div>
 
 
 
 
291
  </div>
292
  </div>
293
+
294
+ <div class="flex items-center justify-between pt-2">
295
+ <div class="text-sm text-gray-500 italic">
296
+ <i class="fas fa-info-circle mr-1"></i> La génération peut prendre jusqu'à 1 minute
297
+ </div>
298
+ <button id="generateBtn" class="btn-primary flex items-center">
299
+ <span class="mr-2">Générer</span>
300
+ <i class="fas fa-chevron-right"></i>
301
+ </button>
302
+ </div>
303
  </div>
304
  </div>
305
+ </div>
306
 
307
+ <!-- Indicateur de chargement -->
308
+ <div id="loading" class="hidden">
309
+ <div class="bg-white rounded-xl shadow-lg p-8 text-center">
310
+ <div class="flex flex-col items-center justify-center space-y-4">
311
+ <div class="relative w-24 h-24">
312
+ <div class="absolute inset-0 border-4 border-primary-100 border-t-primary-500 rounded-full animate-spin"></div>
313
+ <div class="absolute inset-0 flex items-center justify-center">
314
+ <i class="fas fa-lightbulb text-2xl text-primary-500 animate-pulse"></i>
 
315
  </div>
316
+ </div>
317
+ <h3 class="text-xl font-semibold text-gray-800">Création en cours...</h3>
318
+ <p class="text-gray-600">Nous élaborons votre contenu d'apprentissage personnalisé</p>
319
+
320
+ <div class="w-full max-w-md mt-4">
321
+ <div class="progress-bar-container">
322
+ <div id="progressBar" class="progress-bar" style="width: 0%"></div>
323
+ </div>
324
+ <div id="loadingSteps" class="mt-8 text-left">
325
+ <div class="flex items-center mb-3">
326
+ <div class="w-6 h-6 rounded-full bg-primary-500 flex items-center justify-center mr-3">
327
+ <i class="fas fa-check text-white text-xs"></i>
 
328
  </div>
329
+ <span class="text-sm text-gray-700">Analyse du sujet</span>
330
+ </div>
331
+ <div class="flex items-center mb-3">
332
+ <div id="step2" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
333
+ <i class="fas fa-spinner text-white text-xs animate-spin"></i>
334
  </div>
335
+ <span class="text-sm text-gray-500">Recherche d'informations</span>
336
+ </div>
337
+ <div class="flex items-center mb-3">
338
+ <div id="step3" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
339
+ <i class="fas fa-hourglass text-white text-xs"></i>
340
  </div>
341
+ <span class="text-sm text-gray-500">Création du contenu</span>
342
+ </div>
343
+ <div class="flex items-center">
344
+ <div id="step4" class="w-6 h-6 rounded-full bg-gray-200 flex items-center justify-center mr-3">
345
+ <i class="fas fa-hourglass text-white text-xs"></i>
346
  </div>
347
+ <span class="text-sm text-gray-500">Finalisation</span>
348
  </div>
349
  </div>
350
  </div>
351
  </div>
352
  </div>
353
+ </div>
354
 
355
+ <!-- Conteneur pour les Flashcards -->
356
+ <div id="flashcardsContainer" class="mt-8 hidden">
357
+ <!-- Les flashcards seront injectées ici -->
 
 
 
 
 
 
358
  </div>
 
359
 
360
+ <!-- Conteneur pour le Quiz -->
361
+ <div id="quizContainer" class="mt-8 hidden">
362
+ <!-- Les questions du quiz seront injectées ici -->
363
+ </div>
364
+ </div>
365
+ </main>
366
+
367
+ <footer class="bg-gray-900 text-white py-8 mt-20">
368
+ <div class="container mx-auto px-4">
369
+ <div class="flex flex-col md:flex-row justify-between items-center">
370
+ <div class="mb-4 md:mb-0">
371
+ <h2 class="text-xl font-bold flex items-center">
372
+ <i class="fas fa-brain mr-2"></i> Mémorisation Facile
373
+ </h2>
374
+ <p class="text-gray-400 text-sm mt-2">Votre outil d'apprentissage intelligent</p>
 
375
  </div>
376
+ <div class="flex space-x-4">
377
+ <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-twitter"></i></a>
378
+ <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-facebook"></i></a>
379
+ <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-instagram"></i></a>
380
+ <a href="#" class="text-gray-400 hover:text-white transition-colors"><i class="fab fa-youtube"></i></a>
381
  </div>
382
  </div>
383
+ <div class="mt-8 border-t border-gray-800 pt-6 text-sm text-gray-400 text-center">
384
+ © 2025 Mémorisation Facile. Tous droits réservés.
385
+ </div>
386
+ </div>
387
+ </footer>
388
+ </div>
389
+
390
+ <script>
391
+ const generateBtn = document.getElementById('generateBtn');
392
+ const loadingIndicator = document.getElementById('loading');
393
+ const flashcardsContainer = document.getElementById('flashcardsContainer');
394
+ const quizContainer = document.getElementById('quizContainer');
395
+ const topicInput = document.getElementById('topic');
396
+ const progressBar = document.getElementById('progressBar');
397
+ const themeToggle = document.getElementById('themeToggle');
398
+ const step2 = document.getElementById('step2');
399
+ const step3 = document.getElementById('step3');
400
+ const step4 = document.getElementById('step4');
401
+
402
+ // Gestion du thème clair/sombre
403
+ themeToggle.addEventListener('click', function() {
404
+ document.body.classList.toggle('dark-mode');
405
+ const icon = this.querySelector('i');
406
+ if (icon.classList.contains('fa-moon')) {
407
+ icon.classList.remove('fa-moon');
408
+ icon.classList.add('fa-sun');
409
+ } else {
410
+ icon.classList.remove('fa-sun');
411
+ icon.classList.add('fa-moon');
412
+ }
413
+ });
414
+
415
+ // Animation de chargement
416
+ function simulateLoading() {
417
+ let progress = 0;
418
+ const interval = setInterval(() => {
419
+ progress += 1;
420
+ progressBar.style.width = `${progress}%`;
421
+
422
+ if (progress === 25) {
423
+ step2.classList.remove('bg-gray-200');
424
+ step2.classList.add('bg-primary-500');
425
+ step2.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
426
+ } else if (progress === 60) {
427
+ step3.classList.remove('bg-gray-200');
428
+ step3.classList.add('bg-primary-500');
429
+ step3.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
430
+ step4.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>';
431
+ } else if (progress === 95) {
432
+ step4.classList.remove('bg-gray-200');
433
+ step4.classList.add('bg-primary-500');
434
+ step4.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
435
  }
436
+
437
+ if (progress >= 100) {
438
+ clearInterval(interval);
 
 
 
 
439
  }
440
+ }, 50);
441
 
442
+ return interval;
443
+ }
444
+
445
+ // Fonction pour créer des confettis
446
+ function createConfetti() {
447
+ const confettiCount = 100;
448
+ const colors = ['#0ea5e9', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];
449
+
450
+ for (let i = 0; i < confettiCount; i++) {
451
+ const confetti = document.createElement('div');
452
+ confetti.className = 'confetti';
453
+ confetti.style.left = `${Math.random() * 100}vw`;
454
+ confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
455
+ confetti.style.width = `${Math.random() * 10 + 5}px`;
456
+ confetti.style.height = `${Math.random() * 10 + 5}px`;
457
+ confetti.style.animationDuration = `${Math.random() * 3 + 2}s`;
458
+ document.body.appendChild(confetti);
459
+
460
+ setTimeout(() => {
461
+ confetti.remove();
462
+ }, 5000);
463
+ }
464
+ }
465
+
466
+ // Gestion des flashcards
467
+ function flipCard(card) {
468
+ card.classList.toggle('flipped');
469
+ }
470
+
471
+ generateBtn.addEventListener('click', function() {
472
+ const topic = topicInput.value.trim();
473
+ if (!topic) {
474
+ // Animation de secouement sur l'input
475
+ topicInput.classList.add('border-red-500');
476
+ topicInput.classList.add('animate-bounce');
477
+ setTimeout(() => {
478
+ topicInput.classList.remove('animate-bounce');
479
+ topicInput.classList.remove('border-red-500');
480
+ }, 1000);
481
+
482
+ // Afficher un message d'erreur
483
+ const errorMsg = document.createElement('div');
484
+ errorMsg.className = 'text-red-500 text-sm mt-1 ml-2';
485
+ errorMsg.textContent = 'Veuillez entrer un sujet';
486
+ topicInput.parentElement.appendChild(errorMsg);
487
+ setTimeout(() => {
488
+ errorMsg.remove();
489
+ }, 3000);
490
+
491
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  }
493
 
494
+ const contentType = document.querySelector('input[name="contentType"]:checked').value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
+ // Afficher le chargement et cacher les anciens résultats
497
+ loadingIndicator.classList.remove('hidden');
498
+ flashcardsContainer.classList.add('hidden');
499
+ quizContainer.classList.add('hidden');
500
+ flashcardsContainer.innerHTML = '';
501
+ quizContainer.innerHTML = '';
502
+
503
+ // Simuler l'animation de chargement
504
+ const loadingInterval = simulateLoading();
505
+
506
+ // Envoi de la requête
507
+ fetch('/generate', {
508
+ method: 'POST',
509
+ headers: {
510
+ 'Content-Type': 'application/json',
511
+ },
512
+ body: JSON.stringify({ topic, type: contentType }),
513
+ })
514
+ .then(response => {
515
+ if (!response.ok) {
516
+ throw new Error(`Erreur HTTP: ${response.status}`);
517
  }
518
+ return response.json();
519
+ })
520
+ .then(data => {
521
+ // Assurons-nous que l'animation montre au moins 2 secondes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  setTimeout(() => {
523
+ clearInterval(loadingInterval);
524
+ progressBar.style.width = '100%';
525
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  setTimeout(() => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  loadingIndicator.classList.add('hidden');
528
+
529
+ if (data.error) {
530
+ // Afficher une erreur élégante
531
+ const errorContainer = document.createElement('div');
532
+ errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
533
+ errorContainer.innerHTML = `
534
+ <div class="flex">
535
+ <div class="flex-shrink-0">
536
+ <i class="fas fa-exclamation-circle text-red-500"></i>
537
+ </div>
538
+ <div class="ml-3">
539
+ <p class="text-sm text-red-700">
540
+ ${data.error}
541
+ </p>
542
+ </div>
543
+ </div>
544
+ `;
545
+ document.querySelector('.max-w-3xl').appendChild(errorContainer);
546
+ return;
547
+ }
548
+
549
+ if (contentType === 'flashcards' && data.flashcards) {
550
+ flashcardsContainer.classList.remove('hidden');
551
+ displayFlashcards(data.flashcards);
552
+ // Créer un effet de confettis
553
+ createConfetti();
554
+ } else if (contentType === 'quiz' && data.quiz) {
555
+ quizContainer.classList.remove('hidden');
556
+ displayQuiz(data.quiz);
557
+ // Créer un effet de confettis
558
+ createConfetti();
559
+ } else {
560
+ // Afficher une erreur élégante
561
+ const errorContainer = document.createElement('div');
562
+ errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
563
+ errorContainer.innerHTML = `
564
+ <div class="flex">
565
+ <div class="flex-shrink-0">
566
+ <i class="fas fa-exclamation-circle text-red-500"></i>
567
+ </div>
568
+ <div class="ml-3">
569
+ <p class="text-sm text-red-700">
570
+ Aucune donnée reçue ou format incorrect.
571
+ </p>
572
+ </div>
573
+ </div>
574
+ `;
575
+ document.querySelector('.max-w-3xl').appendChild(errorContainer);
576
+ }
577
+ }, 500);
578
+ }, 2000);
579
+ })
580
+ .catch(error => {
581
+ clearInterval(loadingInterval);
582
+ loadingIndicator.classList.add('hidden');
583
+ console.error('Erreur lors de la génération:', error);
584
+
585
+ // Afficher une erreur élégante
586
  const errorContainer = document.createElement('div');
587
+ errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
588
  errorContainer.innerHTML = `
589
  <div class="flex">
590
  <div class="flex-shrink-0">
591
+ <i class="fas fa-exclamation-circle text-red-500"></i>
592
  </div>
593
  <div class="ml-3">
594
+ <p class="text-sm text-red-700">
595
+ Erreur lors de la génération: ${error.message}.
 
596
  </p>
597
  </div>
598
  </div>
599
  `;
600
+ document.querySelector('.max-w-3xl').appendChild(errorContainer);
601
+ });
602
+ });
603
+
604
+ function displayFlashcards(flashcards) {
605
+ const header = document.createElement('div');
606
+ header.className = 'mb-8 text-center';
607
+ header.innerHTML = `
608
+ <h2 class="text-3xl font-bold text-gray-800 mb-2">Vos Flashcards (${flashcards.length})</h2>
609
+ <p >Cliquez sur une carte pour la retourner et voir la réponse.</p>
610
+ <p class="text-gray-600">
611
+ <i class="far fa-lightbulb mr-1"></i> Astuce : Essayez de répondre mentalement avant de retourner la carte
612
+ </p>
613
+ <p><div class="flex items-center justify-center space-x-4 mt-6">
614
+ </div></p>
615
+
616
+ `;
617
+ flashcardsContainer.appendChild(header);
618
+
619
+ const flashcardsGrid = document.createElement('div');
620
+ flashcardsGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6';
621
+ flashcardsContainer.appendChild(flashcardsGrid);
622
+
623
+ flashcards.forEach((card, index) => {
624
+ const cardElement = document.createElement('div');
625
+ cardElement.className = 'flashcard cursor-pointer';
626
+ cardElement.onclick = function() { flipCard(this); };
627
+ cardElement.innerHTML = `
628
+ <div class="flashcard-inner">
629
+ <div class="flashcard-front">
630
+ <div class="w-full">
631
+ <div class="absolute top-2 left-2 text-xs text-gray-400">#${index + 1}</div>
632
+ <h3 class="text-lg font-semibold mb-2 text-center">${card.question}</h3>
633
+ <div class="text-center mt-4">
634
+ <span class="text-xs text-gray-500">Cliquez pour voir la réponse</span>
 
 
 
 
 
 
 
 
 
 
635
  </div>
636
  </div>
637
+ </div>
638
+
639
+ <div class="flashcard-back">
640
+ <div class="w-full">
641
+ <div class="absolute top-2 right-2 text-xs text-white opacity-70">#${index + 1}</div>
642
+ <div class="text-center">
643
+ <p class="font-medium">${card.answer}</p>
644
+ </div>
645
+ <div class="absolute bottom-2 right-2">
646
+ <span class="text-xs text-white opacity-70">Cliquez pour retourner</span>
647
  </div>
648
  </div>
649
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
  </div>
651
+ `;
652
+ flashcardsGrid.appendChild(cardElement);
653
+ });
654
+
655
+ // Ajouter des boutons de navigation et de contrôle
656
+ const controls = document.createElement('div');
657
+ controls.className = 'mt-10 flex flex-col items-center justify-center';
658
+ controls.innerHTML = `
659
+ <div class="flex items-center space-x-4 mb-6">
660
+ <button class="px-4 py-2 bg-primary-100 hover:bg-primary-200 text-primary-700 rounded-md transition duration-300 flex items-center">
661
+ <i class="fas fa-redo-alt mr-2"></i> Recommencer
662
+ </button>
663
+ <button id="newTopicBtn" class="px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white rounded-md transition duration-300 flex items-center">
664
+ <i class="fas fa-plus mr-2"></i> Nouveau sujet
665
+ </button>
666
+ </div>
667
+
668
+ `;
669
+ flashcardsContainer.appendChild(controls);
670
+
671
+ // Ajouter des interactions aux boutons
672
+ document.getElementById('printBtn').addEventListener('click', function() {
673
+ window.print();
674
+ });
675
+
676
+ document.getElementById('saveBtn').addEventListener('click', function() {
677
+ alert('Flashcards sauvegardées !');
678
+ });
679
+
680
+ document.getElementById('shareBtn').addEventListener('click', function() {
681
+ alert('Lien de partage copié dans le presse-papiers !');
682
+ });
683
+
684
+ document.getElementById('newTopicBtn').addEventListener('click', function() {
685
+ window.scrollTo({
686
+ top: 0,
687
+ behavior: 'smooth'
688
  });
689
+ topicInput.focus();
690
+ });
691
+ }
692
+
693
+ function displayQuiz(quizQuestions) {
694
+ const header = document.createElement('div');
695
+ header.className = 'mb-8 text-center';
696
+ header.innerHTML = `
697
+ <h2 class="text-3xl font-bold text-gray-800 mb-2">Votre Quiz (${quizQuestions.length} questions)</h2>
698
+ <p class="text-gray-600">Testez vos connaissances en répondant aux questions</p>
699
+ <div class="flex items-center justify-center mt-6">
700
+ <div class="bg-white px-4 py-2 rounded-full shadow-sm flex items-center">
701
+ <span class="text-primary-700 font-medium">Score: </span>
702
+ <span id="score" class="ml-1 text-primary-700 font-bold">0</span>
703
+ <span class="mx-1 text-gray-400">/</span>
704
+ <span class="text-gray-600">${quizQuestions.length}</span>
705
+ </div>
706
+ </div>
707
+ `;
708
+ quizContainer.appendChild(header);
709
+
710
+ // Créer un conteneur pour les questions
711
+ const questionsContainer = document.createElement('div');
712
+ questionsContainer.className = 'space-y-8';
713
+ quizContainer.appendChild(questionsContainer);
714
+
715
+ // Variable pour suivre le score
716
+ let currentScore = 0;
717
+
718
+ quizQuestions.forEach((question, qIndex) => {
719
+ const questionElement = document.createElement('div');
720
+ questionElement.className = 'bg-white rounded-xl shadow-md p-6 transition-all duration-300';
721
+ questionElement.setAttribute('id', `question-${qIndex}`);
722
+
723
+ let optionsHtml = '';
724
+ const safeCorrectAnswer = question.correctAnswer.replace(/"/g, '"');
725
+
726
+ question.options.forEach((option, oIndex) => {
727
+ optionsHtml += `
728
+ <div class="quiz-option mb-3" id="option-${qIndex}-${oIndex}">
729
+ <input type="radio" id="q${qIndex}-o${oIndex}" name="question-${qIndex}" value="${option}" data-correct="${safeCorrectAnswer}">
730
+ <label for="q${qIndex}-o${oIndex}" class="group">
731
+ ${option}
732
+ <span class="hidden success-icon absolute right-4 text-green-500">
733
+ <i class="fas fa-check-circle"></i>
734
+ </span>
735
+ <span class="hidden error-icon absolute right-4 text-red-500">
736
+ <i class="fas fa-times-circle"></i>
737
+ </span>
738
+ </label>
739
  </div>
740
+ `;
741
+ });
742
+
743
+ questionElement.innerHTML = `
744
+ <div class="flex items-center justify-between mb-4">
745
+ <span class="bg-primary-100 text-primary-800 text-xs font-medium px-2.5 py-0.5 rounded">Question ${qIndex + 1}/${quizQuestions.length}</span>
746
+ <span class="text-gray-400 text-sm">
747
+ <i class="far fa-lightbulb"></i>
748
+ </span>
749
  </div>
750
+ <h3 class="text-xl font-medium text-gray-800 mb-4">${question.question}</h3>
751
+ <div class="options mt-5">
752
+ ${optionsHtml}
753
+ </div>
754
+ <div class="explanation hidden mt-6 p-4 bg-blue-50 border border-blue-100 rounded-lg" id="explanation-${qIndex}">
755
+ <div class="flex items-start">
756
+ <div class="flex-shrink-0 mt-0.5">
757
+ <i class="fas fa-info-circle text-blue-500"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  </div>
759
+ <div class="ml-3">
760
+ <h4 class="text-sm font-medium text-blue-800">Explication</h4>
761
+ <div class="mt-1 text-sm text-blue-700">${question.explanation}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
762
  </div>
763
  </div>
764
+ </div>
765
+ `;
766
+ questionsContainer.appendChild(questionElement);
767
+
768
+ // Ajouter des gestionnaires d'événements pour les options
769
+ const options = questionElement.querySelectorAll('input[type="radio"]');
770
+ options.forEach((option) => {
771
+ option.addEventListener('change', function() {
772
+ const optionId = this.id;
773
+ const [_, qIdx, oIdx] = optionId.match(/q(\d+)-o(\d+)/);
774
+ const selected = this.value;
775
+ const correct = this.getAttribute('data-correct');
776
+ const explanationElement = document.getElementById(`explanation-${qIdx}`);
777
+
778
+ // Vérifier la réponse
779
+ let delay = 0; // Initialiser le délai
780
+ if (selected === correct) {
781
+ // Réponse correcte
782
+ document.getElementById(`option-${qIdx}-${oIdx}`).classList.add('correct');
783
+ currentScore++;
784
+ document.getElementById('score').textContent = currentScore;
785
+
786
+ // Afficher une animation de succès
787
+ const successIcon = this.nextElementSibling.querySelector('.success-icon');
788
+ successIcon.classList.remove('hidden');
789
+ delay = 2000; // 2 secondes pour une réponse correcte
790
+ } else {
791
+ // Réponse incorrecte
792
+ document.getElementById(`option-${qIdx}-${oIdx}`).classList.add('incorrect');
793
+
794
+ // Afficher une animation d'erreur
795
+ const errorIcon = this.nextElementSibling.querySelector('.error-icon');
796
+ errorIcon.classList.remove('hidden');
797
+
798
+ // Mettre en évidence la bonne réponse
799
+ options.forEach((opt, idx) => {
800
+ if (opt.value === correct) {
801
+ document.getElementById(`option-${qIdx}-${idx}`).classList.add('correct');
802
+ const successIcon = opt.nextElementSibling.querySelector('.success-icon');
803
+ successIcon.classList.remove('hidden');
804
+ }
805
+ });
806
+ delay = 5000; // 5 secondes pour une réponse incorrecte
807
+ }
808
 
809
+ // Désactiver toutes les autres options
810
+ options.forEach((opt) => {
811
+ if (opt !== this) {
812
+ opt.disabled = true;
 
 
 
 
813
  }
814
+ });
815
 
816
+ // Afficher l'explication
817
+ explanationElement.classList.remove('hidden');
 
818
 
819
+ // Animer le passage à la question suivante
820
+ setTimeout(() => {
821
+ const nextQuestion = document.getElementById(`question-${parseInt(qIdx) + 1}`);
822
+ if (nextQuestion) {
823
+ window.scrollTo({
824
+ top: nextQuestion.offsetTop - 20,
825
+ behavior: 'smooth'
826
+ });
827
  } else {
828
+ // C'était la dernière question, afficher un récapitulatif
829
+ if (parseInt(qIdx) === quizQuestions.length - 1) {
830
+ displayQuizResults(currentScore, quizQuestions.length);
831
+ }
 
 
 
832
  }
833
+ }, delay);
834
  });
835
  });
836
+ });
837
+ }
838
+
839
+ function displayQuizResults(score, total) {
840
+ const percentage = (score / total) * 100;
841
+ let resultClass, resultIcon, resultMessage;
842
+
843
+ if (percentage >= 80) {
844
+ resultClass = 'bg-green-50 border-green-500 text-green-800';
845
+ resultIcon = '<i class="fas fa-trophy text-3xl text-yellow-500 mb-3"></i>';
846
+ resultMessage = 'Excellent ! Vous maîtrisez ce sujet.';
847
+ } else if (percentage >= 60) {
848
+ resultClass = 'bg-blue-50 border-blue-500 text-blue-800';
849
+ resultIcon = '<i class="fas fa-medal text-3xl text-blue-500 mb-3"></i>';
850
+ resultMessage = 'Bon travail ! Continuez vos efforts.';
851
+ } else {
852
+ resultClass = 'bg-red-50 border-red-500 text-red-800';
853
+ resultIcon = '<i class="fas fa-book-open text-3xl text-red-500 mb-3"></i>';
854
+ resultMessage = 'Continuez à étudier pour améliorer vos connaissances.';
855
  }
856
+
857
+ const resultsElement = document.createElement('div');
858
+ resultsElement.className = `mt-10 p-6 rounded-xl shadow-lg border-l-4 ${resultClass} text-center`;
859
+ resultsElement.innerHTML = `
860
+ <div class="flex flex-col items-center">
861
+ ${resultIcon}
862
+ <h3 class="text-2xl font-bold mb-2">Résultat final: ${score}/${total}</h3>
863
+ <p class="mb-4">${resultMessage}</p>
864
+ <div class="w-full max-w-xs bg-gray-200 rounded-full h-4 mb-4">
865
+ <div class="h-4 rounded-full ${percentage >= 80 ? 'bg-green-500' : percentage >= 60 ? 'bg-blue-500' : 'bg-red-500'}" style="width: ${percentage}%"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
  </div>
867
+ <div class="flex space-x-4 mt-6">
868
+ <button id="retryQuizBtn" class="btn-secondary text-sm">
869
+ <i class="fas fa-redo mr-2"></i> Réessayer
870
+ </button>
871
+ <button id="newQuizBtn" class="btn-primary text-sm">
872
+ <i class="fas fa-plus mr-2"></i> Nouveau quiz
873
+ </button>
874
+ </div>
875
+ </div>
876
+ `;
877
+ quizContainer.appendChild(resultsElement);
878
+
879
+ // Animation de confettis si le score est bon
880
+ if (percentage >= 70) {
881
+ createConfetti();
882
+ }
883
+
884
+ // Ajouter des interactions aux boutons
885
+ document.getElementById('retryQuizBtn').addEventListener('click', function() {
886
+ const radios = document.querySelectorAll('input[type="radio"]');
887
+ radios.forEach(radio => {
888
+ radio.checked = false;
889
+ radio.disabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890
  });
891
+
892
+ const options = document.querySelectorAll('.quiz-option');
893
+ options.forEach(option => {
894
+ option.classList.remove('correct', 'incorrect');
895
+ });
896
+
897
+ const explanations = document.querySelectorAll('.explanation');
898
+ explanations.forEach(exp => {
899
+ exp.classList.add('hidden');
900
+ });
901
+
902
+ const icons = document.querySelectorAll('.success-icon, .error-icon');
903
+ icons.forEach(icon => {
904
+ icon.classList.add('hidden');
905
+ });
906
+
907
+ resultsElement.remove();
908
+ document.getElementById('score').textContent = '0';
909
+
910
+ window.scrollTo({
911
+ top: quizContainer.offsetTop,
912
+ behavior: 'smooth'
913
+ });
914
+ });
915
+
916
+ document.getElementById('newQuizBtn').addEventListener('click', function() {
917
+ window.scrollTo({
918
+ top: 0,
919
+ behavior: 'smooth'
920
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
921
  topicInput.focus();
922
  });
923
+ }
924
+
925
+ // Focus sur le champ de saisie au chargement
926
+ window.addEventListener('load', () => {
927
+ topicInput.focus();
928
+ });
929
+ </script>
930
+
931
  </body>
932
+ </html>