Spaces:
Sleeping
Sleeping
Upload templates/index.html with huggingface_hub
Browse files- 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
|
| 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 |
-
<
|
| 945 |
-
|
| 946 |
-
|
| 947 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 948 |
</div>
|
| 949 |
-
|
| 950 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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>
|