Spaces:
Sleeping
Sleeping
Upload templates/index.html with huggingface_hub
Browse files- templates/index.html +274 -201
templates/index.html
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="fr">
|
| 3 |
<head>
|
|
@@ -126,7 +128,6 @@
|
|
| 126 |
.btn-secondary:hover { background: var(--gray-50); }
|
| 127 |
.btn-large { padding: 12px 24px; font-size: 0.9rem; }
|
| 128 |
|
| 129 |
-
/* Planning Stats Cards */
|
| 130 |
.planning-stats {
|
| 131 |
display: grid;
|
| 132 |
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
@@ -154,7 +155,6 @@
|
|
| 154 |
margin-top: 4px;
|
| 155 |
}
|
| 156 |
|
| 157 |
-
/* Planning Table */
|
| 158 |
.planning-sessions-table {
|
| 159 |
width: 100%;
|
| 160 |
border-collapse: collapse;
|
|
@@ -288,7 +288,6 @@
|
|
| 288 |
.tab-content { display: none; }
|
| 289 |
.tab-content.active { display: block; }
|
| 290 |
|
| 291 |
-
/* Spaced Repetition Curve */
|
| 292 |
.spaced-repetition-container {
|
| 293 |
background: linear-gradient(135deg, #fafafa 0%, #f0f0f0 100%);
|
| 294 |
border-radius: 16px;
|
|
@@ -328,7 +327,6 @@
|
|
| 328 |
.export-btn { background: white; border: 1px solid var(--gray-200); padding: 8px 16px; border-radius: 8px; cursor: pointer; }
|
| 329 |
.export-btn:hover { background: var(--gray-50); }
|
| 330 |
|
| 331 |
-
/* Profile specific styles */
|
| 332 |
.profile-progress {
|
| 333 |
background: var(--gray-100);
|
| 334 |
border-radius: 12px;
|
|
@@ -396,7 +394,6 @@
|
|
| 396 |
.profile-mini-card .value { font-size: 1.5rem; font-weight: 700; color: var(--primary); }
|
| 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;
|
|
@@ -504,7 +501,6 @@
|
|
| 504 |
filter: brightness(0.96);
|
| 505 |
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
| 506 |
}
|
| 507 |
-
/* Palette élégante en niveaux de gris */
|
| 508 |
.heatmap-cell.level-0 { background-color: #f5f5f5; }
|
| 509 |
.heatmap-cell.level-1 { background-color: #d6d6d6; }
|
| 510 |
.heatmap-cell.level-2 { background-color: #a0a0a0; }
|
|
@@ -544,7 +540,6 @@
|
|
| 544 |
.legend-color-box.level-3 { background-color: #6e6e6e; }
|
| 545 |
.legend-color-box.level-4 { background-color: #2c2c2c; }
|
| 546 |
|
| 547 |
-
/* Courbe symétrique */
|
| 548 |
.symmetry-curve-card {
|
| 549 |
background: white;
|
| 550 |
border-radius: 20px;
|
|
@@ -577,7 +572,6 @@
|
|
| 577 |
border-radius: 16px;
|
| 578 |
}
|
| 579 |
|
| 580 |
-
/* Indicateurs modernes */
|
| 581 |
.indicator-grid {
|
| 582 |
display: grid;
|
| 583 |
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
@@ -621,7 +615,6 @@
|
|
| 621 |
letter-spacing: -0.2px;
|
| 622 |
}
|
| 623 |
|
| 624 |
-
/* JaUgES circulaires */
|
| 625 |
.circular-progress {
|
| 626 |
position: relative;
|
| 627 |
width: 140px;
|
|
@@ -716,7 +709,6 @@
|
|
| 716 |
border-color: var(--primary);
|
| 717 |
}
|
| 718 |
|
| 719 |
-
/* Journal Section - Professionnel Noir & Blanc */
|
| 720 |
.journal-section {
|
| 721 |
background: var(--gray-50);
|
| 722 |
border-radius: 12px;
|
|
@@ -822,7 +814,6 @@
|
|
| 822 |
margin-top: 10px;
|
| 823 |
}
|
| 824 |
|
| 825 |
-
/* ========== STYLES POUR LE MESSAGE RÉFLEXIF IA ========== */
|
| 826 |
.ia-reflective-message {
|
| 827 |
background: linear-gradient(135deg, #f8f9fa 0%, #eef2f5 100%);
|
| 828 |
border-left: 5px solid var(--primary);
|
|
@@ -908,6 +899,137 @@
|
|
| 908 |
.ia-reflective-message { padding: 16px 20px; }
|
| 909 |
.ia-message-content { font-size: 0.85rem; }
|
| 910 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 911 |
</style>
|
| 912 |
</head>
|
| 913 |
<body>
|
|
@@ -957,53 +1079,73 @@
|
|
| 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 (
|
| 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">
|
| 969 |
-
|
| 970 |
-
</div>
|
| 971 |
-
<div>
|
| 972 |
-
<div class="ia-title">Message Réflexif </div>
|
| 973 |
-
</div>
|
| 974 |
</div>
|
| 975 |
<div class="ia-message-content">
|
| 976 |
-
Ton engagement est très positif : tu démarres <strong class="ia-stat-highlight">85%</strong> des activités, interagis fortement avec les ressources (<strong class="ia-stat-highlight">80%</strong>) et termines <strong class="ia-stat-highlight">74%</strong> des tâches. Ta régularité de travail (<strong class="ia-stat-highlight">90%</strong>) et ta gestion des échéances (<strong class="ia-stat-highlight">84%</strong>) montrent une excellente persévérance.
|
| 977 |
</div>
|
| 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 |
-
|
| 988 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 989 |
</div>
|
| 990 |
-
<div class="
|
| 991 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1001 |
-
</div>
|
| 1002 |
-
<
|
| 1003 |
-
|
| 1004 |
-
|
| 1005 |
-
|
| 1006 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1007 |
</div>
|
| 1008 |
</div>
|
| 1009 |
</div>
|
|
@@ -1018,42 +1160,15 @@
|
|
| 1018 |
</div>
|
| 1019 |
</section>
|
| 1020 |
|
| 1021 |
-
<!-- MON JOURNAL
|
| 1022 |
<section id="journal" class="tab-content">
|
| 1023 |
<div class="card">
|
| 1024 |
<div class="card-header"><h3><i class="far fa-edit"></i> Mon Journal d'apprentissage</h3></div>
|
| 1025 |
-
|
| 1026 |
-
<div class="
|
| 1027 |
-
|
| 1028 |
-
|
| 1029 |
-
</div>
|
| 1030 |
-
|
| 1031 |
-
<div class="journal-section">
|
| 1032 |
-
<h4><i class="fas fa-tasks"></i> Planifié (To-Do)</h4>
|
| 1033 |
-
<textarea id="plannedText" rows="2" placeholder="Ce que vous prévoyez d'apprendre..."></textarea>
|
| 1034 |
-
</div>
|
| 1035 |
-
<div class="journal-section">
|
| 1036 |
-
<h4><i class="fas fa-sync-alt"></i> En Cours (Doing)</h4>
|
| 1037 |
-
<textarea id="doingText" rows="2" placeholder="Ce que vous faites actuellement..."></textarea>
|
| 1038 |
-
</div>
|
| 1039 |
-
<div class="journal-section">
|
| 1040 |
-
<h4><i class="fas fa-check-circle"></i> Accompli (Done)</h4>
|
| 1041 |
-
<textarea id="doneText" rows="2" placeholder="Ce que vous avez accompli..."></textarea>
|
| 1042 |
-
<div class="self-rating-group">
|
| 1043 |
-
<span><i class="fas fa-star"></i> Auto-évaluation (1-5) :</span>
|
| 1044 |
-
<div class="rating-stars" id="selfRatingGroup">
|
| 1045 |
-
<button type="button" class="star-btn" data-rating="1">1</button>
|
| 1046 |
-
<button type="button" class="star-btn" data-rating="2">2</button>
|
| 1047 |
-
<button type="button" class="star-btn" data-rating="3">3</button>
|
| 1048 |
-
<button type="button" class="star-btn" data-rating="4">4</button>
|
| 1049 |
-
<button type="button" class="star-btn" data-rating="5">5</button>
|
| 1050 |
-
</div>
|
| 1051 |
-
</div>
|
| 1052 |
-
</div>
|
| 1053 |
-
<div class="journal-actions">
|
| 1054 |
-
<button class="btn btn-secondary" onclick="clearJournalForm()"><i class="fas fa-eraser"></i> Effacer le formulaire</button>
|
| 1055 |
-
<button class="btn btn-primary" onclick="saveJournalEntry()"><i class="fas fa-save"></i> Sauvegarder l'entrée</button>
|
| 1056 |
-
</div>
|
| 1057 |
<div class="journal-entries-list" id="journalEntriesList"></div>
|
| 1058 |
</div>
|
| 1059 |
</section>
|
|
@@ -1063,7 +1178,10 @@
|
|
| 1063 |
</div>
|
| 1064 |
|
| 1065 |
<script>
|
| 1066 |
-
//
|
|
|
|
|
|
|
|
|
|
| 1067 |
const competencesData = [
|
| 1068 |
{ "id": "D-MCONJ", "label": "Marques de la conjugaison", "category": "Discours" },
|
| 1069 |
{ "id": "D-PDV", "label": "Points de vue adoptés", "category": "Discours" },
|
|
@@ -1084,157 +1202,97 @@
|
|
| 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 |
-
|
| 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 |
-
|
| 1142 |
-
|
| 1143 |
-
|
| 1144 |
-
|
| 1145 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1146 |
}
|
| 1147 |
|
| 1148 |
-
function
|
| 1149 |
-
|
| 1150 |
-
|
| 1151 |
-
|
| 1152 |
-
|
| 1153 |
-
|
| 1154 |
-
|
| 1155 |
-
|
| 1156 |
-
|
| 1157 |
-
id: Date.now(),
|
| 1158 |
-
date: selectedDate,
|
| 1159 |
-
dateFormatted: new Date(selectedDate).toLocaleDateString('fr-FR'),
|
| 1160 |
-
planned,
|
| 1161 |
-
doing,
|
| 1162 |
-
done,
|
| 1163 |
-
selfRating
|
| 1164 |
-
};
|
| 1165 |
-
journalEntries.unshift(newEntry);
|
| 1166 |
-
localStorage.setItem('perseverAI_journal', JSON.stringify(journalEntries));
|
| 1167 |
-
document.getElementById('plannedText').value = "";
|
| 1168 |
-
document.getElementById('doingText').value = "";
|
| 1169 |
-
document.getElementById('doneText').value = "";
|
| 1170 |
-
document.querySelectorAll('#selfRatingGroup .star-btn').forEach(btn => btn.classList.remove('active'));
|
| 1171 |
-
renderJournalEntries();
|
| 1172 |
-
showToast("Journal sauvegardé");
|
| 1173 |
}
|
| 1174 |
|
| 1175 |
-
function
|
| 1176 |
-
|
| 1177 |
-
document.getElementById('
|
| 1178 |
-
document.getElementById('
|
| 1179 |
-
|
| 1180 |
-
|
| 1181 |
-
|
| 1182 |
-
|
|
|
|
| 1183 |
}
|
| 1184 |
|
| 1185 |
-
function
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
|
| 1189 |
-
|
| 1190 |
-
|
| 1191 |
-
}
|
| 1192 |
-
container.innerHTML = journalEntries.map(entry => `<div class="journal-entry-item">
|
| 1193 |
-
<div class="journal-entry-date"><i class="far fa-calendar-alt"></i> ${entry.dateFormatted || entry.date}</div>
|
| 1194 |
-
${entry.planned ? `<div><span class="journal-badge"><i class="fas fa-tasks"></i> Planifié</span> ${escapeHtml(entry.planned)}</div>` : ''}
|
| 1195 |
-
${entry.doing ? `<div><span class="journal-badge"><i class="fas fa-sync-alt"></i> En cours</span> ${escapeHtml(entry.doing)}</div>` : ''}
|
| 1196 |
-
${entry.done ? `<div><span class="journal-badge"><i class="fas fa-check-circle"></i> Accompli</span> ${escapeHtml(entry.done)}</div>` : ''}
|
| 1197 |
-
${entry.selfRating ? `<div><span class="journal-badge"><i class="fas fa-star"></i> Auto-évaluation</span> ${entry.selfRating}/5</div>` : ''}
|
| 1198 |
-
</div>`).join('');
|
| 1199 |
}
|
| 1200 |
|
| 1201 |
-
function
|
|
|
|
|
|
|
|
|
|
| 1202 |
|
| 1203 |
-
function
|
| 1204 |
-
|
| 1205 |
-
|
| 1206 |
-
|
| 1207 |
-
|
| 1208 |
-
|
| 1209 |
-
}
|
| 1210 |
}
|
| 1211 |
|
| 1212 |
-
/
|
|
|
|
| 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)
|
| 1215 |
-
function drawSymmetryCurve() { const canvas = document.getElementById('symmetryHeatmapCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const
|
| 1216 |
-
function updateCircularProgress(elementId, percent) { const circle = document.getElementById(elementId); if(!circle) return; const
|
| 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 =
|
| 1220 |
-
function
|
| 1221 |
-
function
|
| 1222 |
-
function
|
| 1223 |
-
function
|
| 1224 |
-
function
|
| 1225 |
-
function
|
| 1226 |
-
function saveProfileToStorage() { localStorage.setItem('perseverAI_profile', JSON.stringify(profileData)); }
|
| 1227 |
-
function loadProfileFromStorage() { const saved = localStorage.getItem('perseverAI_profile'); if(saved) Object.assign(profileData, JSON.parse(saved)); updateProfileUI(); }
|
| 1228 |
-
function retentionRate(days, strength = 0.5) { return Math.exp(-days / (strength * 30)) * 100; }
|
| 1229 |
-
function drawSpacedRepetitionCurve(sessions) { if(!sessions.length) return; const canvas = document.getElementById('spacedRepetitionCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const container = canvas.parentElement; const width = container.clientWidth - 40; const height = 180; canvas.width = width; canvas.height = height; ctx.clearRect(0,0,width,height); const startDate = sessions[0].date.getTime(); const endDate = sessions[sessions.length-1].date.getTime(); const totalDays = (endDate - startDate) / (86400000); ctx.beginPath(); ctx.strokeStyle = '#1e1e1e'; ctx.lineWidth = 2.5; let first = true; for(let x = 0; x <= width; x++) { const dayProg = (x/width)*totalDays; const ret = retentionRate(dayProg,0.4); const y = height - (ret/100)*height; if(first) { ctx.moveTo(x,y); first=false; } else ctx.lineTo(x,y); } ctx.stroke(); sessions.forEach(s => { const x = ((s.date.getTime() - startDate) / (endDate - startDate)) * width; const dayProg = (s.date.getTime() - startDate)/86400000; const retAt = retentionRate(dayProg,0.4); const y = height - (retAt/100)*height; ctx.beginPath(); ctx.fillStyle = '#424242'; ctx.arc(x, y, 5, 0, 2*Math.PI); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(x, y, 2, 0, 2*Math.PI); ctx.fill(); }); const datesDiv = document.getElementById('intervalDatesCurve'); if(datesDiv) datesDiv.innerHTML = sessions.map(s => `<span style="margin:0 8px">${s.dateStr}</span>`).join(''); }
|
| 1230 |
-
function generatePlanner() { const competence = document.getElementById('competence')?.value || "comprendre_mots"; const competenceLabel = competencesData.find(c=>c.id===competence)?.label || "Comprendre les mots"; const startDate = new Date(document.getElementById('start')?.value || "2024-01-15"); const sessionDur = document.getElementById('sessionDuration')?.value || "20"; const method = document.querySelector('input[name="productivityMethod"]:checked')?.value || "pomodoro"; const timeSlot = document.querySelector('#timeSlotsGroup input:checked')?.value || "08:00-10:00"; const retention = document.getElementById('retentionQuality')?.value || "2"; const intervals = [1,7,16,35,70,140]; const sessions = []; for(let i=0;i<intervals.length;i++) { let currentDate = new Date(startDate); if(i>0) currentDate.setDate(startDate.getDate() + intervals[i-1]); else currentDate = new Date(startDate); const sessionTypes = ["Apprentissage","Consolidation","Consolidation","Maintien","Maintien","Maintien"]; const strategies = ["Focus intense","Révision active","Révision active","Révision active","Révision active","Révision active"]; sessions.push({ id:i+1, date:new Date(currentDate), dateStr:currentDate.toLocaleDateString('fr-FR'), dayName:currentDate.toLocaleDateString('fr-FR',{weekday:'long'}), competence:competenceLabel, duration:sessionDur+" minutes", interval:intervals[i]+" jours", type:sessionTypes[i], strategy:strategies[i], productivity:method==='pomodoro'?'Pomodoro':'Deep Work', optimalTime:timeSlot }); } currentSessions = sessions; renderPlanningTable(sessions); updatePlanningStats(sessions, retention, sessionDur); drawSpacedRepetitionCurve(sessions); showToast("Planning personnalisé généré !"); showTab('planner'); }
|
| 1231 |
-
function generateDefaultPlanning() { const startDate = new Date("2024-01-17"); const intervals = [1,7,16,35,70,140]; const sessions = []; for(let i=0;i<intervals.length;i++) { let currentDate = new Date(startDate); if(i>0) currentDate.setDate(startDate.getDate() + (i===1?1:intervals[i-1])); else currentDate = new Date(startDate); const sessionTypes = ["Apprentissage","Consolidation","Consolidation","Maintien","Maintien","Maintien"]; sessions.push({ id:i+1, date:new Date(currentDate), dateStr:currentDate.toLocaleDateString('fr-FR'), dayName:currentDate.toLocaleDateString('fr-FR',{weekday:'long'}), competence:"comprendre_mots", duration:"20 minutes", interval:intervals[i]+" jours", type:sessionTypes[i], strategy:"Révision active", productivity:"Pomodoro", optimalTime:"08:00-10:00" }); } currentSessions = sessions; renderPlanningTable(sessions); updatePlanningStats(sessions,"2","20"); drawSpacedRepetitionCurve(sessions); }
|
| 1232 |
-
function renderPlanningTable(sessions) { const tbody = document.getElementById('sessionsTableBody'); if(!tbody) return; tbody.innerHTML = sessions.map(s => `<tr><td><strong>${s.dateStr}</strong><br><span style="font-size:0.7rem;">${s.dayName}</span></td><td><strong>R${s.id}</strong></td><td>${s.competence}</td><td>${s.duration}</td><td>${s.interval}</td><td><span class="session-type-badge ${s.type==='Apprentissage'?'type-apprentissage':(s.type==='Consolidation'?'type-consolidation':'type-maintien')}">${s.type}</span></td><td>${s.strategy}</td><td>${s.productivity}</td><td>${s.optimalTime}</td></tr>`).join(''); }
|
| 1233 |
-
function updatePlanningStats(sessions, quality, duration) { document.getElementById('statSessions').innerText = sessions.length; if(sessions.length > 0) { const lastDate = sessions[sessions.length-1].date; const firstDate = sessions[0].date; const totalDays = Math.ceil((lastDate - firstDate) / (86400000)); document.getElementById('statTotalDays').innerText = totalDays; } const qualityMap = {'0':'1/4','1':'2/4','2':'3/4','3':'4/4'}; document.getElementById('statQuality').innerText = qualityMap[quality] || "3/4"; document.getElementById('statDuration').innerText = duration + "min"; }
|
| 1234 |
-
function exportPlanning() { let csv = "Date,Session,Compétence,Durée,Intervalle,Type,Stratégie,Productivité,Heure Optimale\n"; currentSessions.forEach(s => { csv += `"${s.dateStr}","R${s.id}","${s.competence}","${s.duration}","${s.interval}","${s.type}","${s.strategy}","${s.productivity}","${s.optimalTime}"\n`; }); const blob = new Blob([csv], {type: 'text/csv'}); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'planning_perseverai.csv'; link.click(); showToast("Planning exporté en CSV"); }
|
| 1235 |
function generateRandomEvent() { const template = eventTemplates[Math.floor(Math.random() * eventTemplates.length)]; const newEvent = { id: Date.now() + Math.random(), date: new Date().toLocaleString(), type: template.type, title: template.title, message: template.message, priority: template.priority, status: 'pending' }; eventsList.unshift(newEvent); saveEvents(); renderEventsTable(); updateEventStats(); showToast(`Nouvel événement: ${template.title}`); }
|
| 1236 |
-
function executeAction(eventId, actionType, actionLabel) { const
|
| 1237 |
-
function renderEventsTable() { const tbody = document.getElementById('notificationsTableBody'); if(!tbody) return; if(eventsList.length === 0) { tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;">Aucun événement</td></tr>'; return; } tbody.innerHTML = eventsList.map(
|
| 1238 |
function updateEventStats() { document.getElementById('totalEvents').innerText = eventsList.length; document.getElementById('pendingEvents').innerText = eventsList.filter(e => e.status === 'pending').length; document.getElementById('acceptedEvents').innerText = eventsList.filter(e => e.status === 'accepted').length; if(efficiencyChart) efficiencyChart.data.datasets[0].data = [eventsList.filter(e=>e.status==='accepted').length, eventsList.filter(e=>e.status==='rejected').length, eventsList.filter(e=>e.status==='pending').length]; efficiencyChart?.update(); }
|
| 1239 |
let efficiencyChart = null;
|
| 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 } }); }
|
|
@@ -1242,6 +1300,20 @@
|
|
| 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', () => {
|
| 1247 |
initCompetencesSelect();
|
|
@@ -1262,12 +1334,13 @@
|
|
| 1262 |
updateCircularProgress('regularityCircleProgress', 90);
|
| 1263 |
updateCircularProgress('deadlineCircleProgress', 88);
|
| 1264 |
const todayDate = new Date().toISOString().split('T')[0];
|
| 1265 |
-
|
| 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>
|
|
|
|
|
|
| 1 |
+
Voici le code HTML global modifié. J'ai uniquement retravaillé les deux sections "Analyse audio" (Heatmap d'activité et Radar d'engagement) pour les rendre plus élégantes et professionnelles, tout en réduisant la taille des auteurs (crédits) qui sont désormais plus discrets.
|
| 2 |
+
```html
|
| 3 |
<!DOCTYPE html>
|
| 4 |
<html lang="fr">
|
| 5 |
<head>
|
|
|
|
| 128 |
.btn-secondary:hover { background: var(--gray-50); }
|
| 129 |
.btn-large { padding: 12px 24px; font-size: 0.9rem; }
|
| 130 |
|
|
|
|
| 131 |
.planning-stats {
|
| 132 |
display: grid;
|
| 133 |
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
|
|
| 155 |
margin-top: 4px;
|
| 156 |
}
|
| 157 |
|
|
|
|
| 158 |
.planning-sessions-table {
|
| 159 |
width: 100%;
|
| 160 |
border-collapse: collapse;
|
|
|
|
| 288 |
.tab-content { display: none; }
|
| 289 |
.tab-content.active { display: block; }
|
| 290 |
|
|
|
|
| 291 |
.spaced-repetition-container {
|
| 292 |
background: linear-gradient(135deg, #fafafa 0%, #f0f0f0 100%);
|
| 293 |
border-radius: 16px;
|
|
|
|
| 327 |
.export-btn { background: white; border: 1px solid var(--gray-200); padding: 8px 16px; border-radius: 8px; cursor: pointer; }
|
| 328 |
.export-btn:hover { background: var(--gray-50); }
|
| 329 |
|
|
|
|
| 330 |
.profile-progress {
|
| 331 |
background: var(--gray-100);
|
| 332 |
border-radius: 12px;
|
|
|
|
| 394 |
.profile-mini-card .value { font-size: 1.5rem; font-weight: 700; color: var(--primary); }
|
| 395 |
.flex-between { display: flex; justify-content: space-between; align-items: center; }
|
| 396 |
|
|
|
|
| 397 |
.dashboard-two-columns {
|
| 398 |
display: flex;
|
| 399 |
flex-wrap: wrap;
|
|
|
|
| 501 |
filter: brightness(0.96);
|
| 502 |
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
| 503 |
}
|
|
|
|
| 504 |
.heatmap-cell.level-0 { background-color: #f5f5f5; }
|
| 505 |
.heatmap-cell.level-1 { background-color: #d6d6d6; }
|
| 506 |
.heatmap-cell.level-2 { background-color: #a0a0a0; }
|
|
|
|
| 540 |
.legend-color-box.level-3 { background-color: #6e6e6e; }
|
| 541 |
.legend-color-box.level-4 { background-color: #2c2c2c; }
|
| 542 |
|
|
|
|
| 543 |
.symmetry-curve-card {
|
| 544 |
background: white;
|
| 545 |
border-radius: 20px;
|
|
|
|
| 572 |
border-radius: 16px;
|
| 573 |
}
|
| 574 |
|
|
|
|
| 575 |
.indicator-grid {
|
| 576 |
display: grid;
|
| 577 |
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
|
|
| 615 |
letter-spacing: -0.2px;
|
| 616 |
}
|
| 617 |
|
|
|
|
| 618 |
.circular-progress {
|
| 619 |
position: relative;
|
| 620 |
width: 140px;
|
|
|
|
| 709 |
border-color: var(--primary);
|
| 710 |
}
|
| 711 |
|
|
|
|
| 712 |
.journal-section {
|
| 713 |
background: var(--gray-50);
|
| 714 |
border-radius: 12px;
|
|
|
|
| 814 |
margin-top: 10px;
|
| 815 |
}
|
| 816 |
|
|
|
|
| 817 |
.ia-reflective-message {
|
| 818 |
background: linear-gradient(135deg, #f8f9fa 0%, #eef2f5 100%);
|
| 819 |
border-left: 5px solid var(--primary);
|
|
|
|
| 899 |
.ia-reflective-message { padding: 16px 20px; }
|
| 900 |
.ia-message-content { font-size: 0.85rem; }
|
| 901 |
}
|
| 902 |
+
|
| 903 |
+
/* Styles élégants et professionnels pour les lecteurs audio (réduction auteurs) */
|
| 904 |
+
.audio-modern-card {
|
| 905 |
+
background: #ffffff;
|
| 906 |
+
border-radius: 20px;
|
| 907 |
+
padding: 12px 16px;
|
| 908 |
+
margin-top: 20px;
|
| 909 |
+
border: 1px solid #eaedf2;
|
| 910 |
+
transition: all 0.2s ease;
|
| 911 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
|
| 912 |
+
}
|
| 913 |
+
.audio-modern-card:hover {
|
| 914 |
+
border-color: #d0d5dd;
|
| 915 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
| 916 |
+
}
|
| 917 |
+
.audio-modern-header {
|
| 918 |
+
display: flex;
|
| 919 |
+
align-items: center;
|
| 920 |
+
gap: 10px;
|
| 921 |
+
margin-bottom: 12px;
|
| 922 |
+
}
|
| 923 |
+
.audio-modern-header i {
|
| 924 |
+
font-size: 0.9rem;
|
| 925 |
+
color: #3a3a3a;
|
| 926 |
+
background: #f5f7fa;
|
| 927 |
+
width: 30px;
|
| 928 |
+
height: 30px;
|
| 929 |
+
display: flex;
|
| 930 |
+
align-items: center;
|
| 931 |
+
justify-content: center;
|
| 932 |
+
border-radius: 30px;
|
| 933 |
+
}
|
| 934 |
+
.audio-modern-header span {
|
| 935 |
+
font-weight: 500;
|
| 936 |
+
font-size: 0.75rem;
|
| 937 |
+
color: #2c3e50;
|
| 938 |
+
letter-spacing: -0.1px;
|
| 939 |
+
}
|
| 940 |
+
.audio-modern-controls {
|
| 941 |
+
display: flex;
|
| 942 |
+
align-items: center;
|
| 943 |
+
gap: 12px;
|
| 944 |
+
flex-wrap: wrap;
|
| 945 |
+
}
|
| 946 |
+
.audio-play-btn-mini {
|
| 947 |
+
background: #1e1e1e;
|
| 948 |
+
border: none;
|
| 949 |
+
width: 36px;
|
| 950 |
+
height: 36px;
|
| 951 |
+
border-radius: 36px;
|
| 952 |
+
color: white;
|
| 953 |
+
cursor: pointer;
|
| 954 |
+
transition: all 0.2s;
|
| 955 |
+
display: flex;
|
| 956 |
+
align-items: center;
|
| 957 |
+
justify-content: center;
|
| 958 |
+
font-size: 0.8rem;
|
| 959 |
+
}
|
| 960 |
+
.audio-play-btn-mini:hover {
|
| 961 |
+
background: #000000;
|
| 962 |
+
transform: scale(0.98);
|
| 963 |
+
}
|
| 964 |
+
.audio-progress-modern {
|
| 965 |
+
flex: 1;
|
| 966 |
+
min-width: 140px;
|
| 967 |
+
}
|
| 968 |
+
.audio-progress-bar-modern {
|
| 969 |
+
width: 100%;
|
| 970 |
+
height: 4px;
|
| 971 |
+
background: #e2e8f0;
|
| 972 |
+
border-radius: 10px;
|
| 973 |
+
cursor: pointer;
|
| 974 |
+
position: relative;
|
| 975 |
+
overflow: hidden;
|
| 976 |
+
}
|
| 977 |
+
.audio-progress-fill-modern {
|
| 978 |
+
width: 0%;
|
| 979 |
+
height: 100%;
|
| 980 |
+
background: #1e1e1e;
|
| 981 |
+
border-radius: 10px;
|
| 982 |
+
transition: width 0.1s linear;
|
| 983 |
+
}
|
| 984 |
+
.audio-time-modern {
|
| 985 |
+
font-size: 0.65rem;
|
| 986 |
+
color: #5b6e8c;
|
| 987 |
+
font-family: monospace;
|
| 988 |
+
min-width: 70px;
|
| 989 |
+
}
|
| 990 |
+
.audio-volume-mini {
|
| 991 |
+
display: flex;
|
| 992 |
+
align-items: center;
|
| 993 |
+
gap: 6px;
|
| 994 |
+
}
|
| 995 |
+
.audio-volume-mini i {
|
| 996 |
+
font-size: 0.7rem;
|
| 997 |
+
color: #5b6e8c;
|
| 998 |
+
cursor: pointer;
|
| 999 |
+
}
|
| 1000 |
+
.volume-slider-mini {
|
| 1001 |
+
width: 60px;
|
| 1002 |
+
height: 3px;
|
| 1003 |
+
-webkit-appearance: none;
|
| 1004 |
+
background: #e2e8f0;
|
| 1005 |
+
border-radius: 3px;
|
| 1006 |
+
}
|
| 1007 |
+
.volume-slider-mini::-webkit-slider-thumb {
|
| 1008 |
+
-webkit-appearance: none;
|
| 1009 |
+
width: 8px;
|
| 1010 |
+
height: 8px;
|
| 1011 |
+
border-radius: 50%;
|
| 1012 |
+
background: #1e1e1e;
|
| 1013 |
+
cursor: pointer;
|
| 1014 |
+
}
|
| 1015 |
+
.audio-credit-micro {
|
| 1016 |
+
text-align: right;
|
| 1017 |
+
font-size: 0.55rem;
|
| 1018 |
+
color: #94a3b8;
|
| 1019 |
+
margin-top: 6px;
|
| 1020 |
+
letter-spacing: 0.2px;
|
| 1021 |
+
border-top: none;
|
| 1022 |
+
padding-top: 2px;
|
| 1023 |
+
opacity: 0.6;
|
| 1024 |
+
}
|
| 1025 |
+
.audio-credit-micro i {
|
| 1026 |
+
font-size: 0.5rem;
|
| 1027 |
+
margin-right: 2px;
|
| 1028 |
+
}
|
| 1029 |
+
@media (max-width: 640px) {
|
| 1030 |
+
.audio-modern-controls { flex-direction: column; align-items: stretch; }
|
| 1031 |
+
.audio-progress-modern { width: 100%; }
|
| 1032 |
+
}
|
| 1033 |
</style>
|
| 1034 |
</head>
|
| 1035 |
<body>
|
|
|
|
| 1079 |
<!-- PLANNING -->
|
| 1080 |
<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>
|
| 1081 |
|
| 1082 |
+
<!-- DASHBOARD (version élégante avec audio credit réduit) -->
|
| 1083 |
<section id="dashboard" class="tab-content">
|
| 1084 |
<div class="card">
|
| 1085 |
<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>
|
| 1086 |
|
|
|
|
| 1087 |
<div class="ia-reflective-message">
|
| 1088 |
<div class="ia-message-header">
|
| 1089 |
+
<div class="ia-avatar"><i class="fas fa-brain"></i></div>
|
| 1090 |
+
<div><div class="ia-title">Message Réflexif </div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1091 |
</div>
|
| 1092 |
<div class="ia-message-content">
|
| 1093 |
+
Ton engagement est très positif : tu démarres <strong class="ia-stat-highlight">85%</strong> des activités, interagis fortement avec les ressources (<strong class="ia-stat-highlight">80%</strong>) et termines <strong class="ia-stat-highlight">74%</strong> des tâches. Ta régularité de travail (<strong class="ia-stat-highlight">90%</strong>) et ta gestion des échéances (<strong class="ia-stat-highlight">84%</strong>) montrent une excellente persévérance.
|
| 1094 |
</div>
|
| 1095 |
</div>
|
|
|
|
| 1096 |
|
|
|
|
| 1097 |
<div class="dashboard-two-columns">
|
|
|
|
| 1098 |
<div class="heatmap-wrapper">
|
| 1099 |
<div class="heatmap-container">
|
| 1100 |
+
<div class="heatmap-title"><h4><i class="fas fa-fire" style="color:#f97316;"></i> Heatmap d'activité – 4 semaines</h4><span class="heatmap-sub">Intensité d'engagement quotidien</span></div>
|
| 1101 |
+
<div class="heatmap-grid-wrapper"><div class="heatmap-grid" id="heatmapGrid"></div></div>
|
| 1102 |
+
<div class="legend-heatmap" id="heatmapLegend"></div>
|
| 1103 |
+
</div>
|
| 1104 |
+
<!-- Lecteur audio Heatmap élégant et discret (crédit réduit) -->
|
| 1105 |
+
<div class="audio-modern-card" id="heatmapAudioCard">
|
| 1106 |
+
<div class="audio-modern-header">
|
| 1107 |
+
<i class="fas fa-chart-line"></i>
|
| 1108 |
+
<span>Analyse audio · Heatmap d'activité</span>
|
| 1109 |
</div>
|
| 1110 |
+
<div class="audio-modern-controls">
|
| 1111 |
+
<button class="audio-play-btn-mini" onclick="toggleAudio('heatmapAudio')"><i class="fas fa-play" id="heatmapPlayIcon"></i></button>
|
| 1112 |
+
<div class="audio-progress-modern">
|
| 1113 |
+
<div class="audio-progress-bar-modern" id="heatmapProgressBar" onclick="seekAudio('heatmapAudio', event)">
|
| 1114 |
+
<div class="audio-progress-fill-modern" id="heatmapProgressFill"></div>
|
| 1115 |
+
</div>
|
| 1116 |
+
</div>
|
| 1117 |
+
<div class="audio-time-modern"><span id="heatmapCurrentTime">0:00</span> / <span id="heatmapDuration">0:00</span></div>
|
| 1118 |
+
<div class="audio-volume-mini">
|
| 1119 |
+
<i class="fas fa-volume-up" onclick="toggleMute('heatmapAudio')"></i>
|
| 1120 |
+
<input type="range" class="volume-slider-mini" id="heatmapVolume" min="0" max="1" step="0.01" value="0.7" onchange="setVolume('heatmapAudio', this.value)">
|
| 1121 |
+
</div>
|
| 1122 |
</div>
|
|
|
|
| 1123 |
</div>
|
| 1124 |
</div>
|
| 1125 |
|
|
|
|
| 1126 |
<div class="radar-wrapper">
|
| 1127 |
+
<div class="radar-title"><i class="fas fa-chart-pie"></i> Analyse de l'engagement : Indicateurs de comportement d'apprentissage</div>
|
| 1128 |
+
<div class="radar-container"><canvas id="engagementRadarChart" width="400" height="400" style="max-width:100%; height:auto;"></canvas></div>
|
| 1129 |
+
<div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 16px; font-size: 0.7rem; color: var(--gray-600);"></div>
|
| 1130 |
+
<!-- Lecteur audio Radar élégant -->
|
| 1131 |
+
<div class="audio-modern-card" id="radarAudioCard">
|
| 1132 |
+
<div class="audio-modern-header">
|
| 1133 |
+
<i class="fas fa-chart-pie"></i>
|
| 1134 |
+
<span>Analyse audio · Radar d'engagement</span>
|
| 1135 |
+
</div>
|
| 1136 |
+
<div class="audio-modern-controls">
|
| 1137 |
+
<button class="audio-play-btn-mini" onclick="toggleAudio('radarAudio')"><i class="fas fa-play" id="radarPlayIcon"></i></button>
|
| 1138 |
+
<div class="audio-progress-modern">
|
| 1139 |
+
<div class="audio-progress-bar-modern" id="radarProgressBar" onclick="seekAudio('radarAudio', event)">
|
| 1140 |
+
<div class="audio-progress-fill-modern" id="radarProgressFill"></div>
|
| 1141 |
+
</div>
|
| 1142 |
+
</div>
|
| 1143 |
+
<div class="audio-time-modern"><span id="radarCurrentTime">0:00</span> / <span id="radarDuration">0:00</span></div>
|
| 1144 |
+
<div class="audio-volume-mini">
|
| 1145 |
+
<i class="fas fa-volume-up" onclick="toggleMute('radarAudio')"></i>
|
| 1146 |
+
<input type="range" class="volume-slider-mini" id="radarVolume" min="0" max="1" step="0.01" value="0.7" onchange="setVolume('radarAudio', this.value)">
|
| 1147 |
+
</div>
|
| 1148 |
+
</div>
|
| 1149 |
</div>
|
| 1150 |
</div>
|
| 1151 |
</div>
|
|
|
|
| 1160 |
</div>
|
| 1161 |
</section>
|
| 1162 |
|
| 1163 |
+
<!-- MON JOURNAL -->
|
| 1164 |
<section id="journal" class="tab-content">
|
| 1165 |
<div class="card">
|
| 1166 |
<div class="card-header"><h3><i class="far fa-edit"></i> Mon Journal d'apprentissage</h3></div>
|
| 1167 |
+
<div class="date-selector"><label><i class="far fa-calendar-alt"></i> Date de l'entrée :</label><input type="date" id="journalDate" class="form-control" style="width: auto;" value=""></div>
|
| 1168 |
+
<div class="journal-section"><h4><i class="fas fa-tasks"></i> Planifié (To-Do)</h4><textarea id="plannedText" rows="2" placeholder="Ce que vous prévoyez d'apprendre..."></textarea></div>
|
| 1169 |
+
<div class="journal-section"><h4><i class="fas fa-sync-alt"></i> En Cours (Doing)</h4><textarea id="doingText" rows="2" placeholder="Ce que vous faites actuellement..."></textarea></div>
|
| 1170 |
+
<div class="journal-section"><h4><i class="fas fa-check-circle"></i> Accompli (Done)</h4><textarea id="doneText" rows="2" placeholder="Ce que vous avez accompli..."></textarea><div class="self-rating-group"><span><i class="fas fa-star"></i> Auto-évaluation (1-5) :</span><div class="rating-stars" id="selfRatingGroup"><button type="button" class="star-btn" data-rating="1">1</button><button type="button" class="star-btn" data-rating="2">2</button><button type="button" class="star-btn" data-rating="3">3</button><button type="button" class="star-btn" data-rating="4">4</button><button type="button" class="star-btn" data-rating="5">5</button></div></div></div>
|
| 1171 |
+
<div class="journal-actions"><button class="btn btn-secondary" onclick="clearJournalForm()"><i class="fas fa-eraser"></i> Effacer le formulaire</button><button class="btn btn-primary" onclick="saveJournalEntry()"><i class="fas fa-save"></i> Sauvegarder l'entrée</button></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1172 |
<div class="journal-entries-list" id="journalEntriesList"></div>
|
| 1173 |
</div>
|
| 1174 |
</section>
|
|
|
|
| 1178 |
</div>
|
| 1179 |
|
| 1180 |
<script>
|
| 1181 |
+
// Variables globales pour les lecteurs audio
|
| 1182 |
+
let heatmapAudio = null;
|
| 1183 |
+
let radarAudio = null;
|
| 1184 |
+
|
| 1185 |
const competencesData = [
|
| 1186 |
{ "id": "D-MCONJ", "label": "Marques de la conjugaison", "category": "Discours" },
|
| 1187 |
{ "id": "D-PDV", "label": "Points de vue adoptés", "category": "Discours" },
|
|
|
|
| 1202 |
let profileData = { extraversion: 3, openness: 3, conscientiousness: 4, agreeableness: 3, neuroticism: 3, intrinsic: 3, introjected: 4 };
|
| 1203 |
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" }];
|
| 1204 |
let journalEntries = [];
|
| 1205 |
+
const radarIndicators = ["Démarrage des activités", "Achèvement des activités","Taux d'erreur", "Tentatives de réponses","Éléments des connaissances validées", "Interactions avec les ressources","Score", "Niveau moyen des compétences"];
|
| 1206 |
+
const radarValues = [85, 74, 65, 78, 72, 80, 70, 68];
|
| 1207 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1208 |
function initRadarChart() {
|
| 1209 |
const ctx = document.getElementById('engagementRadarChart')?.getContext('2d');
|
| 1210 |
if(!ctx) return;
|
| 1211 |
if(radarChart) radarChart.destroy();
|
| 1212 |
radarChart = new Chart(ctx, {
|
| 1213 |
type: 'radar',
|
| 1214 |
+
data: { labels: radarIndicators, datasets: [{ label: 'Taux (%)', data: radarValues, backgroundColor: 'rgba(30, 30, 30, 0.2)', borderColor: '#1e1e1e', borderWidth: 2, pointBackgroundColor: '#1e1e1e', pointBorderColor: '#ffffff', pointRadius: 5, fill: true }] },
|
| 1215 |
+
options: { responsive: true, maintainAspectRatio: true, scales: { r: { beginAtZero: true, max: 100, ticks: { stepSize: 20 }, grid: { color: '#e0e0e0' }, pointLabels: { font: { size: 10 } } } }, plugins: { tooltip: { callbacks: { label: (ctx) => `${ctx.label}: ${ctx.raw}%` } }, legend: { display: false } } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1216 |
});
|
| 1217 |
}
|
| 1218 |
|
| 1219 |
+
function initAudioPlayers() {
|
| 1220 |
+
heatmapAudio = new Audio('heatmap.mp3');
|
| 1221 |
+
radarAudio = new Audio('radar.mp3');
|
| 1222 |
+
heatmapAudio.addEventListener('timeupdate', () => updateProgress('heatmap'));
|
| 1223 |
+
heatmapAudio.addEventListener('loadedmetadata', () => document.getElementById('heatmapDuration').innerText = formatTime(heatmapAudio.duration));
|
| 1224 |
+
heatmapAudio.addEventListener('ended', () => { const icon = document.getElementById('heatmapPlayIcon'); if(icon) icon.className = 'fas fa-play'; });
|
| 1225 |
+
radarAudio.addEventListener('timeupdate', () => updateProgress('radar'));
|
| 1226 |
+
radarAudio.addEventListener('loadedmetadata', () => document.getElementById('radarDuration').innerText = formatTime(radarAudio.duration));
|
| 1227 |
+
radarAudio.addEventListener('ended', () => { const icon = document.getElementById('radarPlayIcon'); if(icon) icon.className = 'fas fa-play'; });
|
| 1228 |
+
const heatVol = document.getElementById('heatmapVolume');
|
| 1229 |
+
const radVol = document.getElementById('radarVolume');
|
| 1230 |
+
if(heatVol) heatmapAudio.volume = parseFloat(heatVol.value);
|
| 1231 |
+
if(radVol) radarAudio.volume = parseFloat(radVol.value);
|
| 1232 |
}
|
| 1233 |
|
| 1234 |
+
function toggleAudio(audioId) {
|
| 1235 |
+
let audio = audioId === 'heatmapAudio' ? heatmapAudio : radarAudio;
|
| 1236 |
+
let playIcon = document.getElementById(audioId === 'heatmapAudio' ? 'heatmapPlayIcon' : 'radarPlayIcon');
|
| 1237 |
+
if(audio.paused) {
|
| 1238 |
+
if(audioId === 'heatmapAudio' && radarAudio && !radarAudio.paused) { radarAudio.pause(); const rIcon = document.getElementById('radarPlayIcon'); if(rIcon) rIcon.className = 'fas fa-play'; }
|
| 1239 |
+
else if(audioId === 'radarAudio' && heatmapAudio && !heatmapAudio.paused) { heatmapAudio.pause(); const hIcon = document.getElementById('heatmapPlayIcon'); if(hIcon) hIcon.className = 'fas fa-play'; }
|
| 1240 |
+
audio.play();
|
| 1241 |
+
if(playIcon) playIcon.className = 'fas fa-pause';
|
| 1242 |
+
} else { audio.pause(); if(playIcon) playIcon.className = 'fas fa-play'; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1243 |
}
|
| 1244 |
|
| 1245 |
+
function updateProgress(audioType) {
|
| 1246 |
+
let audio = audioType === 'heatmap' ? heatmapAudio : radarAudio;
|
| 1247 |
+
let fill = document.getElementById(audioType === 'heatmap' ? 'heatmapProgressFill' : 'radarProgressFill');
|
| 1248 |
+
let timeSpan = document.getElementById(audioType === 'heatmap' ? 'heatmapCurrentTime' : 'radarCurrentTime');
|
| 1249 |
+
if(audio && !isNaN(audio.duration) && audio.duration > 0) {
|
| 1250 |
+
let percent = (audio.currentTime / audio.duration) * 100;
|
| 1251 |
+
if(fill) fill.style.width = percent + '%';
|
| 1252 |
+
if(timeSpan) timeSpan.innerText = formatTime(audio.currentTime);
|
| 1253 |
+
}
|
| 1254 |
}
|
| 1255 |
|
| 1256 |
+
function seekAudio(audioId, event) {
|
| 1257 |
+
let audio = audioId === 'heatmapAudio' ? heatmapAudio : radarAudio;
|
| 1258 |
+
let rect = event.currentTarget.getBoundingClientRect();
|
| 1259 |
+
let x = event.clientX - rect.left;
|
| 1260 |
+
let percent = x / rect.width;
|
| 1261 |
+
if(audio && !isNaN(audio.duration) && audio.duration > 0) audio.currentTime = percent * audio.duration;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1262 |
}
|
| 1263 |
|
| 1264 |
+
function setVolume(audioId, value) {
|
| 1265 |
+
let audio = audioId === 'heatmapAudio' ? heatmapAudio : radarAudio;
|
| 1266 |
+
if(audio) audio.volume = parseFloat(value);
|
| 1267 |
+
}
|
| 1268 |
|
| 1269 |
+
function toggleMute(audioId) {
|
| 1270 |
+
let audio = audioId === 'heatmapAudio' ? heatmapAudio : radarAudio;
|
| 1271 |
+
let slider = document.getElementById(audioId === 'heatmapAudio' ? 'heatmapVolume' : 'radarVolume');
|
| 1272 |
+
if(audio) {
|
| 1273 |
+
if(audio.volume > 0) { audio.dataset.prevVolume = audio.volume; audio.volume = 0; if(slider) slider.value = 0; }
|
| 1274 |
+
else { audio.volume = parseFloat(audio.dataset.prevVolume || 0.7); if(slider) slider.value = audio.volume; }
|
| 1275 |
+
}
|
| 1276 |
}
|
| 1277 |
|
| 1278 |
+
function formatTime(seconds) { if(isNaN(seconds)) return "0:00"; let mins = Math.floor(seconds / 60); let secs = Math.floor(seconds % 60); return mins + ":" + (secs < 10 ? "0" + secs : secs); }
|
| 1279 |
+
|
| 1280 |
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 }; }
|
| 1281 |
+
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>`; }
|
| 1282 |
+
function drawSymmetryCurve() { const canvas = document.getElementById('symmetryHeatmapCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const width = canvas.parentElement.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; 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.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(); 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(); }); }
|
| 1283 |
+
function updateCircularProgress(elementId, percent) { const circle = document.getElementById(elementId); if(!circle) return; const r = 54; const circ = 2 * Math.PI * r; const offset = circ - (percent / 100) * circ; circle.style.strokeDasharray = `${circ}`; circle.style.strokeDashoffset = offset; }
|
| 1284 |
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"; }
|
| 1285 |
function setDashboardView(view) { if(view === 'combined') { renderHeatmap(); drawSymmetryCurve(); updateIndicators(); initRadarChart(); } }
|
| 1286 |
+
function initCompetencesSelect() { const select = document.getElementById('competence'); if(select) select.innerHTML = competencesData.map(c => `<option value="${c.id}">${c.label}</option>`).join(''); }
|
| 1287 |
+
function generatePlanner() { const competence = document.getElementById('competence')?.value || "comprendre_mots"; const competenceLabel = competencesData.find(c=>c.id===competence)?.label || "Comprendre les mots"; const startDate = new Date(document.getElementById('start')?.value || "2024-01-15"); const sessionDur = document.getElementById('sessionDuration')?.value || "20"; const method = document.querySelector('input[name="productivityMethod"]:checked')?.value || "pomodoro"; const timeSlot = document.querySelector('#timeSlotsGroup input:checked')?.value || "08:00-10:00"; const retention = document.getElementById('retentionQuality')?.value || "2"; const intervals = [1,7,16,35,70,140]; const sessions = []; for(let i=0;i<intervals.length;i++) { let currentDate = new Date(startDate); if(i>0) currentDate.setDate(startDate.getDate() + intervals[i-1]); else currentDate = new Date(startDate); const sessionTypes = ["Apprentissage","Consolidation","Consolidation","Maintien","Maintien","Maintien"]; sessions.push({ id:i+1, date:new Date(currentDate), dateStr:currentDate.toLocaleDateString('fr-FR'), competence:competenceLabel, duration:sessionDur+" minutes", interval:intervals[i]+" jours", type:sessionTypes[i], strategy:"Révision active", productivity:method==='pomodoro'?'Pomodoro':'Deep Work', optimalTime:timeSlot }); } currentSessions = sessions; renderPlanningTable(sessions); updatePlanningStats(sessions, retention, sessionDur); drawSpacedRepetitionCurve(sessions); showToast("Planning généré"); showTab('planner'); }
|
| 1288 |
+
function generateDefaultPlanning() { const startDate = new Date("2024-01-17"); const intervals = [1,7,16,35,70,140]; const sessions = []; for(let i=0;i<intervals.length;i++) { let currentDate = new Date(startDate); if(i>0) currentDate.setDate(startDate.getDate() + (i===1?1:intervals[i-1])); else currentDate = new Date(startDate); sessions.push({ id:i+1, date:new Date(currentDate), dateStr:currentDate.toLocaleDateString('fr-FR'), competence:"Comprendre les mots", duration:"20 minutes", interval:intervals[i]+" jours", type:i===0?"Apprentissage":"Consolidation", strategy:"Révision active", productivity:"Pomodoro", optimalTime:"08:00-10:00" }); } currentSessions = sessions; renderPlanningTable(sessions); updatePlanningStats(sessions,"2","20"); drawSpacedRepetitionCurve(sessions); }
|
| 1289 |
+
function renderPlanningTable(sessions) { const tbody = document.getElementById('sessionsTableBody'); if(tbody) tbody.innerHTML = sessions.map(s => `<tr><td><strong>${s.dateStr}</strong></td><td>R${s.id}</td><td>${s.competence}</td><td>${s.duration}</td><td>${s.interval}</td><td><span class="session-type-badge">${s.type}</span></td><td>${s.strategy}</td><td>${s.productivity}</td><td>${s.optimalTime}</td></tr>`).join(''); }
|
| 1290 |
+
function updatePlanningStats(sessions, quality, duration) { document.getElementById('statSessions').innerText = sessions.length; if(sessions.length) { const totalDays = Math.ceil((sessions[sessions.length-1].date - sessions[0].date) / 86400000); document.getElementById('statTotalDays').innerText = totalDays; } const qualityMap = {'0':'1/4','1':'2/4','2':'3/4','3':'4/4'}; document.getElementById('statQuality').innerText = qualityMap[quality] || "3/4"; document.getElementById('statDuration').innerText = duration + "min"; }
|
| 1291 |
+
function drawSpacedRepetitionCurve(sessions) { if(!sessions.length) return; const canvas = document.getElementById('spacedRepetitionCurve'); if(!canvas) return; const ctx = canvas.getContext('2d'); const width = canvas.parentElement.clientWidth - 40; const height = 180; canvas.width = width; canvas.height = height; ctx.clearRect(0,0,width,height); const start = sessions[0].date.getTime(); const end = sessions[sessions.length-1].date.getTime(); const totalDays = (end - start) / 86400000; ctx.beginPath(); ctx.strokeStyle = '#1e1e1e'; ctx.lineWidth = 2.5; let first = true; for(let x = 0; x <= width; x++) { const dayProg = (x/width)*totalDays; const ret = Math.exp(-dayProg / (0.4*30)) * 100; const y = height - (ret/100)*height; if(first) { ctx.moveTo(x,y); first=false; } else ctx.lineTo(x,y); } ctx.stroke(); sessions.forEach(s => { const x = ((s.date.getTime() - start) / (end - start)) * width; const dayProg = (s.date.getTime() - start)/86400000; const retAt = Math.exp(-dayProg / (0.4*30)) * 100; const y = height - (retAt/100)*height; ctx.beginPath(); ctx.fillStyle = '#424242'; ctx.arc(x, y, 5, 0, 2*Math.PI); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(x, y, 2, 0, 2*Math.PI); ctx.fill(); }); }
|
| 1292 |
+
function exportPlanning() { let csv = "Date,Session,Compétence,Durée,Intervalle,Type,Stratégie,Productivité,Heure Optimale\n"; currentSessions.forEach(s => { csv += `"${s.dateStr}","R${s.id}","${s.competence}","${s.duration}","${s.interval}","${s.type}","${s.strategy}","${s.productivity}","${s.optimalTime}"\n`; }); const blob = new Blob([csv], {type: 'text/csv'}); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'planning_perseverai.csv'; link.click(); showToast("Exporté CSV"); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1293 |
function generateRandomEvent() { const template = eventTemplates[Math.floor(Math.random() * eventTemplates.length)]; const newEvent = { id: Date.now() + Math.random(), date: new Date().toLocaleString(), type: template.type, title: template.title, message: template.message, priority: template.priority, status: 'pending' }; eventsList.unshift(newEvent); saveEvents(); renderEventsTable(); updateEventStats(); showToast(`Nouvel événement: ${template.title}`); }
|
| 1294 |
+
function executeAction(eventId, actionType, actionLabel) { const idx = eventsList.findIndex(e => e.id == eventId); if(idx === -1) return; eventsList[idx].status = actionType === 'ACCEPT' ? 'accepted' : (actionType === 'REJECT' ? 'rejected' : 'hold'); renderEventsTable(); updateEventStats(); saveEvents(); showToast(`${actionLabel}`); }
|
| 1295 |
+
function renderEventsTable() { const tbody = document.getElementById('notificationsTableBody'); if(!tbody) return; if(eventsList.length === 0) { tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;">Aucun événement</td></tr>'; return; } tbody.innerHTML = eventsList.map(e => `<tr><td>${e.date}</td><td>${e.type}</td><td><strong>${e.title}</strong></td><td>${e.message}</td><td><span class="priority-${e.priority}">${e.priority}</span></td><td><span class="status-badge ${e.status === 'accepted' ? 'status-accepted' : 'status-pending'}">${e.status === 'accepted' ? 'Accepté' : 'En attente'}</span></td><td class="action-cell"><button class="action-icon-btn action-accept" onclick="executeAction(${e.id}, 'ACCEPT', 'Accepté')">✓ Accepter</button><button class="action-icon-btn action-reject" onclick="executeAction(${e.id}, 'REJECT', 'Rejeté')">✗ Rejeter</button><button class="action-icon-btn action-hold" onclick="executeAction(${e.id}, 'HOLD', 'En attente')">⏸ Attente</button></td></tr>`).join(''); }
|
| 1296 |
function updateEventStats() { document.getElementById('totalEvents').innerText = eventsList.length; document.getElementById('pendingEvents').innerText = eventsList.filter(e => e.status === 'pending').length; document.getElementById('acceptedEvents').innerText = eventsList.filter(e => e.status === 'accepted').length; if(efficiencyChart) efficiencyChart.data.datasets[0].data = [eventsList.filter(e=>e.status==='accepted').length, eventsList.filter(e=>e.status==='rejected').length, eventsList.filter(e=>e.status==='pending').length]; efficiencyChart?.update(); }
|
| 1297 |
let efficiencyChart = null;
|
| 1298 |
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 } }); }
|
|
|
|
| 1300 |
function loadSavedData() { const saved = localStorage.getItem('eventsList'); if(saved) eventsList = JSON.parse(saved); }
|
| 1301 |
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(); } }
|
| 1302 |
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); }
|
| 1303 |
+
function setupRatingListeners() { document.querySelectorAll('.big5-rating .rating-btn, .motivation-rating .rating-btn').forEach(btn => btn.addEventListener('click', function() { const container = this.closest('.big5-rating, .motivation-rating'); const trait = container.dataset.trait || container.dataset.motivation; const value = parseInt(this.dataset.value); profileData[trait] = value; updateRatingUI(container, value); updateProfileUI(); saveProfileToStorage(); })); }
|
| 1304 |
+
function updateRatingUI(container, value) { container.querySelectorAll('.rating-btn').forEach(btn => { btn.classList.remove('active'); if(parseInt(btn.dataset.value) === value) btn.classList.add('active'); }); }
|
| 1305 |
+
function updateProfileUI() { document.getElementById('summaryExtraversion').innerText = profileData.extraversion; document.getElementById('summaryOpenness').innerText = profileData.openness; document.getElementById('summaryConscientiousness').innerText = profileData.conscientiousness; document.getElementById('summaryIntrinsic').innerText = profileData.intrinsic; document.getElementById('summaryIntrojected').innerText = profileData.introjected; document.getElementById('neuroticismScore').innerText = profileData.neuroticism; document.getElementById('introjectedScore').innerText = profileData.introjected; let completed = Object.values(profileData).filter(v => v).length; const percent = Math.round((completed / 7) * 100); document.getElementById('profileCompletionPercent').innerText = percent + '%'; document.getElementById('profileProgressFill').style.width = percent + '%'; document.getElementById('completedCount').innerText = Math.min(completed, 5); let level = "Débutant"; if(profileData.conscientiousness >= 4 && profileData.intrinsic >= 4) level = "Avancé"; else if(profileData.conscientiousness >= 3) level = "Intermédiaire"; document.getElementById('profileLevel').innerText = level; document.getElementById('recommendationsContainer').innerHTML = ["◼ Qualité du sommeil: Maintenez 7-8 heures.", "◼ Planification: Renforcez votre organisation."].map(r => `<p>${r}</p>`).join(''); }
|
| 1306 |
+
function autoFillProfile() { profileData = { extraversion: 4, openness: 4, conscientiousness: 5, agreeableness: 4, neuroticism: 2, intrinsic: 5, introjected: 3 }; document.querySelectorAll('.big5-rating, .motivation-rating').forEach(container => { const key = container.dataset.trait || container.dataset.motivation; if(profileData[key]) updateRatingUI(container, profileData[key]); }); updateProfileUI(); saveProfileToStorage(); showToast("Profil auto-rempli"); }
|
| 1307 |
+
function saveProfileData() { saveProfileToStorage(); showToast("Profil sauvegardé"); }
|
| 1308 |
+
function finalizeProfile() { saveProfileToStorage(); showToast("Profil finalisé"); }
|
| 1309 |
+
function saveProfileToStorage() { localStorage.setItem('perseverAI_profile', JSON.stringify(profileData)); }
|
| 1310 |
+
function loadProfileFromStorage() { const saved = localStorage.getItem('perseverAI_profile'); if(saved) Object.assign(profileData, JSON.parse(saved)); updateProfileUI(); }
|
| 1311 |
+
function loadJournalEntries() { const saved = localStorage.getItem('perseverAI_journal'); if(saved) journalEntries = JSON.parse(saved); renderJournalEntries(); }
|
| 1312 |
+
function saveJournalEntry() { const selectedDate = document.getElementById('journalDate')?.value || new Date().toISOString().split('T')[0]; const planned = document.getElementById('plannedText')?.value.trim() || ""; const doing = document.getElementById('doingText')?.value.trim() || ""; const done = document.getElementById('doneText')?.value.trim() || ""; const activeRating = document.querySelector('#selfRatingGroup .star-btn.active'); let selfRating = activeRating ? parseInt(activeRating.dataset.rating) : null; if(!planned && !doing && !done) { showToast("Renseignez au moins un champ"); return; } const newEntry = { id: Date.now(), date: selectedDate, dateFormatted: new Date(selectedDate).toLocaleDateString('fr-FR'), planned, doing, done, selfRating }; journalEntries.unshift(newEntry); localStorage.setItem('perseverAI_journal', JSON.stringify(journalEntries)); document.getElementById('plannedText').value = ""; document.getElementById('doingText').value = ""; document.getElementById('doneText').value = ""; document.querySelectorAll('#selfRatingGroup .star-btn').forEach(btn => btn.classList.remove('active')); renderJournalEntries(); showToast("Journal sauvegardé"); }
|
| 1313 |
+
function clearJournalForm() { document.getElementById('plannedText').value = ""; document.getElementById('doingText').value = ""; document.getElementById('doneText').value = ""; document.querySelectorAll('#selfRatingGroup .star-btn').forEach(btn => btn.classList.remove('active')); document.getElementById('journalDate').value = new Date().toISOString().split('T')[0]; showToast("Formulaire effacé"); }
|
| 1314 |
+
function renderJournalEntries() { const container = document.getElementById('journalEntriesList'); if(!container) return; if(journalEntries.length === 0) { container.innerHTML = '<div style="text-align:center; color:var(--gray-500); padding:20px;"><i class="fas fa-book-open"></i> Aucune entrée</div>'; return; } container.innerHTML = journalEntries.map(entry => `<div class="journal-entry-item"><div class="journal-entry-date"><i class="far fa-calendar-alt"></i> ${entry.dateFormatted || entry.date}</div>${entry.planned ? `<div><span class="journal-badge"><i class="fas fa-tasks"></i> Planifié</span> ${escapeHtml(entry.planned)}</div>` : ''}${entry.doing ? `<div><span class="journal-badge"><i class="fas fa-sync-alt"></i> En cours</span> ${escapeHtml(entry.doing)}</div>` : ''}${entry.done ? `<div><span class="journal-badge"><i class="fas fa-check-circle"></i> Accompli</span> ${escapeHtml(entry.done)}</div>` : ''}${entry.selfRating ? `<div><span class="journal-badge"><i class="fas fa-star"></i> Auto-évaluation</span> ${entry.selfRating}/5</div>` : ''}</div>`).join(''); }
|
| 1315 |
+
function escapeHtml(str) { return str.replace(/[&<>]/g, function(m){ if(m==='&') return '&'; if(m==='<') return '<'; if(m==='>') return '>'; return m;}); }
|
| 1316 |
+
function setupRatingStars() { document.querySelectorAll('#selfRatingGroup .star-btn').forEach(btn => { btn.addEventListener('click', function() { document.querySelectorAll('#selfRatingGroup .star-btn').forEach(b => b.classList.remove('active')); this.classList.add('active'); }); }); }
|
| 1317 |
|
| 1318 |
document.addEventListener('DOMContentLoaded', () => {
|
| 1319 |
initCompetencesSelect();
|
|
|
|
| 1334 |
updateCircularProgress('regularityCircleProgress', 90);
|
| 1335 |
updateCircularProgress('deadlineCircleProgress', 88);
|
| 1336 |
const todayDate = new Date().toISOString().split('T')[0];
|
| 1337 |
+
if(document.getElementById('journalDate')) document.getElementById('journalDate').value = todayDate;
|
|
|
|
| 1338 |
initRadarChart();
|
| 1339 |
+
initAudioPlayers();
|
| 1340 |
});
|
| 1341 |
function initProfileModule() { loadProfileFromStorage(); setupRatingListeners(); updateProfileUI(); }
|
| 1342 |
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); });
|
| 1343 |
</script>
|
| 1344 |
</body>
|
| 1345 |
+
</html>
|
| 1346 |
+
```
|