BitDown commited on
Commit
d0a3ba3
·
verified ·
1 Parent(s): a9f0fbf

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +572 -687
index.html CHANGED
@@ -3,8 +3,9 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Track Lift - Suivi de Musculation</title>
7
  <style>
 
8
  :root {
9
  --bg-dark: #121212;
10
  --bg-card: #1e1e1e;
@@ -13,435 +14,234 @@
13
  --accent-dark: #3a8a3d;
14
  --danger: #f44336;
15
  }
16
-
17
- * {
18
- margin: 0;
19
- padding: 0;
20
- box-sizing: border-box;
21
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
22
- }
23
-
24
- body {
25
- background-color: var(--bg-dark);
26
- color: var(--text-light);
27
- min-height: 100vh;
28
- padding-bottom: 80px; /* Pour le menu fixe en bas */
29
- }
30
-
31
- .container {
32
- width: 100%;
33
- max-width: 800px;
34
- margin: 0 auto;
35
- padding: 1rem;
36
- }
37
-
38
- header {
39
- padding: 1rem 0;
40
- text-align: center;
41
- border-bottom: 1px solid #333;
42
- margin-bottom: 1rem;
43
- }
44
-
45
- h1, h2, h3 {
46
- color: var(--accent);
47
- }
48
-
49
- .btn {
50
- background-color: var(--accent);
51
- color: white;
52
- border: none;
53
- padding: 0.6rem 1.2rem;
54
- border-radius: 4px;
55
- cursor: pointer;
56
- font-weight: bold;
57
- transition: background-color 0.2s;
58
- }
59
-
60
- .btn:hover {
61
- background-color: var(--accent-dark);
62
- }
63
-
64
- .btn-outline {
65
- background-color: transparent;
66
- color: var(--accent);
67
- border: 1px solid var(--accent);
68
- }
69
-
70
- .btn-danger {
71
- background-color: var(--danger);
72
- }
73
-
74
- input, select, textarea {
75
- width: 100%;
76
- padding: 0.6rem;
77
- margin-bottom: 1rem;
78
- background-color: #2a2a2a;
79
- border: 1px solid #444;
80
- border-radius: 4px;
81
- color: var(--text-light);
82
- }
83
-
84
- input[type="checkbox"] {
85
- width: auto;
86
- margin-right: 0.5rem;
87
- }
88
-
89
- .card {
90
- background-color: var(--bg-card);
91
- border-radius: 8px;
92
- padding: 1rem;
93
- margin-bottom: 1rem;
94
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
95
- }
96
-
97
- .form-group {
98
- margin-bottom: 1rem;
99
- }
100
-
101
- .form-row {
102
- display: flex;
103
- gap: 0.5rem;
104
- margin-bottom: 0.5rem;
105
- }
106
-
107
- .form-row > * {
108
- flex: 1;
109
- margin-bottom: 0;
110
- }
111
-
112
- label {
113
- display: block;
114
- margin-bottom: 0.3rem;
115
- color: #bbb;
116
- }
117
-
118
- .exercise {
119
- border-left: 3px solid var(--accent);
120
- padding-left: 1rem;
121
- margin-bottom: 1.5rem;
122
- }
123
-
124
- .exercise-header {
125
- display: flex;
126
- justify-content: space-between;
127
- align-items: center;
128
- margin-bottom: 0.5rem;
129
- }
130
-
131
- .series-container {
132
- margin-left: 0.5rem;
133
- margin-top: 0.5rem;
134
- }
135
-
136
- .series {
137
- background-color: #252525;
138
- padding: 0.7rem;
139
- border-radius: 4px;
140
- margin-bottom: 0.5rem;
141
- }
142
-
143
- .nav-bottom {
144
- position: fixed;
145
- bottom: 0;
146
- left: 0;
147
- width: 100%;
148
- background-color: #1a1a1a;
149
- display: flex;
150
- justify-content: space-around;
151
- padding: 0.7rem 0;
152
- box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
153
- }
154
-
155
- .nav-item {
156
- text-align: center;
157
- color: #888;
158
- text-decoration: none;
159
- font-size: 0.85rem;
160
- transition: color 0.2s;
161
- }
162
-
163
- .nav-item.active {
164
- color: var(--accent);
165
- }
166
-
167
- .nav-icon {
168
- font-size: 1.4rem;
169
- margin-bottom: 0.2rem;
170
- }
171
-
172
- .workout-card {
173
- border-left: 3px solid var(--accent);
174
- cursor: pointer;
175
- transition: transform 0.2s;
176
- }
177
-
178
- .workout-card:hover {
179
- transform: translateX(5px);
180
- }
181
-
182
- .workout-header {
183
- display: flex;
184
- justify-content: space-between;
185
- }
186
-
187
- .stat-card {
188
- text-align: center;
189
- padding: 1rem;
190
- }
191
-
192
- .stat-value {
193
- font-size: 1.8rem;
194
- color: var(--accent);
195
- font-weight: bold;
196
- }
197
-
198
- .stats-grid {
199
- display: grid;
200
- grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
201
- gap: 1rem;
202
- }
203
-
204
- .hidden {
205
- display: none;
206
- }
207
-
208
- #app-container > div {
209
- display: none;
210
- }
211
-
212
- #app-container > div.active {
213
- display: block;
214
- }
215
-
216
- .flex-between {
217
- display: flex;
218
- justify-content: space-between;
219
- align-items: center;
220
- }
221
-
222
- .badge {
223
- background-color: var(--accent);
224
- color: white;
225
- padding: 0.2rem 0.5rem;
226
- border-radius: 10px;
227
- font-size: 0.8rem;
228
- }
229
-
230
- .spinner {
231
- border: 4px solid rgba(0, 0, 0, 0.1);
232
- width: 36px;
233
- height: 36px;
234
- border-radius: 50%;
235
- border-left-color: var(--accent);
236
- animation: spin 1s linear infinite;
237
- margin: 2rem auto;
238
- display: none;
239
- }
240
-
241
- @keyframes spin {
242
- 0% { transform: rotate(0deg); }
243
- 100% { transform: rotate(360deg); }
244
- }
245
-
246
- .exercise-summary {
247
- margin: 0.3rem 0;
248
- padding: 0.3rem 0;
249
- border-bottom: 1px solid #333;
250
- }
251
-
252
- .satisfaction {
253
- display: flex;
254
- align-items: center;
255
- justify-content: center;
256
- flex-direction: column;
257
- margin-top: 1rem;
258
- }
259
-
260
- .satisfaction-value {
261
- font-size: 2rem;
262
- color: var(--accent);
263
- margin-top: 0.5rem;
264
- }
265
-
266
- /* Pour les téléphones */
267
- @media (max-width: 600px) {
268
- .form-row {
269
- flex-direction: column;
270
- gap: 0;
271
- }
272
-
273
- .container {
274
- padding: 0.5rem;
275
- }
276
-
277
- h1 {
278
- font-size: 1.5rem;
279
- }
280
- }
281
  </style>
282
  </head>
283
  <body>
284
  <div class="container">
285
  <header>
286
- <h1>Track Lift</h1>
287
  <p>Suivi de vos séances de musculation</p>
288
  </header>
289
-
290
- <div id="app-container">
291
- <!-- Page d'accueil / Liste des séances -->
292
- <div id="home-page" class="active">
293
- <div class="flex-between">
294
- <h2>Mes séances</h2>
295
- <button id="new-workout-btn" class="btn">Nouvelle séance</button>
296
- </div>
297
-
298
- <div id="workouts-list" class="workout-list">
299
- <!-- Liste des séances qui sera remplie dynamiquement -->
300
- <div class="spinner"></div>
301
- <p id="empty-workout-message" class="hidden" style="text-align: center; margin-top: 2rem;">
302
- Aucune séance enregistrée. Créez votre première séance avec le bouton ci-dessus.
303
- </p>
304
  </div>
 
 
 
 
 
305
  </div>
306
-
307
- <!-- Page Nouvelle Séance -->
308
- <div id="new-workout-page" class="hidden">
309
- <div class="flex-between">
310
- <h2>Nouvelle séance</h2>
311
- <button id="save-workout-btn" class="btn">Enregistrer</button>
312
- </div>
313
-
314
- <div class="card">
315
- <div class="form-group">
316
- <label for="workout-name">Nom de la séance</label>
317
- <input type="text" id="workout-name" placeholder="Ex: Push, Jambes, Full body...">
 
 
 
318
  </div>
319
-
320
- <div class="form-row">
321
- <div class="form-group">
322
- <label for="workout-date">Date</label>
323
- <input type="date" id="workout-date">
324
- </div>
325
- <div class="form-group">
326
- <label for="workout-duration">Durée (minutes)</label>
327
- <input type="number" id="workout-duration" min="1" placeholder="60">
328
- </div>
329
  </div>
330
  </div>
331
-
332
- <h3 style="margin: 1rem 0;">Exercices</h3>
333
- <div id="exercises-container">
334
- <!-- Exercices ajoutés dynamiquement -->
335
- </div>
336
-
337
- <button id="add-exercise-btn" class="btn btn-outline" style="width: 100%; margin-top: 1rem;">
338
- + Ajouter un exercice
339
- </button>
340
-
341
- <div class="card" style="margin-top: 2rem;">
342
- <div class="form-group">
343
- <label for="satisfaction">Niveau de satisfaction (1-100%)</label>
344
- <input type="range" id="satisfaction" min="1" max="100" value="75">
345
- <div class="satisfaction">
346
- <span>Satisfaction</span>
347
- <div class="satisfaction-value">75%</div>
348
  </div>
349
  </div>
350
- </div>
351
- </div>
352
-
353
- <!-- Page Détail Séance -->
354
- <div id="workout-details-page" class="hidden">
355
- <div class="flex-between">
356
- <h2 id="detail-workout-name">Détail de la séance</h2>
357
- <button id="back-to-home" class="btn btn-outline">Retour</button>
358
- </div>
359
-
360
- <div class="card">
361
- <div class="workout-details-info">
362
  <div class="form-row">
363
- <p><strong>Date:</strong> <span id="detail-date"></span></p>
364
- <p><strong>Durée:</strong> <span id="detail-duration"></span> min</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  </div>
366
  </div>
367
  </div>
368
-
369
- <div class="stats-grid" style="margin-top: 1rem;">
370
- <div class="card stat-card">
371
- <div class="stat-value" id="detail-tonnage">0</div>
372
- <div>Tonnage total (kg)</div>
 
373
  </div>
374
- <div class="card stat-card">
375
- <div class="stat-value" id="detail-satisfaction">0%</div>
376
- <div>Satisfaction</div>
 
 
 
 
377
  </div>
378
- <div class="card stat-card">
379
- <div class="stat-value" id="detail-exercises-count">0</div>
380
- <div>Exercices</div>
 
 
 
 
 
 
 
 
 
 
381
  </div>
 
 
 
382
  </div>
383
-
384
- <h3 style="margin: 1.5rem 0 1rem;">Exercices réalisés</h3>
385
- <div id="detail-exercises-container">
386
- <!-- Exercices affichés dynamiquement -->
387
- </div>
388
-
389
- <button id="delete-workout-btn" class="btn btn-danger" style="width: 100%; margin-top: 2rem;">
390
- Supprimer cette séance
391
- </button>
392
- </div>
393
-
394
- <!-- Page Statistiques -->
395
- <div id="stats-page" class="hidden">
396
- <h2>Statistiques</h2>
397
-
398
- <div class="stats-grid">
399
- <div class="card stat-card">
400
- <div class="stat-value" id="stats-workout-count">0</div>
401
- <div>Séances totales</div>
402
- </div>
403
- <div class="card stat-card">
404
- <div class="stat-value" id="stats-avg-tonnage">0</div>
405
- <div>Tonnage moyen</div>
406
  </div>
407
- <div class="card stat-card">
408
- <div class="stat-value" id="stats-avg-satisfaction">0%</div>
409
- <div>Satisfaction moyenne</div>
 
 
410
  </div>
411
  </div>
412
-
413
- <h3 style="margin: 1.5rem 0 1rem;">Dernières tendances</h3>
414
- <div class="card">
415
- <p style="text-align: center; margin: 1rem 0;">
416
- Les statistiques détaillées seront calculées après plusieurs séances enregistrées.
417
- </p>
418
- </div>
419
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
420
  </div>
421
  </div>
422
-
423
- <!-- Menu de navigation en bas -->
424
- <nav class="nav-bottom">
425
- <a href="#" class="nav-item active" data-page="home-page">
426
- <div class="nav-icon">📋</div>
427
- <div>Séances</div>
428
- </a>
429
- <a href="#" class="nav-item" data-page="stats-page">
430
- <div class="nav-icon">📊</div>
431
- <div>Stats</div>
432
- </a>
433
- </nav>
434
-
435
  <script>
436
- // Structure de données
437
- let workouts = [];
438
- const STORAGE_KEY = 'workout-tracker-data';
439
-
440
- // Éléments DOM
 
 
 
 
 
 
 
 
 
441
  const appContainer = document.getElementById('app-container');
442
  const navItems = document.querySelectorAll('.nav-item');
443
  const newWorkoutBtn = document.getElementById('new-workout-btn');
444
  const saveWorkoutBtn = document.getElementById('save-workout-btn');
 
445
  const addExerciseBtn = document.getElementById('add-exercise-btn');
446
  const exercisesContainer = document.getElementById('exercises-container');
447
  const workoutsList = document.getElementById('workouts-list');
@@ -450,271 +250,357 @@
450
  const satisfactionRange = document.getElementById('satisfaction');
451
  const satisfactionValue = document.querySelector('.satisfaction-value');
452
  const emptyWorkoutMessage = document.getElementById('empty-workout-message');
453
-
454
- // Variables globales
455
- let currentWorkoutId = null;
456
-
457
- // Initialisation
458
  document.addEventListener('DOMContentLoaded', () => {
459
- loadWorkouts();
460
- renderWorkoutsList();
 
 
 
 
 
 
 
461
  initEventListeners();
462
- setTodayDate();
 
 
 
463
  });
464
-
465
- // Définir la date d'aujourd'hui par défaut
466
- function setTodayDate() {
467
- const today = new Date().toISOString().split('T')[0];
468
- document.getElementById('workout-date').value = today;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  }
470
-
471
- // Charger les données depuis le stockage local
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  function loadWorkouts() {
473
- const savedData = localStorage.getItem(STORAGE_KEY);
474
- if (savedData) {
475
- workouts = JSON.parse(savedData);
 
476
  }
 
 
 
477
  }
478
-
479
- // Sauvegarder les données dans le stockage local
480
  function saveWorkouts() {
481
- localStorage.setItem(STORAGE_KEY, JSON.stringify(workouts));
 
 
 
 
 
 
482
  }
483
-
484
- // Initialiser les événements
485
  function initEventListeners() {
486
- // Navigation
 
 
 
 
 
 
 
 
 
 
 
487
  navItems.forEach(item => {
488
  item.addEventListener('click', (e) => {
489
  e.preventDefault();
 
490
  const targetPage = item.getAttribute('data-page');
491
-
492
- // Changer la page active
493
- document.querySelectorAll('#app-container > div').forEach(page => {
494
- page.classList.remove('active');
495
- });
496
- document.getElementById(targetPage).classList.add('active');
497
-
498
- // Mettre à jour la navigation
499
- navItems.forEach(navItem => navItem.classList.remove('active'));
500
- item.classList.add('active');
501
-
502
- // Si on va à la page statistiques, mettre à jour les stats
503
  if (targetPage === 'stats-page') {
504
  updateStats();
505
  }
506
  });
507
  });
508
-
509
- // Nouvelle séance
510
  newWorkoutBtn.addEventListener('click', () => {
 
511
  showPage('new-workout-page');
512
  clearNewWorkoutForm();
513
- currentWorkoutId = null;
514
  });
515
-
516
- // Enregistrer la séance
 
 
 
 
 
517
  saveWorkoutBtn.addEventListener('click', saveWorkout);
518
-
519
- // Ajouter un exercice
520
  addExerciseBtn.addEventListener('click', addExercise);
521
-
522
- // Retour à l'accueil
523
  backToHomeBtn.addEventListener('click', () => {
524
  showPage('home-page');
525
  });
526
-
527
- // Supprimer une séance
528
  deleteWorkoutBtn.addEventListener('click', deleteWorkout);
529
-
530
- // Affichage du pourcentage de satisfaction en temps réel
531
  satisfactionRange.addEventListener('input', () => {
532
  satisfactionValue.textContent = `${satisfactionRange.value}%`;
533
  });
534
  }
535
-
536
- // Afficher une page spécifique
 
 
 
 
 
 
 
 
 
537
  function showPage(pageId) {
 
 
 
 
 
 
538
  document.querySelectorAll('#app-container > div').forEach(page => {
539
- page.classList.remove('active');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  });
541
- document.getElementById(pageId).classList.add('active');
542
-
543
- // Mettre à jour la navigation
544
- const navItem = document.querySelector(`.nav-item[data-page="${pageId}"]`);
545
- if (navItem) {
546
- navItems.forEach(item => item.classList.remove('active'));
547
- navItem.classList.add('active');
548
- }
549
  }
550
-
551
- // Ajouter un exercice au formulaire
552
  function addExercise() {
 
 
 
553
  const exerciseId = `exercise-${Date.now()}`;
554
  const exerciseDiv = document.createElement('div');
555
- exerciseDiv.className = 'exercise card';
556
  exerciseDiv.setAttribute('data-exercise-id', exerciseId);
557
-
558
  exerciseDiv.innerHTML = `
559
  <div class="exercise-header">
560
- <input type="text" placeholder="Nom de l'exercice" class="exercise-name">
561
- <button class="btn btn-danger remove-exercise" style="padding: 0.3rem 0.6rem;">×</button>
562
  </div>
563
- <div class="form-row">
564
- <div class="form-group">
565
- <label>
566
- <input type="checkbox" class="unilateral-checkbox"> Exercice unilatéral
567
- </label>
568
- </div>
569
  </div>
570
  <div class="series-container">
571
- <!-- Les séries seront ajoutées ici -->
572
  </div>
573
- <button class="btn btn-outline add-series" style="width: 100%; margin-top: 0.5rem;">
574
- + Ajouter une série
575
- </button>
576
  `;
577
-
578
  exercisesContainer.appendChild(exerciseDiv);
579
-
580
- // Ajouter les écouteurs d'événements
581
  const removeBtn = exerciseDiv.querySelector('.remove-exercise');
582
- removeBtn.addEventListener('click', () => {
583
- exerciseDiv.remove();
584
- });
585
-
586
  const addSeriesBtn = exerciseDiv.querySelector('.add-series');
587
- addSeriesBtn.addEventListener('click', () => {
588
- addSeries(exerciseDiv.querySelector('.series-container'));
589
- });
590
-
591
- // Ajouter la première série automatiquement
592
- addSeries(exerciseDiv.querySelector('.series-container'));
593
  }
594
-
595
- // Ajouter une série à un exercice
596
  function addSeries(container) {
 
 
 
597
  const seriesId = `series-${Date.now()}`;
598
  const seriesDiv = document.createElement('div');
599
- seriesDiv.className = 'series';
600
  seriesDiv.setAttribute('data-series-id', seriesId);
601
-
602
  seriesDiv.innerHTML = `
603
- <div class="form-row">
604
- <div class="form-group">
605
- <label>Répétitions</label>
606
- <input type="number" class="reps" min="1" placeholder="12">
607
  </div>
608
- <div class="form-group">
609
- <label>Charge (kg)</label>
610
- <input type="number" class="weight" min="0" step="0.5" placeholder="20">
611
  </div>
612
- <div class="form-group" style="flex: 0.5;">
613
- <label>
614
- <input type="checkbox" class="degressive-checkbox"> Série dégressive
615
- </label>
616
  </div>
617
- <button class="btn btn-danger remove-series" style="align-self: flex-end; padding: 0.3rem 0.6rem; flex: 0.2;">×</button>
618
  </div>
619
  `;
620
-
621
  container.appendChild(seriesDiv);
622
-
623
- // Écouteur pour supprimer une série
624
  const removeBtn = seriesDiv.querySelector('.remove-series');
625
- removeBtn.addEventListener('click', () => {
626
- seriesDiv.remove();
627
- });
628
  }
629
-
630
- // Sauvegarder la séance
631
  function saveWorkout() {
632
- // Récupérer les données de base
 
 
 
633
  const workoutName = document.getElementById('workout-name').value.trim();
634
  const workoutDate = document.getElementById('workout-date').value;
635
  const workoutDuration = parseInt(document.getElementById('workout-duration').value) || 0;
636
  const satisfaction = parseInt(document.getElementById('satisfaction').value);
637
-
638
- // Vérifier les données obligatoires
639
- if (!workoutName) {
640
- alert("Veuillez saisir un nom pour la séance");
641
- return;
642
- }
643
-
644
- if (!workoutDate) {
645
- alert("Veuillez saisir une date");
646
- return;
647
- }
648
-
649
- if (workoutDuration <= 0) {
650
- alert("Veuillez saisir une durée valide");
651
- return;
652
- }
653
-
654
- // Récupérer les exercices
655
  const exercises = [];
656
  const exerciseElements = exercisesContainer.querySelectorAll('.exercise');
657
-
658
- if (exerciseElements.length === 0) {
659
- alert("Ajoutez au moins un exercice");
660
- return;
661
- }
662
-
663
- for (const exerciseEl of exerciseElements) {
664
  const exerciseName = exerciseEl.querySelector('.exercise-name').value.trim();
665
  const isUnilateral = exerciseEl.querySelector('.unilateral-checkbox').checked;
666
-
667
  if (!exerciseName) {
668
- alert("Veuillez saisir un nom pour chaque exercice");
 
669
  return;
670
  }
671
-
672
- // Récupérer les séries
673
  const series = [];
674
  const seriesElements = exerciseEl.querySelectorAll('.series');
675
-
676
  if (seriesElements.length === 0) {
677
- alert(`Ajoutez au moins une série à l'exercice "${exerciseName}"`);
 
678
  return;
679
  }
680
-
681
- for (const seriesEl of seriesElements) {
 
 
682
  const reps = parseInt(seriesEl.querySelector('.reps').value) || 0;
683
  const weight = parseFloat(seriesEl.querySelector('.weight').value) || 0;
684
  const isDegressive = seriesEl.querySelector('.degressive-checkbox').checked;
685
-
686
  if (reps <= 0) {
687
- alert(`Veuillez saisir un nombre de répétitions valide pour l'exercice "${exerciseName}"`);
 
688
  return;
689
  }
690
-
691
- series.push({
692
- reps,
693
- weight,
694
- isDegressive
695
- });
 
696
  }
697
-
698
- exercises.push({
699
- name: exerciseName,
700
- isUnilateral,
701
- series
 
 
 
 
 
 
 
702
  });
703
- }
704
-
705
- // Calculer le tonnage total
706
- let totalTonnage = 0;
707
- exercises.forEach(exercise => {
708
- exercise.series.forEach(series => {
709
- // Si l'exercice est unilatéral, on multiplie par 2 le tonnage
710
- const weightFactor = exercise.isUnilateral ? 2 : 1;
711
- totalTonnage += series.reps * series.weight * weightFactor;
712
- });
713
- });
714
-
715
- // Créer l'objet séance
716
  const workout = {
717
- id: currentWorkoutId || `workout-${Date.now()}`,
718
  name: workoutName,
719
  date: workoutDate,
720
  duration: workoutDuration,
@@ -722,187 +608,186 @@ exercises.forEach(exercise => {
722
  totalTonnage,
723
  satisfaction
724
  };
725
-
726
- // Ajouter ou mettre à jour la séance
727
- if (currentWorkoutId) {
728
- // Mise à jour d'une séance existante
729
- const index = workouts.findIndex(w => w.id === currentWorkoutId);
730
- if (index !== -1) {
731
- workouts[index] = workout;
732
- }
733
  } else {
734
- // Nouvelle séance
735
- workouts.push(workout);
736
  }
737
-
738
- // Sauvegarder les données
739
  saveWorkouts();
740
-
741
- // Retourner à la liste des séances
742
  showPage('home-page');
743
  renderWorkoutsList();
744
  }
745
-
746
- // Afficher la liste des séances
747
  function renderWorkoutsList() {
748
- // Vider la liste
749
- workoutsList.innerHTML = '';
750
-
751
- // Afficher le message si pas de séances
 
 
 
 
 
752
  if (workouts.length === 0) {
753
  emptyWorkoutMessage.classList.remove('hidden');
754
- return;
755
  }
756
-
757
  emptyWorkoutMessage.classList.add('hidden');
758
-
759
- // Trier les séances par date (la plus récente en premier)
760
- const sortedWorkouts = [...workouts].sort((a, b) => {
761
- return new Date(b.date) - new Date(a.date);
762
- });
763
-
764
- // Générer le HTML pour chaque séance
765
- sortedWorkouts.forEach(workout => {
766
  const workoutDate = new Date(workout.date).toLocaleDateString('fr-FR');
767
  const workoutDiv = document.createElement('div');
768
- workoutDiv.className = 'card workout-card';
769
  workoutDiv.setAttribute('data-workout-id', workout.id);
770
-
771
  workoutDiv.innerHTML = `
772
  <div class="workout-header">
773
  <h3>${workout.name}</h3>
774
  <div class="badge">${workoutDate}</div>
775
  </div>
776
- <div class="workout-details">
777
- <p><strong>Durée:</strong> ${workout.duration} min</p>
778
- <p><strong>Exercices:</strong> ${workout.exercises.length}</p>
779
- <p><strong>Tonnage:</strong> ${workout.totalTonnage.toFixed(1)} kg</p>
780
- <p><strong>Satisfaction:</strong> ${workout.satisfaction}%</p>
781
  </div>
782
  `;
783
-
784
- // Ajouter l'écouteur pour afficher les détails
785
- workoutDiv.addEventListener('click', () => {
786
- displayWorkoutDetails(workout.id);
787
- });
788
-
789
  workoutsList.appendChild(workoutDiv);
790
  });
791
-
792
- // Mettre à jour les statistiques globales
793
- updateStats();
794
  }
795
-
796
- // Afficher les détails d'une séance
797
  function displayWorkoutDetails(workoutId) {
 
 
 
798
  const workout = workouts.find(w => w.id === workoutId);
799
- if (!workout) return;
800
-
801
- // Mettre à jour les informations de base
802
  document.getElementById('detail-workout-name').textContent = workout.name;
803
  document.getElementById('detail-date').textContent = new Date(workout.date).toLocaleDateString('fr-FR');
804
  document.getElementById('detail-duration').textContent = workout.duration;
805
  document.getElementById('detail-tonnage').textContent = workout.totalTonnage.toFixed(1);
806
  document.getElementById('detail-satisfaction').textContent = `${workout.satisfaction}%`;
807
  document.getElementById('detail-exercises-count').textContent = workout.exercises.length;
808
-
809
- // Afficher les exercices
810
- const exercisesContainer = document.getElementById('detail-exercises-container');
811
- exercisesContainer.innerHTML = '';
812
-
 
813
  workout.exercises.forEach(exercise => {
814
  const exerciseDiv = document.createElement('div');
815
- exerciseDiv.className = 'card';
816
-
817
- let seriesHtml = '';
818
- exercise.series.forEach(series => {
819
- const degressiveLabel = series.isDegressive ? ' <span class="badge">Dégressive</span>' : '';
820
- const weightFactor = exercise.isUnilateral ? 2 : 1;
821
- const seriesTonnage = series.reps * series.weight * weightFactor;
822
-
823
- seriesHtml += `
824
- <div class="exercise-summary">
825
- <div class="flex-between">
826
- <span>${series.reps} répétitions × ${series.weight} kg${degressiveLabel}</span>
827
- <span>${seriesTonnage.toFixed(1)} kg</span>
828
- </div>
829
- </div>
830
- `;
831
- });
832
-
833
  const unilateralLabel = exercise.isUnilateral ? ' <span class="badge">Unilatéral</span>' : '';
834
-
835
  exerciseDiv.innerHTML = `
836
- <h3>${exercise.name}${unilateralLabel}</h3>
837
  <div class="series-summary">
838
  ${seriesHtml}
839
  </div>
840
  `;
841
-
842
- exercisesContainer.appendChild(exerciseDiv);
843
  });
844
-
845
- // Stocker l'ID pour les éditions/suppressions
 
846
  currentWorkoutId = workoutId;
847
-
848
- // Afficher la page
849
  showPage('workout-details-page');
850
  }
851
-
852
- // Supprimer une séance
853
- function deleteWorkout() {
854
- if (!currentWorkoutId) return;
855
-
856
- const confirmDelete = confirm("Êtes-vous sûr de vouloir supprimer cette séance ?");
857
- if (!confirmDelete) return
858
 
859
- // Continuation du script précédent
860
 
 
 
 
 
 
861
  if (!confirmDelete) return;
862
-
863
- // Filtrer la séance à supprimer
864
  workouts = workouts.filter(w => w.id !== currentWorkoutId);
865
-
866
- // Sauvegarder les données
867
  saveWorkouts();
868
-
869
- // Retourner à la liste des séances
870
  showPage('home-page');
871
- renderWorkoutsList();
872
  }
873
-
874
- // Effacer le formulaire de nouvelle séance
875
  function clearNewWorkoutForm() {
 
 
 
876
  document.getElementById('workout-name').value = '';
877
  document.getElementById('workout-duration').value = '';
878
- setTodayDate();
879
- exercisesContainer.innerHTML = '';
880
- document.getElementById('satisfaction').value = 75;
881
  satisfactionValue.textContent = '75%';
 
882
  }
883
-
884
- // Mettre à jour les statistiques
885
  function updateStats() {
886
- if (workouts.length === 0) {
887
- document.getElementById('stats-workout-count').textContent = '0';
 
 
 
 
 
888
  document.getElementById('stats-avg-tonnage').textContent = '0';
889
  document.getElementById('stats-avg-satisfaction').textContent = '0%';
890
  return;
891
  }
892
-
893
- // Nombre total de séances
894
- document.getElementById('stats-workout-count').textContent = workouts.length;
895
-
896
- // Tonnage moyen
897
- const totalTonnage = workouts.reduce((sum, workout) => sum + workout.totalTonnage, 0);
898
- const avgTonnage = totalTonnage / workouts.length;
899
  document.getElementById('stats-avg-tonnage').textContent = avgTonnage.toFixed(1);
900
-
901
- // Satisfaction moyenne
902
  const totalSatisfaction = workouts.reduce((sum, workout) => sum + workout.satisfaction, 0);
903
- const avgSatisfaction = totalSatisfaction / workouts.length;
904
  document.getElementById('stats-avg-satisfaction').textContent = `${Math.round(avgSatisfaction)}%`;
905
  }
 
906
  </script>
907
  </body>
908
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>LiftTrack - Suivi Musculation Multi-Utilisateurs</title>
7
  <style>
8
+ /* --- Styles existants (raccourcis pour la lisibilité) --- */
9
  :root {
10
  --bg-dark: #121212;
11
  --bg-card: #1e1e1e;
 
14
  --accent-dark: #3a8a3d;
15
  --danger: #f44336;
16
  }
17
+ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
18
+ body { background-color: var(--bg-dark); color: var(--text-light); min-height: 100vh; padding-bottom: 80px; }
19
+ .container { width: 100%; max-width: 800px; margin: 0 auto; padding: 1rem; }
20
+ header { padding: 1rem 0; text-align: center; border-bottom: 1px solid #333; margin-bottom: 1rem; }
21
+ h1, h2, h3 { color: var(--accent); }
22
+ .btn { background-color: var(--accent); color: white; border: none; padding: 0.6rem 1.2rem; border-radius: 4px; cursor: pointer; font-weight: bold; transition: background-color 0.2s; }
23
+ .btn:hover { background-color: var(--accent-dark); }
24
+ .btn-outline { background-color: transparent; color: var(--accent); border: 1px solid var(--accent); }
25
+ .btn-danger { background-color: var(--danger); }
26
+ input, select, textarea { width: 100%; padding: 0.6rem; margin-bottom: 1rem; background-color: #2a2a2a; border: 1px solid #444; border-radius: 4px; color: var(--text-light); }
27
+ input[type="checkbox"] { width: auto; margin-right: 0.5rem; }
28
+ .card { background-color: var(--bg-card); border-radius: 8px; padding: 1rem; margin-bottom: 1rem; box-shadow: 0 2px 5px rgba(0,0,0,0.2); }
29
+ .form-group { margin-bottom: 1rem; }
30
+ .form-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; }
31
+ .form-row > * { flex: 1; margin-bottom: 0; }
32
+ label { display: block; margin-bottom: 0.3rem; color: #bbb; }
33
+ .exercise { border-left: 3px solid var(--accent); padding-left: 1rem; margin-bottom: 1.5rem; }
34
+ .exercise-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; }
35
+ .series-container { margin-left: 0.5rem; margin-top: 0.5rem; }
36
+ .series { background-color: #252525; padding: 0.7rem; border-radius: 4px; margin-bottom: 0.5rem; }
37
+ .nav-bottom { position: fixed; bottom: 0; left: 0; width: 100%; background-color: #1a1a1a; display: flex; justify-content: space-around; padding: 0.7rem 0; box-shadow: 0 -2px 10px rgba(0,0,0,0.3); }
38
+ .nav-item { text-align: center; color: #888; text-decoration: none; font-size: 0.85rem; transition: color 0.2s; }
39
+ .nav-item.active { color: var(--accent); }
40
+ .nav-icon { font-size: 1.4rem; margin-bottom: 0.2rem; }
41
+ .workout-card { border-left: 3px solid var(--accent); cursor: pointer; transition: transform 0.2s; }
42
+ .workout-card:hover { transform: translateX(5px); }
43
+ .workout-header { display: flex; justify-content: space-between; }
44
+ .stat-card { text-align: center; padding: 1rem; }
45
+ .stat-value { font-size: 1.8rem; color: var(--accent); font-weight: bold; }
46
+ .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem; }
47
+ .hidden { display: none; }
48
+ #app-container > div:not(.active) { display: none; } /* Hide inactive pages */
49
+ #login-page { display: block; } /* Show login initially */
50
+ #main-app-content { display: none; } /* Hide main app initially */
51
+ .flex-between { display: flex; justify-content: space-between; align-items: center; }
52
+ .badge { background-color: var(--accent); color: white; padding: 0.2rem 0.5rem; border-radius: 10px; font-size: 0.8rem; }
53
+ .spinner { border: 4px solid rgba(0, 0, 0, 0.1); width: 36px; height: 36px; border-radius: 50%; border-left-color: var(--accent); animation: spin 1s linear infinite; margin: 2rem auto; display: none; }
54
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
55
+ .exercise-summary { margin: 0.3rem 0; padding: 0.3rem 0; border-bottom: 1px solid #333; }
56
+ .satisfaction { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 1rem; }
57
+ .satisfaction-value { font-size: 2rem; color: var(--accent); margin-top: 0.5rem; }
58
+ @media (max-width: 600px) { .form-row { flex-direction: column; gap: 0; } .container { padding: 0.5rem; } h1 { font-size: 1.5rem; } }
59
+ /* Styles pour la page de connexion */
60
+ #login-page .card { max-width: 400px; margin: 2rem auto; }
61
+ #login-page h2 { text-align: center; margin-bottom: 1.5rem; }
62
+ #login-error { color: var(--danger); text-align: center; margin-top: 1rem; display: none; }
63
+ .user-info { text-align: right; margin-bottom: 1rem; font-size: 0.9rem; color: #bbb;}
64
+ .user-info button { margin-left: 0.5rem; padding: 0.2rem 0.5rem; font-size: 0.8rem; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  </style>
66
  </head>
67
  <body>
68
  <div class="container">
69
  <header>
70
+ <h1>LiftTrack</h1>
71
  <p>Suivi de vos séances de musculation</p>
72
  </header>
73
+
74
+ <!-- Écran de Connexion -->
75
+ <div id="login-page">
76
+ <div class="card">
77
+ <h2>Accès Utilisateur</h2>
78
+ <div class="form-group">
79
+ <label for="username">Nom d'utilisateur</label>
80
+ <input type="text" id="username" placeholder="Entrez votre nom ou pseudo">
 
 
 
 
 
 
 
81
  </div>
82
+ <button id="login-btn" class="btn" style="width: 100%;">Accéder / Créer</button>
83
+ <p id="login-error">Nom d'utilisateur invalide.</p>
84
+ <p style="font-size: 0.8rem; color: #888; text-align: center; margin-top: 1rem;">
85
+ Entrez un nom pour accéder à vos données ou créer un nouvel espace si le nom n'existe pas.
86
+ </p>
87
  </div>
88
+ </div>
89
+
90
+ <!-- Contenu Principal de l'Application (initiallement caché) -->
91
+ <div id="main-app-content">
92
+ <div class="user-info">
93
+ Connecté en tant que: <strong id="current-user-display"></strong>
94
+ <button id="logout-btn" class="btn btn-outline">Changer d'utilisateur</button>
95
+ </div>
96
+
97
+ <div id="app-container">
98
+ <!-- Page Accueil / Liste Séances -->
99
+ <div id="home-page" class="active">
100
+ <div class="flex-between">
101
+ <h2>Mes séances</h2>
102
+ <button id="new-workout-btn" class="btn">Nouvelle séance</button>
103
  </div>
104
+ <div id="workouts-list" class="workout-list">
105
+ <div class="spinner"></div>
106
+ <p id="empty-workout-message" class="hidden" style="text-align: center; margin-top: 2rem;">
107
+ Aucune séance enregistrée pour cet utilisateur.
108
+ </p>
 
 
 
 
 
109
  </div>
110
  </div>
111
+
112
+ <!-- Page Nouvelle Séance -->
113
+ <div id="new-workout-page" class="hidden">
114
+ <div class="flex-between">
115
+ <h2>Nouvelle séance</h2>
116
+ <div> <!-- Conteneur pour les boutons -->
117
+ <button id="cancel-new-workout-btn" class="btn btn-outline" style="margin-right: 0.5rem;">Annuler</button>
118
+ <button id="save-workout-btn" class="btn">Enregistrer</button>
 
 
 
 
 
 
 
 
 
119
  </div>
120
  </div>
121
+ <div class="card">
122
+ <div class="form-group">
123
+ <label for="workout-name">Nom de la séance</label>
124
+ <input type="text" id="workout-name" placeholder="Ex: Push, Legs, Full Body...">
125
+ </div>
 
 
 
 
 
 
 
126
  <div class="form-row">
127
+ <div class="form-group">
128
+ <label for="workout-date">Date</label>
129
+ <input type="date" id="workout-date">
130
+ </div>
131
+ <div class="form-group">
132
+ <label for="workout-duration">Durée (min)</label>
133
+ <input type="number" id="workout-duration" min="1" placeholder="60">
134
+ </div>
135
+ </div>
136
+ </div>
137
+ <h3 style="margin: 1rem 0;">Exercices</h3>
138
+ <div id="exercises-container"></div>
139
+ <button id="add-exercise-btn" class="btn btn-outline" style="width: 100%; margin-top: 1rem;">+ Ajouter Exercice</button>
140
+ <div class="card" style="margin-top: 2rem;">
141
+ <div class="form-group">
142
+ <label for="satisfaction">Niveau de satisfaction (1-100%)</label>
143
+ <input type="range" id="satisfaction" min="1" max="100" value="75">
144
+ <div class="satisfaction">
145
+ <span>Satisfaction</span>
146
+ <div class="satisfaction-value">75%</div>
147
+ </div>
148
  </div>
149
  </div>
150
  </div>
151
+
152
+ <!-- Page Détail Séance -->
153
+ <div id="workout-details-page" class="hidden">
154
+ <div class="flex-between">
155
+ <h2 id="detail-workout-name">Détail Séance</h2>
156
+ <button id="back-to-home" class="btn btn-outline">Retour</button>
157
  </div>
158
+ <div class="card">
159
+ <div class="workout-details-info">
160
+ <div class="form-row">
161
+ <p><strong>Date:</strong> <span id="detail-date"></span></p>
162
+ <p><strong>Durée:</strong> <span id="detail-duration"></span> min</p>
163
+ </div>
164
+ </div>
165
  </div>
166
+ <div class="stats-grid" style="margin-top: 1rem;">
167
+ <div class="card stat-card">
168
+ <div class="stat-value" id="detail-tonnage">0</div>
169
+ <div>Tonnage Total (kg)</div>
170
+ </div>
171
+ <div class="card stat-card">
172
+ <div class="stat-value" id="detail-satisfaction">0%</div>
173
+ <div>Satisfaction</div>
174
+ </div>
175
+ <div class="card stat-card">
176
+ <div class="stat-value" id="detail-exercises-count">0</div>
177
+ <div>Exercices</div>
178
+ </div>
179
  </div>
180
+ <h3 style="margin: 1.5rem 0 1rem;">Exercices Réalisés</h3>
181
+ <div id="detail-exercises-container"></div>
182
+ <button id="delete-workout-btn" class="btn btn-danger" style="width: 100%; margin-top: 2rem;">Supprimer cette séance</button>
183
  </div>
184
+
185
+ <!-- Page Statistiques -->
186
+ <div id="stats-page" class="hidden">
187
+ <h2>Statistiques</h2>
188
+ <div class="stats-grid">
189
+ <div class="card stat-card">
190
+ <div class="stat-value" id="stats-workout-count">0</div>
191
+ <div>Séances Totales</div>
192
+ </div>
193
+ <div class="card stat-card">
194
+ <div class="stat-value" id="stats-avg-tonnage">0</div>
195
+ <div>Tonnage Moyen</div>
196
+ </div>
197
+ <div class="card stat-card">
198
+ <div class="stat-value" id="stats-avg-satisfaction">0%</div>
199
+ <div>Satisfaction Moyenne</div>
200
+ </div>
 
 
 
 
 
 
201
  </div>
202
+ <h3 style="margin: 1.5rem 0 1rem;">Tendances Récentes</h3>
203
+ <div class="card">
204
+ <p style="text-align: center; margin: 1rem 0;">
205
+ Les statistiques détaillées seront affichées après plusieurs séances enregistrées.
206
+ </p>
207
  </div>
208
  </div>
 
 
 
 
 
 
 
209
  </div>
210
+
211
+ <!-- Menu Navigation Bas -->
212
+ <nav class="nav-bottom">
213
+ <a href="#" class="nav-item active" data-page="home-page">
214
+ <div class="nav-icon">📋</div>
215
+ <div>Séances</div>
216
+ </a>
217
+ <a href="#" class="nav-item" data-page="stats-page">
218
+ <div class="nav-icon">📊</div>
219
+ <div>Stats</div>
220
+ </a>
221
+ </nav>
222
  </div>
223
  </div>
224
+
 
 
 
 
 
 
 
 
 
 
 
 
225
  <script>
226
+ // --- State Variables ---
227
+ let workouts = []; // Workouts for the current user
228
+ let currentUser = null; // Stores the logged-in username
229
+ let currentWorkoutId = null; // For editing/deleting specific workout
230
+
231
+ // --- DOM Elements ---
232
+ const loginPage = document.getElementById('login-page');
233
+ const mainAppContent = document.getElementById('main-app-content');
234
+ const usernameInput = document.getElementById('username');
235
+ const loginBtn = document.getElementById('login-btn');
236
+ const loginError = document.getElementById('login-error');
237
+ const currentUserDisplay = document.getElementById('current-user-display');
238
+ const logoutBtn = document.getElementById('logout-btn');
239
+
240
  const appContainer = document.getElementById('app-container');
241
  const navItems = document.querySelectorAll('.nav-item');
242
  const newWorkoutBtn = document.getElementById('new-workout-btn');
243
  const saveWorkoutBtn = document.getElementById('save-workout-btn');
244
+ const cancelNewWorkoutBtn = document.getElementById('cancel-new-workout-btn'); // Added cancel button
245
  const addExerciseBtn = document.getElementById('add-exercise-btn');
246
  const exercisesContainer = document.getElementById('exercises-container');
247
  const workoutsList = document.getElementById('workouts-list');
 
250
  const satisfactionRange = document.getElementById('satisfaction');
251
  const satisfactionValue = document.querySelector('.satisfaction-value');
252
  const emptyWorkoutMessage = document.getElementById('empty-workout-message');
253
+ const workoutDateInput = document.getElementById('workout-date'); // For setting date
254
+
255
+
256
+ // --- Initialization ---
 
257
  document.addEventListener('DOMContentLoaded', () => {
258
+ // Check if a user was previously logged in (in this browser session)
259
+ // Note: Using sessionStorage means they need to "log in" each time they open the tab/browser.
260
+ // You could use localStorage here too for longer persistence, but it's less secure if the computer is shared.
261
+ const rememberedUser = sessionStorage.getItem('liftTrackCurrentUser');
262
+ if (rememberedUser) {
263
+ loginUser(rememberedUser);
264
+ } else {
265
+ showLoginPage();
266
+ }
267
  initEventListeners();
268
+ // Set today's date only if the main app is potentially visible
269
+ if (currentUser) {
270
+ setTodayDate();
271
+ }
272
  });
273
+
274
+ // --- Authentication Functions ---
275
+
276
+ function showLoginPage() {
277
+ loginPage.style.display = 'block';
278
+ mainAppContent.style.display = 'none';
279
+ loginError.style.display = 'none'; // Hide error on show
280
+ usernameInput.value = ''; // Clear input
281
+ }
282
+
283
+ function showApp() {
284
+ loginPage.style.display = 'none';
285
+ mainAppContent.style.display = 'block';
286
+ currentUserDisplay.textContent = currentUser;
287
+ setTodayDate(); // Set date when app becomes visible
288
+ loadWorkouts(); // Load data for the current user
289
+ renderWorkoutsList(); // Display workouts
290
+ showPage('home-page'); // Default to home page
291
+ updateStats(); // Update stats for the logged-in user
292
+ }
293
+
294
+ function handleLogin() {
295
+ const username = usernameInput.value.trim();
296
+ if (username) {
297
+ loginUser(username);
298
+ } else {
299
+ loginError.textContent = "Veuillez entrer un nom d'utilisateur.";
300
+ loginError.style.display = 'block';
301
+ }
302
  }
303
+
304
+ function loginUser(username) {
305
+ currentUser = username;
306
+ sessionStorage.setItem('liftTrackCurrentUser', currentUser); // Remember user for the session
307
+ showApp();
308
+ }
309
+
310
+ function handleLogout() {
311
+ currentUser = null;
312
+ sessionStorage.removeItem('liftTrackCurrentUser'); // Forget user
313
+ workouts = []; // Clear current workout data from memory
314
+ showLoginPage();
315
+ }
316
+
317
+ // --- Data Persistence Functions (User-Specific) ---
318
+
319
+ function getStorageKey() {
320
+ if (!currentUser) return null; // Should not happen if logged in
321
+ // IMPORTANT: Make the key safe for localStorage (no weird chars)
322
+ const safeUsername = currentUser.replace(/[^a-zA-Z0-9_-]/g, '_');
323
+ return `liftTrackData_${safeUsername}`;
324
+ }
325
+
326
  function loadWorkouts() {
327
+ const storageKey = getStorageKey();
328
+ if (!storageKey) {
329
+ workouts = [];
330
+ return;
331
  }
332
+ const savedData = localStorage.getItem(storageKey);
333
+ workouts = savedData ? JSON.parse(savedData) : [];
334
+ console.log(`Loaded ${workouts.length} workouts for user ${currentUser}`);
335
  }
336
+
 
337
  function saveWorkouts() {
338
+ const storageKey = getStorageKey();
339
+ if (!storageKey) {
340
+ console.error("Cannot save, no user logged in.");
341
+ return;
342
+ }
343
+ localStorage.setItem(storageKey, JSON.stringify(workouts));
344
+ console.log(`Saved ${workouts.length} workouts for user ${currentUser}`);
345
  }
346
+
347
+ // --- Event Listeners ---
348
  function initEventListeners() {
349
+ loginBtn.addEventListener('click', handleLogin);
350
+ logoutBtn.addEventListener('click', handleLogout);
351
+
352
+ // Prevent form submission if username input is focused and Enter is pressed
353
+ usernameInput.addEventListener('keypress', (e) => {
354
+ if (e.key === 'Enter') {
355
+ e.preventDefault(); // Stop default form submission behavior
356
+ handleLogin(); // Trigger login logic
357
+ }
358
+ });
359
+
360
+ // Navigation (unchanged logic, just needs to be inside init)
361
  navItems.forEach(item => {
362
  item.addEventListener('click', (e) => {
363
  e.preventDefault();
364
+ if (!currentUser) return; // Don't navigate if not logged in
365
  const targetPage = item.getAttribute('data-page');
366
+ showPage(targetPage); // Use the existing showPage function
 
 
 
 
 
 
 
 
 
 
 
367
  if (targetPage === 'stats-page') {
368
  updateStats();
369
  }
370
  });
371
  });
372
+
373
+ // New Workout Button (unchanged logic)
374
  newWorkoutBtn.addEventListener('click', () => {
375
+ if (!currentUser) return;
376
  showPage('new-workout-page');
377
  clearNewWorkoutForm();
378
+ currentWorkoutId = null; // Ensure it's a new workout
379
  });
380
+
381
+ // Cancel New Workout Button
382
+ cancelNewWorkoutBtn.addEventListener('click', () => {
383
+ showPage('home-page'); // Go back home without saving
384
+ });
385
+
386
+ // Save Workout Button (logic moved to saveWorkout function)
387
  saveWorkoutBtn.addEventListener('click', saveWorkout);
388
+
389
+ // Add Exercise Button (logic moved to addExercise function)
390
  addExerciseBtn.addEventListener('click', addExercise);
391
+
392
+ // Back to Home Button (unchanged logic)
393
  backToHomeBtn.addEventListener('click', () => {
394
  showPage('home-page');
395
  });
396
+
397
+ // Delete Workout Button (logic moved to deleteWorkout function)
398
  deleteWorkoutBtn.addEventListener('click', deleteWorkout);
399
+
400
+ // Satisfaction Slider (unchanged logic)
401
  satisfactionRange.addEventListener('input', () => {
402
  satisfactionValue.textContent = `${satisfactionRange.value}%`;
403
  });
404
  }
405
+
406
+ // --- Core App Logic (Mostly Unchanged, ensure they run only when logged in) ---
407
+
408
+ function setTodayDate() {
409
+ // Check if the element exists before setting the value
410
+ if(workoutDateInput) {
411
+ const today = new Date().toISOString().split('T')[0];
412
+ workoutDateInput.value = today;
413
+ }
414
+ }
415
+
416
  function showPage(pageId) {
417
+ // Only change pages if a user is logged in
418
+ if (!currentUser) {
419
+ showLoginPage(); // Redirect to login if somehow called without user
420
+ return;
421
+ }
422
+
423
  document.querySelectorAll('#app-container > div').forEach(page => {
424
+ page.classList.remove('active'); // Use classList for better practice
425
+ });
426
+ const pageToShow = document.getElementById(pageId);
427
+ if (pageToShow) {
428
+ pageToShow.classList.add('active');
429
+ } else {
430
+ console.error(`Page with ID ${pageId} not found.`);
431
+ // Optionally default to home page if target not found
432
+ document.getElementById('home-page').classList.add('active');
433
+ pageId = 'home-page'; // Update pageId for nav highlighting
434
+ }
435
+
436
+
437
+ // Update nav highlighting
438
+ navItems.forEach(item => {
439
+ if (item.getAttribute('data-page') === pageId) {
440
+ item.classList.add('active');
441
+ } else {
442
+ item.classList.remove('active');
443
+ }
444
  });
 
 
 
 
 
 
 
 
445
  }
446
+
447
+
448
  function addExercise() {
449
+ // Check if user is logged in before allowing interaction
450
+ if (!currentUser) return;
451
+
452
  const exerciseId = `exercise-${Date.now()}`;
453
  const exerciseDiv = document.createElement('div');
454
+ exerciseDiv.className = 'card exercise'; // Added 'card' class for consistency
455
  exerciseDiv.setAttribute('data-exercise-id', exerciseId);
456
+
457
  exerciseDiv.innerHTML = `
458
  <div class="exercise-header">
459
+ <input type="text" placeholder="Nom Exercice" class="exercise-name" style="margin-bottom: 0;"> <!-- Remove margin-bottom -->
460
+ <button class="btn btn-danger remove-exercise" style="padding: 0.3rem 0.6rem; margin-left: 0.5rem;">×</button> <!-- Use × for 'x' -->
461
  </div>
462
+ <div class="form-group" style="margin-top: 0.5rem; margin-bottom: 0.5rem;"> <!-- Reduced margin -->
463
+ <label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.9rem;">
464
+ <input type="checkbox" class="unilateral-checkbox" style="margin-bottom: 0; margin-right: 0.3rem;"> Unilatéral
465
+ </label>
 
 
466
  </div>
467
  <div class="series-container">
468
+ <!-- Series added here -->
469
  </div>
470
+ <button class="btn btn-outline add-series" style="width: 100%; margin-top: 0.5rem; padding: 0.4rem;">+ Ajouter Série</button>
 
 
471
  `;
472
+
473
  exercisesContainer.appendChild(exerciseDiv);
474
+
475
+ // Add event listeners for remove/add series (ensure context is correct)
476
  const removeBtn = exerciseDiv.querySelector('.remove-exercise');
477
+ removeBtn.addEventListener('click', () => exerciseDiv.remove());
478
+
 
 
479
  const addSeriesBtn = exerciseDiv.querySelector('.add-series');
480
+ const seriesContainer = exerciseDiv.querySelector('.series-container'); // Get container specific to this exercise
481
+ addSeriesBtn.addEventListener('click', () => addSeries(seriesContainer)); // Pass the correct container
482
+
483
+ // Add the first series automatically
484
+ addSeries(seriesContainer);
 
485
  }
486
+
 
487
  function addSeries(container) {
488
+ // Check user login
489
+ if (!currentUser) return;
490
+
491
  const seriesId = `series-${Date.now()}`;
492
  const seriesDiv = document.createElement('div');
493
+ seriesDiv.className = 'series'; // Keep original class
494
  seriesDiv.setAttribute('data-series-id', seriesId);
495
+
496
  seriesDiv.innerHTML = `
497
+ <div class="form-row" style="align-items: flex-end;"> <!-- Align items bottom -->
498
+ <div class="form-group" style="flex: 1.5;"> <!-- More space for reps -->
499
+ <label style="font-size: 0.8rem;">Reps</label>
500
+ <input type="number" class="reps" min="1" placeholder="10" style="padding: 0.4rem;">
501
  </div>
502
+ <div class="form-group" style="flex: 1.5;"> <!-- More space for weight -->
503
+ <label style="font-size: 0.8rem;">Charge (kg)</label>
504
+ <input type="number" class="weight" min="0" step="0.1" placeholder="20" style="padding: 0.4rem;">
505
  </div>
506
+ <div class="form-group" style="flex: 1; display: flex; align-items: center; padding-bottom: 0.6rem;"> <!-- Align checkbox -->
507
+ <label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.8rem; margin-bottom: 0;">
508
+ <input type="checkbox" class="degressive-checkbox" style="margin-bottom: 0; margin-right: 0.3rem;"> Dégressive
509
+ </label>
510
  </div>
511
+ <button class="btn btn-danger remove-series" style="padding: 0.3rem 0.6rem; margin-bottom: 0.6rem; flex: 0.5;">×</button>
512
  </div>
513
  `;
514
+
515
  container.appendChild(seriesDiv);
516
+
517
+ // Add remove listener for this specific series
518
  const removeBtn = seriesDiv.querySelector('.remove-series');
519
+ removeBtn.addEventListener('click', () => seriesDiv.remove());
 
 
520
  }
521
+
522
+
523
  function saveWorkout() {
524
+ // Check user login
525
+ if (!currentUser) return;
526
+
527
+ // Retrieve base data (unchanged)
528
  const workoutName = document.getElementById('workout-name').value.trim();
529
  const workoutDate = document.getElementById('workout-date').value;
530
  const workoutDuration = parseInt(document.getElementById('workout-duration').value) || 0;
531
  const satisfaction = parseInt(document.getElementById('satisfaction').value);
532
+
533
+ // Validation (unchanged)
534
+ if (!workoutName) { alert("Nom de séance requis."); return; }
535
+ if (!workoutDate) { alert("Date requise."); return; }
536
+ if (workoutDuration <= 0) { alert("Durée invalide."); return; }
537
+
538
+ // Retrieve exercises (unchanged logic)
 
 
 
 
 
 
 
 
 
 
 
539
  const exercises = [];
540
  const exerciseElements = exercisesContainer.querySelectorAll('.exercise');
541
+
542
+ if (exerciseElements.length === 0) { alert("Ajoutez au moins un exercice."); return; }
543
+
544
+ let validationFailed = false; // Flag to stop processing if an error occurs
545
+ exerciseElements.forEach(exerciseEl => {
546
+ if (validationFailed) return; // Stop if previous exercise had an error
547
+
548
  const exerciseName = exerciseEl.querySelector('.exercise-name').value.trim();
549
  const isUnilateral = exerciseEl.querySelector('.unilateral-checkbox').checked;
550
+
551
  if (!exerciseName) {
552
+ alert("Nom requis pour chaque exercice.");
553
+ validationFailed = true;
554
  return;
555
  }
556
+
 
557
  const series = [];
558
  const seriesElements = exerciseEl.querySelectorAll('.series');
559
+
560
  if (seriesElements.length === 0) {
561
+ alert(`Ajoutez au moins une série à l'exercice "${exerciseName}".`);
562
+ validationFailed = true;
563
  return;
564
  }
565
+
566
+ seriesElements.forEach(seriesEl => {
567
+ if (validationFailed) return;
568
+
569
  const reps = parseInt(seriesEl.querySelector('.reps').value) || 0;
570
  const weight = parseFloat(seriesEl.querySelector('.weight').value) || 0;
571
  const isDegressive = seriesEl.querySelector('.degressive-checkbox').checked;
572
+
573
  if (reps <= 0) {
574
+ alert(`Nombre de répétitions valide requis pour "${exerciseName}".`);
575
+ validationFailed = true;
576
  return;
577
  }
578
+ // Allow weight 0 (bodyweight, etc.) but maybe validate if needed
579
+ // if (weight < 0) { alert(`Charge valide requise pour "${exerciseName}".`); return; }
580
+
581
+ series.push({ reps, weight, isDegressive });
582
+ });
583
+ if (!validationFailed) {
584
+ exercises.push({ name: exerciseName, isUnilateral, series });
585
  }
586
+ });
587
+
588
+ // Stop if validation failed during exercise/series loop
589
+ if (validationFailed) return;
590
+
591
+
592
+ // Calculate total tonnage (unchanged logic)
593
+ let totalTonnage = 0;
594
+ exercises.forEach(exercise => {
595
+ exercise.series.forEach(serie => {
596
+ const weightFactor = exercise.isUnilateral ? 2 : 1;
597
+ totalTonnage += serie.reps * serie.weight * weightFactor;
598
  });
599
+ });
600
+
601
+ // Create workout object (unchanged logic)
 
 
 
 
 
 
 
 
 
 
602
  const workout = {
603
+ id: currentWorkoutId || `workout-${Date.now()}`, // Use existing ID if editing
604
  name: workoutName,
605
  date: workoutDate,
606
  duration: workoutDuration,
 
608
  totalTonnage,
609
  satisfaction
610
  };
611
+
612
+ // Add or update workout in the user's list
613
+ const existingIndex = workouts.findIndex(w => w.id === workout.id);
614
+ if (existingIndex > -1) {
615
+ workouts[existingIndex] = workout; // Update
 
 
 
616
  } else {
617
+ workouts.push(workout); // Add new
 
618
  }
619
+
620
+ // Save data for the current user
621
  saveWorkouts();
622
+
623
+ // Go back to home page and refresh list
624
  showPage('home-page');
625
  renderWorkoutsList();
626
  }
627
+
 
628
  function renderWorkoutsList() {
629
+ // Check login
630
+ if (!currentUser) {
631
+ workoutsList.innerHTML = ''; // Clear list if somehow called logged out
632
+ emptyWorkoutMessage.classList.remove('hidden');
633
+ return;
634
+ }
635
+
636
+ workoutsList.innerHTML = ''; // Clear previous list
637
+
638
  if (workouts.length === 0) {
639
  emptyWorkoutMessage.classList.remove('hidden');
640
+ return; // Stop if no workouts
641
  }
642
+
643
  emptyWorkoutMessage.classList.add('hidden');
644
+
645
+ // Sort workouts by date (most recent first) (unchanged)
646
+ const sortedWorkouts = [...workouts].sort((a, b) => new Date(b.date) - new Date(a.date));
647
+
648
+ // Generate HTML for each workout card (unchanged, but uses user's `workouts` array)
649
+ sortedWorkouts.forEach(workout => {
 
 
650
  const workoutDate = new Date(workout.date).toLocaleDateString('fr-FR');
651
  const workoutDiv = document.createElement('div');
652
+ workoutDiv.className = 'card workout-card'; // Use class for styling/selection
653
  workoutDiv.setAttribute('data-workout-id', workout.id);
654
+
655
  workoutDiv.innerHTML = `
656
  <div class="workout-header">
657
  <h3>${workout.name}</h3>
658
  <div class="badge">${workoutDate}</div>
659
  </div>
660
+ <div class="workout-details" style="margin-top: 0.5rem; font-size: 0.9rem; color: #ccc;">
661
+ <p><strong>Durée:</strong> ${workout.duration} min | <strong>Exercices:</strong> ${workout.exercises.length}</p>
662
+ <p><strong>Tonnage:</strong> ${workout.totalTonnage.toFixed(1)} kg | <strong>Satisfaction:</strong> ${workout.satisfaction}%</p>
 
 
663
  </div>
664
  `;
665
+
666
+ // Add listener to show details on click
667
+ workoutDiv.addEventListener('click', () => displayWorkoutDetails(workout.id));
668
+
 
 
669
  workoutsList.appendChild(workoutDiv);
670
  });
671
+
672
+ updateStats(); // Update stats after rendering list
 
673
  }
674
+
 
675
  function displayWorkoutDetails(workoutId) {
676
+ // Check login
677
+ if (!currentUser) return;
678
+
679
  const workout = workouts.find(w => w.id === workoutId);
680
+ if (!workout) return; // Workout not found for this user
681
+
682
+ // Update basic info (unchanged)
683
  document.getElementById('detail-workout-name').textContent = workout.name;
684
  document.getElementById('detail-date').textContent = new Date(workout.date).toLocaleDateString('fr-FR');
685
  document.getElementById('detail-duration').textContent = workout.duration;
686
  document.getElementById('detail-tonnage').textContent = workout.totalTonnage.toFixed(1);
687
  document.getElementById('detail-satisfaction').textContent = `${workout.satisfaction}%`;
688
  document.getElementById('detail-exercises-count').textContent = workout.exercises.length;
689
+
690
+
691
+ // Display exercises (unchanged logic)
692
+ const detailExercisesContainer = document.getElementById('detail-exercises-container');
693
+ detailExercisesContainer.innerHTML = ''; // Clear previous details
694
+
695
  workout.exercises.forEach(exercise => {
696
  const exerciseDiv = document.createElement('div');
697
+ exerciseDiv.className = 'card'; // Use card style for each exercise detail
698
+
699
+ let seriesHtml = '';
700
+ exercise.series.forEach((serie, index) => {
701
+ const degressiveLabel = serie.isDegressive ? ' <span class="badge" style="background-color: var(--accent-dark);">Dégressive</span>' : '';
702
+ const weightFactor = exercise.isUnilateral ? 2 : 1;
703
+ const seriesTonnage = serie.reps * serie.weight * weightFactor;
704
+
705
+ seriesHtml += `
706
+ <div class="exercise-summary">
707
+ <div class="flex-between" style="font-size: 0.9rem;">
708
+ <span>Série ${index + 1}: ${serie.reps} reps × ${serie.weight} kg${degressiveLabel}</span>
709
+ <span style="color: #ccc;">(Tonnage: ${seriesTonnage.toFixed(1)} kg)</span>
710
+ </div>
711
+ </div>
712
+ `;
713
+ });
714
+
715
  const unilateralLabel = exercise.isUnilateral ? ' <span class="badge">Unilatéral</span>' : '';
716
+
717
  exerciseDiv.innerHTML = `
718
+ <h3 style="font-size: 1.1rem; margin-bottom: 0.5rem;">${exercise.name}${unilateralLabel}</h3>
719
  <div class="series-summary">
720
  ${seriesHtml}
721
  </div>
722
  `;
723
+
724
+ detailExercisesContainer.appendChild(exerciseDiv);
725
  });
726
+
727
+
728
+ // Store ID for potential delete/edit
729
  currentWorkoutId = workoutId;
730
+
731
+ // Show the details page
732
  showPage('workout-details-page');
733
  }
 
 
 
 
 
 
 
734
 
 
735
 
736
+ function deleteWorkout() {
737
+ // Check login and if a workout is selected
738
+ if (!currentUser || !currentWorkoutId) return;
739
+
740
+ const confirmDelete = confirm("Êtes-vous sûr de vouloir supprimer cette séance ? Cette action est irréversible.");
741
  if (!confirmDelete) return;
742
+
743
+ // Filter out the workout to delete from the current user's data
744
  workouts = workouts.filter(w => w.id !== currentWorkoutId);
745
+
746
+ // Save the updated data for the user
747
  saveWorkouts();
748
+
749
+ // Go back to home and refresh the list
750
  showPage('home-page');
751
+ renderWorkoutsList(); // This will also update stats
752
  }
753
+
 
754
  function clearNewWorkoutForm() {
755
+ // Check login
756
+ if (!currentUser) return;
757
+
758
  document.getElementById('workout-name').value = '';
759
  document.getElementById('workout-duration').value = '';
760
+ setTodayDate(); // Reset date
761
+ exercisesContainer.innerHTML = ''; // Clear exercises
762
+ satisfactionRange.value = 75; // Reset satisfaction
763
  satisfactionValue.textContent = '75%';
764
+ currentWorkoutId = null; // Ensure it's treated as new
765
  }
766
+
 
767
  function updateStats() {
768
+ // Check login
769
+ if (!currentUser) return;
770
+
771
+ const workoutCount = workouts.length;
772
+ document.getElementById('stats-workout-count').textContent = workoutCount;
773
+
774
+ if (workoutCount === 0) {
775
  document.getElementById('stats-avg-tonnage').textContent = '0';
776
  document.getElementById('stats-avg-satisfaction').textContent = '0%';
777
  return;
778
  }
779
+
780
+ // Average Tonnage (unchanged)
781
+ const totalTonnageAll = workouts.reduce((sum, workout) => sum + workout.totalTonnage, 0);
782
+ const avgTonnage = totalTonnageAll / workoutCount;
 
 
 
783
  document.getElementById('stats-avg-tonnage').textContent = avgTonnage.toFixed(1);
784
+
785
+ // Average Satisfaction (unchanged)
786
  const totalSatisfaction = workouts.reduce((sum, workout) => sum + workout.satisfaction, 0);
787
+ const avgSatisfaction = totalSatisfaction / workoutCount;
788
  document.getElementById('stats-avg-satisfaction').textContent = `${Math.round(avgSatisfaction)}%`;
789
  }
790
+
791
  </script>
792
  </body>
793
  </html>