Docfile commited on
Commit
e190b23
·
verified ·
1 Parent(s): 25e64e8

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +480 -308
templates/index.html CHANGED
@@ -222,9 +222,9 @@
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="https://mariam-241.vercel.app/indeex" class="text-3xl font-bold text-primary-700 flex items-center">
226
  <i class="fas fa-home mr-3 text-primary-500"></i>
227
- Accueil
228
  </a>
229
  <div class="flex space-x-2">
230
  <button id="themeToggle" class="p-2 rounded-full hover:bg-gray-100">
@@ -361,7 +361,8 @@
361
  </div>
362
  </main>
363
 
364
- <footer class="bg-gray-900 text-white py-8 mt-20">
 
365
  <div class="container mx-auto px-4">
366
  <div class="flex flex-col md:flex-row justify-between items-center">
367
  <div class="mb-4 md:mb-0">
@@ -378,7 +379,7 @@
378
  </div>
379
  </div>
380
  <div class="mt-8 border-t border-gray-800 pt-6 text-sm text-gray-400 text-center">
381
- &copy; 2025 Mémorisation Facile. Tous droits réservés.
382
  </div>
383
  </div>
384
  </footer>
@@ -398,7 +399,7 @@
398
 
399
  // Gestion du thème clair/sombre
400
  themeToggle.addEventListener('click', function() {
401
- document.body.classList.toggle('dark-mode');
402
  const icon = this.querySelector('i');
403
  if (icon.classList.contains('fa-moon')) {
404
  icon.classList.remove('fa-moon');
@@ -407,239 +408,325 @@
407
  icon.classList.remove('fa-sun');
408
  icon.classList.add('fa-moon');
409
  }
 
 
 
 
 
 
 
 
410
  });
411
 
412
  // Animation de chargement
413
  function simulateLoading() {
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  let progress = 0;
415
  const interval = setInterval(() => {
416
  progress += 1;
417
- progressBar.style.width = `${progress}%`;
418
-
419
  if (progress === 25) {
420
  step2.classList.remove('bg-gray-200');
421
  step2.classList.add('bg-primary-500');
422
  step2.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
 
423
  } else if (progress === 60) {
424
  step3.classList.remove('bg-gray-200');
425
  step3.classList.add('bg-primary-500');
426
  step3.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
427
  step4.innerHTML = '<i class="fas fa-spinner text-white text-xs animate-spin"></i>';
428
  } else if (progress === 95) {
429
- step4.classList.remove('bg-gray-200');
430
  step4.classList.add('bg-primary-500');
431
  step4.innerHTML = '<i class="fas fa-check text-white text-xs"></i>';
432
  }
433
-
434
  if (progress >= 100) {
435
  clearInterval(interval);
 
436
  }
437
- }, 50);
438
-
439
- return interval;
440
  }
441
-
442
  // Fonction pour créer des confettis
443
  function createConfetti() {
444
  const confettiCount = 100;
445
  const colors = ['#0ea5e9', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];
446
-
447
  for (let i = 0; i < confettiCount; i++) {
448
  const confetti = document.createElement('div');
449
- confetti.className = 'confetti';
450
  confetti.style.left = `${Math.random() * 100}vw`;
451
  confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
452
  confetti.style.width = `${Math.random() * 10 + 5}px`;
453
  confetti.style.height = `${Math.random() * 10 + 5}px`;
454
- confetti.style.animationDuration = `${Math.random() * 3 + 2}s`;
455
- document.body.appendChild(confetti);
 
 
456
 
 
 
457
  setTimeout(() => {
458
  confetti.remove();
459
- }, 5000);
460
  }
461
  }
462
 
 
463
  // Gestion des flashcards
464
  function flipCard(card) {
465
  card.classList.toggle('flipped');
466
  }
467
-
468
  generateBtn.addEventListener('click', function() {
469
  const topic = topicInput.value.trim();
 
 
 
 
 
 
 
 
 
470
  if (!topic) {
471
  // Animation de secouement sur l'input
472
  topicInput.classList.add('border-red-500');
473
- topicInput.classList.add('animate-bounce');
474
  setTimeout(() => {
475
  topicInput.classList.remove('animate-bounce');
476
  topicInput.classList.remove('border-red-500');
477
  }, 1000);
478
-
479
- // Afficher un message d'erreur
480
  const errorMsg = document.createElement('div');
481
  errorMsg.className = 'text-red-500 text-sm mt-1 ml-2';
482
  errorMsg.textContent = 'Veuillez entrer un sujet';
483
  topicInput.parentElement.appendChild(errorMsg);
484
- setTimeout(() => {
485
- errorMsg.remove();
486
- }, 3000);
487
-
488
  return;
489
  }
490
 
491
  const contentType = document.querySelector('input[name="contentType"]:checked').value;
492
 
493
- // Afficher le chargement et cacher les anciens résultats
494
- loadingIndicator.classList.remove('hidden');
495
  flashcardsContainer.classList.add('hidden');
496
  quizContainer.classList.add('hidden');
497
  flashcardsContainer.innerHTML = '';
498
  quizContainer.innerHTML = '';
499
-
 
500
  // Simuler l'animation de chargement
501
  const loadingInterval = simulateLoading();
 
 
502
 
503
- // Envoi de la requête
504
- fetch('/generate', {
505
- method: 'POST',
506
- headers: {
507
- 'Content-Type': 'application/json',
508
- },
509
- body: JSON.stringify({ topic, type: contentType }),
510
- })
511
- .then(response => {
512
- if (!response.ok) {
513
- throw new Error(`Erreur HTTP: ${response.status}`);
514
- }
515
- return response.json();
516
- })
517
- .then(data => {
518
- // Assurons-nous que l'animation montre au moins 2 secondes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  setTimeout(() => {
520
- clearInterval(loadingInterval);
521
- progressBar.style.width = '100%';
522
-
523
- setTimeout(() => {
524
- loadingIndicator.classList.add('hidden');
525
-
526
- if (data.error) {
527
- // Afficher une erreur élégante
528
- const errorContainer = document.createElement('div');
529
- errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
530
- errorContainer.innerHTML = `
531
- <div class="flex">
532
- <div class="flex-shrink-0">
533
- <i class="fas fa-exclamation-circle text-red-500"></i>
534
- </div>
535
- <div class="ml-3">
536
- <p class="text-sm text-red-700">
537
- ${data.error}
538
- </p>
539
- </div>
540
- </div>
541
- `;
542
- document.querySelector('.max-w-3xl').appendChild(errorContainer);
543
- return;
544
- }
545
 
546
- if (contentType === 'flashcards' && data.flashcards) {
547
- flashcardsContainer.classList.remove('hidden');
548
- displayFlashcards(data.flashcards);
549
- // Créer un effet de confettis
550
- createConfetti();
551
- } else if (contentType === 'quiz' && data.quiz) {
552
- quizContainer.classList.remove('hidden');
553
- displayQuiz(data.quiz);
554
- // Créer un effet de confettis
555
- createConfetti();
556
- } else {
557
- // Afficher une erreur élégante
558
- const errorContainer = document.createElement('div');
559
- errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
560
- errorContainer.innerHTML = `
561
- <div class="flex">
562
- <div class="flex-shrink-0">
563
- <i class="fas fa-exclamation-circle text-red-500"></i>
564
- </div>
565
- <div class="ml-3">
566
- <p class="text-sm text-red-700">
567
- Aucune donnée reçue ou format incorrect.
568
- </p>
569
- </div>
570
- </div>
571
- `;
572
- document.querySelector('.max-w-3xl').appendChild(errorContainer);
573
- }
574
- }, 500);
575
- }, 2000);
576
- })
577
- .catch(error => {
578
- clearInterval(loadingInterval);
579
- loadingIndicator.classList.add('hidden');
580
- console.error('Erreur lors de la génération:', error);
581
-
582
- // Afficher une erreur élégante
583
- const errorContainer = document.createElement('div');
584
- errorContainer.className = 'bg-red-50 border-l-4 border-red-500 p-4 rounded-md mt-4';
585
- errorContainer.innerHTML = `
586
- <div class="flex">
587
- <div class="flex-shrink-0">
588
- <i class="fas fa-exclamation-circle text-red-500"></i>
589
- </div>
590
- <div class="ml-3">
591
- <p class="text-sm text-red-700">
592
- Erreur lors de la génération: ${error.message}.
593
- </p>
594
- </div>
595
- </div>
596
- `;
597
- document.querySelector('.max-w-3xl').appendChild(errorContainer);
598
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
- function displayFlashcards(flashcards) {
602
  const header = document.createElement('div');
603
  header.className = 'mb-8 text-center';
604
  header.innerHTML = `
605
  <h2 class="text-3xl font-bold text-gray-800 mb-2">Vos Flashcards (${flashcards.length})</h2>
606
- <p >Cliquez sur une carte pour la retourner et voir la réponse.</p>
607
- <p class="text-gray-600">
608
- <i class="far fa-lightbulb mr-1"></i> Astuce : Essayez de répondre mentalement avant de retourner la carte
609
- </p>
610
- <p><div class="flex items-center justify-center space-x-4 mt-6">
611
- </div></p>
612
-
613
  `;
614
  flashcardsContainer.appendChild(header);
615
-
616
  const flashcardsGrid = document.createElement('div');
617
  flashcardsGrid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6';
618
  flashcardsContainer.appendChild(flashcardsGrid);
619
-
620
  flashcards.forEach((card, index) => {
621
  const cardElement = document.createElement('div');
 
 
 
622
  cardElement.className = 'flashcard cursor-pointer';
623
  cardElement.onclick = function() { flipCard(this); };
624
  cardElement.innerHTML = `
625
  <div class="flashcard-inner">
626
  <div class="flashcard-front">
627
- <div class="w-full">
628
- <div class="absolute top-2 left-2 text-xs text-gray-400">#${index + 1}</div>
629
- <h3 class="text-lg font-semibold mb-2 text-center">${card.question}</h3>
630
- <div class="text-center mt-4">
631
  <span class="text-xs text-gray-500">Cliquez pour voir la réponse</span>
632
  </div>
633
  </div>
634
  </div>
635
-
636
  <div class="flashcard-back">
637
- <div class="w-full">
638
- <div class="absolute top-2 right-2 text-xs text-white opacity-70">#${index + 1}</div>
639
- <div class="text-center">
640
  <p class="font-medium">${card.answer}</p>
641
  </div>
642
- <div class="absolute bottom-2 right-2">
643
  <span class="text-xs text-white opacity-70">Cliquez pour retourner</span>
644
  </div>
645
  </div>
@@ -648,53 +735,58 @@
648
  `;
649
  flashcardsGrid.appendChild(cardElement);
650
  });
651
-
652
- // Ajouter des boutons de navigation et de contrôle
653
  const controls = document.createElement('div');
654
- controls.className = 'mt-10 flex flex-col items-center justify-center';
655
- controls.innerHTML = `
656
- <div class="flex items-center space-x-4 mb-6">
657
- <button class="px-4 py-2 bg-primary-100 hover:bg-primary-200 text-primary-700 rounded-md transition duration-300 flex items-center">
658
- <i class="fas fa-redo-alt mr-2"></i> Recommencer
659
- </button>
660
- <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">
661
  <i class="fas fa-plus mr-2"></i> Nouveau sujet
662
  </button>
 
 
 
663
  </div>
664
-
665
- `;
666
  flashcardsContainer.appendChild(controls);
667
-
668
  // Ajouter des interactions aux boutons
669
- document.getElementById('printBtn').addEventListener('click', function() {
670
- window.print();
671
- });
672
-
673
- document.getElementById('saveBtn').addEventListener('click', function() {
674
- alert('Flashcards sauvegardées !');
675
- });
676
-
677
- document.getElementById('shareBtn').addEventListener('click', function() {
678
- alert('Lien de partage copié dans le presse-papiers !');
679
- });
680
-
681
- document.getElementById('newTopicBtn').addEventListener('click', function() {
682
- window.scrollTo({
683
- top: 0,
684
- behavior: 'smooth'
685
- });
686
  topicInput.focus();
 
 
 
 
 
 
 
 
 
 
687
  });
688
  }
689
 
690
- function displayQuiz(quizQuestions) {
 
 
691
  const header = document.createElement('div');
692
  header.className = 'mb-8 text-center';
693
  header.innerHTML = `
694
  <h2 class="text-3xl font-bold text-gray-800 mb-2">Votre Quiz (${quizQuestions.length} questions)</h2>
695
  <p class="text-gray-600">Testez vos connaissances en répondant aux questions</p>
696
  <div class="flex items-center justify-center mt-6">
697
- <div class="bg-white px-4 py-2 rounded-full shadow-sm flex items-center">
698
  <span class="text-primary-700 font-medium">Score: </span>
699
  <span id="score" class="ml-1 text-primary-700 font-bold">0</span>
700
  <span class="mx-1 text-gray-400">/</span>
@@ -703,222 +795,302 @@
703
  </div>
704
  `;
705
  quizContainer.appendChild(header);
706
-
707
- // Créer un conteneur pour les questions
708
  const questionsContainer = document.createElement('div');
709
  questionsContainer.className = 'space-y-8';
710
  quizContainer.appendChild(questionsContainer);
711
-
712
- // Variable pour suivre le score
713
  let currentScore = 0;
714
-
 
715
  quizQuestions.forEach((question, qIndex) => {
716
  const questionElement = document.createElement('div');
717
- questionElement.className = 'bg-white rounded-xl shadow-md p-6 transition-all duration-300';
718
  questionElement.setAttribute('id', `question-${qIndex}`);
719
-
 
 
 
720
  let optionsHtml = '';
721
- const safeCorrectAnswer = question.correctAnswer.replace(/"/g, '&quot;');
722
-
723
- question.options.forEach((option, oIndex) => {
 
 
 
 
 
724
  optionsHtml += `
725
  <div class="quiz-option mb-3" id="option-${qIndex}-${oIndex}">
726
- <input type="radio" id="q${qIndex}-o${oIndex}" name="question-${qIndex}" value="${option}" data-correct="${safeCorrectAnswer}">
727
- <label for="q${qIndex}-o${oIndex}" class="group">
728
- ${option}
729
- <span class="hidden success-icon absolute right-4 text-green-500">
730
  <i class="fas fa-check-circle"></i>
731
  </span>
732
- <span class="hidden error-icon absolute right-4 text-red-500">
733
  <i class="fas fa-times-circle"></i>
734
  </span>
735
  </label>
736
  </div>
737
  `;
738
  });
739
-
740
  questionElement.innerHTML = `
741
  <div class="flex items-center justify-between mb-4">
742
- <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>
743
- <span class="text-gray-400 text-sm">
744
- <i class="far fa-lightbulb"></i>
745
- </span>
746
  </div>
747
- <h3 class="text-xl font-medium text-gray-800 mb-4">${question.question}</h3>
748
- <div class="options mt-5">
749
  ${optionsHtml}
750
  </div>
751
- <div class="explanation hidden mt-6 p-4 bg-blue-50 border border-blue-100 rounded-lg" id="explanation-${qIndex}">
752
  <div class="flex items-start">
753
  <div class="flex-shrink-0 mt-0.5">
754
- <i class="fas fa-info-circle text-blue-500"></i>
755
  </div>
756
  <div class="ml-3">
757
- <h4 class="text-sm font-medium text-blue-800">Explication</h4>
758
- <div class="mt-1 text-sm text-blue-700">${question.explanation}</div>
759
  </div>
760
  </div>
761
  </div>
762
  `;
763
  questionsContainer.appendChild(questionElement);
764
-
765
- // Ajouter des gestionnaires d'événements pour les options
766
- const options = questionElement.querySelectorAll('input[type="radio"]');
767
- options.forEach((option) => {
768
- option.addEventListener('change', function() {
769
- const optionId = this.id;
770
- const [_, qIdx, oIdx] = optionId.match(/q(\d+)-o(\d+)/);
771
- const selected = this.value;
772
- const correct = this.getAttribute('data-correct');
 
 
 
 
 
773
  const explanationElement = document.getElementById(`explanation-${qIdx}`);
774
-
775
- // Vérifier la réponse
776
- let delay = 0; // Initialiser le délai
777
- if (selected === correct) {
 
 
778
  // Réponse correcte
779
- document.getElementById(`option-${qIdx}-${oIdx}`).classList.add('correct');
780
- currentScore++;
 
781
  document.getElementById('score').textContent = currentScore;
782
-
783
- // Afficher une animation de succès
784
- const successIcon = this.nextElementSibling.querySelector('.success-icon');
785
- successIcon.classList.remove('hidden');
786
- delay = 2000; // 2 secondes pour une réponse correcte
787
  } else {
788
  // Réponse incorrecte
789
- document.getElementById(`option-${qIdx}-${oIdx}`).classList.add('incorrect');
790
-
791
- // Afficher une animation d'erreur
792
- const errorIcon = this.nextElementSibling.querySelector('.error-icon');
793
- errorIcon.classList.remove('hidden');
794
-
795
- // Mettre en évidence la bonne réponse
796
- options.forEach((opt, idx) => {
797
- if (opt.value === correct) {
798
- document.getElementById(`option-${qIdx}-${idx}`).classList.add('correct');
799
- const successIcon = opt.nextElementSibling.querySelector('.success-icon');
800
- successIcon.classList.remove('hidden');
801
  }
802
  });
803
- delay = 5000; // 5 secondes pour une réponse incorrecte
804
  }
805
 
806
- // Désactiver toutes les autres options
807
- options.forEach((opt) => {
808
- if (opt !== this) {
809
- opt.disabled = true;
810
- }
811
- });
812
-
813
  // Afficher l'explication
814
  explanationElement.classList.remove('hidden');
 
815
 
816
- // Animer le passage à la question suivante
817
- setTimeout(() => {
818
- const nextQuestion = document.getElementById(`question-${parseInt(qIdx) + 1}`);
819
- if (nextQuestion) {
820
- window.scrollTo({
821
- top: nextQuestion.offsetTop - 20,
822
- behavior: 'smooth'
823
- });
824
- } else {
825
- // C'était la dernière question, afficher un récapitulatif
826
- if (parseInt(qIdx) === quizQuestions.length - 1) {
827
- displayQuizResults(currentScore, quizQuestions.length);
828
- }
829
- }
830
- }, delay);
831
  });
832
  });
833
  });
834
  }
835
-
 
836
  function displayQuizResults(score, total) {
837
- const percentage = (score / total) * 100;
838
- let resultClass, resultIcon, resultMessage;
839
-
 
 
 
 
840
  if (percentage >= 80) {
841
  resultClass = 'bg-green-50 border-green-500 text-green-800';
842
- resultIcon = '<i class="fas fa-trophy text-3xl text-yellow-500 mb-3"></i>';
843
  resultMessage = 'Excellent ! Vous maîtrisez ce sujet.';
844
- } else if (percentage >= 60) {
 
845
  resultClass = 'bg-blue-50 border-blue-500 text-blue-800';
846
- resultIcon = '<i class="fas fa-medal text-3xl text-blue-500 mb-3"></i>';
847
  resultMessage = 'Bon travail ! Continuez vos efforts.';
 
848
  } else {
849
  resultClass = 'bg-red-50 border-red-500 text-red-800';
850
- resultIcon = '<i class="fas fa-book-open text-3xl text-red-500 mb-3"></i>';
851
  resultMessage = 'Continuez à étudier pour améliorer vos connaissances.';
 
852
  }
853
-
854
  const resultsElement = document.createElement('div');
855
- resultsElement.className = `mt-10 p-6 rounded-xl shadow-lg border-l-4 ${resultClass} text-center`;
 
856
  resultsElement.innerHTML = `
857
  <div class="flex flex-col items-center">
858
  ${resultIcon}
859
- <h3 class="text-2xl font-bold mb-2">Résultat final: ${score}/${total}</h3>
860
- <p class="mb-4">${resultMessage}</p>
861
- <div class="w-full max-w-xs bg-gray-200 rounded-full h-4 mb-4">
862
- <div class="h-4 rounded-full ${percentage >= 80 ? 'bg-green-500' : percentage >= 60 ? 'bg-blue-500' : 'bg-red-500'}" style="width: ${percentage}%"></div>
 
 
863
  </div>
864
- <div class="flex space-x-4 mt-6">
865
- <button id="retryQuizBtn" class="btn-secondary text-sm">
866
- <i class="fas fa-redo mr-2"></i> Réessayer
 
867
  </button>
868
- <button id="newQuizBtn" class="btn-primary text-sm">
869
- <i class="fas fa-plus mr-2"></i> Nouveau quiz
870
  </button>
871
  </div>
872
  </div>
873
  `;
874
- quizContainer.appendChild(resultsElement);
875
-
876
  // Animation de confettis si le score est bon
877
  if (percentage >= 70) {
878
  createConfetti();
879
  }
880
-
 
 
 
 
881
  // Ajouter des interactions aux boutons
882
  document.getElementById('retryQuizBtn').addEventListener('click', function() {
883
- const radios = document.querySelectorAll('input[type="radio"]');
884
- radios.forEach(radio => {
885
- radio.checked = false;
886
- radio.disabled = false;
887
- });
888
-
889
- const options = document.querySelectorAll('.quiz-option');
890
- options.forEach(option => {
891
- option.classList.remove('correct', 'incorrect');
892
- });
893
-
894
- const explanations = document.querySelectorAll('.explanation');
895
- explanations.forEach(exp => {
896
- exp.classList.add('hidden');
897
- });
898
-
899
- const icons = document.querySelectorAll('.success-icon, .error-icon');
900
- icons.forEach(icon => {
901
- icon.classList.add('hidden');
902
- });
903
-
904
  resultsElement.remove();
 
905
  document.getElementById('score').textContent = '0';
906
-
907
- window.scrollTo({
908
- top: quizContainer.offsetTop,
909
- behavior: 'smooth'
 
 
 
 
 
 
 
 
 
 
 
 
910
  });
 
 
 
 
 
 
 
 
 
911
  });
912
-
913
- document.getElementById('newQuizBtn').addEventListener('click', function() {
914
- window.scrollTo({
915
- top: 0,
916
- behavior: 'smooth'
917
- });
 
 
 
 
 
918
  topicInput.focus();
919
  });
920
  }
921
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
922
  // Focus sur le champ de saisie au chargement
923
  window.addEventListener('load', () => {
924
  topicInput.focus();
 
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">
 
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">
 
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>
 
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');
 
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>
 
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>
 
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();