OUAREDAEK commited on
Commit
48a3ebd
·
verified ·
1 Parent(s): 9083cba

Upload templates/index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. templates/index.html +1349 -0
templates/index.html ADDED
@@ -0,0 +1,1349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cognitive-Driven AI for Language Learning | Professionnel</title>
7
+
8
+ <!-- Chart.js pour les visualisations -->
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ <!-- Font Awesome pour les icônes -->
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+
13
+ <style>
14
+ :root {
15
+ --primary-color: #3b82f6;
16
+ --secondary-color: #10b981;
17
+ --accent-color: #8b5cf6;
18
+ --warning-color: #f59e0b;
19
+ --danger-color: #ef4444;
20
+ --dark-color: #1e293b;
21
+ --light-color: #f8fafc;
22
+ --gray-color: #64748b;
23
+ }
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
33
+ background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
34
+ color: var(--dark-color);
35
+ line-height: 1.6;
36
+ min-height: 100vh;
37
+ }
38
+
39
+ .container {
40
+ max-width: 1400px;
41
+ margin: 0 auto;
42
+ padding: 20px;
43
+ }
44
+
45
+ /* Header */
46
+ .header {
47
+ background: linear-gradient(135deg, var(--dark-color) 0%, #0f172a 100%);
48
+ color: white;
49
+ padding: 2rem;
50
+ border-radius: 15px;
51
+ margin-bottom: 2rem;
52
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
53
+ position: relative;
54
+ overflow: hidden;
55
+ }
56
+
57
+ .header::before {
58
+ content: '';
59
+ position: absolute;
60
+ top: 0;
61
+ left: 0;
62
+ right: 0;
63
+ height: 4px;
64
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color), var(--accent-color));
65
+ }
66
+
67
+ .header h1 {
68
+ font-size: 2.5rem;
69
+ margin-bottom: 0.5rem;
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 15px;
73
+ }
74
+
75
+ .header p {
76
+ font-size: 1.1rem;
77
+ opacity: 0.9;
78
+ max-width: 800px;
79
+ }
80
+
81
+ .badge {
82
+ background: var(--primary-color);
83
+ color: white;
84
+ padding: 5px 15px;
85
+ border-radius: 20px;
86
+ font-size: 0.9rem;
87
+ display: inline-block;
88
+ margin-top: 10px;
89
+ }
90
+
91
+ /* Navigation */
92
+ .nav-tabs {
93
+ display: flex;
94
+ background: white;
95
+ border-radius: 12px;
96
+ overflow: hidden;
97
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
98
+ margin-bottom: 2rem;
99
+ flex-wrap: wrap;
100
+ }
101
+
102
+ .nav-tab {
103
+ flex: 1;
104
+ min-width: 200px;
105
+ padding: 1.2rem;
106
+ border: none;
107
+ background: transparent;
108
+ color: var(--gray-color);
109
+ font-size: 1rem;
110
+ font-weight: 600;
111
+ cursor: pointer;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ gap: 10px;
116
+ transition: all 0.3s ease;
117
+ }
118
+
119
+ .nav-tab:hover {
120
+ background: #f8fafc;
121
+ color: var(--primary-color);
122
+ }
123
+
124
+ .nav-tab.active {
125
+ background: var(--primary-color);
126
+ color: white;
127
+ box-shadow: 0 5px 15px rgba(59, 130, 246, 0.3);
128
+ }
129
+
130
+ /* Sections */
131
+ .tab-content {
132
+ display: none;
133
+ animation: fadeIn 0.5s ease;
134
+ }
135
+
136
+ .tab-content.active {
137
+ display: block;
138
+ }
139
+
140
+ @keyframes fadeIn {
141
+ from { opacity: 0; transform: translateY(10px); }
142
+ to { opacity: 1; transform: translateY(0); }
143
+ }
144
+
145
+ .card {
146
+ background: white;
147
+ border-radius: 15px;
148
+ padding: 2rem;
149
+ margin-bottom: 2rem;
150
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
151
+ border-left: 5px solid var(--primary-color);
152
+ }
153
+
154
+ .card-header {
155
+ display: flex;
156
+ justify-content: space-between;
157
+ align-items: center;
158
+ margin-bottom: 1.5rem;
159
+ padding-bottom: 1rem;
160
+ border-bottom: 2px solid #f1f5f9;
161
+ }
162
+
163
+ .card-header h3 {
164
+ font-size: 1.5rem;
165
+ color: var(--dark-color);
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 10px;
169
+ }
170
+
171
+ /* Form Elements */
172
+ .form-group {
173
+ margin-bottom: 1.5rem;
174
+ }
175
+
176
+ .form-label {
177
+ display: block;
178
+ margin-bottom: 0.5rem;
179
+ font-weight: 600;
180
+ color: var(--dark-color);
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 8px;
184
+ }
185
+
186
+ .form-control {
187
+ width: 100%;
188
+ padding: 12px 15px;
189
+ border: 2px solid #e2e8f0;
190
+ border-radius: 10px;
191
+ font-size: 1rem;
192
+ transition: all 0.3s;
193
+ background: #f8fafc;
194
+ }
195
+
196
+ .form-control:focus {
197
+ outline: none;
198
+ border-color: var(--primary-color);
199
+ background: white;
200
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
201
+ }
202
+
203
+ .form-row {
204
+ display: flex;
205
+ gap: 20px;
206
+ margin-bottom: 1.5rem;
207
+ }
208
+
209
+ .form-col {
210
+ flex: 1;
211
+ }
212
+
213
+ /* Buttons */
214
+ .btn {
215
+ padding: 12px 25px;
216
+ border: none;
217
+ border-radius: 10px;
218
+ font-size: 1rem;
219
+ font-weight: 600;
220
+ cursor: pointer;
221
+ transition: all 0.3s;
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ gap: 10px;
226
+ }
227
+
228
+ .btn-primary {
229
+ background: linear-gradient(135deg, var(--primary-color) 0%, #2563eb 100%);
230
+ color: white;
231
+ }
232
+
233
+ .btn-primary:hover {
234
+ transform: translateY(-2px);
235
+ box-shadow: 0 10px 20px rgba(59, 130, 246, 0.3);
236
+ }
237
+
238
+ .btn-secondary {
239
+ background: var(--secondary-color);
240
+ color: white;
241
+ }
242
+
243
+ .btn-large {
244
+ padding: 15px 30px;
245
+ font-size: 1.1rem;
246
+ }
247
+
248
+ /* Visualizations */
249
+ .visualization-container {
250
+ background: white;
251
+ border-radius: 15px;
252
+ padding: 2rem;
253
+ margin-top: 2rem;
254
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05);
255
+ }
256
+
257
+ .chart-container {
258
+ position: relative;
259
+ height: 400px;
260
+ width: 100%;
261
+ }
262
+
263
+ /* Planner Cards */
264
+ .planning-grid {
265
+ display: grid;
266
+ grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
267
+ gap: 20px;
268
+ margin-top: 2rem;
269
+ }
270
+
271
+ .session-card {
272
+ background: white;
273
+ border-radius: 12px;
274
+ padding: 1.5rem;
275
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
276
+ border-top: 4px solid var(--primary-color);
277
+ transition: transform 0.3s;
278
+ }
279
+
280
+ .session-card:hover {
281
+ transform: translateY(-5px);
282
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
283
+ }
284
+
285
+ .session-header {
286
+ display: flex;
287
+ justify-content: space-between;
288
+ align-items: flex-start;
289
+ margin-bottom: 1rem;
290
+ }
291
+
292
+ .session-date {
293
+ font-size: 1.2rem;
294
+ font-weight: bold;
295
+ color: var(--dark-color);
296
+ }
297
+
298
+ .session-repetition {
299
+ background: var(--primary-color);
300
+ color: white;
301
+ padding: 5px 15px;
302
+ border-radius: 20px;
303
+ font-size: 0.9rem;
304
+ }
305
+
306
+ .session-details {
307
+ margin-top: 1rem;
308
+ display: flex;
309
+ flex-direction: column;
310
+ gap: 8px;
311
+ }
312
+
313
+ .detail-item {
314
+ display: flex;
315
+ justify-content: space-between;
316
+ padding: 5px 0;
317
+ border-bottom: 1px dashed #e2e8f0;
318
+ }
319
+
320
+ /* Notifications */
321
+ .notification-card {
322
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
323
+ border-left: 5px solid var(--warning-color);
324
+ padding: 1.5rem;
325
+ border-radius: 12px;
326
+ margin-bottom: 1rem;
327
+ animation: slideIn 0.5s ease;
328
+ }
329
+
330
+ @keyframes slideIn {
331
+ from { transform: translateX(-20px); opacity: 0; }
332
+ to { transform: translateX(0); opacity: 1; }
333
+ }
334
+
335
+ /* Stats Cards */
336
+ .stats-grid {
337
+ display: grid;
338
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
339
+ gap: 20px;
340
+ margin-top: 2rem;
341
+ }
342
+
343
+ .stat-card {
344
+ background: white;
345
+ border-radius: 12px;
346
+ padding: 1.5rem;
347
+ text-align: center;
348
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
349
+ }
350
+
351
+ .stat-value {
352
+ font-size: 2.5rem;
353
+ font-weight: bold;
354
+ color: var(--primary-color);
355
+ margin: 10px 0;
356
+ }
357
+
358
+ .stat-label {
359
+ color: var(--gray-color);
360
+ font-size: 0.9rem;
361
+ }
362
+
363
+ /* Quality Indicators */
364
+ .quality-indicator {
365
+ display: flex;
366
+ gap: 10px;
367
+ margin: 15px 0;
368
+ }
369
+
370
+ .quality-dot {
371
+ width: 25px;
372
+ height: 25px;
373
+ border-radius: 50%;
374
+ cursor: pointer;
375
+ transition: all 0.3s;
376
+ display: flex;
377
+ align-items: center;
378
+ justify-content: center;
379
+ color: white;
380
+ font-weight: bold;
381
+ font-size: 0.8rem;
382
+ }
383
+
384
+ .quality-dot:hover {
385
+ transform: scale(1.2);
386
+ }
387
+
388
+ .quality-0 { background: linear-gradient(135deg, #ef4444, #dc2626); }
389
+ .quality-1 { background: linear-gradient(135deg, #f59e0b, #d97706); }
390
+ .quality-2 { background: linear-gradient(135deg, #10b981, #059669); }
391
+ .quality-3 { background: linear-gradient(135deg, #3b82f6, #1d4ed8); }
392
+
393
+ .quality-dot.active {
394
+ box-shadow: 0 0 0 3px white, 0 0 0 6px currentColor;
395
+ }
396
+
397
+ /* Responsive */
398
+ @media (max-width: 768px) {
399
+ .form-row {
400
+ flex-direction: column;
401
+ gap: 0;
402
+ }
403
+
404
+ .nav-tabs {
405
+ flex-direction: column;
406
+ }
407
+
408
+ .planning-grid {
409
+ grid-template-columns: 1fr;
410
+ }
411
+
412
+ .header h1 {
413
+ font-size: 2rem;
414
+ }
415
+ }
416
+ </style>
417
+ </head>
418
+ <body>
419
+ <div class="container">
420
+ <!-- Header -->
421
+ <header class="header">
422
+ <h1><i class="fas fa-brain"></i> Cognitive-Driven AI for Language Learning</h1>
423
+ <p>Optimizing Long-Term Memory Retention through Spaced Repetition (SM-2), Self-Regulated Learning, Metacognition, and Gentle Reminders via Multi-Armed Bandits</p>
424
+ <div class="badge">Version Professionnelle 2.0 | SM-2 Niveau Avancé</div>
425
+ </header>
426
+
427
+ <!-- Navigation -->
428
+ <nav class="nav-tabs">
429
+ <button class="nav-tab active" onclick="showTab('objectif')">
430
+ <i class="fas fa-bullseye"></i> Objectif
431
+ </button>
432
+ <button class="nav-tab" onclick="showTab('planner')">
433
+ <i class="fas fa-calendar-alt"></i> Planner SM-2
434
+ </button>
435
+ <button class="nav-tab" onclick="showTab('journal')">
436
+ <i class="fas fa-book"></i> Journal SRL
437
+ </button>
438
+ <button class="nav-tab" onclick="showTab('notifications')">
439
+ <i class="fas fa-bell"></i> Notifications IA
440
+ </button>
441
+ <button class="nav-tab" onclick="showTab('stats')">
442
+ <i class="fas fa-chart-line"></i> Statistiques
443
+ </button>
444
+ <button class="nav-tab" onclick="showTab('learn')">
445
+ <i class="fas fa-graduation-cap"></i> Apprendre SM-2
446
+ </button>
447
+ </nav>
448
+
449
+ <!-- Objectif Section -->
450
+ <section id="objectif" class="tab-content active">
451
+ <div class="card">
452
+ <div class="card-header">
453
+ <h3><i class="fas fa-bullseye"></i> Définir mon Objectif d'Apprentissage</h3>
454
+ </div>
455
+
456
+ <div class="form-row">
457
+ <div class="form-col">
458
+ <label class="form-label"><i class="fas fa-language"></i> Compétence Cible</label>
459
+ <select id="competence" class="form-control" onchange="previewSM2Curve()">
460
+ <option value="">Sélectionnez une compétence...</option>
461
+ </select>
462
+ </div>
463
+ <div class="form-col">
464
+ <label class="form-label"><i class="fas fa-brain"></i> Niveau Actuel</label>
465
+ <select id="level" class="form-control">
466
+ <option value="0">Débutant</option>
467
+ <option value="1" selected>Intermédiaire</option>
468
+ <option value="2">Avancé</option>
469
+ <option value="3">Expert</option>
470
+ </select>
471
+ </div>
472
+ </div>
473
+
474
+ <div class="form-row">
475
+ <div class="form-col">
476
+ <label class="form-label"><i class="fas fa-calendar-start"></i> Date de Début</label>
477
+ <input type="date" id="start" class="form-control" value="2026-01-29">
478
+ </div>
479
+ <div class="form-col">
480
+ <label class="form-label"><i class="fas fa-calendar-end"></i> Date de Fin</label>
481
+ <input type="date" id="end" class="form-control" value="2026-12-31">
482
+ </div>
483
+ </div>
484
+
485
+ <div class="form-group">
486
+ <label class="form-label"><i class="fas fa-clock"></i> Durée par Session (minutes)</label>
487
+ <select id="duration" class="form-control">
488
+ <option value="10">10 minutes (Micro-learning)</option>
489
+ <option value="20" selected>20 minutes (Optimal)</option>
490
+ <option value="30">30 minutes (Approfondissement)</option>
491
+ <option value="45">45 minutes (Session intensive)</option>
492
+ </select>
493
+ </div>
494
+
495
+ <div class="form-group">
496
+ <label class="form-label"><i class="fas fa-chart-line"></i> Qualité Estimée de Rétention</label>
497
+ <div class="quality-indicator">
498
+ <div class="quality-dot quality-0" data-quality="0" onclick="selectQuality(0)">0</div>
499
+ <div class="quality-dot quality-1" data-quality="1" onclick="selectQuality(1)">1</div>
500
+ <div class="quality-dot quality-2 active" data-quality="2" onclick="selectQuality(2)">2</div>
501
+ <div class="quality-dot quality-3" data-quality="3" onclick="selectQuality(3)">3</div>
502
+ </div>
503
+ <small id="quality-description">Bon - Consolidation progressive</small>
504
+ </div>
505
+
506
+ <button class="btn btn-primary btn-large" onclick="generatePlanner()">
507
+ <i class="fas fa-cogs"></i> Générer le Planner SM-2
508
+ </button>
509
+
510
+ <div id="preview" style="display: none; margin-top: 2rem;">
511
+ <h4><i class="fas fa-eye"></i> Aperçu de la Courbe SM-2</h4>
512
+ <div class="chart-container">
513
+ <canvas id="intervalChart"></canvas>
514
+ </div>
515
+ </div>
516
+ </div>
517
+ </section>
518
+
519
+ <!-- Planner Section -->
520
+ <section id="planner" class="tab-content">
521
+ <div class="card">
522
+ <div class="card-header">
523
+ <h3><i class="fas fa-calendar-alt"></i> Planner SM-2 Personnalisé</h3>
524
+ <button class="btn btn-secondary" onclick="exportPlanner()">
525
+ <i class="fas fa-download"></i> Exporter
526
+ </button>
527
+ </div>
528
+
529
+ <div id="plannerStats" class="stats-grid"></div>
530
+
531
+ <div id="plannerContent" class="planning-grid"></div>
532
+
533
+ <div class="visualization-container">
534
+ <h4><i class="fas fa-chart-line"></i> Visualisation des Intervalles SM-2</h4>
535
+ <div class="chart-container">
536
+ <canvas id="sm2Visualization"></canvas>
537
+ </div>
538
+ </div>
539
+ </div>
540
+ </section>
541
+
542
+ <!-- Journal Section -->
543
+ <section id="journal" class="tab-content">
544
+ <div class="card">
545
+ <div class="card-header">
546
+ <h3><i class="fas fa-book"></i> Journal d'Apprentissage Autorégulé</h3>
547
+ <button class="btn btn-secondary" onclick="loadJournalEntries()">
548
+ <i class="fas fa-history"></i> Historique
549
+ </button>
550
+ </div>
551
+
552
+ <div class="form-row">
553
+ <div class="form-col">
554
+ <label class="form-label"><i class="fas fa-tasks"></i> Planifié (To-Do)</label>
555
+ <textarea id="todo" class="form-control" rows="3" placeholder="Ce que vous prévoyez d'apprendre..."></textarea>
556
+ </div>
557
+ <div class="form-col">
558
+ <label class="form-label"><i class="fas fa-spinner"></i> En Cours (Doing)</label>
559
+ <textarea id="doing" class="form-control" rows="3" placeholder="Ce que vous faites actuellement..."></textarea>
560
+ </div>
561
+ </div>
562
+
563
+ <div class="form-group">
564
+ <label class="form-label"><i class="fas fa-check-circle"></i> Accompli (Done)</label>
565
+ <textarea id="done" class="form-control" rows="3" placeholder="Ce que vous avez accompli..."></textarea>
566
+ </div>
567
+
568
+ <div class="form-group">
569
+ <label class="form-label"><i class="fas fa-star"></i> Auto-évaluation (1-5)</label>
570
+ <div class="quality-indicator">
571
+ <div class="quality-dot quality-0" onclick="setEvaluation(1)">1</div>
572
+ <div class="quality-dot quality-1" onclick="setEvaluation(2)">2</div>
573
+ <div class="quality-dot quality-2 active" onclick="setEvaluation(3)">3</div>
574
+ <div class="quality-dot quality-3" onclick="setEvaluation(4)">4</div>
575
+ <div class="quality-dot" onclick="setEvaluation(5)" style="background:linear-gradient(135deg, #8b5cf6, #7c3aed)">5</div>
576
+ </div>
577
+ <input type="hidden" id="auto_eval" value="3">
578
+ </div>
579
+
580
+ <div class="form-group">
581
+ <label class="form-label"><i class="fas fa-lightbulb"></i> Stratégie Métacognitive</label>
582
+ <textarea id="metacognition" class="form-control" rows="3" placeholder="Quelle stratégie avez-vous utilisée ? Que changeriez-vous ?"></textarea>
583
+ </div>
584
+
585
+ <button class="btn btn-primary btn-large" onclick="saveJournal()">
586
+ <i class="fas fa-save"></i> Sauvegarder l'Entrée
587
+ </button>
588
+
589
+ <div id="journalHistory" style="display: none; margin-top: 2rem;">
590
+ <h4><i class="fas fa-history"></i> Dernières Entrées</h4>
591
+ <div id="journalEntries"></div>
592
+ </div>
593
+ </div>
594
+ </section>
595
+
596
+ <!-- Notifications Section -->
597
+ <section id="notifications" class="tab-content">
598
+ <div class="card">
599
+ <div class="card-header">
600
+ <h3><i class="fas fa-bell"></i> Notifications Intelligentes par Bandits Multi-Bras</h3>
601
+ <button class="btn btn-secondary" onclick="getIntelligentReminder()">
602
+ <i class="fas fa-sync"></i> Nouveau Rappel
603
+ </button>
604
+ </div>
605
+
606
+ <div id="notificationContainer">
607
+ <div class="notification-card">
608
+ <p><i class="fas fa-info-circle"></i> Cliquez sur "Nouveau Rappel" pour recevoir une notification personnalisée basée sur vos habitudes d'apprentissage.</p>
609
+ </div>
610
+ </div>
611
+
612
+ <div class="visualization-container">
613
+ <h4><i class="fas fa-chart-bar"></i> Efficacité des Types de Notifications</h4>
614
+ <div class="chart-container">
615
+ <canvas id="notificationChart"></canvas>
616
+ </div>
617
+ </div>
618
+ </div>
619
+ </section>
620
+
621
+ <!-- Statistics Section -->
622
+ <section id="stats" class="tab-content">
623
+ <div class="card">
624
+ <div class="card-header">
625
+ <h3><i class="fas fa-chart-line"></i> Tableau de Bord d'Apprentissage</h3>
626
+ <button class="btn btn-secondary" onclick="refreshStats()">
627
+ <i class="fas fa-redo"></i> Actualiser
628
+ </button>
629
+ </div>
630
+
631
+ <div id="statsContainer" class="stats-grid"></div>
632
+
633
+ <div class="visualization-container">
634
+ <h4><i class="fas fa-chart-area"></i> Progression de la Rétention</h4>
635
+ <div class="chart-container">
636
+ <canvas id="retentionChart"></canvas>
637
+ </div>
638
+ </div>
639
+ </div>
640
+ </section>
641
+
642
+ <!-- Learn SM-2 Section -->
643
+ <section id="learn" class="tab-content">
644
+ <div class="card">
645
+ <div class="card-header">
646
+ <h3><i class="fas fa-graduation-cap"></i> Apprendre l'Algorithme SM-2</h3>
647
+ </div>
648
+
649
+ <div id="sm2Explanation"></div>
650
+
651
+ <div class="visualization-container">
652
+ <h4><i class="fas fa-chart-network"></i> Comparaison des Niveaux de Qualité SM-2</h4>
653
+ <div class="chart-container">
654
+ <canvas id="comparisonChart"></canvas>
655
+ </div>
656
+ </div>
657
+ </div>
658
+ </section>
659
+ </div>
660
+
661
+ <script>
662
+ // Variables globales
663
+ let currentChart = null;
664
+ let currentQuality = 2; // Niveau 2 par défaut
665
+ let previewChart = null;
666
+
667
+ // Initialisation
668
+ document.addEventListener('DOMContentLoaded', function() {
669
+ loadCompetences();
670
+ refreshStats();
671
+ loadSM2Explanation();
672
+ updateComparisonChart();
673
+ });
674
+
675
+ // Navigation
676
+ function showTab(tabId) {
677
+ // Masquer toutes les sections
678
+ document.querySelectorAll('.tab-content').forEach(section => {
679
+ section.classList.remove('active');
680
+ });
681
+
682
+ // Désactiver tous les onglets
683
+ document.querySelectorAll('.nav-tab').forEach(tab => {
684
+ tab.classList.remove('active');
685
+ });
686
+
687
+ // Activer la section sélectionnée
688
+ document.getElementById(tabId).classList.add('active');
689
+
690
+ // Activer l'onglet correspondant
691
+ const tabNames = {
692
+ 'objectif': 'Objectif',
693
+ 'planner': 'Planner',
694
+ 'journal': 'Journal',
695
+ 'notifications': 'Notifications',
696
+ 'stats': 'Statistiques',
697
+ 'learn': 'Apprendre'
698
+ };
699
+
700
+ document.querySelectorAll('.nav-tab').forEach(tab => {
701
+ if (tab.textContent.includes(tabNames[tabId])) {
702
+ tab.classList.add('active');
703
+ }
704
+ });
705
+
706
+ // Actions spécifiques par onglet
707
+ if (tabId === 'stats') {
708
+ refreshStats();
709
+ } else if (tabId === 'learn') {
710
+ updateComparisonChart();
711
+ }
712
+ }
713
+
714
+ // Charger les compétences
715
+ async function loadCompetences() {
716
+ try {
717
+ const response = await fetch('/api/competences');
718
+ const competences = await response.json();
719
+
720
+ const select = document.getElementById('competence');
721
+ select.innerHTML = '<option value="">Sélectionnez une compétence...</option>';
722
+
723
+ competences.forEach(comp => {
724
+ const option = document.createElement('option');
725
+ option.value = comp.label;
726
+ option.textContent = `${comp.id} - ${comp.label} (${comp.category})`;
727
+ select.appendChild(option);
728
+ });
729
+ } catch (error) {
730
+ console.error('Erreur chargement compétences:', error);
731
+ }
732
+ }
733
+
734
+ // Sélection de la qualité
735
+ function selectQuality(level) {
736
+ currentQuality = level;
737
+
738
+ // Mettre à jour les indicateurs visuels
739
+ document.querySelectorAll('.quality-dot').forEach(dot => {
740
+ dot.classList.remove('active');
741
+ });
742
+
743
+ document.querySelector(`.quality-dot[data-quality="${level}"]`).classList.add('active');
744
+
745
+ // Mettre à jour la description
746
+ const descriptions = [
747
+ "Difficulté - Révision fréquente nécessaire",
748
+ "Moyen - Intervalles standard recommandés",
749
+ "Bon - Consolidation progressive",
750
+ "Excellent - Rétention à long terme optimale"
751
+ ];
752
+
753
+ document.getElementById('quality-description').textContent = descriptions[level];
754
+
755
+ // Mettre à jour l'aperçu
756
+ previewSM2Curve();
757
+ }
758
+
759
+ // Prévisualiser la courbe SM-2
760
+ async function previewSM2Curve() {
761
+ const competence = document.getElementById('competence').value;
762
+ if (!competence) {
763
+ document.getElementById('preview').style.display = 'none';
764
+ return;
765
+ }
766
+
767
+ try {
768
+ const response = await fetch(`/api/planner/visualize?competence=${encodeURIComponent(competence)}&quality=${currentQuality}`);
769
+ const data = await response.json();
770
+
771
+ if (data.success) {
772
+ document.getElementById('preview').style.display = 'block';
773
+
774
+ const ctx = document.getElementById('intervalChart').getContext('2d');
775
+
776
+ if (previewChart) {
777
+ previewChart.destroy();
778
+ }
779
+
780
+ const labels = data.intervals.map((_, i) => `Répétition ${i + 1}`);
781
+
782
+ previewChart = new Chart(ctx, {
783
+ type: 'bar',
784
+ data: {
785
+ labels: labels,
786
+ datasets: [{
787
+ label: 'Intervalle (jours)',
788
+ data: data.intervals,
789
+ backgroundColor: '#3b82f6',
790
+ borderColor: '#2563eb',
791
+ borderWidth: 2
792
+ }]
793
+ },
794
+ options: {
795
+ responsive: true,
796
+ maintainAspectRatio: false,
797
+ plugins: {
798
+ title: {
799
+ display: true,
800
+ text: `Courbe SM-2 - Niveau ${currentQuality}`
801
+ }
802
+ },
803
+ scales: {
804
+ y: {
805
+ beginAtZero: true,
806
+ title: {
807
+ display: true,
808
+ text: 'Jours'
809
+ }
810
+ }
811
+ }
812
+ }
813
+ });
814
+ }
815
+ } catch (error) {
816
+ console.error('Erreur génération preview:', error);
817
+ }
818
+ }
819
+
820
+ // Générer le planner
821
+ async function generatePlanner() {
822
+ const competence = document.getElementById('competence').value;
823
+ const startDate = document.getElementById('start').value;
824
+ const duration = document.getElementById('duration').value;
825
+
826
+ if (!competence || !startDate) {
827
+ alert('Veuillez sélectionner une compétence et une date de début');
828
+ return;
829
+ }
830
+
831
+ try {
832
+ const response = await fetch('/api/planner/generate', {
833
+ method: 'POST',
834
+ headers: { 'Content-Type': 'application/json' },
835
+ body: JSON.stringify({
836
+ competence: competence,
837
+ start_date: startDate,
838
+ session_duration: duration,
839
+ repetitions: 6,
840
+ quality_level: currentQuality
841
+ })
842
+ });
843
+
844
+ const data = await response.json();
845
+
846
+ if (data.success) {
847
+ displayPlanner(data.plan, data.intervals);
848
+ showTab('planner');
849
+ } else {
850
+ alert('Erreur: ' + data.error);
851
+ }
852
+ } catch (error) {
853
+ console.error('Erreur génération planner:', error);
854
+ alert('Erreur lors de la génération du planner');
855
+ }
856
+ }
857
+
858
+ // Afficher le planner
859
+ function displayPlanner(plan, intervals) {
860
+ const container = document.getElementById('plannerContent');
861
+ const statsContainer = document.getElementById('plannerStats');
862
+
863
+ // Calculer le total des jours
864
+ const totalDays = intervals.reduce((a, b) => a + b, 0);
865
+
866
+ // Statistiques
867
+ statsContainer.innerHTML = `
868
+ <div class="stat-card">
869
+ <div class="stat-value">${plan.length}</div>
870
+ <div class="stat-label">Sessions Planifiées</div>
871
+ </div>
872
+ <div class="stat-card">
873
+ <div class="stat-value">${totalDays}</div>
874
+ <div class="stat-label">Jours Totaux</div>
875
+ </div>
876
+ <div class="stat-card">
877
+ <div class="stat-value">${currentQuality + 1}/4</div>
878
+ <div class="stat-label">Niveau de Qualité</div>
879
+ </div>
880
+ <div class="stat-card">
881
+ <div class="stat-value">${document.getElementById('duration').value}min</div>
882
+ <div class="stat-label">Durée/Session</div>
883
+ </div>
884
+ `;
885
+
886
+ // Sessions
887
+ container.innerHTML = '';
888
+ plan.forEach(session => {
889
+ const card = document.createElement('div');
890
+ card.className = 'session-card';
891
+ card.innerHTML = `
892
+ <div class="session-header">
893
+ <div class="session-date">
894
+ <i class="fas fa-calendar-day"></i> ${session.date}
895
+ <div style="font-size: 0.9rem; color: #64748b;">${session.jour}</div>
896
+ </div>
897
+ <div class="session-repetition">
898
+ R${session.repetition}
899
+ </div>
900
+ </div>
901
+ <h4 style="margin: 10px 0; color: #1e293b;">${session.competence}</h4>
902
+ <div class="session-details">
903
+ <div class="detail-item">
904
+ <span><i class="fas fa-clock"></i> Durée</span>
905
+ <span>${session.duree}</span>
906
+ </div>
907
+ <div class="detail-item">
908
+ <span><i class="fas fa-sync-alt"></i> Intervalle</span>
909
+ <span>${session.intervalle}</span>
910
+ </div>
911
+ <div class="detail-item">
912
+ <span><i class="fas fa-bullseye"></i> Type</span>
913
+ <span>${session.type}</span>
914
+ </div>
915
+ <div class="detail-item">
916
+ <span><i class="fas fa-lightbulb"></i> Stratégie</span>
917
+ <span>${session.strategie}</span>
918
+ </div>
919
+ </div>
920
+ <div style="margin-top: 15px; padding: 10px; background: #f1f5f9; border-radius: 8px; font-size: 0.9rem;">
921
+ <i class="fas fa-bell"></i> ${session.reminder || ''}
922
+ </div>
923
+ `;
924
+ container.appendChild(card);
925
+ });
926
+
927
+ // Visualisation
928
+ createSM2Visualization(intervals, plan[0]?.competence || 'Compétence');
929
+ }
930
+
931
+ // Créer la visualisation SM-2
932
+ function createSM2Visualization(intervals, competence) {
933
+ const ctx = document.getElementById('sm2Visualization').getContext('2d');
934
+
935
+ if (currentChart) {
936
+ currentChart.destroy();
937
+ }
938
+
939
+ const labels = intervals.map((_, i) => `Répétition ${i + 1}`);
940
+
941
+ currentChart = new Chart(ctx, {
942
+ type: 'line',
943
+ data: {
944
+ labels: labels,
945
+ datasets: [{
946
+ label: 'Intervalle (jours)',
947
+ data: intervals,
948
+ borderColor: '#3b82f6',
949
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
950
+ borderWidth: 3,
951
+ fill: true,
952
+ tension: 0.4
953
+ }]
954
+ },
955
+ options: {
956
+ responsive: true,
957
+ maintainAspectRatio: false,
958
+ plugins: {
959
+ title: {
960
+ display: true,
961
+ text: `Courbe de Répétition Espacée - ${competence}`,
962
+ font: { size: 16 }
963
+ },
964
+ tooltip: {
965
+ callbacks: {
966
+ label: function(context) {
967
+ return `Intervalle: ${context.raw} jours`;
968
+ }
969
+ }
970
+ }
971
+ },
972
+ scales: {
973
+ y: {
974
+ beginAtZero: true,
975
+ title: {
976
+ display: true,
977
+ text: 'Jours'
978
+ }
979
+ },
980
+ x: {
981
+ title: {
982
+ display: true,
983
+ text: 'Numéro de Répétition'
984
+ }
985
+ }
986
+ }
987
+ }
988
+ });
989
+ }
990
+
991
+ // Journal
992
+ function setEvaluation(score) {
993
+ document.getElementById('auto_eval').value = score;
994
+
995
+ // Mettre à jour les indicateurs
996
+ document.querySelectorAll('.quality-indicator .quality-dot').forEach((dot, index) => {
997
+ dot.classList.toggle('active', index === score - 1);
998
+ });
999
+ }
1000
+
1001
+ async function saveJournal() {
1002
+ const entry = {
1003
+ todo: document.getElementById('todo').value,
1004
+ doing: document.getElementById('doing').value,
1005
+ done: document.getElementById('done').value,
1006
+ metacognition: document.getElementById('metacognition').value,
1007
+ auto_eval: document.getElementById('auto_eval').value,
1008
+ competence: document.getElementById('competence').value,
1009
+ timestamp: new Date().toISOString()
1010
+ };
1011
+
1012
+ try {
1013
+ const response = await fetch('/api/journal', {
1014
+ method: 'POST',
1015
+ headers: { 'Content-Type': 'application/json' },
1016
+ body: JSON.stringify(entry)
1017
+ });
1018
+
1019
+ const result = await response.json();
1020
+
1021
+ if (result.success) {
1022
+ alert('✅ Journal sauvegardé avec succès !');
1023
+ clearJournalForm();
1024
+ } else {
1025
+ alert('Erreur: ' + result.error);
1026
+ }
1027
+ } catch (error) {
1028
+ console.error('Erreur sauvegarde journal:', error);
1029
+ alert('Erreur lors de la sauvegarde');
1030
+ }
1031
+ }
1032
+
1033
+ function clearJournalForm() {
1034
+ document.getElementById('todo').value = '';
1035
+ document.getElementById('doing').value = '';
1036
+ document.getElementById('done').value = '';
1037
+ document.getElementById('metacognition').value = '';
1038
+ setEvaluation(3);
1039
+ }
1040
+
1041
+ async function loadJournalEntries() {
1042
+ try {
1043
+ const response = await fetch('/api/journal/entries?limit=5');
1044
+ const entries = await response.json();
1045
+
1046
+ const container = document.getElementById('journalEntries');
1047
+ container.innerHTML = '';
1048
+
1049
+ entries.forEach(entry => {
1050
+ const div = document.createElement('div');
1051
+ div.className = 'session-card';
1052
+ div.innerHTML = `
1053
+ <div class="session-header">
1054
+ <div class="session-date">
1055
+ ${new Date(entry.timestamp).toLocaleDateString('fr-FR')}
1056
+ </div>
1057
+ <div style="background: #10b981; color: white; padding: 5px 15px; border-radius: 20px;">
1058
+ ${entry.auto_eval || '?'}/5
1059
+ </div>
1060
+ </div>
1061
+ ${entry.competence ? `<div style="color: #3b82f6; margin: 5px 0;"><i class="fas fa-language"></i> ${entry.competence}</div>` : ''}
1062
+ ${entry.done ? `<div style="margin-top: 10px;"><strong>Accompli:</strong> ${entry.done.substring(0, 100)}...</div>` : ''}
1063
+ `;
1064
+ container.appendChild(div);
1065
+ });
1066
+
1067
+ document.getElementById('journalHistory').style.display = 'block';
1068
+ } catch (error) {
1069
+ console.error('Erreur chargement journal:', error);
1070
+ }
1071
+ }
1072
+
1073
+ // Notifications
1074
+ async function getIntelligentReminder() {
1075
+ try {
1076
+ const response = await fetch('/api/notifications/intelligent');
1077
+ const data = await response.json();
1078
+
1079
+ const container = document.getElementById('notificationContainer');
1080
+ const card = document.createElement('div');
1081
+ card.className = 'notification-card';
1082
+
1083
+ const icons = {
1084
+ 'motivation': '🌱',
1085
+ 'rappel_court': '⏰',
1086
+ 'encouragement': '💪',
1087
+ 'metacognition': '🤔'
1088
+ };
1089
+
1090
+ card.innerHTML = `
1091
+ <div style="display: flex; align-items: center; gap: 15px; margin-bottom: 10px;">
1092
+ <div style="font-size: 2rem;">${icons[data.type] || '🔔'}</div>
1093
+ <div>
1094
+ <div style="font-weight: bold; color: #1e293b;">${data.type ? data.type.charAt(0).toUpperCase() + data.type.slice(1).replace('_', ' ') : 'Notification'}</div>
1095
+ <div style="font-size: 0.9rem; color: #64748b;">
1096
+ Efficacité: ${(data.effectiveness_score * 100).toFixed(1)}%
1097
+ </div>
1098
+ </div>
1099
+ </div>
1100
+ <p style="font-size: 1.1rem; line-height: 1.5;">${data.message}</p>
1101
+ <div style="margin-top: 10px; font-size: 0.8rem; color: #64748b; text-align: right;">
1102
+ ${new Date(data.timestamp).toLocaleTimeString('fr-FR')}
1103
+ </div>
1104
+ `;
1105
+
1106
+ container.insertBefore(card, container.firstChild);
1107
+
1108
+ } catch (error) {
1109
+ console.error('Erreur notification:', error);
1110
+ }
1111
+ }
1112
+
1113
+ // Statistiques
1114
+ async function refreshStats() {
1115
+ try {
1116
+ const response = await fetch('/api/learner/stats');
1117
+ const stats = await response.json();
1118
+
1119
+ const container = document.getElementById('statsContainer');
1120
+ container.innerHTML = `
1121
+ <div class="stat-card">
1122
+ <div class="stat-value">${stats.total_sessions}</div>
1123
+ <div class="stat-label">Sessions Complétées</div>
1124
+ </div>
1125
+ <div class="stat-card">
1126
+ <div class="stat-value">${stats.success_rate.toFixed(2)}/5</div>
1127
+ <div class="stat-label">Score Moyen</div>
1128
+ </div>
1129
+ <div class="stat-card">
1130
+ <div class="stat-value">${stats.journal_entries}</div>
1131
+ <div class="stat-label">Entrées Journal</div>
1132
+ </div>
1133
+ <div class="stat-card">
1134
+ <div class="stat-value">${stats.planned_sessions}</div>
1135
+ <div class="stat-label">Sessions Planifiées</div>
1136
+ </div>
1137
+ <div class="stat-card">
1138
+ <div class="stat-value" style="color: ${stats.engagement_level === 'high' ? '#10b981' : stats.engagement_level === 'medium' ? '#f59e0b' : '#ef4444'}">
1139
+ ${stats.engagement_level}
1140
+ </div>
1141
+ <div class="stat-label">Niveau d'Engagement</div>
1142
+ </div>
1143
+ <div class="stat-card">
1144
+ <div class="stat-value" style="color: ${stats.risk_level === 'low' ? '#10b981' : stats.risk_level === 'medium' ? '#f59e0b' : '#ef4444'}">
1145
+ ${stats.risk_level}
1146
+ </div>
1147
+ <div class="stat-label">Niveau de Risque</div>
1148
+ </div>
1149
+ `;
1150
+
1151
+ // Mettre à jour le graphique de rétention
1152
+ updateRetentionChart(stats);
1153
+
1154
+ } catch (error) {
1155
+ console.error('Erreur statistiques:', error);
1156
+ }
1157
+ }
1158
+
1159
+ function updateRetentionChart(stats) {
1160
+ const ctx = document.getElementById('retentionChart').getContext('2d');
1161
+
1162
+ if (window.retentionChart) {
1163
+ window.retentionChart.destroy();
1164
+ }
1165
+
1166
+ // Données simulées pour la démonstration
1167
+ const labels = ['Sem 1', 'Sem 2', 'Sem 3', 'Sem 4', 'Sem 5', 'Sem 6'];
1168
+ const retentionData = [0.3, 0.5, 0.65, 0.75, 0.8, 0.85];
1169
+ const engagementData = [0.7, 0.8, 0.75, 0.85, 0.9, 0.88];
1170
+
1171
+ window.retentionChart = new Chart(ctx, {
1172
+ type: 'line',
1173
+ data: {
1174
+ labels: labels,
1175
+ datasets: [
1176
+ {
1177
+ label: 'Taux de Rétention',
1178
+ data: retentionData,
1179
+ borderColor: '#3b82f6',
1180
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
1181
+ borderWidth: 3,
1182
+ fill: true,
1183
+ tension: 0.4
1184
+ },
1185
+ {
1186
+ label: 'Niveau d\'Engagement',
1187
+ data: engagementData,
1188
+ borderColor: '#10b981',
1189
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
1190
+ borderWidth: 3,
1191
+ fill: true,
1192
+ tension: 0.4
1193
+ }
1194
+ ]
1195
+ },
1196
+ options: {
1197
+ responsive: true,
1198
+ maintainAspectRatio: false,
1199
+ scales: {
1200
+ y: {
1201
+ beginAtZero: true,
1202
+ max: 1,
1203
+ ticks: {
1204
+ callback: function(value) {
1205
+ return (value * 100) + '%';
1206
+ }
1207
+ }
1208
+ }
1209
+ },
1210
+ plugins: {
1211
+ tooltip: {
1212
+ callbacks: {
1213
+ label: function(context) {
1214
+ return context.dataset.label + ': ' + (context.raw * 100).toFixed(1) + '%';
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1220
+ });
1221
+ }
1222
+
1223
+ // Apprendre SM-2
1224
+ async function loadSM2Explanation() {
1225
+ try {
1226
+ const response = await fetch('/api/sm2/explain');
1227
+ const explanation = await response.json();
1228
+
1229
+ const container = document.getElementById('sm2Explanation');
1230
+ container.innerHTML = `
1231
+ <div class="card" style="margin-bottom: 20px;">
1232
+ <h3 style="color: #3b82f6; margin-bottom: 15px;">${explanation.title}</h3>
1233
+ <p style="margin-bottom: 20px; line-height: 1.6;">${explanation.description}</p>
1234
+
1235
+ <h4 style="margin-bottom: 15px; color: #1e293b;"><i class="fas fa-layer-group"></i> Niveaux de Qualité</h4>
1236
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-bottom: 20px;">
1237
+ ${Object.entries(explanation.quality_levels).map(([level, desc]) => `
1238
+ <div style="padding: 15px; background: #f8fafc; border-radius: 10px; border-left: 4px solid ${getQualityColor(level)};">
1239
+ <div style="font-weight: bold; color: ${getQualityColor(level)};">Niveau ${level}</div>
1240
+ <div style="font-size: 0.9rem; color: #64748b;">${desc}</div>
1241
+ </div>
1242
+ `).join('')}
1243
+ </div>
1244
+
1245
+ <h4 style="margin-bottom: 15px; color: #1e293b;"><i class="fas fa-lightbulb"></i> Principes Fondamentaux</h4>
1246
+ <ul style="margin-bottom: 20px; padding-left: 20px;">
1247
+ ${explanation.principles.map(principle => `
1248
+ <li style="margin-bottom: 10px; line-height: 1.5;">${principle}</li>
1249
+ `).join('')}
1250
+ </ul>
1251
+
1252
+ <h4 style="margin-bottom: 15px; color: #1e293b;"><i class="fas fa-check-circle"></i> Recommandations</h4>
1253
+ <ul style="padding-left: 20px;">
1254
+ ${explanation.recommendations.map(recommendation => `
1255
+ <li style="margin-bottom: 10px; line-height: 1.5;">${recommendation}</li>
1256
+ `).join('')}
1257
+ </ul>
1258
+ </div>
1259
+ `;
1260
+ } catch (error) {
1261
+ console.error('Erreur chargement explication SM-2:', error);
1262
+ }
1263
+ }
1264
+
1265
+ function getQualityColor(level) {
1266
+ const colors = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6'];
1267
+ return colors[parseInt(level)] || colors[0];
1268
+ }
1269
+
1270
+ function updateComparisonChart() {
1271
+ const ctx = document.getElementById('comparisonChart').getContext('2d');
1272
+
1273
+ if (window.comparisonChart) {
1274
+ window.comparisonChart.destroy();
1275
+ }
1276
+
1277
+ // Données des différents niveaux de qualité
1278
+ const intervals = {
1279
+ 'Difficulté (N0)': [1, 2, 4, 8, 16, 32],
1280
+ 'Moyen (N1)': [1, 3, 7, 14, 30, 60],
1281
+ 'Bon (N2)': [2, 7, 16, 35, 70, 140],
1282
+ 'Excellent (N3)': [3, 10, 25, 60, 120, 240]
1283
+ };
1284
+
1285
+ const labels = ['R1', 'R2', 'R3', 'R4', 'R5', 'R6'];
1286
+ const colors = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6'];
1287
+
1288
+ const datasets = Object.entries(intervals).map(([label, data], index) => ({
1289
+ label: label,
1290
+ data: data,
1291
+ borderColor: colors[index],
1292
+ backgroundColor: colors[index] + '20',
1293
+ borderWidth: 2,
1294
+ tension: 0.4,
1295
+ fill: false
1296
+ }));
1297
+
1298
+ window.comparisonChart = new Chart(ctx, {
1299
+ type: 'line',
1300
+ data: {
1301
+ labels: labels,
1302
+ datasets: datasets
1303
+ },
1304
+ options: {
1305
+ responsive: true,
1306
+ maintainAspectRatio: false,
1307
+ scales: {
1308
+ y: {
1309
+ beginAtZero: true,
1310
+ title: {
1311
+ display: true,
1312
+ text: 'Intervalle (jours)'
1313
+ }
1314
+ }
1315
+ },
1316
+ plugins: {
1317
+ title: {
1318
+ display: true,
1319
+ text: 'Comparaison des Courbes SM-2 par Niveau de Qualité',
1320
+ font: { size: 16 }
1321
+ },
1322
+ tooltip: {
1323
+ callbacks: {
1324
+ label: function(context) {
1325
+ return `${context.dataset.label}: ${context.raw} jours`;
1326
+ }
1327
+ }
1328
+ }
1329
+ }
1330
+ }
1331
+ });
1332
+ }
1333
+
1334
+ // Export
1335
+ function exportPlanner() {
1336
+ const plannerData = document.getElementById('plannerContent').innerText;
1337
+ const blob = new Blob([plannerData], { type: 'text/plain' });
1338
+ const url = URL.createObjectURL(blob);
1339
+ const a = document.createElement('a');
1340
+ a.href = url;
1341
+ a.download = 'planner-sm2.txt';
1342
+ document.body.appendChild(a);
1343
+ a.click();
1344
+ document.body.removeChild(a);
1345
+ URL.revokeObjectURL(url);
1346
+ }
1347
+ </script>
1348
+ </body>
1349
+ </html>