OUAREDAEK commited on
Commit
f852379
·
verified ·
1 Parent(s): 77531a0

Upload templates/index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. templates/index.html +352 -221
templates/index.html CHANGED
@@ -887,6 +887,83 @@ button:disabled {
887
  border-left: 3px solid #1976D2;
888
  }
889
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890
  /* ================= ONGLET 3: MESSAGE RÉFLEXIF ET ÉVALUATION ================= */
891
  #tab-evaluation #reflagent {
892
  background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
@@ -1657,6 +1734,11 @@ textarea:focus {
1657
  font-size: 7px;
1658
  gap: 2px;
1659
  }
 
 
 
 
 
1660
  }
1661
  </style>
1662
  </head>
@@ -1706,7 +1788,7 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
1706
  Cet outil permet d'<strong>évaluer la qualité des messages auto-réflexifs</strong> générés par <strong>ReflAgent</strong>,
1707
  un tuteur pédagogique personnalisé basé sur l'IA, conçu pour favoriser l'apprentissage auto-régulé (SRL)
1708
  en renforçant la conscience métacognitive par un accompagnement réflexif.
1709
- Les messages réflexifs ciblent des dimensions clés, notamment la sensibilisation aux risques dinactivité et d'échec, le degré d'activité et d''implication, ainsi que la stimulation de l'engagement et de la motivation.
1710
  </p>
1711
 
1712
  <h4 style="margin-top: 25px;"><span>🤖</span> Qu'est-ce que ReflAgent ?</h4>
@@ -1739,7 +1821,7 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
1739
  <div class="feature-title">Dashboard Étudiant</div>
1740
  <div class="feature-description">
1741
  <strong>Visualisation complète du profil d'apprentissage</strong><br>
1742
- Explorez le manifeste de l'étudiant, ses compétences et son engagement à laide dun radar, ainsi que sa persistance et la gestion de l'effort via une heatmap temporelle intégrant des indicateurs de persévérance.
1743
  </div>
1744
  </div>
1745
 
@@ -1781,7 +1863,7 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
1781
  <p>Évaluez la qualité des messages générés par ReflAgent dans l'onglet "Message Réflexif & Évaluation".</p>
1782
  </div>
1783
  <div class="step-item">
1784
- <strong>Explorer l'exemple d'analyse </strong>
1785
  <p>Consultez l'onglet "Exemple d'Analyse" pour comprendre la méthodologie d'évaluation.</p>
1786
  </div>
1787
  </div>
@@ -1983,6 +2065,24 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
1983
  </div>
1984
  </div>
1985
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1986
  <!-- HEATMAP COMPACTE AVEC TITRES ALIGNÉS -->
1987
  <div class="compact-heatmap-and-indicators">
1988
  <h3>🔥 Heatmap d'activité - Analyse de la persistance et du management de l'effort (persévérance)</h3>
@@ -2086,6 +2186,24 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
2086
  </div>
2087
  </div>
2088
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2089
  </div>
2090
 
2091
  <!-- ONGLET 4: MESSAGE RÉFLEXIF ET ÉVALUATION -->
@@ -2129,7 +2247,7 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
2129
  <!-- Les étoiles seront ajoutées par JavaScript -->
2130
  </div>
2131
  <small style="color: #666; display: block; margin-top: 5px;">
2132
- Le message auto-réflexif montre un équilibre harmonieux entre les encouragements et et l'indication du niveau de risque de l'apprenant ?
2133
  </small>
2134
  </div>
2135
 
@@ -2139,7 +2257,7 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
2139
  <!-- Les étoiles seront ajoutées par JavaScript -->
2140
  </div>
2141
  <small style="color: #666; display: block; margin-top: 5px;">
2142
- Le message auto-réflexif stimule l'étudiant et le rendre compétitif ?
2143
  </small>
2144
  </div>
2145
 
@@ -2305,13 +2423,18 @@ Bienvenue sur notre outil d'évaluation qualitative des messages auto-réflexifs
2305
  </div>
2306
  </div>
2307
 
2308
- <!-- Script principal -->
2309
  <script>
2310
  // ================= VARIABLES =================
2311
  let scenarios = [];
2312
  let currentScenarioIndex = 0;
2313
  let radarMetrics = [];
2314
 
 
 
 
 
 
 
2315
  // Métriques de radar par défaut (sans abréviations)
2316
  const DEFAULT_RADAR_METRICS = [
2317
  { label: "Démarrage des activités", value: 85 },
@@ -2643,19 +2766,205 @@ const profileMessages = {
2643
  }
2644
  };
2645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2646
  // ================= INIT =================
2647
  document.addEventListener("DOMContentLoaded", () => {
2648
  console.log("✅ DOM chargé, initialisation...");
 
2649
  loadScenarios();
2650
  initTrustStars();
2651
  initTrustSave();
2652
  initTabs();
2653
  initGDPR();
2654
 
2655
- // Initialiser la barre de confiance à 0%
2656
  updateTrustBar();
2657
 
2658
- // Ajouter les écouteurs d'événements pour la navigation
2659
  document.getElementById("prevScenario").onclick = () => loadScenario(currentScenarioIndex - 1);
2660
  document.getElementById("nextScenario").onclick = () => loadScenario(currentScenarioIndex + 1);
2661
  document.getElementById("crossReadersBtn").onclick = navigateToCrossReading;
@@ -2666,16 +2975,13 @@ function initTabs() {
2666
  const tabBtns = document.querySelectorAll('.tab-btn');
2667
  const tabContents = document.querySelectorAll('.tab-content');
2668
 
2669
- // Par défaut, le premier onglet (Accueil) est actif
2670
  tabBtns.forEach(btn => {
2671
  btn.addEventListener('click', () => {
2672
  const tabId = btn.getAttribute('data-tab');
2673
 
2674
- // Retirer la classe active de tous les boutons et contenus
2675
  tabBtns.forEach(b => b.classList.remove('active'));
2676
  tabContents.forEach(c => c.classList.remove('active'));
2677
 
2678
- // Ajouter la classe active au bouton et au contenu correspondant
2679
  btn.classList.add('active');
2680
  document.getElementById(tabId).classList.add('active');
2681
  });
@@ -2689,25 +2995,20 @@ function initGDPR() {
2689
  const evaluatorFields = document.getElementById('evaluatorFields');
2690
  const anonymousMessage = document.getElementById('anonymousMessage');
2691
 
2692
- // Activer/désactiver les champs selon le choix GDPR
2693
  function updateGDPRFields() {
2694
  if (gdprAccept.checked) {
2695
- // Afficher les champs et les activer
2696
  evaluatorFields.style.display = 'block';
2697
  anonymousMessage.style.display = 'none';
2698
 
2699
- // Activer les champs
2700
  document.getElementById('evaluatorName').disabled = false;
2701
  document.getElementById('evaluatorEmail').disabled = false;
2702
  document.getElementById('evaluatorRole').disabled = false;
2703
  document.getElementById('expertiseLevel').disabled = false;
2704
  document.getElementById('messageMastery').disabled = false;
2705
  } else if (gdprDecline.checked) {
2706
- // Cacher les champs et afficher le message anonyme
2707
  evaluatorFields.style.display = 'none';
2708
  anonymousMessage.style.display = 'block';
2709
 
2710
- // Désactiver et vider les champs
2711
  document.getElementById('evaluatorName').disabled = true;
2712
  document.getElementById('evaluatorName').value = '';
2713
  document.getElementById('evaluatorEmail').disabled = true;
@@ -2721,12 +3022,10 @@ function initGDPR() {
2721
  }
2722
  }
2723
 
2724
- // Écouter les changements
2725
  if (gdprAccept && gdprDecline) {
2726
  gdprAccept.addEventListener('change', updateGDPRFields);
2727
  gdprDecline.addEventListener('change', updateGDPRFields);
2728
 
2729
- // Initialiser
2730
  updateGDPRFields();
2731
  }
2732
  }
@@ -2734,7 +3033,6 @@ function initGDPR() {
2734
  // ================= CHARGEMENT JSON =================
2735
  function loadScenarios() {
2736
  console.log("Chargement des scénarios...");
2737
- // Tentative de chargement depuis différents chemins
2738
  const possiblePaths = [
2739
  "/static/scenarios.json",
2740
  "scenarios.json",
@@ -2788,31 +3086,21 @@ function loadScenarios() {
2788
  tryLoad(0);
2789
  }
2790
 
2791
- // Générer les scénarios par défaut
2792
  function generateDefaultScenarios() {
2793
  console.log("Génération des 16 scénarios par défaut");
2794
 
2795
  const defaultScenarios = [...DEFAULT_SCENARIOS];
2796
 
2797
- // Ajouter des données complètes à chaque scénario
2798
  defaultScenarios.forEach((scenario, index) => {
2799
- // Compétences aléatoires
2800
  scenario.competences = generateRandomCompetences(14);
2801
-
2802
- // Données radar spécifiques au profil
2803
  scenario.radar = generateProfileRadarData(scenario.title);
2804
-
2805
- // Message ReflAgent spécifique
2806
  scenario.reflAgent = generateReflAgentMessage(scenario.title);
2807
-
2808
- // Niveau de risque selon le profil
2809
  scenario.risk = calculateRiskLevel(scenario.title);
2810
  });
2811
 
2812
  return defaultScenarios;
2813
  }
2814
 
2815
- // Générer des compétences aléatoires
2816
  function generateRandomCompetences(count) {
2817
  const competenceLabels = [
2818
  "Analyse de texte", "Rédaction académique", "Recherche documentaire",
@@ -2825,7 +3113,6 @@ function generateRandomCompetences(count) {
2825
  const competences = [];
2826
  for (let i = 0; i < count; i++) {
2827
  const labelIndex = i % competenceLabels.length;
2828
- // Valeur aléatoire entre 30 et 95 selon l'index
2829
  const value = 30 + Math.floor(Math.random() * 65);
2830
  competences.push({
2831
  label: competenceLabels[labelIndex],
@@ -2836,7 +3123,6 @@ function generateRandomCompetences(count) {
2836
  return competences;
2837
  }
2838
 
2839
- // Générer des données radar selon le profil
2840
  function generateProfileRadarData(profileTitle) {
2841
  let baseValues;
2842
 
@@ -2893,14 +3179,12 @@ function generateProfileRadarData(profileTitle) {
2893
  baseValues = [70, 65, 62, 50, 65, 70, 68];
2894
  }
2895
 
2896
- // Ajouter une légère variation
2897
  return DEFAULT_RADAR_METRICS.map((metric, index) => ({
2898
  label: metric.label,
2899
  value: Math.max(10, Math.min(100, baseValues[index] + (Math.random() * 10 - 5)))
2900
  }));
2901
  }
2902
 
2903
- // Générer un message ReflAgent selon le profil
2904
  function generateReflAgentMessage(profileTitle) {
2905
  const messages = {
2906
  "Étudiant engagé": "Félicitations ! Ton engagement est remarquable. Continue sur cette lancée en maintenant ta régularité de travail.",
@@ -2924,7 +3208,6 @@ function generateReflAgentMessage(profileTitle) {
2924
  return messages[profileTitle] || "Analyse de votre profil d'apprentissage en cours...";
2925
  }
2926
 
2927
- // Calculer le niveau de risque selon le profil
2928
  function calculateRiskLevel(profileTitle) {
2929
  const riskLevels = {
2930
  "Étudiant engagé": 20,
@@ -2958,10 +3241,8 @@ function showError(message) {
2958
  </div>`;
2959
  }
2960
 
2961
- // Afficher un message dans la console et éventuellement une alerte
2962
  console.error(message);
2963
 
2964
- // Désactiver la navigation
2965
  document.getElementById("prevScenario").disabled = true;
2966
  document.getElementById("nextScenario").disabled = true;
2967
  }
@@ -2982,13 +3263,11 @@ function loadScenario(index) {
2982
  currentScenarioIndex = index;
2983
  const s = scenarios[index];
2984
 
2985
- // Mettre à jour le titre
2986
  const titleElement = document.getElementById("scenarioTitle");
2987
  if (titleElement) {
2988
  titleElement.textContent = `Scénario ${s.id} – ${s.title}`;
2989
  }
2990
 
2991
- // Mettre à jour le manifeste
2992
  const manifestList = document.getElementById("manifest-list");
2993
  if (manifestList) {
2994
  manifestList.innerHTML = `
@@ -2999,20 +3278,15 @@ function loadScenario(index) {
2999
  </div>`;
3000
  }
3001
 
3002
- // Mettre à jour les compétences compactes
3003
  if (s.competences) {
3004
  updateCompactCompetences(s.competences);
3005
  }
3006
 
3007
- // Mettre à jour le radar avec les nouvelles métriques
3008
  if (s.radar && s.radar.length > 0) {
3009
- // Filtrer pour enlever "Engagement global" si présent (au cas où)
3010
  radarMetrics = s.radar.filter(metric => metric.label !== "Engagement global");
3011
  } else {
3012
- // Utiliser les métriques par défaut avec variation pour chaque scénario
3013
  radarMetrics = DEFAULT_RADAR_METRICS.map(metric => {
3014
- // Variation de ±15% selon l'index du scénario pour rendre les données plus réalistes
3015
- const variation = (index % 7) * 5 - 15; // -15 à +15
3016
  const newValue = Math.max(10, Math.min(95, metric.value + variation));
3017
  return {
3018
  label: metric.label,
@@ -3021,47 +3295,34 @@ function loadScenario(index) {
3021
  });
3022
  }
3023
 
3024
- // Mettre à jour la grille des métriques radar compacte
3025
  updateCompactRadarMetricsGrid();
3026
-
3027
- // Mettre à jour le graphique radar
3028
  drawRadar();
3029
-
3030
- // Mettre à jour le message bot radar compact
3031
  updateCompactRadarBotMessage(s);
3032
-
3033
- // Mettre à jour la heatmap enrichie compacte avec les axes
3034
  drawCompactEnrichedHeatmapWithAxes(s);
3035
 
3036
- // FORCER LE MODE "VUE COMBINÉE" PAR DÉFAUT
3037
  const modeSelect = document.getElementById("persistenceMode");
3038
  if (modeSelect) {
3039
  modeSelect.value = "combined";
3040
  updatePersistenceIndicators("combined");
3041
  }
3042
 
3043
- // Mettre à jour la persévérance spécifique au profil
3044
  updatePerseveranceProfile(s.title);
3045
 
3046
- // Mettre à jour le message ReflAgent
3047
  const reflagentElement = document.getElementById("reflagent");
3048
  if (reflagentElement) {
3049
  reflagentElement.textContent = s.reflAgent || "Message d'analyse non disponible pour ce scénario.";
3050
  }
3051
 
3052
- // Mettre à jour les boutons de navigation
3053
  updateNavButtons();
3054
-
3055
- // Mettre à jour les statistiques
3056
  updateStats(s);
3057
-
3058
- // Réinitialiser l'évaluation de confiance
3059
  resetTrustEvaluation();
 
 
 
3060
  }
3061
 
3062
  // ================= FONCTIONS POUR LA VERSION COMPACTE =================
3063
 
3064
- // Mettre à jour les compétences compactes
3065
  function updateCompactCompetences(competences) {
3066
  const container = document.getElementById("compact-competences");
3067
  if (!container) return;
@@ -3073,16 +3334,13 @@ function updateCompactCompetences(competences) {
3073
  return;
3074
  }
3075
 
3076
- // Créer un conteneur de grille
3077
  const gridContainer = document.createElement('div');
3078
  gridContainer.className = 'compact-competences-grid';
3079
 
3080
- // Limiter à 14 compétences pour la matrice 3x4 (avec 2 cases vides)
3081
  const maxCompetences = 14;
3082
  const displayedCompetences = competences.slice(0, maxCompetences);
3083
 
3084
  displayedCompetences.forEach(comp => {
3085
- // Déterminer la couleur selon la valeur
3086
  let color, colorClass;
3087
  if (comp.value < 50) {
3088
  color = '#E53935';
@@ -3095,7 +3353,6 @@ function updateCompactCompetences(competences) {
3095
  colorClass = 'high';
3096
  }
3097
 
3098
- // Créer l'élément de compétence matricielle
3099
  const compItem = document.createElement('div');
3100
  compItem.className = 'compact-competence-matrix-item';
3101
  compItem.innerHTML = `
@@ -3118,7 +3375,6 @@ function updateCompactCompetences(competences) {
3118
  gridContainer.appendChild(compItem);
3119
  });
3120
 
3121
- // Si nous avons moins de 14 compétences, ajouter des cases vides pour compléter la matrice
3122
  const emptySlots = maxCompetences - displayedCompetences.length;
3123
  for (let i = 0; i < emptySlots; i++) {
3124
  const emptyItem = document.createElement('div');
@@ -3146,7 +3402,6 @@ function updateCompactCompetences(competences) {
3146
  container.appendChild(gridContainer);
3147
  }
3148
 
3149
- // Mettre à jour la grille des métriques radar compacte
3150
  function updateCompactRadarMetricsGrid() {
3151
  const grid = document.getElementById("compact-radar-metrics-grid");
3152
  if (!grid) return;
@@ -3159,14 +3414,11 @@ function updateCompactRadarMetricsGrid() {
3159
  }
3160
 
3161
  radarMetrics.forEach(metric => {
3162
- // Déterminer la classe de couleur selon la valeur
3163
  let colorClass = "";
3164
-
3165
  if (metric.value >= 70) colorClass = "high";
3166
  else if (metric.value >= 40) colorClass = "medium";
3167
  else colorClass = "low";
3168
 
3169
- // Déterminer l'icône selon le type de métrique
3170
  let icon = "📊";
3171
  if (metric.label.includes("Démarrage")) icon = "🚀";
3172
  else if (metric.label.includes("Éléments des connaissances validées")) icon = "🎓";
@@ -3176,7 +3428,6 @@ function updateCompactRadarMetricsGrid() {
3176
  else if (metric.label.includes("Interactions")) icon = "📚";
3177
  else if (metric.label.includes("Score")) icon = "🔧";
3178
 
3179
- // Déterminer la description
3180
  let description = "";
3181
  if (metric.label.includes("Démarrage")) description = "Rapidité d'initiation de l'activité d'apprentissage";
3182
  else if (metric.label.includes("Éléments des connaissances validées")) description = "Taux de validation des skills";
@@ -3199,14 +3450,12 @@ function updateCompactRadarMetricsGrid() {
3199
  });
3200
  }
3201
 
3202
- // Mettre à jour le message bot radar compact
3203
  function updateCompactRadarBotMessage(scenario) {
3204
  const radarBotContent = document.getElementById("compact-radar-bot-content");
3205
  const radarBotInsights = document.getElementById("compact-radar-bot-insights");
3206
 
3207
  if (!radarBotContent || !radarBotInsights) return;
3208
 
3209
- // Récupérer le message pour le profil ou utiliser un message par défaut
3210
  const profileMessage = profileMessages[scenario.title] || {
3211
  title: "Profil d'Apprentissage",
3212
  message: "Analyse de votre profil d'apprentissage en cours...",
@@ -3217,10 +3466,8 @@ function updateCompactRadarBotMessage(scenario) {
3217
  ]
3218
  };
3219
 
3220
- // Mettre à jour l'interface
3221
  radarBotContent.innerHTML = `<strong>${profileMessage.title}</strong><br>${profileMessage.message}`;
3222
 
3223
- // Mettre à jour les insights
3224
  radarBotInsights.innerHTML = '';
3225
  profileMessage.insights.forEach(insight => {
3226
  const insightElement = document.createElement('div');
@@ -3233,7 +3480,6 @@ function updateCompactRadarBotMessage(scenario) {
3233
  });
3234
  }
3235
 
3236
- // Dessiner la heatmap enrichie compacte avec axes
3237
  function drawCompactEnrichedHeatmapWithAxes(scenario) {
3238
  const canvas = document.getElementById("heatmap");
3239
  if (!canvas) return;
@@ -3245,98 +3491,28 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3245
  const weeks = 4;
3246
  const totalDays = weeks * 7;
3247
 
3248
- // Effacer le canvas
3249
  ctx.clearRect(0, 0, canvas.width, canvas.height);
3250
 
3251
- // Titre enrichi
3252
  const scenarioTitles = {
3253
- 1: {
3254
- title: "Engagé mais désorganisé",
3255
- emoji: "😅",
3256
- pattern: "Effort concentré avant échéances"
3257
- },
3258
- 2: {
3259
- title: "Actif mais peu efficace",
3260
- emoji: "😰",
3261
- pattern: "Démarrage lent, accélération tardive"
3262
- },
3263
- 3: {
3264
- title: "Autonome et stratégique",
3265
- emoji: "😊",
3266
- pattern: "Travail régulier et équilibré"
3267
- },
3268
- 4: {
3269
- title: "Motivé et constant",
3270
- emoji: "👍",
3271
- pattern: "Rythme régulier toute la semaine"
3272
- },
3273
- 5: {
3274
- title: "Surinvestissement",
3275
- emoji: "🔥",
3276
- pattern: "Activité intense, risque de surcharge"
3277
- },
3278
- 6: {
3279
- title: "Stratégique et ciblé",
3280
- emoji: "🎯",
3281
- pattern: "Efforts concentrés sur les priorités"
3282
- },
3283
- 7: {
3284
- title: "Amélioration continue",
3285
- emoji: "📈",
3286
- pattern: "Progression visible semaine après semaine"
3287
- },
3288
- 8: {
3289
- title: "Difficultés persistantes",
3290
- emoji: "😔",
3291
- pattern: "Efforts dispersés, peu de régularité"
3292
- },
3293
- 9: {
3294
- title: "Évitement",
3295
- emoji: "😨",
3296
- pattern: "Activité minimale, pics sporadiques"
3297
- },
3298
- 10: {
3299
- title: "Risque d'abandon",
3300
- emoji: "⚠️",
3301
- pattern: "Très faible activité, irrégularité totale"
3302
- },
3303
- 11: {
3304
- title: "Progression technique",
3305
- emoji: "🔧",
3306
- pattern: "Amélioration régulière des compétences"
3307
- },
3308
- 12: {
3309
- title: "Résilience",
3310
- emoji: "💪",
3311
- pattern: "Persévérance malgré les difficultés"
3312
- },
3313
- 13: {
3314
- title: "Inactivité",
3315
- emoji: "😴",
3316
- pattern: "Très peu d'activité détectée"
3317
- },
3318
- 14: {
3319
- title: "Désengagement",
3320
- emoji: "😑",
3321
- pattern: "Activité sporadique et décroissante"
3322
- },
3323
- 15: {
3324
- title: "Persévérant malgré le doute",
3325
- emoji: "🌱",
3326
- pattern: "Accélération progressive en fin de semaine"
3327
- },
3328
- 16: {
3329
- title: "Actif mais à risque d'épuisement",
3330
- emoji: "⚡",
3331
- pattern: "Forte activité en début de semaine"
3332
- }
3333
  };
3334
 
3335
- const scenarioInfo = scenarioTitles[scenario.id] || {
3336
- title: "Votre activité",
3337
- emoji: "📊",
3338
- pattern: "Schéma à analyser"
3339
- };
3340
 
3341
  ctx.font = "bold 14px Arial";
3342
  ctx.fillStyle = "#1976D2";
@@ -3347,17 +3523,13 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3347
  ctx.fillStyle = "#666";
3348
  ctx.fillText(`${scenarioInfo.pattern}`, canvas.width / 2, 45);
3349
 
3350
- // Générer les données d'activité selon le scénario
3351
  const activityData = generateCompactPersistenceData(scenario.id, totalDays);
3352
 
3353
- // Dessiner la heatmap avec axes
3354
  const colors = ["#EBEDF0", "#C6E48B", "#7BC96F", "#239A3B", "#196127"];
3355
 
3356
- // Position de départ de la grille
3357
  const startX = 60;
3358
  const startY = 80;
3359
 
3360
- // Ajouter les labels des jours (à gauche)
3361
  ctx.font = "11px Arial";
3362
  ctx.fillStyle = "#555";
3363
  ctx.textAlign = "right";
@@ -3368,7 +3540,6 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3368
  ctx.fillText(days[weekday], startX - 10, y);
3369
  }
3370
 
3371
- // Ajouter les numéros des semaines (en haut)
3372
  ctx.font = "11px Arial";
3373
  ctx.fillStyle = "#555";
3374
  ctx.textAlign = "center";
@@ -3379,22 +3550,17 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3379
  ctx.fillText(`S${week + 1}`, x, startY - 5);
3380
  }
3381
 
3382
- // Ajouter un titre pour les axes
3383
  ctx.font = "10px Arial";
3384
  ctx.fillStyle = "#777";
3385
  ctx.textAlign = "center";
3386
-
3387
- // Titre pour les semaines
3388
  ctx.fillText("Semaines", canvas.width / 2, startY - 15);
3389
 
3390
- // Titre pour les jours
3391
  ctx.save();
3392
  ctx.translate(startX - 25, startY + (cellSize * 7) / 2);
3393
  ctx.rotate(-Math.PI / 2);
3394
  ctx.fillText("Jours", 0, 0);
3395
  ctx.restore();
3396
 
3397
- // Dessiner les cellules
3398
  for (let day = 0; day < totalDays; day++) {
3399
  const week = Math.floor(day / 7);
3400
  const weekday = day % 7;
@@ -3404,12 +3570,10 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3404
 
3405
  const intensity = activityData[day];
3406
 
3407
- // Dessiner la cellule
3408
  ctx.fillStyle = colors[intensity];
3409
  ctx.fillRect(x, y, cellSize, cellSize);
3410
 
3411
- // Bordure avec indication des échéances (vendredi = jour important)
3412
- if (weekday === 4) { // Vendredi
3413
  ctx.strokeStyle = "#FF6B6B";
3414
  ctx.lineWidth = 1.5;
3415
  } else {
@@ -3419,17 +3583,13 @@ function drawCompactEnrichedHeatmapWithAxes(scenario) {
3419
  ctx.strokeRect(x, y, cellSize, cellSize);
3420
  }
3421
 
3422
- // Analyser et afficher les métriques de persévérance compactes
3423
  analyzeCompactPersistenceMetrics(activityData, scenario.id);
3424
 
3425
- // Mettre à jour les indicateurs selon le mode sélectionné
3426
  const currentMode = document.getElementById("persistenceMode").value;
3427
  updatePersistenceIndicators(currentMode);
3428
  }
3429
 
3430
- // Mettre à jour les indicateurs selon le mode d'analyse
3431
  function updatePersistenceIndicators(mode) {
3432
- // Récupérer les éléments
3433
  const regularityIndicator = document.getElementById("regularity-indicator");
3434
  const regularityDesc = document.getElementById("regularity-description");
3435
  const deadlineIndicator = document.getElementById("deadline-indicator");
@@ -3437,14 +3597,10 @@ function updatePersistenceIndicators(mode) {
3437
  const effortIndicator = document.getElementById("effort-indicator");
3438
  const effortDesc = document.getElementById("effort-description");
3439
 
3440
- const regularityScore = document.getElementById("compact-regularityScore");
3441
- const deadlineScore = document.getElementById("compact-deadlineScore");
3442
- const effortPattern = document.getElementById("compact-effortPattern");
3443
  const regularityText = document.getElementById("compact-regularityText");
3444
 
3445
  if (!regularityIndicator || !deadlineIndicator || !effortIndicator) return;
3446
 
3447
- // Réinitialiser l'affichage (tout masquer par défaut)
3448
  regularityIndicator.style.display = "none";
3449
  regularityDesc.style.display = "none";
3450
  deadlineIndicator.style.display = "none";
@@ -3459,10 +3615,8 @@ function updatePersistenceIndicators(mode) {
3459
 
3460
  switch(mode) {
3461
  case "regularity":
3462
- // Afficher seulement la régularité
3463
  regularityIndicator.style.display = "flex";
3464
  regularityDesc.style.display = "block";
3465
-
3466
  if (regularityText) {
3467
  regularityText.innerHTML = `
3468
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
@@ -3472,12 +3626,9 @@ function updatePersistenceIndicators(mode) {
3472
  `;
3473
  }
3474
  break;
3475
-
3476
  case "deadline":
3477
- // Afficher seulement la gestion des échéances
3478
  deadlineIndicator.style.display = "flex";
3479
  deadlineDesc.style.display = "block";
3480
-
3481
  if (regularityText) {
3482
  regularityText.innerHTML = `
3483
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
@@ -3487,12 +3638,9 @@ function updatePersistenceIndicators(mode) {
3487
  `;
3488
  }
3489
  break;
3490
-
3491
  case "effortPattern":
3492
- // Afficher seulement le schéma d'effort
3493
  effortIndicator.style.display = "flex";
3494
  effortDesc.style.display = "block";
3495
-
3496
  if (regularityText) {
3497
  regularityText.innerHTML = `
3498
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
@@ -3502,16 +3650,13 @@ function updatePersistenceIndicators(mode) {
3502
  `;
3503
  }
3504
  break;
3505
-
3506
  case "combined":
3507
- // Afficher tous les indicateurs
3508
  regularityIndicator.style.display = "flex";
3509
  regularityDesc.style.display = "block";
3510
  deadlineIndicator.style.display = "flex";
3511
  deadlineDesc.style.display = "block";
3512
  effortIndicator.style.display = "flex";
3513
  effortDesc.style.display = "block";
3514
-
3515
  if (regularityText) {
3516
  regularityText.innerHTML = `
3517
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
@@ -3521,9 +3666,7 @@ function updatePersistenceIndicators(mode) {
3521
  `;
3522
  }
3523
  break;
3524
-
3525
  default:
3526
- // Par défaut, tout afficher
3527
  regularityIndicator.style.display = "flex";
3528
  regularityDesc.style.display = "block";
3529
  deadlineIndicator.style.display = "flex";
@@ -3533,15 +3676,15 @@ function updatePersistenceIndicators(mode) {
3533
  }
3534
  }
3535
 
3536
- // Mettre à jour la persévérance spécifique au profil
3537
  function updatePerseveranceProfile(scenarioTitle) {
3538
  const profile = perseveranceProfiles[scenarioTitle];
3539
 
 
 
 
 
 
3540
  if (!profile) {
3541
- console.warn(`Aucun profil de persévérance trouvé pour: ${scenarioTitle}`);
3542
-
3543
- // Valeurs par défaut
3544
- const regularityText = document.getElementById("compact-regularityText");
3545
  if (regularityText) {
3546
  regularityText.innerHTML = `
3547
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
@@ -3552,12 +3695,6 @@ function updatePerseveranceProfile(scenarioTitle) {
3552
  return;
3553
  }
3554
 
3555
- // Mettre à jour l'interface avec les valeurs spécifiques
3556
- const regularityScore = document.getElementById("compact-regularityScore");
3557
- const deadlineScore = document.getElementById("compact-deadlineScore");
3558
- const effortPattern = document.getElementById("compact-effortPattern");
3559
- const regularityText = document.getElementById("compact-regularityText");
3560
-
3561
  if (regularityScore) {
3562
  regularityScore.textContent = profile.regularity + "%";
3563
  regularityScore.style.color = profile.color;
@@ -3586,11 +3723,9 @@ function updatePerseveranceProfile(scenarioTitle) {
3586
  }
3587
  }
3588
 
3589
- // Générer des données de persévérance compactes
3590
  function generateCompactPersistenceData(scenarioId, totalDays) {
3591
  const data = new Array(totalDays).fill(0);
3592
 
3593
- // Utiliser les valeurs du profil pour générer des données cohérentes
3594
  const profile = Object.values(perseveranceProfiles)[scenarioId - 1] || perseveranceProfiles["Étudiant engagé"];
3595
 
3596
  for (let i = 0; i < totalDays; i++) {
@@ -3598,7 +3733,6 @@ function generateCompactPersistenceData(scenarioId, totalDays) {
3598
  const week = Math.floor(i / 7);
3599
  const progress = i / totalDays;
3600
 
3601
- // Adapter les données selon le schéma d'effort du profil
3602
  if (profile.effortPattern.includes("Pic d'activité jeudi/vendredi")) {
3603
  if (weekday === 3 || weekday === 4) {
3604
  data[i] = 4;
@@ -3622,7 +3756,6 @@ function generateCompactPersistenceData(scenarioId, totalDays) {
3622
  data[i] = Math.random() > 0.8 ? 1 : 0;
3623
  }
3624
  } else {
3625
- // Régulier
3626
  if (weekday === 5 || weekday === 6) {
3627
  data[i] = 2;
3628
  } else {
@@ -3634,10 +3767,8 @@ function generateCompactPersistenceData(scenarioId, totalDays) {
3634
  return data;
3635
  }
3636
 
3637
- // Analyser les métriques de persévérance compactes
3638
  function analyzeCompactPersistenceMetrics(activityData, scenarioId) {
3639
- // Cette fonction est maintenant remplacée par les valeurs prédéfinies dans updatePerseveranceProfile
3640
- // On ne fait rien ici pour éviter d'écraser les valeurs
3641
  }
3642
 
3643
  // ================= RADAR =================
@@ -3656,7 +3787,6 @@ function drawRadar() {
3656
 
3657
  ctx.clearRect(0, 0, canvas.width, canvas.height);
3658
 
3659
- // Dessiner les cercles concentriques avec pourcentages
3660
  for (let i = 1; i <= 5; i++) {
3661
  ctx.beginPath();
3662
  ctx.arc(cx, cy, (radius / 5) * i, 0, 2 * Math.PI);
@@ -3675,7 +3805,6 @@ function drawRadar() {
3675
  }
3676
  }
3677
 
3678
- // Dessiner les axes
3679
  radarMetrics.forEach((m, i) => {
3680
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
3681
 
@@ -3700,7 +3829,6 @@ function drawRadar() {
3700
  ctx.fillText(label, tx, ty);
3701
  });
3702
 
3703
- // Dessiner le polygone
3704
  ctx.beginPath();
3705
  radarMetrics.forEach((m, i) => {
3706
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
@@ -3717,7 +3845,6 @@ function drawRadar() {
3717
  ctx.lineWidth = 2;
3718
  ctx.stroke();
3719
 
3720
- // Dessiner les points avec les pourcentages
3721
  radarMetrics.forEach((m, i) => {
3722
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
3723
  const r = radius * (m.value / 100);
@@ -4262,18 +4389,22 @@ function changePersistenceAnalysis() {
4262
  updatePersistenceIndicators(mode);
4263
  }
4264
 
4265
- // Exposer les fonctions globales
4266
  window.changePersistenceAnalysis = changePersistenceAnalysis;
4267
  window.updateStats = updateStats;
4268
  window.showNotification = showNotification;
4269
  window.fillSampleEvaluator = fillSampleEvaluator;
4270
  window.clearEvaluatorFields = clearEvaluatorFields;
4271
  window.loadScenario = loadScenario;
 
 
 
 
 
 
4272
  </script>
4273
 
4274
- </div> <!-- Fin de la div principale #reflection -->
4275
 
4276
- <!-- PIED DE PAGE -->
4277
  <div style="
4278
  margin-top: 40px;
4279
  padding: 20px;
 
887
  border-left: 3px solid #1976D2;
888
  }
889
 
890
+ /* ================= AUDIO NARRATION STYLES PROFESSIONNELS RÉDUITS ================= */
891
+ .audio-narration-compact {
892
+ background: #f8f9fa;
893
+ border-radius: 8px;
894
+ margin: 8px 0;
895
+ display: flex;
896
+ align-items: center;
897
+ gap: 12px;
898
+ padding: 8px 15px;
899
+ border: 1px solid #e9ecef;
900
+ }
901
+
902
+ .audio-narration-compact .audio-icon {
903
+ font-size: 18px;
904
+ color: #1976D2;
905
+ display: flex;
906
+ align-items: center;
907
+ gap: 6px;
908
+ }
909
+
910
+ .audio-narration-compact .audio-icon span {
911
+ font-size: 12px;
912
+ font-weight: 500;
913
+ color: #495057;
914
+ }
915
+
916
+ .audio-narration-compact .audio-controls {
917
+ display: flex;
918
+ align-items: center;
919
+ gap: 8px;
920
+ flex-wrap: wrap;
921
+ }
922
+
923
+ .audio-narration-compact .audio-btn {
924
+ background: #e9ecef;
925
+ color: #495057;
926
+ padding: 4px 10px;
927
+ font-size: 11px;
928
+ border-radius: 6px;
929
+ box-shadow: none;
930
+ font-weight: 500;
931
+ }
932
+
933
+ .audio-narration-compact .audio-btn:hover {
934
+ background: #1976D2;
935
+ color: white;
936
+ transform: none;
937
+ }
938
+
939
+ .audio-narration-compact .audio-progress {
940
+ width: 120px;
941
+ height: 3px;
942
+ background: #dee2e6;
943
+ border-radius: 3px;
944
+ overflow: hidden;
945
+ cursor: pointer;
946
+ }
947
+
948
+ .audio-narration-compact .audio-progress-fill {
949
+ height: 100%;
950
+ background: #1976D2;
951
+ width: 0%;
952
+ transition: width 0.1s linear;
953
+ }
954
+
955
+ .audio-narration-compact .audio-time {
956
+ font-size: 10px;
957
+ font-family: monospace;
958
+ color: #868e96;
959
+ }
960
+
961
+ .audio-narration-compact .audio-status {
962
+ font-size: 10px;
963
+ color: #6c757d;
964
+ font-style: italic;
965
+ }
966
+
967
  /* ================= ONGLET 3: MESSAGE RÉFLEXIF ET ÉVALUATION ================= */
968
  #tab-evaluation #reflagent {
969
  background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
 
1734
  font-size: 7px;
1735
  gap: 2px;
1736
  }
1737
+
1738
+ .audio-narration-compact {
1739
+ flex-wrap: wrap;
1740
+ justify-content: center;
1741
+ }
1742
  }
1743
  </style>
1744
  </head>
 
1788
  Cet outil permet d'<strong>évaluer la qualité des messages auto-réflexifs</strong> générés par <strong>ReflAgent</strong>,
1789
  un tuteur pédagogique personnalisé basé sur l'IA, conçu pour favoriser l'apprentissage auto-régulé (SRL)
1790
  en renforçant la conscience métacognitive par un accompagnement réflexif.
1791
+ Les messages réflexifs ciblent des dimensions clés, notamment la sensibilisation aux risques d'inactivité et d'échec, le degré d'activité et d'implication, ainsi que la stimulation de l'engagement et de la motivation.
1792
  </p>
1793
 
1794
  <h4 style="margin-top: 25px;"><span>🤖</span> Qu'est-ce que ReflAgent ?</h4>
 
1821
  <div class="feature-title">Dashboard Étudiant</div>
1822
  <div class="feature-description">
1823
  <strong>Visualisation complète du profil d'apprentissage</strong><br>
1824
+ Explorez le manifeste de l'étudiant, ses compétences et son engagement à l'aide d'un radar, ainsi que sa persistance et la gestion de l'effort via une heatmap temporelle intégrant des indicateurs de persévérance.
1825
  </div>
1826
  </div>
1827
 
 
1863
  <p>Évaluez la qualité des messages générés par ReflAgent dans l'onglet "Message Réflexif & Évaluation".</p>
1864
  </div>
1865
  <div class="step-item">
1866
+ <strong>Explorer l'exemple d'analyse</strong>
1867
  <p>Consultez l'onglet "Exemple d'Analyse" pour comprendre la méthodologie d'évaluation.</p>
1868
  </div>
1869
  </div>
 
2065
  </div>
2066
  </div>
2067
 
2068
+ <!-- AUDIO NARRATION COMPACTE POUR LE RADAR -->
2069
+ <div class="audio-narration-compact" id="radarAudioContainer">
2070
+ <div class="audio-icon">
2071
+ <span>🎙️</span>
2072
+ <span>Radar</span>
2073
+ </div>
2074
+ <div class="audio-controls">
2075
+ <button class="audio-btn" id="playRadarAudioBtn" onclick="playRadarNarration()">▶️</button>
2076
+ <button class="audio-btn" id="pauseRadarAudioBtn" onclick="pauseRadarNarration()">⏸️</button>
2077
+ <button class="audio-btn" id="stopRadarAudioBtn" onclick="stopRadarNarration()">⏹️</button>
2078
+ <div class="audio-progress" id="radarAudioProgress">
2079
+ <div class="audio-progress-fill" id="radarAudioProgressFill"></div>
2080
+ </div>
2081
+ <span class="audio-time" id="radarAudioTime">00:00</span>
2082
+ </div>
2083
+ <div class="audio-status" id="radarAudioStatus">Prêt</div>
2084
+ </div>
2085
+
2086
  <!-- HEATMAP COMPACTE AVEC TITRES ALIGNÉS -->
2087
  <div class="compact-heatmap-and-indicators">
2088
  <h3>🔥 Heatmap d'activité - Analyse de la persistance et du management de l'effort (persévérance)</h3>
 
2186
  </div>
2187
  </div>
2188
  </div>
2189
+
2190
+ <!-- AUDIO NARRATION COMPACTE POUR LA HEATMAP -->
2191
+ <div class="audio-narration-compact" id="heatmapAudioContainer">
2192
+ <div class="audio-icon">
2193
+ <span>🎙️</span>
2194
+ <span>Heatmap</span>
2195
+ </div>
2196
+ <div class="audio-controls">
2197
+ <button class="audio-btn" id="playHeatmapAudioBtn" onclick="playHeatmapNarration()">▶️</button>
2198
+ <button class="audio-btn" id="pauseHeatmapAudioBtn" onclick="pauseHeatmapNarration()">⏸️</button>
2199
+ <button class="audio-btn" id="stopHeatmapAudioBtn" onclick="stopHeatmapNarration()">⏹️</button>
2200
+ <div class="audio-progress" id="heatmapAudioProgress">
2201
+ <div class="audio-progress-fill" id="heatmapAudioProgressFill"></div>
2202
+ </div>
2203
+ <span class="audio-time" id="heatmapAudioTime">00:00</span>
2204
+ </div>
2205
+ <div class="audio-status" id="heatmapAudioStatus">Prêt</div>
2206
+ </div>
2207
  </div>
2208
 
2209
  <!-- ONGLET 4: MESSAGE RÉFLEXIF ET ÉVALUATION -->
 
2247
  <!-- Les étoiles seront ajoutées par JavaScript -->
2248
  </div>
2249
  <small style="color: #666; display: block; margin-top: 5px;">
2250
+ Le message auto-réflexif montre un équilibre harmonieux entre les encouragements et l'indication du niveau de risque de l'apprenant ?
2251
  </small>
2252
  </div>
2253
 
 
2257
  <!-- Les étoiles seront ajoutées par JavaScript -->
2258
  </div>
2259
  <small style="color: #666; display: block; margin-top: 5px;">
2260
+ Le message auto-réflexif stimule l'étudiant et le rend compétitif ?
2261
  </small>
2262
  </div>
2263
 
 
2423
  </div>
2424
  </div>
2425
 
 
2426
  <script>
2427
  // ================= VARIABLES =================
2428
  let scenarios = [];
2429
  let currentScenarioIndex = 0;
2430
  let radarMetrics = [];
2431
 
2432
+ // Objets Audio pour la narration
2433
+ let radarAudio = null;
2434
+ let heatmapAudio = null;
2435
+ let currentRadarAudioSrc = "";
2436
+ let currentHeatmapAudioSrc = "";
2437
+
2438
  // Métriques de radar par défaut (sans abréviations)
2439
  const DEFAULT_RADAR_METRICS = [
2440
  { label: "Démarrage des activités", value: 85 },
 
2766
  }
2767
  };
2768
 
2769
+ // ================= FONCTIONS AUDIO NARRATION =================
2770
+
2771
+ // Version CORRECTE pour dossier static/audio/
2772
+ function getRadarAudioPath(scenarioId) {
2773
+ return `static/audio/radar_${scenarioId}.mp3`;
2774
+ }
2775
+
2776
+ // Également pour la heatmap
2777
+ function getHeatmapAudioPath(scenarioId) {
2778
+ return `static/audio/heatmap_${scenarioId}.mp3`;
2779
+ }
2780
+ function initAudioElements() {
2781
+ radarAudio = new Audio();
2782
+ heatmapAudio = new Audio();
2783
+
2784
+ radarAudio.addEventListener('timeupdate', function() {
2785
+ if (radarAudio.duration) {
2786
+ const percent = (radarAudio.currentTime / radarAudio.duration) * 100;
2787
+ const fill = document.getElementById('radarAudioProgressFill');
2788
+ if (fill) fill.style.width = percent + '%';
2789
+ const timeEl = document.getElementById('radarAudioTime');
2790
+ if (timeEl) {
2791
+ timeEl.textContent = formatTime(radarAudio.currentTime);
2792
+ }
2793
+ }
2794
+ });
2795
+
2796
+ radarAudio.addEventListener('ended', function() {
2797
+ const statusEl = document.getElementById('radarAudioStatus');
2798
+ if (statusEl) statusEl.textContent = 'Terminé';
2799
+ const fill = document.getElementById('radarAudioProgressFill');
2800
+ if (fill) fill.style.width = '0%';
2801
+ });
2802
+
2803
+ radarAudio.addEventListener('play', function() {
2804
+ const statusEl = document.getElementById('radarAudioStatus');
2805
+ if (statusEl) statusEl.textContent = 'Lecture...';
2806
+ });
2807
+
2808
+ radarAudio.addEventListener('pause', function() {
2809
+ const statusEl = document.getElementById('radarAudioStatus');
2810
+ if (statusEl) statusEl.textContent = 'Pause';
2811
+ });
2812
+
2813
+ heatmapAudio.addEventListener('timeupdate', function() {
2814
+ if (heatmapAudio.duration) {
2815
+ const percent = (heatmapAudio.currentTime / heatmapAudio.duration) * 100;
2816
+ const fill = document.getElementById('heatmapAudioProgressFill');
2817
+ if (fill) fill.style.width = percent + '%';
2818
+ const timeEl = document.getElementById('heatmapAudioTime');
2819
+ if (timeEl) {
2820
+ timeEl.textContent = formatTime(heatmapAudio.currentTime);
2821
+ }
2822
+ }
2823
+ });
2824
+
2825
+ heatmapAudio.addEventListener('ended', function() {
2826
+ const statusEl = document.getElementById('heatmapAudioStatus');
2827
+ if (statusEl) statusEl.textContent = 'Terminé';
2828
+ const fill = document.getElementById('heatmapAudioProgressFill');
2829
+ if (fill) fill.style.width = '0%';
2830
+ });
2831
+
2832
+ heatmapAudio.addEventListener('play', function() {
2833
+ const statusEl = document.getElementById('heatmapAudioStatus');
2834
+ if (statusEl) statusEl.textContent = 'Lecture...';
2835
+ });
2836
+
2837
+ heatmapAudio.addEventListener('pause', function() {
2838
+ const statusEl = document.getElementById('heatmapAudioStatus');
2839
+ if (statusEl) statusEl.textContent = 'Pause';
2840
+ });
2841
+
2842
+ const radarProgress = document.getElementById('radarAudioProgress');
2843
+ if (radarProgress) {
2844
+ radarProgress.addEventListener('click', function(e) {
2845
+ if (radarAudio.duration) {
2846
+ const rect = this.getBoundingClientRect();
2847
+ const percent = (e.clientX - rect.left) / rect.width;
2848
+ radarAudio.currentTime = percent * radarAudio.duration;
2849
+ }
2850
+ });
2851
+ }
2852
+
2853
+ const heatmapProgress = document.getElementById('heatmapAudioProgress');
2854
+ if (heatmapProgress) {
2855
+ heatmapProgress.addEventListener('click', function(e) {
2856
+ if (heatmapAudio.duration) {
2857
+ const rect = this.getBoundingClientRect();
2858
+ const percent = (e.clientX - rect.left) / rect.width;
2859
+ heatmapAudio.currentTime = percent * heatmapAudio.duration;
2860
+ }
2861
+ });
2862
+ }
2863
+ }
2864
+
2865
+ function formatTime(seconds) {
2866
+ if (isNaN(seconds)) return '00:00';
2867
+ const mins = Math.floor(seconds / 60);
2868
+ const secs = Math.floor(seconds % 60);
2869
+ return mins.toString().padStart(2, '0') + ':' + secs.toString().padStart(2, '0');
2870
+ }
2871
+
2872
+ function loadRadarAudioForScenario(scenarioId) {
2873
+ if (!radarAudio) initAudioElements();
2874
+ const audioPath = getRadarAudioPath(scenarioId);
2875
+ currentRadarAudioSrc = audioPath;
2876
+ radarAudio.src = audioPath;
2877
+ radarAudio.load();
2878
+ const statusEl = document.getElementById('radarAudioStatus');
2879
+ if (statusEl) statusEl.textContent = 'Prêt';
2880
+ const timeEl = document.getElementById('radarAudioTime');
2881
+ if (timeEl) timeEl.textContent = '00:00';
2882
+ const fill = document.getElementById('radarAudioProgressFill');
2883
+ if (fill) fill.style.width = '0%';
2884
+ }
2885
+
2886
+ function loadHeatmapAudioForScenario(scenarioId) {
2887
+ if (!heatmapAudio) initAudioElements();
2888
+ const audioPath = getHeatmapAudioPath(scenarioId);
2889
+ currentHeatmapAudioSrc = audioPath;
2890
+ heatmapAudio.src = audioPath;
2891
+ heatmapAudio.load();
2892
+ const statusEl = document.getElementById('heatmapAudioStatus');
2893
+ if (statusEl) statusEl.textContent = 'Prêt';
2894
+ const timeEl = document.getElementById('heatmapAudioTime');
2895
+ if (timeEl) timeEl.textContent = '00:00';
2896
+ const fill = document.getElementById('heatmapAudioProgressFill');
2897
+ if (fill) fill.style.width = '0%';
2898
+ }
2899
+
2900
+ function playRadarNarration() {
2901
+ if (radarAudio && radarAudio.src) {
2902
+ radarAudio.play().catch(e => {
2903
+ console.warn('Impossible de lire le fichier audio radar:', e);
2904
+ const statusEl = document.getElementById('radarAudioStatus');
2905
+ if (statusEl) statusEl.textContent = 'Fichier non trouvé';
2906
+ });
2907
+ } else {
2908
+ const statusEl = document.getElementById('radarAudioStatus');
2909
+ if (statusEl) statusEl.textContent = 'Aucun fichier';
2910
+ }
2911
+ }
2912
+
2913
+ function pauseRadarNarration() {
2914
+ if (radarAudio) radarAudio.pause();
2915
+ }
2916
+
2917
+ function stopRadarNarration() {
2918
+ if (radarAudio) {
2919
+ radarAudio.pause();
2920
+ radarAudio.currentTime = 0;
2921
+ const fill = document.getElementById('radarAudioProgressFill');
2922
+ if (fill) fill.style.width = '0%';
2923
+ const statusEl = document.getElementById('radarAudioStatus');
2924
+ if (statusEl) statusEl.textContent = 'Arrêté';
2925
+ }
2926
+ }
2927
+
2928
+ function playHeatmapNarration() {
2929
+ if (heatmapAudio && heatmapAudio.src) {
2930
+ heatmapAudio.play().catch(e => {
2931
+ console.warn('Impossible de lire le fichier audio heatmap:', e);
2932
+ const statusEl = document.getElementById('heatmapAudioStatus');
2933
+ if (statusEl) statusEl.textContent = 'Fichier non trouvé';
2934
+ });
2935
+ } else {
2936
+ const statusEl = document.getElementById('heatmapAudioStatus');
2937
+ if (statusEl) statusEl.textContent = 'Aucun fichier';
2938
+ }
2939
+ }
2940
+
2941
+ function pauseHeatmapNarration() {
2942
+ if (heatmapAudio) heatmapAudio.pause();
2943
+ }
2944
+
2945
+ function stopHeatmapNarration() {
2946
+ if (heatmapAudio) {
2947
+ heatmapAudio.pause();
2948
+ heatmapAudio.currentTime = 0;
2949
+ const fill = document.getElementById('heatmapAudioProgressFill');
2950
+ if (fill) fill.style.width = '0%';
2951
+ const statusEl = document.getElementById('heatmapAudioStatus');
2952
+ if (statusEl) statusEl.textContent = 'Arrêté';
2953
+ }
2954
+ }
2955
+
2956
  // ================= INIT =================
2957
  document.addEventListener("DOMContentLoaded", () => {
2958
  console.log("✅ DOM chargé, initialisation...");
2959
+ initAudioElements();
2960
  loadScenarios();
2961
  initTrustStars();
2962
  initTrustSave();
2963
  initTabs();
2964
  initGDPR();
2965
 
 
2966
  updateTrustBar();
2967
 
 
2968
  document.getElementById("prevScenario").onclick = () => loadScenario(currentScenarioIndex - 1);
2969
  document.getElementById("nextScenario").onclick = () => loadScenario(currentScenarioIndex + 1);
2970
  document.getElementById("crossReadersBtn").onclick = navigateToCrossReading;
 
2975
  const tabBtns = document.querySelectorAll('.tab-btn');
2976
  const tabContents = document.querySelectorAll('.tab-content');
2977
 
 
2978
  tabBtns.forEach(btn => {
2979
  btn.addEventListener('click', () => {
2980
  const tabId = btn.getAttribute('data-tab');
2981
 
 
2982
  tabBtns.forEach(b => b.classList.remove('active'));
2983
  tabContents.forEach(c => c.classList.remove('active'));
2984
 
 
2985
  btn.classList.add('active');
2986
  document.getElementById(tabId).classList.add('active');
2987
  });
 
2995
  const evaluatorFields = document.getElementById('evaluatorFields');
2996
  const anonymousMessage = document.getElementById('anonymousMessage');
2997
 
 
2998
  function updateGDPRFields() {
2999
  if (gdprAccept.checked) {
 
3000
  evaluatorFields.style.display = 'block';
3001
  anonymousMessage.style.display = 'none';
3002
 
 
3003
  document.getElementById('evaluatorName').disabled = false;
3004
  document.getElementById('evaluatorEmail').disabled = false;
3005
  document.getElementById('evaluatorRole').disabled = false;
3006
  document.getElementById('expertiseLevel').disabled = false;
3007
  document.getElementById('messageMastery').disabled = false;
3008
  } else if (gdprDecline.checked) {
 
3009
  evaluatorFields.style.display = 'none';
3010
  anonymousMessage.style.display = 'block';
3011
 
 
3012
  document.getElementById('evaluatorName').disabled = true;
3013
  document.getElementById('evaluatorName').value = '';
3014
  document.getElementById('evaluatorEmail').disabled = true;
 
3022
  }
3023
  }
3024
 
 
3025
  if (gdprAccept && gdprDecline) {
3026
  gdprAccept.addEventListener('change', updateGDPRFields);
3027
  gdprDecline.addEventListener('change', updateGDPRFields);
3028
 
 
3029
  updateGDPRFields();
3030
  }
3031
  }
 
3033
  // ================= CHARGEMENT JSON =================
3034
  function loadScenarios() {
3035
  console.log("Chargement des scénarios...");
 
3036
  const possiblePaths = [
3037
  "/static/scenarios.json",
3038
  "scenarios.json",
 
3086
  tryLoad(0);
3087
  }
3088
 
 
3089
  function generateDefaultScenarios() {
3090
  console.log("Génération des 16 scénarios par défaut");
3091
 
3092
  const defaultScenarios = [...DEFAULT_SCENARIOS];
3093
 
 
3094
  defaultScenarios.forEach((scenario, index) => {
 
3095
  scenario.competences = generateRandomCompetences(14);
 
 
3096
  scenario.radar = generateProfileRadarData(scenario.title);
 
 
3097
  scenario.reflAgent = generateReflAgentMessage(scenario.title);
 
 
3098
  scenario.risk = calculateRiskLevel(scenario.title);
3099
  });
3100
 
3101
  return defaultScenarios;
3102
  }
3103
 
 
3104
  function generateRandomCompetences(count) {
3105
  const competenceLabels = [
3106
  "Analyse de texte", "Rédaction académique", "Recherche documentaire",
 
3113
  const competences = [];
3114
  for (let i = 0; i < count; i++) {
3115
  const labelIndex = i % competenceLabels.length;
 
3116
  const value = 30 + Math.floor(Math.random() * 65);
3117
  competences.push({
3118
  label: competenceLabels[labelIndex],
 
3123
  return competences;
3124
  }
3125
 
 
3126
  function generateProfileRadarData(profileTitle) {
3127
  let baseValues;
3128
 
 
3179
  baseValues = [70, 65, 62, 50, 65, 70, 68];
3180
  }
3181
 
 
3182
  return DEFAULT_RADAR_METRICS.map((metric, index) => ({
3183
  label: metric.label,
3184
  value: Math.max(10, Math.min(100, baseValues[index] + (Math.random() * 10 - 5)))
3185
  }));
3186
  }
3187
 
 
3188
  function generateReflAgentMessage(profileTitle) {
3189
  const messages = {
3190
  "Étudiant engagé": "Félicitations ! Ton engagement est remarquable. Continue sur cette lancée en maintenant ta régularité de travail.",
 
3208
  return messages[profileTitle] || "Analyse de votre profil d'apprentissage en cours...";
3209
  }
3210
 
 
3211
  function calculateRiskLevel(profileTitle) {
3212
  const riskLevels = {
3213
  "Étudiant engagé": 20,
 
3241
  </div>`;
3242
  }
3243
 
 
3244
  console.error(message);
3245
 
 
3246
  document.getElementById("prevScenario").disabled = true;
3247
  document.getElementById("nextScenario").disabled = true;
3248
  }
 
3263
  currentScenarioIndex = index;
3264
  const s = scenarios[index];
3265
 
 
3266
  const titleElement = document.getElementById("scenarioTitle");
3267
  if (titleElement) {
3268
  titleElement.textContent = `Scénario ${s.id} – ${s.title}`;
3269
  }
3270
 
 
3271
  const manifestList = document.getElementById("manifest-list");
3272
  if (manifestList) {
3273
  manifestList.innerHTML = `
 
3278
  </div>`;
3279
  }
3280
 
 
3281
  if (s.competences) {
3282
  updateCompactCompetences(s.competences);
3283
  }
3284
 
 
3285
  if (s.radar && s.radar.length > 0) {
 
3286
  radarMetrics = s.radar.filter(metric => metric.label !== "Engagement global");
3287
  } else {
 
3288
  radarMetrics = DEFAULT_RADAR_METRICS.map(metric => {
3289
+ const variation = (index % 7) * 5 - 15;
 
3290
  const newValue = Math.max(10, Math.min(95, metric.value + variation));
3291
  return {
3292
  label: metric.label,
 
3295
  });
3296
  }
3297
 
 
3298
  updateCompactRadarMetricsGrid();
 
 
3299
  drawRadar();
 
 
3300
  updateCompactRadarBotMessage(s);
 
 
3301
  drawCompactEnrichedHeatmapWithAxes(s);
3302
 
 
3303
  const modeSelect = document.getElementById("persistenceMode");
3304
  if (modeSelect) {
3305
  modeSelect.value = "combined";
3306
  updatePersistenceIndicators("combined");
3307
  }
3308
 
 
3309
  updatePerseveranceProfile(s.title);
3310
 
 
3311
  const reflagentElement = document.getElementById("reflagent");
3312
  if (reflagentElement) {
3313
  reflagentElement.textContent = s.reflAgent || "Message d'analyse non disponible pour ce scénario.";
3314
  }
3315
 
 
3316
  updateNavButtons();
 
 
3317
  updateStats(s);
 
 
3318
  resetTrustEvaluation();
3319
+
3320
+ loadRadarAudioForScenario(s.id);
3321
+ loadHeatmapAudioForScenario(s.id);
3322
  }
3323
 
3324
  // ================= FONCTIONS POUR LA VERSION COMPACTE =================
3325
 
 
3326
  function updateCompactCompetences(competences) {
3327
  const container = document.getElementById("compact-competences");
3328
  if (!container) return;
 
3334
  return;
3335
  }
3336
 
 
3337
  const gridContainer = document.createElement('div');
3338
  gridContainer.className = 'compact-competences-grid';
3339
 
 
3340
  const maxCompetences = 14;
3341
  const displayedCompetences = competences.slice(0, maxCompetences);
3342
 
3343
  displayedCompetences.forEach(comp => {
 
3344
  let color, colorClass;
3345
  if (comp.value < 50) {
3346
  color = '#E53935';
 
3353
  colorClass = 'high';
3354
  }
3355
 
 
3356
  const compItem = document.createElement('div');
3357
  compItem.className = 'compact-competence-matrix-item';
3358
  compItem.innerHTML = `
 
3375
  gridContainer.appendChild(compItem);
3376
  });
3377
 
 
3378
  const emptySlots = maxCompetences - displayedCompetences.length;
3379
  for (let i = 0; i < emptySlots; i++) {
3380
  const emptyItem = document.createElement('div');
 
3402
  container.appendChild(gridContainer);
3403
  }
3404
 
 
3405
  function updateCompactRadarMetricsGrid() {
3406
  const grid = document.getElementById("compact-radar-metrics-grid");
3407
  if (!grid) return;
 
3414
  }
3415
 
3416
  radarMetrics.forEach(metric => {
 
3417
  let colorClass = "";
 
3418
  if (metric.value >= 70) colorClass = "high";
3419
  else if (metric.value >= 40) colorClass = "medium";
3420
  else colorClass = "low";
3421
 
 
3422
  let icon = "📊";
3423
  if (metric.label.includes("Démarrage")) icon = "🚀";
3424
  else if (metric.label.includes("Éléments des connaissances validées")) icon = "🎓";
 
3428
  else if (metric.label.includes("Interactions")) icon = "📚";
3429
  else if (metric.label.includes("Score")) icon = "🔧";
3430
 
 
3431
  let description = "";
3432
  if (metric.label.includes("Démarrage")) description = "Rapidité d'initiation de l'activité d'apprentissage";
3433
  else if (metric.label.includes("Éléments des connaissances validées")) description = "Taux de validation des skills";
 
3450
  });
3451
  }
3452
 
 
3453
  function updateCompactRadarBotMessage(scenario) {
3454
  const radarBotContent = document.getElementById("compact-radar-bot-content");
3455
  const radarBotInsights = document.getElementById("compact-radar-bot-insights");
3456
 
3457
  if (!radarBotContent || !radarBotInsights) return;
3458
 
 
3459
  const profileMessage = profileMessages[scenario.title] || {
3460
  title: "Profil d'Apprentissage",
3461
  message: "Analyse de votre profil d'apprentissage en cours...",
 
3466
  ]
3467
  };
3468
 
 
3469
  radarBotContent.innerHTML = `<strong>${profileMessage.title}</strong><br>${profileMessage.message}`;
3470
 
 
3471
  radarBotInsights.innerHTML = '';
3472
  profileMessage.insights.forEach(insight => {
3473
  const insightElement = document.createElement('div');
 
3480
  });
3481
  }
3482
 
 
3483
  function drawCompactEnrichedHeatmapWithAxes(scenario) {
3484
  const canvas = document.getElementById("heatmap");
3485
  if (!canvas) return;
 
3491
  const weeks = 4;
3492
  const totalDays = weeks * 7;
3493
 
 
3494
  ctx.clearRect(0, 0, canvas.width, canvas.height);
3495
 
 
3496
  const scenarioTitles = {
3497
+ 1: { title: "Engagé mais désorganisé", emoji: "😅", pattern: "Effort concentré avant échéances" },
3498
+ 2: { title: "Actif mais peu efficace", emoji: "😰", pattern: "Démarrage lent, accélération tardive" },
3499
+ 3: { title: "Autonome et stratégique", emoji: "😊", pattern: "Travail régulier et équilibré" },
3500
+ 4: { title: "Motivé et constant", emoji: "👍", pattern: "Rythme régulier toute la semaine" },
3501
+ 5: { title: "Surinvestissement", emoji: "🔥", pattern: "Activité intense, risque de surcharge" },
3502
+ 6: { title: "Stratégique et ciblé", emoji: "🎯", pattern: "Efforts concentrés sur les priorités" },
3503
+ 7: { title: "Amélioration continue", emoji: "📈", pattern: "Progression visible semaine après semaine" },
3504
+ 8: { title: "Difficultés persistantes", emoji: "😔", pattern: "Efforts dispersés, peu de régularité" },
3505
+ 9: { title: "Évitement", emoji: "😨", pattern: "Activité minimale, pics sporadiques" },
3506
+ 10: { title: "Risque d'abandon", emoji: "⚠️", pattern: "Très faible activité, irrégularité totale" },
3507
+ 11: { title: "Progression technique", emoji: "🔧", pattern: "Amélioration régulière des compétences" },
3508
+ 12: { title: "Résilience", emoji: "💪", pattern: "Persévérance malgré les difficultés" },
3509
+ 13: { title: "Inactivité", emoji: "😴", pattern: "Très peu d'activité détectée" },
3510
+ 14: { title: "Désengagement", emoji: "😑", pattern: "Activité sporadique et décroissante" },
3511
+ 15: { title: "Persévérant malgré le doute", emoji: "🌱", pattern: "Accélération progressive en fin de semaine" },
3512
+ 16: { title: "Actif mais à risque d'épuisement", emoji: "⚡", pattern: "Forte activité en début de semaine" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3513
  };
3514
 
3515
+ const scenarioInfo = scenarioTitles[scenario.id] || { title: "Votre activité", emoji: "📊", pattern: "Schéma à analyser" };
 
 
 
 
3516
 
3517
  ctx.font = "bold 14px Arial";
3518
  ctx.fillStyle = "#1976D2";
 
3523
  ctx.fillStyle = "#666";
3524
  ctx.fillText(`${scenarioInfo.pattern}`, canvas.width / 2, 45);
3525
 
 
3526
  const activityData = generateCompactPersistenceData(scenario.id, totalDays);
3527
 
 
3528
  const colors = ["#EBEDF0", "#C6E48B", "#7BC96F", "#239A3B", "#196127"];
3529
 
 
3530
  const startX = 60;
3531
  const startY = 80;
3532
 
 
3533
  ctx.font = "11px Arial";
3534
  ctx.fillStyle = "#555";
3535
  ctx.textAlign = "right";
 
3540
  ctx.fillText(days[weekday], startX - 10, y);
3541
  }
3542
 
 
3543
  ctx.font = "11px Arial";
3544
  ctx.fillStyle = "#555";
3545
  ctx.textAlign = "center";
 
3550
  ctx.fillText(`S${week + 1}`, x, startY - 5);
3551
  }
3552
 
 
3553
  ctx.font = "10px Arial";
3554
  ctx.fillStyle = "#777";
3555
  ctx.textAlign = "center";
 
 
3556
  ctx.fillText("Semaines", canvas.width / 2, startY - 15);
3557
 
 
3558
  ctx.save();
3559
  ctx.translate(startX - 25, startY + (cellSize * 7) / 2);
3560
  ctx.rotate(-Math.PI / 2);
3561
  ctx.fillText("Jours", 0, 0);
3562
  ctx.restore();
3563
 
 
3564
  for (let day = 0; day < totalDays; day++) {
3565
  const week = Math.floor(day / 7);
3566
  const weekday = day % 7;
 
3570
 
3571
  const intensity = activityData[day];
3572
 
 
3573
  ctx.fillStyle = colors[intensity];
3574
  ctx.fillRect(x, y, cellSize, cellSize);
3575
 
3576
+ if (weekday === 4) {
 
3577
  ctx.strokeStyle = "#FF6B6B";
3578
  ctx.lineWidth = 1.5;
3579
  } else {
 
3583
  ctx.strokeRect(x, y, cellSize, cellSize);
3584
  }
3585
 
 
3586
  analyzeCompactPersistenceMetrics(activityData, scenario.id);
3587
 
 
3588
  const currentMode = document.getElementById("persistenceMode").value;
3589
  updatePersistenceIndicators(currentMode);
3590
  }
3591
 
 
3592
  function updatePersistenceIndicators(mode) {
 
3593
  const regularityIndicator = document.getElementById("regularity-indicator");
3594
  const regularityDesc = document.getElementById("regularity-description");
3595
  const deadlineIndicator = document.getElementById("deadline-indicator");
 
3597
  const effortIndicator = document.getElementById("effort-indicator");
3598
  const effortDesc = document.getElementById("effort-description");
3599
 
 
 
 
3600
  const regularityText = document.getElementById("compact-regularityText");
3601
 
3602
  if (!regularityIndicator || !deadlineIndicator || !effortIndicator) return;
3603
 
 
3604
  regularityIndicator.style.display = "none";
3605
  regularityDesc.style.display = "none";
3606
  deadlineIndicator.style.display = "none";
 
3615
 
3616
  switch(mode) {
3617
  case "regularity":
 
3618
  regularityIndicator.style.display = "flex";
3619
  regularityDesc.style.display = "block";
 
3620
  if (regularityText) {
3621
  regularityText.innerHTML = `
3622
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
 
3626
  `;
3627
  }
3628
  break;
 
3629
  case "deadline":
 
3630
  deadlineIndicator.style.display = "flex";
3631
  deadlineDesc.style.display = "block";
 
3632
  if (regularityText) {
3633
  regularityText.innerHTML = `
3634
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
 
3638
  `;
3639
  }
3640
  break;
 
3641
  case "effortPattern":
 
3642
  effortIndicator.style.display = "flex";
3643
  effortDesc.style.display = "block";
 
3644
  if (regularityText) {
3645
  regularityText.innerHTML = `
3646
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
 
3650
  `;
3651
  }
3652
  break;
 
3653
  case "combined":
 
3654
  regularityIndicator.style.display = "flex";
3655
  regularityDesc.style.display = "block";
3656
  deadlineIndicator.style.display = "flex";
3657
  deadlineDesc.style.display = "block";
3658
  effortIndicator.style.display = "flex";
3659
  effortDesc.style.display = "block";
 
3660
  if (regularityText) {
3661
  regularityText.innerHTML = `
3662
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
 
3666
  `;
3667
  }
3668
  break;
 
3669
  default:
 
3670
  regularityIndicator.style.display = "flex";
3671
  regularityDesc.style.display = "block";
3672
  deadlineIndicator.style.display = "flex";
 
3676
  }
3677
  }
3678
 
 
3679
  function updatePerseveranceProfile(scenarioTitle) {
3680
  const profile = perseveranceProfiles[scenarioTitle];
3681
 
3682
+ const regularityScore = document.getElementById("compact-regularityScore");
3683
+ const deadlineScore = document.getElementById("compact-deadlineScore");
3684
+ const effortPattern = document.getElementById("compact-effortPattern");
3685
+ const regularityText = document.getElementById("compact-regularityText");
3686
+
3687
  if (!profile) {
 
 
 
 
3688
  if (regularityText) {
3689
  regularityText.innerHTML = `
3690
  <div style="font-size: 12px; color: #555; line-height: 1.4;">
 
3695
  return;
3696
  }
3697
 
 
 
 
 
 
 
3698
  if (regularityScore) {
3699
  regularityScore.textContent = profile.regularity + "%";
3700
  regularityScore.style.color = profile.color;
 
3723
  }
3724
  }
3725
 
 
3726
  function generateCompactPersistenceData(scenarioId, totalDays) {
3727
  const data = new Array(totalDays).fill(0);
3728
 
 
3729
  const profile = Object.values(perseveranceProfiles)[scenarioId - 1] || perseveranceProfiles["Étudiant engagé"];
3730
 
3731
  for (let i = 0; i < totalDays; i++) {
 
3733
  const week = Math.floor(i / 7);
3734
  const progress = i / totalDays;
3735
 
 
3736
  if (profile.effortPattern.includes("Pic d'activité jeudi/vendredi")) {
3737
  if (weekday === 3 || weekday === 4) {
3738
  data[i] = 4;
 
3756
  data[i] = Math.random() > 0.8 ? 1 : 0;
3757
  }
3758
  } else {
 
3759
  if (weekday === 5 || weekday === 6) {
3760
  data[i] = 2;
3761
  } else {
 
3767
  return data;
3768
  }
3769
 
 
3770
  function analyzeCompactPersistenceMetrics(activityData, scenarioId) {
3771
+ // Fonction conservée pour compatibilité
 
3772
  }
3773
 
3774
  // ================= RADAR =================
 
3787
 
3788
  ctx.clearRect(0, 0, canvas.width, canvas.height);
3789
 
 
3790
  for (let i = 1; i <= 5; i++) {
3791
  ctx.beginPath();
3792
  ctx.arc(cx, cy, (radius / 5) * i, 0, 2 * Math.PI);
 
3805
  }
3806
  }
3807
 
 
3808
  radarMetrics.forEach((m, i) => {
3809
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
3810
 
 
3829
  ctx.fillText(label, tx, ty);
3830
  });
3831
 
 
3832
  ctx.beginPath();
3833
  radarMetrics.forEach((m, i) => {
3834
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
 
3845
  ctx.lineWidth = 2;
3846
  ctx.stroke();
3847
 
 
3848
  radarMetrics.forEach((m, i) => {
3849
  const angle = (2 * Math.PI * i) / n - Math.PI / 2;
3850
  const r = radius * (m.value / 100);
 
4389
  updatePersistenceIndicators(mode);
4390
  }
4391
 
 
4392
  window.changePersistenceAnalysis = changePersistenceAnalysis;
4393
  window.updateStats = updateStats;
4394
  window.showNotification = showNotification;
4395
  window.fillSampleEvaluator = fillSampleEvaluator;
4396
  window.clearEvaluatorFields = clearEvaluatorFields;
4397
  window.loadScenario = loadScenario;
4398
+ window.playRadarNarration = playRadarNarration;
4399
+ window.pauseRadarNarration = pauseRadarNarration;
4400
+ window.stopRadarNarration = stopRadarNarration;
4401
+ window.playHeatmapNarration = playHeatmapNarration;
4402
+ window.pauseHeatmapNarration = pauseHeatmapNarration;
4403
+ window.stopHeatmapNarration = stopHeatmapNarration;
4404
  </script>
4405
 
4406
+ </div>
4407
 
 
4408
  <div style="
4409
  margin-top: 40px;
4410
  padding: 20px;