OUAREDAEK commited on
Commit
d3a685e
·
verified ·
1 Parent(s): 3d1fefc

Upload templates/index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. templates/index.html +126 -16
templates/index.html CHANGED
@@ -397,11 +397,48 @@
397
  .flex-between { display: flex; justify-content: space-between; align-items: center; }
398
 
399
  /* DASHBOARD PROFESSIONNEL : HEATMAP MODERNE & ELEGANT */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  .heatmap-container {
401
  background: white;
402
  border-radius: 20px;
403
  padding: 24px 20px;
404
- margin-bottom: 32px;
405
  border: 1px solid var(--gray-200);
406
  box-shadow: var(--shadow-sm);
407
  transition: box-shadow 0.2s ease;
@@ -920,12 +957,12 @@
920
  <!-- PLANNING -->
921
  <section id="planner" class="tab-content"><div class="card"><div class="card-header"><h3><i class="fas fa-calendar-alt"></i> Planner Personnalisé</h3><button class="export-btn" onclick="exportPlanning()"><i class="fas fa-download"></i> Exporter</button></div><div class="planning-stats" id="planningStats"><div class="planning-stat-item"><div class="planning-stat-number" id="statSessions">6</div><div class="planning-stat-label">Sessions Planifiées</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statTotalDays">270</div><div class="planning-stat-label">Jours Totaux</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statQuality">3/4</div><div class="planning-stat-label">Niveau de Qualité</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statDuration">20min</div><div class="planning-stat-label">Durée/Session</div></div></div><div style="overflow-x:auto;"><table class="planning-sessions-table" id="sessionsTable"><thead><tr><th>Date</th><th>Session</th><th>Compétence</th><th>Durée</th><th>Intervalle</th><th>Type</th><th>Stratégie</th><th>Productivité</th><th>Heure Optimale</th></tr></thead><tbody id="sessionsTableBody"></tbody></table></div><div class="spaced-repetition-container"><h4><i class="fas fa-chart-line"></i> Courbe de Répétition Espacée</h4><div class="curve-legend"><div class="legend-item"><div class="legend-color" style="background:#1e1e1e;"></div><span>Seuil de rétention</span></div><div class="legend-item"><div class="legend-color square" style="background:#424242;"></div><span>Sessions</span></div></div><canvas id="spacedRepetitionCurve" width="100%" height="200" style="max-height:200px; width:100%;"></canvas><div id="intervalDatesCurve" style="font-size:0.7rem; text-align:center; margin-top:8px;"></div></div></div></section>
922
 
923
- <!-- DASHBOARD (avec message réflexif IA) -->
924
  <section id="dashboard" class="tab-content">
925
  <div class="card">
926
  <div class="card-header"><h3><i class="fas fa-chart-simple"></i> Analyse de la persistance et du management de l'effort (persévérance)</h3><div class="dashboard-view-toggle"><button class="view-btn active" data-view="combined" onclick="setDashboardView('combined')">📊 Vue d'ensemble</button></div></div>
927
 
928
- <!-- ========== MESSAGE RÉFLEXIF IA AJOUTÉ ========== -->
929
  <div class="ia-reflective-message">
930
  <div class="ia-message-header">
931
  <div class="ia-avatar">
@@ -941,16 +978,36 @@
941
  </div>
942
  <!-- ========== FIN DU MESSAGE RÉFLEXIF ========== -->
943
 
944
- <div class="heatmap-container">
945
- <div class="heatmap-title">
946
- <h4><i class="fas fa-fire" style="color:#f97316;"></i> Heatmap d'activité 4 semaines</h4>
947
- <span class="heatmap-sub">Intensité d'engagement quotidien</span>
 
 
 
 
 
 
 
 
 
 
948
  </div>
949
- <div class="heatmap-grid-wrapper">
950
- <div class="heatmap-grid" id="heatmapGrid"></div>
 
 
 
 
 
 
 
 
 
 
951
  </div>
952
- <div class="legend-heatmap" id="heatmapLegend"></div>
953
  </div>
 
954
  <div class="symmetry-curve-card"><div class="curve-header"><h4><i class="fas fa-chart-line"></i> La moyenne d'intensité par jour</h4><div class="curve-legend-mini"><span style="color:#1e1e1e;">● Intensité moyenne</span><span style="color:#616161;">● Miroir symétrique</span></div></div><canvas id="symmetryHeatmapCurve" width="100%" height="180" style="max-height:180px; width:100%;" class="symmetry-canvas"></canvas><p style="font-size:0.7rem; color:var(--gray-500); margin-top:12px; text-align:center;"><i class="fas fa-chart-simple"></i> Courbe de la moyenne d'intensité par jour (symétrie gauche/droite). Visualisation des pics d'activité.</p></div>
955
  <div class="indicator-grid">
956
  <div class="indicator-card"><div class="indicator-header"><div class="indicator-icon"><i class="fas fa-clock"></i></div><div class="indicator-title">Régularité du travail</div></div><div class="circular-progress" id="regularityCircle"><svg viewBox="0 0 120 120"><circle class="bg-circle" cx="60" cy="60" r="54"></circle><circle class="progress-circle" cx="60" cy="60" r="54" id="regularityCircleProgress"></circle></svg><div class="progress-percent" id="regularityPercent">90%</div><div class="progress-label"></div></div><div class="indicator-sub">La constance de l'activité de l'apprenant dans le temps.</div></div>
@@ -1023,11 +1080,64 @@
1023
  { "id": "T-ORG", "label": "Organisation textuelle", "category": "Texte" },
1024
  { "id": "T-REP", "label": "Effets de style", "category": "Texte" }
1025
  ];
1026
- let eventsList = [], currentSessions = [], spacedChart = null, symmetryChart = null;
1027
  let profileData = { extraversion: 3, openness: 3, conscientiousness: 4, agreeableness: 3, neuroticism: 3, intrinsic: 3, introjected: 4 };
1028
  const eventTemplates = [{ type: "Rappel d'étude", title: "Session de révision", message: "Votre session est prévue aujourd'hui.", priority: "high" },{ type: "Conseil IA", title: "Optimisation", message: "Révisez les conjugaisons.", priority: "medium" },{ type: "Encouragement", title: "Progression", message: "80% des objectifs atteints.", priority: "low" }];
1029
  let journalEntries = [];
1030
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1031
  // JOURNAL : NOUVELLES FONCTIONS AVEC DATE
1032
  function loadJournalEntries() {
1033
  const saved = localStorage.getItem('perseverAI_journal');
@@ -1099,13 +1209,13 @@
1099
  });
1100
  }
1101
 
1102
- // Autres fonctions existantes (non modifiées)
1103
  function generateHeatmapData() { const days = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']; const weeks = 4; let matrix = []; for(let w = 0; w < weeks; w++) { let week = []; for(let d = 0; d < 7; d++) { let base = (w * 7 + d) % 10; let level = 0; if(base < 3) level = 1; else if(base < 6) level = 2; else if(base < 8) level = 3; else level = 4; if(d === 3 || d === 4) level = Math.min(4, level+1); week.push(level); } matrix.push(week); } return { days, weeks: matrix }; }
1104
  function renderHeatmap() { const container = document.getElementById('heatmapGrid'); if(!container) return; const { days, weeks } = generateHeatmapData(); container.innerHTML = ''; for(let i = 0; i < days.length; i++) { let dayDiv = document.createElement('div'); dayDiv.className = 'heatmap-day-label'; dayDiv.innerText = days[i]; container.appendChild(dayDiv); } for(let w = 0; w < weeks.length; w++) { for(let d = 0; d < weeks[w].length; d++) { let cell = document.createElement('div'); cell.className = `heatmap-cell level-${weeks[w][d]}`; cell.title = `${days[d]} Semaine ${w+1} : Niveau ${weeks[w][d]}`; container.appendChild(cell); } } const legendContainer = document.getElementById('heatmapLegend'); if(legendContainer) { legendContainer.innerHTML = `<div class="legend-heatmap-item"><div class="legend-color-box level-0"></div><span>Aucune activité</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-1"></div><span>Faible</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-2"></div><span>Moyenne</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-3"></div><span>Élevée</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-4"></div><span>Intense</span></div>`; } }
1105
  function drawSymmetryCurve() { const canvas = document.getElementById('symmetryHeatmapCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const container = canvas.parentElement; const width = container.clientWidth - 40; const height = 160; canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, width, height); const { days, weeks } = generateHeatmapData(); let avgIntensity = new Array(7).fill(0); for(let w = 0; w < weeks.length; w++) for(let d = 0; d < 7; d++) avgIntensity[d] += weeks[w][d]; avgIntensity = avgIntensity.map(v => v / weeks.length); let reversed = [...avgIntensity].reverse(); let combined = avgIntensity.map((v,i)=> (v + reversed[i])/2); const points = combined.map((val, idx) => ({ x: (idx / 6) * width, y: height - (val / 4) * height })); ctx.beginPath(); ctx.strokeStyle = '#1e1e1e'; ctx.lineWidth = 2.5; ctx.setLineDash([]); points.forEach((p,i) => i===0 ? ctx.moveTo(p.x,p.y) : ctx.lineTo(p.x,p.y)); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = '#616161'; ctx.lineWidth = 2; ctx.setLineDash([6,8]); let mirrorPoints = points.map(p => ({ x: width - p.x, y: p.y })); mirrorPoints.forEach((p,i)=> i===0 ? ctx.moveTo(p.x,p.y) : ctx.lineTo(p.x,p.y)); ctx.stroke(); ctx.setLineDash([]); points.forEach(p => { ctx.beginPath(); ctx.fillStyle = '#1e1e1e'; ctx.arc(p.x, p.y, 4, 0, 2*Math.PI); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, 2*Math.PI); ctx.fill(); }); ctx.fillStyle = '#475569'; ctx.font = '9px Inter'; days.forEach((d,i) => { let x = (i/6)*width; ctx.fillText(d, x-8, height-6); }); }
1106
  function updateCircularProgress(elementId, percent) { const circle = document.getElementById(elementId); if(!circle) return; const radius = 54; const circumference = 2 * Math.PI * radius; const offset = circumference - (percent / 100) * circumference; circle.style.strokeDasharray = `${circumference}`; circle.style.strokeDashoffset = offset; }
1107
  function updateIndicators() { document.getElementById('regularityPercent').innerText = '90%'; document.getElementById('deadlinePercent').innerText = '88%'; updateCircularProgress('regularityCircleProgress', 90); updateCircularProgress('deadlineCircleProgress', 88); document.getElementById('effortPattern').innerHTML = "Effort concentré (Pic d'activité jeudi/vendredi)"; document.getElementById('persistenceScore').innerHTML = "Très élevée"; }
1108
- function setDashboardView(view) { if(view === 'combined') { renderHeatmap(); drawSymmetryCurve(); updateIndicators(); } }
1109
  function initCompetencesSelect() { const select = document.getElementById('competence'); if(select) select.innerHTML = '<option value="comprendre_mots">Comprendre les mots</option>' + competencesData.map(c => `<option value="${c.id}">${c.label}</option>`).join(''); }
1110
  function setupRatingListeners() { document.querySelectorAll('.big5-rating .rating-btn').forEach(btn => btn.addEventListener('click', function() { const container = this.closest('.big5-rating'); const trait = container.dataset.trait; const value = parseInt(this.dataset.value); profileData[trait] = value; updateRatingUI(container, value); updateProfileUI(); saveProfileToStorage(); })); document.querySelectorAll('.motivation-rating .rating-btn').forEach(btn => btn.addEventListener('click', function() { const container = this.closest('.motivation-rating'); const motivation = container.dataset.motivation; const value = parseInt(this.dataset.value); profileData[motivation] = value; updateRatingUI(container, value); updateProfileUI(); saveProfileToStorage(); })); }
1111
  function updateRatingUI(container, value) { container.querySelectorAll('.rating-btn').forEach(btn => { btn.classList.remove('active'); if(parseInt(btn.dataset.value) === value) btn.classList.add('active'); }); }
@@ -1130,7 +1240,7 @@
1130
  function createChart() { const ctx = document.getElementById('notificationEfficiencyChart')?.getContext('2d'); if(ctx) efficiencyChart = new Chart(ctx, { type: 'bar', data: { labels: ['Acceptés', 'Rejetés', 'En attente'], datasets: [{ label: 'Actions', data: [0,0,0], backgroundColor: ['#2e7d32','#c62828','#b76e0e'] }] }, options: { responsive: true } }); }
1131
  function saveEvents() { localStorage.setItem('eventsList', JSON.stringify(eventsList)); }
1132
  function loadSavedData() { const saved = localStorage.getItem('eventsList'); if(saved) eventsList = JSON.parse(saved); }
1133
- function showTab(tabId) { document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active')); document.getElementById(tabId).classList.add('active'); document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active')); if(window.event?.target) window.event.target.classList.add('active'); if(tabId === 'dashboard') { renderHeatmap(); drawSymmetryCurve(); updateIndicators(); } if(tabId === 'planner') { if(currentSessions.length === 0) generateDefaultPlanning(); else drawSpacedRepetitionCurve(currentSessions); } if(tabId === 'notifications') { renderEventsTable(); updateEventStats(); } if(tabId === 'profile') { loadProfileFromStorage(); updateProfileUI(); } if(tabId === 'journal') { loadJournalEntries(); } }
1134
  function showToast(msg) { let div = document.createElement('div'); div.style.cssText = `position:fixed; bottom:20px; right:20px; background:#1e293b; color:white; padding:12px 20px; border-radius:8px; z-index:1000;`; div.innerHTML = msg; document.body.appendChild(div); setTimeout(()=>div.remove(), 2500); }
1135
 
1136
  document.addEventListener('DOMContentLoaded', () => {
@@ -1151,13 +1261,13 @@
1151
  loadJournalEntries();
1152
  updateCircularProgress('regularityCircleProgress', 90);
1153
  updateCircularProgress('deadlineCircleProgress', 88);
1154
- // Initialisation de la date du journal
1155
  const todayDate = new Date().toISOString().split('T')[0];
1156
  const journalDateInput = document.getElementById('journalDate');
1157
  if(journalDateInput) journalDateInput.value = todayDate;
 
1158
  });
1159
  function initProfileModule() { loadProfileFromStorage(); setupRatingListeners(); updateProfileUI(); }
1160
- window.addEventListener('resize', () => { if(document.getElementById('dashboard').classList.contains('active')) drawSymmetryCurve(); if(document.getElementById('planner').classList.contains('active') && currentSessions.length > 0) drawSpacedRepetitionCurve(currentSessions); });
1161
  </script>
1162
  </body>
1163
  </html>
 
397
  .flex-between { display: flex; justify-content: space-between; align-items: center; }
398
 
399
  /* DASHBOARD PROFESSIONNEL : HEATMAP MODERNE & ELEGANT */
400
+ .dashboard-two-columns {
401
+ display: flex;
402
+ flex-wrap: wrap;
403
+ gap: 24px;
404
+ margin-bottom: 32px;
405
+ }
406
+ .heatmap-wrapper {
407
+ flex: 1.2;
408
+ min-width: 280px;
409
+ }
410
+ .radar-wrapper {
411
+ flex: 1;
412
+ min-width: 280px;
413
+ background: white;
414
+ border-radius: 20px;
415
+ padding: 20px;
416
+ border: 1px solid var(--gray-200);
417
+ box-shadow: var(--shadow-sm);
418
+ transition: box-shadow 0.2s ease;
419
+ }
420
+ .radar-wrapper:hover {
421
+ box-shadow: var(--shadow-md);
422
+ }
423
+ .radar-title {
424
+ font-weight: 600;
425
+ font-size: 0.9rem;
426
+ color: var(--gray-800);
427
+ margin-bottom: 16px;
428
+ padding-bottom: 8px;
429
+ border-bottom: 1px solid #eef2f8;
430
+ display: flex;
431
+ align-items: center;
432
+ gap: 8px;
433
+ }
434
+ .radar-container canvas {
435
+ max-height: 300px;
436
+ width: 100%;
437
+ }
438
  .heatmap-container {
439
  background: white;
440
  border-radius: 20px;
441
  padding: 24px 20px;
 
442
  border: 1px solid var(--gray-200);
443
  box-shadow: var(--shadow-sm);
444
  transition: box-shadow 0.2s ease;
 
957
  <!-- PLANNING -->
958
  <section id="planner" class="tab-content"><div class="card"><div class="card-header"><h3><i class="fas fa-calendar-alt"></i> Planner Personnalisé</h3><button class="export-btn" onclick="exportPlanning()"><i class="fas fa-download"></i> Exporter</button></div><div class="planning-stats" id="planningStats"><div class="planning-stat-item"><div class="planning-stat-number" id="statSessions">6</div><div class="planning-stat-label">Sessions Planifiées</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statTotalDays">270</div><div class="planning-stat-label">Jours Totaux</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statQuality">3/4</div><div class="planning-stat-label">Niveau de Qualité</div></div><div class="planning-stat-item"><div class="planning-stat-number" id="statDuration">20min</div><div class="planning-stat-label">Durée/Session</div></div></div><div style="overflow-x:auto;"><table class="planning-sessions-table" id="sessionsTable"><thead><tr><th>Date</th><th>Session</th><th>Compétence</th><th>Durée</th><th>Intervalle</th><th>Type</th><th>Stratégie</th><th>Productivité</th><th>Heure Optimale</th></tr></thead><tbody id="sessionsTableBody"></tbody></table></div><div class="spaced-repetition-container"><h4><i class="fas fa-chart-line"></i> Courbe de Répétition Espacée</h4><div class="curve-legend"><div class="legend-item"><div class="legend-color" style="background:#1e1e1e;"></div><span>Seuil de rétention</span></div><div class="legend-item"><div class="legend-color square" style="background:#424242;"></div><span>Sessions</span></div></div><canvas id="spacedRepetitionCurve" width="100%" height="200" style="max-height:200px; width:100%;"></canvas><div id="intervalDatesCurve" style="font-size:0.7rem; text-align:center; margin-top:8px;"></div></div></div></section>
959
 
960
+ <!-- DASHBOARD (avec radar à côté heatmap + message réflexif IA) -->
961
  <section id="dashboard" class="tab-content">
962
  <div class="card">
963
  <div class="card-header"><h3><i class="fas fa-chart-simple"></i> Analyse de la persistance et du management de l'effort (persévérance)</h3><div class="dashboard-view-toggle"><button class="view-btn active" data-view="combined" onclick="setDashboardView('combined')">📊 Vue d'ensemble</button></div></div>
964
 
965
+ <!-- ========== MESSAGE RÉFLEXIF IA ========== -->
966
  <div class="ia-reflective-message">
967
  <div class="ia-message-header">
968
  <div class="ia-avatar">
 
978
  </div>
979
  <!-- ========== FIN DU MESSAGE RÉFLEXIF ========== -->
980
 
981
+ <!-- SECTION HEATMAP + RADAR CÔTE À CÔTE -->
982
+ <div class="dashboard-two-columns">
983
+ <!-- Heatmap (gauche ou 1.2) -->
984
+ <div class="heatmap-wrapper">
985
+ <div class="heatmap-container">
986
+ <div class="heatmap-title">
987
+ <h4><i class="fas fa-fire" style="color:#f97316;"></i> Heatmap d'activité – 4 semaines</h4>
988
+ <span class="heatmap-sub">Intensité d'engagement quotidien</span>
989
+ </div>
990
+ <div class="heatmap-grid-wrapper">
991
+ <div class="heatmap-grid" id="heatmapGrid"></div>
992
+ </div>
993
+ <div class="legend-heatmap" id="heatmapLegend"></div>
994
+ </div>
995
  </div>
996
+
997
+ <!-- Radar (droite) -->
998
+ <div class="radar-wrapper">
999
+ <div class="radar-title">
1000
+ <i class="fas fa-chart-pie"></i> Analyse de l'engagement : Indicateurs de comportement d'apprentissage
1001
+ </div>
1002
+ <div class="radar-container">
1003
+ <canvas id="engagementRadarChart" width="400" height="400" style="max-width:100%; height:auto;"></canvas>
1004
+ </div>
1005
+ <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 16px; font-size: 0.7rem; color: var(--gray-600);">
1006
+
1007
+ </div>
1008
  </div>
 
1009
  </div>
1010
+
1011
  <div class="symmetry-curve-card"><div class="curve-header"><h4><i class="fas fa-chart-line"></i> La moyenne d'intensité par jour</h4><div class="curve-legend-mini"><span style="color:#1e1e1e;">● Intensité moyenne</span><span style="color:#616161;">● Miroir symétrique</span></div></div><canvas id="symmetryHeatmapCurve" width="100%" height="180" style="max-height:180px; width:100%;" class="symmetry-canvas"></canvas><p style="font-size:0.7rem; color:var(--gray-500); margin-top:12px; text-align:center;"><i class="fas fa-chart-simple"></i> Courbe de la moyenne d'intensité par jour (symétrie gauche/droite). Visualisation des pics d'activité.</p></div>
1012
  <div class="indicator-grid">
1013
  <div class="indicator-card"><div class="indicator-header"><div class="indicator-icon"><i class="fas fa-clock"></i></div><div class="indicator-title">Régularité du travail</div></div><div class="circular-progress" id="regularityCircle"><svg viewBox="0 0 120 120"><circle class="bg-circle" cx="60" cy="60" r="54"></circle><circle class="progress-circle" cx="60" cy="60" r="54" id="regularityCircleProgress"></circle></svg><div class="progress-percent" id="regularityPercent">90%</div><div class="progress-label"></div></div><div class="indicator-sub">La constance de l'activité de l'apprenant dans le temps.</div></div>
 
1080
  { "id": "T-ORG", "label": "Organisation textuelle", "category": "Texte" },
1081
  { "id": "T-REP", "label": "Effets de style", "category": "Texte" }
1082
  ];
1083
+ let eventsList = [], currentSessions = [], spacedChart = null, symmetryChart = null, radarChart = null;
1084
  let profileData = { extraversion: 3, openness: 3, conscientiousness: 4, agreeableness: 3, neuroticism: 3, intrinsic: 3, introjected: 4 };
1085
  const eventTemplates = [{ type: "Rappel d'étude", title: "Session de révision", message: "Votre session est prévue aujourd'hui.", priority: "high" },{ type: "Conseil IA", title: "Optimisation", message: "Révisez les conjugaisons.", priority: "medium" },{ type: "Encouragement", title: "Progression", message: "80% des objectifs atteints.", priority: "low" }];
1086
  let journalEntries = [];
1087
 
1088
+ // Données pour le radar (8 indicateurs)
1089
+ const radarIndicators = [
1090
+ "Démarrage des activités", "Achèvement des activités",
1091
+ "Taux d'erreur", "Tentatives de réponses",
1092
+ "Éléments des connaissances validées", "Interactions avec les ressources",
1093
+ "Score", "Niveau moyen des compétences"
1094
+ ];
1095
+ const radarValues = [85, 74, 65, 78, 72, 80, 70, 68]; // Pourcentages
1096
+ // Paires inversées pour l'affichage symétrique dans le titre
1097
+
1098
+ // Fonction pour créer ou mettre à jour le radar chart
1099
+ function initRadarChart() {
1100
+ const ctx = document.getElementById('engagementRadarChart')?.getContext('2d');
1101
+ if(!ctx) return;
1102
+ if(radarChart) radarChart.destroy();
1103
+ radarChart = new Chart(ctx, {
1104
+ type: 'radar',
1105
+ data: {
1106
+ labels: radarIndicators,
1107
+ datasets: [{
1108
+ label: 'Taux (%)',
1109
+ data: radarValues,
1110
+ backgroundColor: 'rgba(30, 30, 30, 0.2)',
1111
+ borderColor: '#1e1e1e',
1112
+ borderWidth: 2,
1113
+ pointBackgroundColor: '#1e1e1e',
1114
+ pointBorderColor: '#ffffff',
1115
+ pointRadius: 5,
1116
+ pointHoverRadius: 7,
1117
+ fill: true
1118
+ }]
1119
+ },
1120
+ options: {
1121
+ responsive: true,
1122
+ maintainAspectRatio: true,
1123
+ scales: {
1124
+ r: {
1125
+ beginAtZero: true,
1126
+ max: 100,
1127
+ ticks: { stepSize: 20, backdropColor: 'transparent' },
1128
+ grid: { color: '#e0e0e0' },
1129
+ angleLines: { color: '#e0e0e0' },
1130
+ pointLabels: { font: { size: 10, weight: '500' }, color: '#424242' }
1131
+ }
1132
+ },
1133
+ plugins: {
1134
+ tooltip: { callbacks: { label: (ctx) => `${ctx.label}: ${ctx.raw}%` } },
1135
+ legend: { display: false }
1136
+ }
1137
+ }
1138
+ });
1139
+ }
1140
+
1141
  // JOURNAL : NOUVELLES FONCTIONS AVEC DATE
1142
  function loadJournalEntries() {
1143
  const saved = localStorage.getItem('perseverAI_journal');
 
1209
  });
1210
  }
1211
 
1212
+ // Autres fonctions existantes
1213
  function generateHeatmapData() { const days = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']; const weeks = 4; let matrix = []; for(let w = 0; w < weeks; w++) { let week = []; for(let d = 0; d < 7; d++) { let base = (w * 7 + d) % 10; let level = 0; if(base < 3) level = 1; else if(base < 6) level = 2; else if(base < 8) level = 3; else level = 4; if(d === 3 || d === 4) level = Math.min(4, level+1); week.push(level); } matrix.push(week); } return { days, weeks: matrix }; }
1214
  function renderHeatmap() { const container = document.getElementById('heatmapGrid'); if(!container) return; const { days, weeks } = generateHeatmapData(); container.innerHTML = ''; for(let i = 0; i < days.length; i++) { let dayDiv = document.createElement('div'); dayDiv.className = 'heatmap-day-label'; dayDiv.innerText = days[i]; container.appendChild(dayDiv); } for(let w = 0; w < weeks.length; w++) { for(let d = 0; d < weeks[w].length; d++) { let cell = document.createElement('div'); cell.className = `heatmap-cell level-${weeks[w][d]}`; cell.title = `${days[d]} Semaine ${w+1} : Niveau ${weeks[w][d]}`; container.appendChild(cell); } } const legendContainer = document.getElementById('heatmapLegend'); if(legendContainer) { legendContainer.innerHTML = `<div class="legend-heatmap-item"><div class="legend-color-box level-0"></div><span>Aucune activité</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-1"></div><span>Faible</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-2"></div><span>Moyenne</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-3"></div><span>Élevée</span></div><div class="legend-heatmap-item"><div class="legend-color-box level-4"></div><span>Intense</span></div>`; } }
1215
  function drawSymmetryCurve() { const canvas = document.getElementById('symmetryHeatmapCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const container = canvas.parentElement; const width = container.clientWidth - 40; const height = 160; canvas.width = width; canvas.height = height; ctx.clearRect(0, 0, width, height); const { days, weeks } = generateHeatmapData(); let avgIntensity = new Array(7).fill(0); for(let w = 0; w < weeks.length; w++) for(let d = 0; d < 7; d++) avgIntensity[d] += weeks[w][d]; avgIntensity = avgIntensity.map(v => v / weeks.length); let reversed = [...avgIntensity].reverse(); let combined = avgIntensity.map((v,i)=> (v + reversed[i])/2); const points = combined.map((val, idx) => ({ x: (idx / 6) * width, y: height - (val / 4) * height })); ctx.beginPath(); ctx.strokeStyle = '#1e1e1e'; ctx.lineWidth = 2.5; ctx.setLineDash([]); points.forEach((p,i) => i===0 ? ctx.moveTo(p.x,p.y) : ctx.lineTo(p.x,p.y)); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = '#616161'; ctx.lineWidth = 2; ctx.setLineDash([6,8]); let mirrorPoints = points.map(p => ({ x: width - p.x, y: p.y })); mirrorPoints.forEach((p,i)=> i===0 ? ctx.moveTo(p.x,p.y) : ctx.lineTo(p.x,p.y)); ctx.stroke(); ctx.setLineDash([]); points.forEach(p => { ctx.beginPath(); ctx.fillStyle = '#1e1e1e'; ctx.arc(p.x, p.y, 4, 0, 2*Math.PI); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(p.x, p.y, 2, 0, 2*Math.PI); ctx.fill(); }); ctx.fillStyle = '#475569'; ctx.font = '9px Inter'; days.forEach((d,i) => { let x = (i/6)*width; ctx.fillText(d, x-8, height-6); }); }
1216
  function updateCircularProgress(elementId, percent) { const circle = document.getElementById(elementId); if(!circle) return; const radius = 54; const circumference = 2 * Math.PI * radius; const offset = circumference - (percent / 100) * circumference; circle.style.strokeDasharray = `${circumference}`; circle.style.strokeDashoffset = offset; }
1217
  function updateIndicators() { document.getElementById('regularityPercent').innerText = '90%'; document.getElementById('deadlinePercent').innerText = '88%'; updateCircularProgress('regularityCircleProgress', 90); updateCircularProgress('deadlineCircleProgress', 88); document.getElementById('effortPattern').innerHTML = "Effort concentré (Pic d'activité jeudi/vendredi)"; document.getElementById('persistenceScore').innerHTML = "Très élevée"; }
1218
+ function setDashboardView(view) { if(view === 'combined') { renderHeatmap(); drawSymmetryCurve(); updateIndicators(); initRadarChart(); } }
1219
  function initCompetencesSelect() { const select = document.getElementById('competence'); if(select) select.innerHTML = '<option value="comprendre_mots">Comprendre les mots</option>' + competencesData.map(c => `<option value="${c.id}">${c.label}</option>`).join(''); }
1220
  function setupRatingListeners() { document.querySelectorAll('.big5-rating .rating-btn').forEach(btn => btn.addEventListener('click', function() { const container = this.closest('.big5-rating'); const trait = container.dataset.trait; const value = parseInt(this.dataset.value); profileData[trait] = value; updateRatingUI(container, value); updateProfileUI(); saveProfileToStorage(); })); document.querySelectorAll('.motivation-rating .rating-btn').forEach(btn => btn.addEventListener('click', function() { const container = this.closest('.motivation-rating'); const motivation = container.dataset.motivation; const value = parseInt(this.dataset.value); profileData[motivation] = value; updateRatingUI(container, value); updateProfileUI(); saveProfileToStorage(); })); }
1221
  function updateRatingUI(container, value) { container.querySelectorAll('.rating-btn').forEach(btn => { btn.classList.remove('active'); if(parseInt(btn.dataset.value) === value) btn.classList.add('active'); }); }
 
1240
  function createChart() { const ctx = document.getElementById('notificationEfficiencyChart')?.getContext('2d'); if(ctx) efficiencyChart = new Chart(ctx, { type: 'bar', data: { labels: ['Acceptés', 'Rejetés', 'En attente'], datasets: [{ label: 'Actions', data: [0,0,0], backgroundColor: ['#2e7d32','#c62828','#b76e0e'] }] }, options: { responsive: true } }); }
1241
  function saveEvents() { localStorage.setItem('eventsList', JSON.stringify(eventsList)); }
1242
  function loadSavedData() { const saved = localStorage.getItem('eventsList'); if(saved) eventsList = JSON.parse(saved); }
1243
+ function showTab(tabId) { document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active')); document.getElementById(tabId).classList.add('active'); document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active')); if(window.event?.target) window.event.target.classList.add('active'); if(tabId === 'dashboard') { renderHeatmap(); drawSymmetryCurve(); updateIndicators(); initRadarChart(); } if(tabId === 'planner') { if(currentSessions.length === 0) generateDefaultPlanning(); else drawSpacedRepetitionCurve(currentSessions); } if(tabId === 'notifications') { renderEventsTable(); updateEventStats(); } if(tabId === 'profile') { loadProfileFromStorage(); updateProfileUI(); } if(tabId === 'journal') { loadJournalEntries(); } }
1244
  function showToast(msg) { let div = document.createElement('div'); div.style.cssText = `position:fixed; bottom:20px; right:20px; background:#1e293b; color:white; padding:12px 20px; border-radius:8px; z-index:1000;`; div.innerHTML = msg; document.body.appendChild(div); setTimeout(()=>div.remove(), 2500); }
1245
 
1246
  document.addEventListener('DOMContentLoaded', () => {
 
1261
  loadJournalEntries();
1262
  updateCircularProgress('regularityCircleProgress', 90);
1263
  updateCircularProgress('deadlineCircleProgress', 88);
 
1264
  const todayDate = new Date().toISOString().split('T')[0];
1265
  const journalDateInput = document.getElementById('journalDate');
1266
  if(journalDateInput) journalDateInput.value = todayDate;
1267
+ initRadarChart();
1268
  });
1269
  function initProfileModule() { loadProfileFromStorage(); setupRatingListeners(); updateProfileUI(); }
1270
+ window.addEventListener('resize', () => { if(document.getElementById('dashboard').classList.contains('active')) { drawSymmetryCurve(); if(radarChart) radarChart.resize(); } if(document.getElementById('planner').classList.contains('active') && currentSessions.length > 0) drawSpacedRepetitionCurve(currentSessions); });
1271
  </script>
1272
  </body>
1273
  </html>