Spaces:
Running
Running
Update index.html
Browse files- index.html +100 -178
index.html
CHANGED
@@ -7,7 +7,7 @@
|
|
7 |
<!-- Ajout de la librairie pour les confettis -->
|
8 |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script>
|
9 |
<style>
|
10 |
-
/* --- Styles CSS (
|
11 |
:root {
|
12 |
--bg-dark: #121212;
|
13 |
--bg-card: #1e1e1e;
|
@@ -15,7 +15,7 @@
|
|
15 |
--accent: #4CAF50;
|
16 |
--accent-dark: #3a8a3d;
|
17 |
--danger: #f44336;
|
18 |
-
--superset-border: #4a90e2;
|
19 |
}
|
20 |
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
|
21 |
body { background-color: var(--bg-dark); color: var(--text-light); min-height: 100vh; padding-bottom: 80px; }
|
@@ -45,10 +45,6 @@
|
|
45 |
display: block; /* Afficher l'exercice actif */
|
46 |
animation: fadeIn 0.3s ease-in-out;
|
47 |
}
|
48 |
-
/* Style pour exercice débutant un superset (dans le formulaire) */
|
49 |
-
.exercise.is-superset-start {
|
50 |
-
border-left-color: var(--superset-border);
|
51 |
-
}
|
52 |
.exercise-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; gap: 0.5rem;}
|
53 |
.exercise-header input {margin-bottom: 0;}
|
54 |
.series-container { margin-left: 0.5rem; margin-top: 0.5rem; }
|
@@ -69,7 +65,7 @@
|
|
69 |
#app-container > div:not(.active) { display: none !important; }
|
70 |
.flex-between { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem;}
|
71 |
.badge { background-color: var(--accent); color: white; padding: 0.2rem 0.5rem; border-radius: 10px; font-size: 0.8rem; white-space: nowrap; }
|
72 |
-
.badge-superset
|
73 |
.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; }
|
74 |
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
75 |
.exercise-summary { margin: 0.3rem 0; padding: 0.3rem 0; border-bottom: 1px solid #333; font-size: 0.9rem;}
|
@@ -77,18 +73,7 @@
|
|
77 |
.satisfaction { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 1rem; }
|
78 |
.satisfaction-value { font-size: 2rem; color: var(--accent); margin-top: 0.5rem; }
|
79 |
.exercise-navigation { display: flex; justify-content: space-between; margin-top: 1rem; margin-bottom: 1rem; }
|
80 |
-
|
81 |
-
border-left: 3px solid var(--superset-border);
|
82 |
-
margin-bottom: 0.2rem; /* Réduire l'espace avant le suivant */
|
83 |
-
}
|
84 |
-
.detail-exercise-card.is-superset-continuation {
|
85 |
-
border-left: 3px solid var(--superset-border);
|
86 |
-
padding-top: 0.5rem;
|
87 |
-
margin-top: -0.8rem; /* Rapprocher visuellement */
|
88 |
-
border-top-left-radius: 0; /* Pour lier visuellement */
|
89 |
-
border-bottom-left-radius: 8px;
|
90 |
-
}
|
91 |
-
|
92 |
|
93 |
/* Animation d'apparition/zoom pour nouvelle séance */
|
94 |
@keyframes fadeInZoom {
|
@@ -115,7 +100,7 @@
|
|
115 |
.flex-between > div { width: 100%; display: flex; justify-content: flex-end; margin-top: 0.5rem;}
|
116 |
.user-info { text-align: center; margin-bottom: 0.5rem;}
|
117 |
.user-info button { display: block; margin: 0.5rem auto 0;}
|
118 |
-
.exercise-navigation button { padding: 0.5rem; font-size: 0.9rem;}
|
119 |
}
|
120 |
/* Animation fade in standard */
|
121 |
@keyframes fadeIn {
|
@@ -133,7 +118,6 @@
|
|
133 |
|
134 |
<!-- Écran de Connexion -->
|
135 |
<div id="login-page">
|
136 |
-
<!-- Contenu page connexion -->
|
137 |
<div class="card">
|
138 |
<h2>Accès Utilisateur</h2>
|
139 |
<div class="form-group">
|
@@ -233,8 +217,8 @@
|
|
233 |
let workouts = [];
|
234 |
let currentUser = null;
|
235 |
let currentWorkoutId = null;
|
236 |
-
let currentExerciseIndex = 0;
|
237 |
-
let workoutExercisesForm = []; //
|
238 |
|
239 |
// --- Éléments DOM ---
|
240 |
const loginPage = document.getElementById('login-page');
|
@@ -251,8 +235,8 @@
|
|
251 |
const saveWorkoutBtn = document.getElementById('save-workout-btn');
|
252 |
const cancelNewWorkoutBtn = document.getElementById('cancel-new-workout-btn');
|
253 |
const addExerciseBtn = document.getElementById('add-exercise-btn');
|
254 |
-
const exercisesContainer = document.getElementById('exercises-container');
|
255 |
-
const workoutsList = document.getElementById('workouts-list');
|
256 |
const backToHomeBtn = document.getElementById('back-to-home');
|
257 |
const deleteWorkoutBtn = document.getElementById('delete-workout-btn');
|
258 |
const satisfactionRange = document.getElementById('satisfaction');
|
@@ -263,7 +247,6 @@
|
|
263 |
const nextExerciseBtn = document.getElementById('next-exercise-btn');
|
264 |
const currentExerciseIndicator = document.getElementById('current-exercise-indicator');
|
265 |
|
266 |
-
|
267 |
// --- Initialisation ---
|
268 |
document.addEventListener('DOMContentLoaded', () => {
|
269 |
const rememberedUser = sessionStorage.getItem('liftTrackCurrentUser');
|
@@ -272,18 +255,18 @@
|
|
272 |
});
|
273 |
|
274 |
// --- Authentification & Persistance (inchangées) ---
|
275 |
-
function showLoginPage() {
|
276 |
-
function showApp() {
|
277 |
-
function handleLogin() {
|
278 |
-
function loginUser(username) {
|
279 |
-
function handleLogout() {
|
280 |
-
function getStorageKey() {
|
281 |
-
function loadWorkouts() {
|
282 |
const storageKey = getStorageKey(); if (!storageKey) { workouts = []; return; }
|
283 |
if (spinner) spinner.classList.remove('hidden'); emptyWorkoutMessage.classList.add('hidden'); workoutsList.innerHTML = '';
|
284 |
setTimeout(() => { try { const savedData = localStorage.getItem(storageKey); workouts = savedData ? JSON.parse(savedData) : []; console.log(`Chargé ${workouts.length} séances pour ${currentUser}`); } catch (e) { console.error("Erreur parsing localStorage:", e); workouts = []; alert("Erreur chargement données."); } finally { if (spinner) spinner.classList.add('hidden'); renderWorkoutsList(); } }, 150);
|
285 |
}
|
286 |
-
function saveWorkouts() {
|
287 |
const storageKey = getStorageKey(); if (!storageKey) { console.error("Sauvegarde impossible: non connecté."); return; } try { localStorage.setItem(storageKey, JSON.stringify(workouts)); console.log(`Sauvegardé ${workouts.length} séances pour ${currentUser}`); } catch (e) { console.error("Erreur sauvegarde localStorage:", e); alert("Erreur sauvegarde données."); }
|
288 |
}
|
289 |
|
@@ -293,61 +276,36 @@
|
|
293 |
logoutBtn.addEventListener('click', handleLogout);
|
294 |
usernameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); handleLogin(); } });
|
295 |
|
296 |
-
navItems.forEach(item => {
|
297 |
item.addEventListener('click', (e) => { e.preventDefault(); if (!currentUser) return; const targetPageId = item.getAttribute('data-page'); showPage(targetPageId); navItems.forEach(nav => nav.classList.remove('active')); item.classList.add('active'); if (targetPageId === 'stats-page') { updateStats(); } });
|
298 |
});
|
299 |
|
300 |
-
// Bouton Nouvelle Séance
|
301 |
newWorkoutBtn.addEventListener('click', () => {
|
302 |
-
if (!currentUser) return;
|
303 |
-
currentWorkoutId = null; // C'est une nouvelle séance
|
304 |
-
clearNewWorkoutForm(); // Réinitialise le formulaire et ajoute le premier exercice vide
|
305 |
-
showPage('new-workout-page');
|
306 |
-
// Déclencher l'animation d'entrée (la classe 'active' est déjà ajoutée par showPage)
|
307 |
-
// L'animation CSS sur #new-workout-page.active s'en charge
|
308 |
});
|
309 |
|
310 |
cancelNewWorkoutBtn.addEventListener('click', () => showPage('home-page'));
|
311 |
-
saveWorkoutBtn.addEventListener('click', saveWorkout);
|
312 |
-
addExerciseBtn.addEventListener('click', handleAddNewExercise);
|
313 |
-
|
314 |
-
// Navigation formulaire exercices
|
315 |
prevExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, -1));
|
316 |
nextExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, 1));
|
317 |
-
|
318 |
backToHomeBtn.addEventListener('click', () => showPage('home-page'));
|
319 |
deleteWorkoutBtn.addEventListener('click', deleteWorkout);
|
320 |
satisfactionRange.addEventListener('input', () => { satisfactionValue.textContent = `${satisfactionRange.value}%`; });
|
321 |
}
|
322 |
|
323 |
// --- Logique Principale ---
|
324 |
-
function setTodayDate() {
|
325 |
|
326 |
function showPage(pageId) {
|
327 |
if (!currentUser) { showLoginPage(); return; }
|
328 |
-
|
329 |
-
document.querySelectorAll('#app-container > div').forEach(page => {
|
330 |
-
page.classList.remove('active');
|
331 |
-
});
|
332 |
-
|
333 |
const pageToShow = document.getElementById(pageId);
|
334 |
-
if (pageToShow) {
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
pageId = 'home-page';
|
340 |
-
}
|
341 |
-
|
342 |
-
// Gérer la nav active
|
343 |
-
navItems.forEach(item => { item.classList.remove('active'); });
|
344 |
-
const activeNavItem = document.querySelector(`.nav-item[data-page="${pageId === 'stats-page' ? 'stats-page' : 'home-page'}"]`);
|
345 |
-
if(activeNavItem) activeNavItem.classList.add('active');
|
346 |
-
|
347 |
-
// Si on affiche la page nouvelle séance, s'assurer que le premier exercice est visible
|
348 |
-
if (pageId === 'new-workout-page') {
|
349 |
-
renderActiveExerciseForm(); // Affichera l'exercice à currentExerciseIndex
|
350 |
-
}
|
351 |
}
|
352 |
|
353 |
// --- Logique Formulaire Nouvelle Séance ---
|
@@ -360,73 +318,65 @@
|
|
360 |
if (!currentUser) return;
|
361 |
const exerciseId = `exercise-${Date.now()}`;
|
362 |
const exerciseDiv = document.createElement('div');
|
363 |
-
exerciseDiv.className = 'card exercise';
|
364 |
exerciseDiv.setAttribute('data-exercise-id', exerciseId);
|
365 |
|
|
|
366 |
exerciseDiv.innerHTML = `
|
367 |
<div class="exercise-header">
|
368 |
<input type="text" placeholder="Nom Exercice" class="exercise-name">
|
369 |
<button class="btn btn-danger remove-exercise" style="padding: 0.3rem 0.6rem; flex-shrink: 0;">×</button>
|
370 |
</div>
|
371 |
-
<div class="form-
|
372 |
<label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.9rem;">
|
373 |
<input type="checkbox" class="unilateral-checkbox"> Unilatéral
|
374 |
</label>
|
375 |
-
|
376 |
-
<input type="checkbox" class="superset-checkbox"> Début de Superset
|
377 |
-
</label>
|
378 |
</div>
|
379 |
<div class="series-container"></div>
|
380 |
<button class="btn btn-outline add-series" style="width: 100%; margin-top: 0.5rem; padding: 0.4rem;">+ Ajouter Série</button>
|
381 |
`;
|
382 |
-
exercisesContainer.appendChild(exerciseDiv);
|
383 |
|
384 |
-
//
|
385 |
-
|
386 |
|
387 |
// Écouteurs pour cet exercice
|
388 |
const removeBtn = exerciseDiv.querySelector('.remove-exercise');
|
389 |
-
// Gérer la suppression d'exercice dans le formulaire
|
390 |
removeBtn.addEventListener('click', () => {
|
391 |
const indexToRemove = workoutExercisesForm.indexOf(exerciseDiv);
|
392 |
if (indexToRemove > -1) {
|
393 |
-
exerciseDiv.remove();
|
394 |
-
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); //
|
395 |
-
|
396 |
-
if (currentExerciseIndex >=
|
397 |
-
|
398 |
-
}
|
399 |
-
// Si on supprime le dernier, l'index doit pointer sur le nouveau dernier (ou 0 si vide)
|
400 |
-
if (currentExerciseIndex >= workoutExercisesForm.length) {
|
401 |
-
currentExerciseIndex = Math.max(0, workoutExercisesForm.length - 1);
|
402 |
-
}
|
403 |
-
renderActiveExerciseForm(); // Afficher le bon exercice après suppression
|
404 |
}
|
405 |
});
|
406 |
|
407 |
-
|
408 |
const addSeriesBtn = exerciseDiv.querySelector('.add-series');
|
409 |
const seriesContainer = exerciseDiv.querySelector('.series-container');
|
410 |
addSeriesBtn.addEventListener('click', () => addSeries(seriesContainer));
|
411 |
|
412 |
-
addSeries(seriesContainer);
|
413 |
|
414 |
if (navigateToNew) {
|
415 |
-
currentExerciseIndex = workoutExercisesForm.length - 1;
|
416 |
renderActiveExerciseForm();
|
417 |
} else {
|
418 |
-
updateExerciseNavButtons();
|
419 |
}
|
420 |
-
|
421 |
}
|
422 |
|
423 |
-
function addSeries(container) {
|
424 |
if (!currentUser || !container) return; const seriesId = `series-${Date.now()}`; const seriesDiv = document.createElement('div'); seriesDiv.className = 'series'; seriesDiv.setAttribute('data-series-id', seriesId);
|
425 |
seriesDiv.innerHTML = `<div class="form-row" style="align-items: flex-end; gap: 0.8rem;"> <div class="form-group" style="flex: 1.2;"> <label>Reps</label> <input type="number" class="reps" min="1" placeholder="10" style="padding: 0.4rem;"> </div> <div class="form-group" style="flex: 1.2;"> <label>Charge (kg)</label> <input type="number" class="weight" min="0" step="0.1" placeholder="20" style="padding: 0.4rem;"> </div> <div class="form-group" style="flex: 1; display: flex; align-items: center; padding-bottom: 0.6rem; min-width: 100px;"> <label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.8rem; margin-bottom: 0; white-space: nowrap;"> <input type="checkbox" class="degressive-checkbox" style="margin-right: 0.3rem;"> Dégressive </label> </div> <button class="btn btn-danger remove-series" style="padding: 0.3rem 0.6rem; margin-bottom: 0.6rem; flex-basis: 30px; flex-grow: 0; align-self: center;">×</button> </div>`;
|
426 |
container.appendChild(seriesDiv); const removeBtn = seriesDiv.querySelector('.remove-series'); removeBtn.addEventListener('click', () => seriesDiv.remove());
|
427 |
}
|
428 |
|
429 |
function navigateExerciseForm(direction) {
|
|
|
|
|
430 |
const newIndex = currentExerciseIndex + direction;
|
431 |
if (newIndex >= 0 && newIndex < workoutExercisesForm.length) {
|
432 |
currentExerciseIndex = newIndex;
|
@@ -435,25 +385,14 @@
|
|
435 |
}
|
436 |
|
437 |
function renderActiveExerciseForm() {
|
438 |
-
|
439 |
-
|
440 |
-
workoutExercisesForm.forEach(ex => ex.classList.remove('active-exercise'));
|
441 |
|
442 |
-
// Afficher l'exercice courant s'il existe
|
443 |
if (currentExerciseIndex >= 0 && currentExerciseIndex < workoutExercisesForm.length) {
|
444 |
const currentExDiv = workoutExercisesForm[currentExerciseIndex];
|
445 |
currentExDiv.classList.add('active-exercise');
|
446 |
-
//
|
447 |
-
const isSuperset = currentExDiv.querySelector('.superset-checkbox').checked;
|
448 |
-
if (isSuperset) {
|
449 |
-
currentExDiv.classList.add('is-superset-start');
|
450 |
-
} else {
|
451 |
-
currentExDiv.classList.remove('is-superset-start');
|
452 |
-
}
|
453 |
-
|
454 |
}
|
455 |
-
|
456 |
-
// Mettre à jour l'indicateur et les boutons
|
457 |
updateExerciseNavButtons();
|
458 |
}
|
459 |
|
@@ -470,65 +409,71 @@
|
|
470 |
}
|
471 |
}
|
472 |
|
473 |
-
|
474 |
function clearNewWorkoutForm() {
|
475 |
if (!currentUser) return;
|
476 |
-
document.getElementById('workout-name').value = '';
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
satisfactionValue.textContent = '75%';
|
483 |
-
currentWorkoutId = null;
|
484 |
-
currentExerciseIndex = 0; // Revenir au premier potentiel exercice
|
485 |
-
addExercise(false); // Ajouter le premier bloc d'exercice vide mais ne pas naviguer (render s'en charge)
|
486 |
-
renderActiveExerciseForm(); // Afficher le premier exercice et MAJ boutons
|
487 |
}
|
488 |
|
489 |
function saveWorkout() {
|
490 |
if (!currentUser) return;
|
491 |
|
|
|
|
|
|
|
492 |
const workoutName = document.getElementById('workout-name').value.trim();
|
493 |
const workoutDate = document.getElementById('workout-date').value;
|
494 |
const workoutDuration = parseInt(document.getElementById('workout-duration').value) || 0;
|
495 |
const satisfaction = parseInt(document.getElementById('satisfaction').value);
|
496 |
|
497 |
-
if (!workoutName) { alert("
|
498 |
-
if (!workoutDate) { alert("
|
499 |
-
if (workoutDuration <= 0) { alert("
|
500 |
|
501 |
-
//
|
502 |
-
const exerciseElements = workoutExercisesForm;
|
503 |
const exercises = [];
|
504 |
-
if (exerciseElements.length === 0) { alert("
|
505 |
|
506 |
let validationError = null;
|
507 |
-
exerciseElements.forEach(exerciseEl => {
|
508 |
if (validationError) return;
|
509 |
-
const exerciseName = exerciseEl.querySelector('.exercise-name').value.trim();
|
510 |
-
const isUnilateral = exerciseEl.querySelector('.unilateral-checkbox').checked;
|
511 |
-
const isSupersetStart = exerciseEl.querySelector('.superset-checkbox').checked; // Récupérer état superset
|
512 |
|
513 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
514 |
|
515 |
const series = [];
|
516 |
const seriesElements = exerciseEl.querySelectorAll('.series');
|
517 |
-
if (seriesElements.length === 0) { validationError = `
|
518 |
|
519 |
seriesElements.forEach(seriesEl => {
|
520 |
if (validationError) return;
|
521 |
const repsInput = seriesEl.querySelector('.reps'); const weightInput = seriesEl.querySelector('.weight');
|
522 |
const reps = parseInt(repsInput.value) || 0; const weight = parseFloat(weightInput.value) || 0;
|
523 |
const isDegressive = seriesEl.querySelector('.degressive-checkbox').checked;
|
524 |
-
if (reps <= 0) { validationError = `Reps invalides pour "${exerciseName}".`; repsInput.focus(); return; }
|
525 |
-
if (weight < 0) { validationError = `Charge invalide pour "${exerciseName}".`; weightInput.focus(); return; }
|
526 |
series.push({ reps, weight, isDegressive });
|
527 |
});
|
528 |
|
529 |
if (!validationError) {
|
530 |
-
//
|
531 |
-
exercises.push({ name: exerciseName, isUnilateral,
|
532 |
}
|
533 |
});
|
534 |
|
@@ -543,26 +488,23 @@
|
|
543 |
});
|
544 |
});
|
545 |
|
|
|
546 |
const workout = {
|
547 |
id: currentWorkoutId || `workout-${Date.now()}`, name: workoutName, date: workoutDate, duration: workoutDuration,
|
548 |
-
exercises: exercises, //
|
549 |
totalTonnage: totalTonnage, satisfaction: satisfaction
|
550 |
};
|
551 |
-
|
552 |
const existingIndex = workouts.findIndex(w => w.id === workout.id);
|
553 |
if (existingIndex > -1) { workouts[existingIndex] = workout; } else { workouts.push(workout); }
|
554 |
|
555 |
saveWorkouts();
|
556 |
-
|
557 |
-
// Animation Confettis !
|
558 |
-
confetti({ particleCount: 150, spread: 90, origin: { y: 0.6 } });
|
559 |
-
|
560 |
showPage('home-page');
|
561 |
renderWorkoutsList();
|
562 |
}
|
563 |
|
564 |
-
// --- Affichage et Stats
|
565 |
-
function renderWorkoutsList() {
|
566 |
if (!currentUser) return; workoutsList.innerHTML = ''; if (workouts.length === 0) { emptyWorkoutMessage.classList.remove('hidden'); updateStats(); return; } emptyWorkoutMessage.classList.add('hidden');
|
567 |
const sortedWorkouts = [...workouts].sort((a, b) => new Date(b.date) - new Date(a.date));
|
568 |
sortedWorkouts.forEach(workout => { const workoutDate = new Date(workout.date).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }); const workoutDiv = document.createElement('div'); workoutDiv.className = 'card workout-card'; workoutDiv.setAttribute('data-workout-id', workout.id); workoutDiv.innerHTML = `<div class="workout-header"><h3 style="margin-bottom: 0.5rem;">${workout.name}</h3><div class="badge">${workoutDate}</div></div><div class="workout-details" style="font-size: 0.9rem; color: #ccc; margin-top: 0.5rem;"><span>${workout.duration} min</span> | <span>${workout.exercises.length} exo${workout.exercises.length > 1 ? 's' : ''}</span> | <span>${workout.totalTonnage.toFixed(1)} kg</span> | <span>${workout.satisfaction}%</span></div>`; workoutDiv.addEventListener('click', () => displayWorkoutDetails(workout.id)); workoutsList.appendChild(workoutDiv); });
|
@@ -570,47 +512,27 @@
|
|
570 |
}
|
571 |
|
572 |
function displayWorkoutDetails(workoutId) {
|
573 |
-
if (!currentUser) return;
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
// MAJ Infos générales (inchangé)
|
578 |
-
document.getElementById('detail-workout-name').textContent = workout.name; document.getElementById('detail-date').textContent = new Date(workout.date).toLocaleDateString('fr-FR'); document.getElementById('detail-duration').textContent = workout.duration; document.getElementById('detail-tonnage').textContent = workout.totalTonnage.toFixed(1); document.getElementById('detail-satisfaction').textContent = `${workout.satisfaction}%`; document.getElementById('detail-exercises-count').textContent = workout.exercises.length;
|
579 |
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
// MAJ Exercices avec indicateur Superset
|
584 |
-
workout.exercises.forEach((exercise, index) => {
|
585 |
const exerciseDiv = document.createElement('div');
|
586 |
-
|
587 |
-
exerciseDiv.classList.add('card', 'detail-exercise-card'); // Classe de base
|
588 |
-
let isContinuation = false;
|
589 |
-
if (exercise.isSupersetStart) {
|
590 |
-
exerciseDiv.classList.add('is-superset-start');
|
591 |
-
}
|
592 |
-
// Vérifier si l'exercice *précédent* était un début de superset
|
593 |
-
if (index > 0 && workout.exercises[index - 1].isSupersetStart) {
|
594 |
-
exerciseDiv.classList.add('is-superset-continuation');
|
595 |
-
isContinuation = true;
|
596 |
-
}
|
597 |
-
|
598 |
|
599 |
let seriesHtml = '';
|
600 |
-
exercise.series.forEach((serie, sIndex) => {
|
601 |
const degressiveLabel = serie.isDegressive ? ' <span class="badge" style="font-size: 0.7rem; background-color: var(--accent-dark);">Dégr.</span>' : ''; const weightFactor = exercise.isUnilateral ? 2 : 1; const seriesTonnage = serie.reps * serie.weight * weightFactor;
|
602 |
seriesHtml += `<div class="exercise-summary"><div class="flex-between"><span>Série ${sIndex + 1}: ${serie.reps} reps × ${serie.weight} kg${degressiveLabel}</span><span style="color: #aaa;">(${seriesTonnage.toFixed(1)} kg)</span></div></div>`;
|
603 |
});
|
604 |
|
605 |
const unilateralLabel = exercise.isUnilateral ? ' <span class="badge" style="font-size: 0.7rem;">Unilat.</span>' : '';
|
606 |
-
//
|
607 |
-
const supersetLabel = exercise.isSupersetStart ? ` <span class="badge badge-superset" style="font-size: 0.7rem;">Superset 🔽</span>` : '';
|
608 |
|
609 |
exerciseDiv.innerHTML = `
|
610 |
-
<h3 style="font-size: 1.1rem; margin-bottom: 0.5rem;">${exercise.name}${unilateralLabel}
|
611 |
-
<div class="series-summary-container">
|
612 |
-
${seriesHtml}
|
613 |
-
</div>`;
|
614 |
detailExercisesContainer.appendChild(exerciseDiv);
|
615 |
});
|
616 |
|
@@ -618,11 +540,11 @@
|
|
618 |
showPage('workout-details-page');
|
619 |
}
|
620 |
|
621 |
-
function deleteWorkout() {
|
622 |
if (!currentUser || !currentWorkoutId) return; const workoutToDelete = workouts.find(w => w.id === currentWorkoutId); if (!workoutToDelete) return; const confirmDelete = confirm(`Supprimer séance "${workoutToDelete.name}" du ${new Date(workoutToDelete.date).toLocaleDateString('fr-FR')} ?`); if (!confirmDelete) return; workouts = workouts.filter(w => w.id !== currentWorkoutId); saveWorkouts(); currentWorkoutId = null; showPage('home-page'); renderWorkoutsList();
|
623 |
}
|
624 |
|
625 |
-
function updateStats() {
|
626 |
if (!currentUser) return; const workoutCount = workouts.length; document.getElementById('stats-workout-count').textContent = workoutCount; if (workoutCount === 0) { document.getElementById('stats-avg-tonnage').textContent = '0'; document.getElementById('stats-avg-satisfaction').textContent = '0%'; return; } const totalTonnageAll = workouts.reduce((sum, workout) => sum + (workout.totalTonnage || 0), 0); const avgTonnage = totalTonnageAll / workoutCount; document.getElementById('stats-avg-tonnage').textContent = avgTonnage.toFixed(1); const totalSatisfaction = workouts.reduce((sum, workout) => sum + (workout.satisfaction || 0), 0); const avgSatisfaction = totalSatisfaction / workoutCount; document.getElementById('stats-avg-satisfaction').textContent = `${Math.round(avgSatisfaction)}%`;
|
627 |
}
|
628 |
|
|
|
7 |
<!-- Ajout de la librairie pour les confettis -->
|
8 |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script>
|
9 |
<style>
|
10 |
+
/* --- Styles CSS (Superset enlevé) --- */
|
11 |
:root {
|
12 |
--bg-dark: #121212;
|
13 |
--bg-card: #1e1e1e;
|
|
|
15 |
--accent: #4CAF50;
|
16 |
--accent-dark: #3a8a3d;
|
17 |
--danger: #f44336;
|
18 |
+
/* --superset-border: #4a90e2; enlevé */
|
19 |
}
|
20 |
* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
|
21 |
body { background-color: var(--bg-dark); color: var(--text-light); min-height: 100vh; padding-bottom: 80px; }
|
|
|
45 |
display: block; /* Afficher l'exercice actif */
|
46 |
animation: fadeIn 0.3s ease-in-out;
|
47 |
}
|
|
|
|
|
|
|
|
|
48 |
.exercise-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; gap: 0.5rem;}
|
49 |
.exercise-header input {margin-bottom: 0;}
|
50 |
.series-container { margin-left: 0.5rem; margin-top: 0.5rem; }
|
|
|
65 |
#app-container > div:not(.active) { display: none !important; }
|
66 |
.flex-between { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem;}
|
67 |
.badge { background-color: var(--accent); color: white; padding: 0.2rem 0.5rem; border-radius: 10px; font-size: 0.8rem; white-space: nowrap; }
|
68 |
+
/* .badge-superset enlevé */
|
69 |
.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; }
|
70 |
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
71 |
.exercise-summary { margin: 0.3rem 0; padding: 0.3rem 0; border-bottom: 1px solid #333; font-size: 0.9rem;}
|
|
|
73 |
.satisfaction { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 1rem; }
|
74 |
.satisfaction-value { font-size: 2rem; color: var(--accent); margin-top: 0.5rem; }
|
75 |
.exercise-navigation { display: flex; justify-content: space-between; margin-top: 1rem; margin-bottom: 1rem; }
|
76 |
+
/* Styles détail superset enlevés */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
/* Animation d'apparition/zoom pour nouvelle séance */
|
79 |
@keyframes fadeInZoom {
|
|
|
100 |
.flex-between > div { width: 100%; display: flex; justify-content: flex-end; margin-top: 0.5rem;}
|
101 |
.user-info { text-align: center; margin-bottom: 0.5rem;}
|
102 |
.user-info button { display: block; margin: 0.5rem auto 0;}
|
103 |
+
.exercise-navigation button { padding: 0.5rem; font-size: 0.9rem;}
|
104 |
}
|
105 |
/* Animation fade in standard */
|
106 |
@keyframes fadeIn {
|
|
|
118 |
|
119 |
<!-- Écran de Connexion -->
|
120 |
<div id="login-page">
|
|
|
121 |
<div class="card">
|
122 |
<h2>Accès Utilisateur</h2>
|
123 |
<div class="form-group">
|
|
|
217 |
let workouts = [];
|
218 |
let currentUser = null;
|
219 |
let currentWorkoutId = null;
|
220 |
+
let currentExerciseIndex = 0;
|
221 |
+
let workoutExercisesForm = []; // Référence aux divs .exercise dans le formulaire
|
222 |
|
223 |
// --- Éléments DOM ---
|
224 |
const loginPage = document.getElementById('login-page');
|
|
|
235 |
const saveWorkoutBtn = document.getElementById('save-workout-btn');
|
236 |
const cancelNewWorkoutBtn = document.getElementById('cancel-new-workout-btn');
|
237 |
const addExerciseBtn = document.getElementById('add-exercise-btn');
|
238 |
+
const exercisesContainer = document.getElementById('exercises-container'); // Conteneur formulaire
|
239 |
+
const workoutsList = document.getElementById('workouts-list'); // Conteneur affichage liste
|
240 |
const backToHomeBtn = document.getElementById('back-to-home');
|
241 |
const deleteWorkoutBtn = document.getElementById('delete-workout-btn');
|
242 |
const satisfactionRange = document.getElementById('satisfaction');
|
|
|
247 |
const nextExerciseBtn = document.getElementById('next-exercise-btn');
|
248 |
const currentExerciseIndicator = document.getElementById('current-exercise-indicator');
|
249 |
|
|
|
250 |
// --- Initialisation ---
|
251 |
document.addEventListener('DOMContentLoaded', () => {
|
252 |
const rememberedUser = sessionStorage.getItem('liftTrackCurrentUser');
|
|
|
255 |
});
|
256 |
|
257 |
// --- Authentification & Persistance (inchangées) ---
|
258 |
+
function showLoginPage() { loginPage.style.display = 'block'; mainAppContent.style.display = 'none'; loginError.style.display = 'none'; usernameInput.value = ''; currentUser = null; }
|
259 |
+
function showApp() { if (!currentUser) return; loginPage.style.display = 'none'; mainAppContent.style.display = 'block'; currentUserDisplay.textContent = currentUser; setTodayDate(); loadWorkouts(); showPage('home-page'); }
|
260 |
+
function handleLogin() { const username = usernameInput.value.trim(); if (username && username.length > 0) { loginUser(username); } else { loginError.textContent = "Veuillez entrer un nom d'utilisateur."; loginError.style.display = 'block'; }}
|
261 |
+
function loginUser(username) { currentUser = username; sessionStorage.setItem('liftTrackCurrentUser', currentUser); showApp(); }
|
262 |
+
function handleLogout() { currentUser = null; sessionStorage.removeItem('liftTrackCurrentUser'); workouts = []; showLoginPage(); }
|
263 |
+
function getStorageKey() { if (!currentUser) return null; const safeUsername = currentUser.replace(/[^a-zA-Z0-9_-]/g, '_'); return `liftTrackData_${safeUsername}`; }
|
264 |
+
function loadWorkouts() {
|
265 |
const storageKey = getStorageKey(); if (!storageKey) { workouts = []; return; }
|
266 |
if (spinner) spinner.classList.remove('hidden'); emptyWorkoutMessage.classList.add('hidden'); workoutsList.innerHTML = '';
|
267 |
setTimeout(() => { try { const savedData = localStorage.getItem(storageKey); workouts = savedData ? JSON.parse(savedData) : []; console.log(`Chargé ${workouts.length} séances pour ${currentUser}`); } catch (e) { console.error("Erreur parsing localStorage:", e); workouts = []; alert("Erreur chargement données."); } finally { if (spinner) spinner.classList.add('hidden'); renderWorkoutsList(); } }, 150);
|
268 |
}
|
269 |
+
function saveWorkouts() {
|
270 |
const storageKey = getStorageKey(); if (!storageKey) { console.error("Sauvegarde impossible: non connecté."); return; } try { localStorage.setItem(storageKey, JSON.stringify(workouts)); console.log(`Sauvegardé ${workouts.length} séances pour ${currentUser}`); } catch (e) { console.error("Erreur sauvegarde localStorage:", e); alert("Erreur sauvegarde données."); }
|
271 |
}
|
272 |
|
|
|
276 |
logoutBtn.addEventListener('click', handleLogout);
|
277 |
usernameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); handleLogin(); } });
|
278 |
|
279 |
+
navItems.forEach(item => {
|
280 |
item.addEventListener('click', (e) => { e.preventDefault(); if (!currentUser) return; const targetPageId = item.getAttribute('data-page'); showPage(targetPageId); navItems.forEach(nav => nav.classList.remove('active')); item.classList.add('active'); if (targetPageId === 'stats-page') { updateStats(); } });
|
281 |
});
|
282 |
|
|
|
283 |
newWorkoutBtn.addEventListener('click', () => {
|
284 |
+
if (!currentUser) return; currentWorkoutId = null; clearNewWorkoutForm(); showPage('new-workout-page');
|
|
|
|
|
|
|
|
|
|
|
285 |
});
|
286 |
|
287 |
cancelNewWorkoutBtn.addEventListener('click', () => showPage('home-page'));
|
288 |
+
saveWorkoutBtn.addEventListener('click', saveWorkout);
|
289 |
+
addExerciseBtn.addEventListener('click', handleAddNewExercise);
|
|
|
|
|
290 |
prevExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, -1));
|
291 |
nextExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, 1));
|
|
|
292 |
backToHomeBtn.addEventListener('click', () => showPage('home-page'));
|
293 |
deleteWorkoutBtn.addEventListener('click', deleteWorkout);
|
294 |
satisfactionRange.addEventListener('input', () => { satisfactionValue.textContent = `${satisfactionRange.value}%`; });
|
295 |
}
|
296 |
|
297 |
// --- Logique Principale ---
|
298 |
+
function setTodayDate() { if(workoutDateInput) { try { const today = new Date().toISOString().split('T')[0]; workoutDateInput.value = today; } catch (e) { console.error("Impossible définir date:", e); workoutDateInput.value = ''; } } }
|
299 |
|
300 |
function showPage(pageId) {
|
301 |
if (!currentUser) { showLoginPage(); return; }
|
302 |
+
document.querySelectorAll('#app-container > div').forEach(page => page.classList.remove('active'));
|
|
|
|
|
|
|
|
|
303 |
const pageToShow = document.getElementById(pageId);
|
304 |
+
if (pageToShow) { pageToShow.classList.add('active'); } else { console.error(`Page ID "${pageId}" non trouvée. Affichage home-page.`); document.getElementById('home-page').classList.add('active'); pageId = 'home-page'; }
|
305 |
+
navItems.forEach(item => { item.classList.remove('active'); });
|
306 |
+
const activeNavItem = document.querySelector(`.nav-item[data-page="${pageId === 'stats-page' ? 'stats-page' : 'home-page'}"]`);
|
307 |
+
if(activeNavItem) activeNavItem.classList.add('active');
|
308 |
+
if (pageId === 'new-workout-page') { renderActiveExerciseForm(); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
}
|
310 |
|
311 |
// --- Logique Formulaire Nouvelle Séance ---
|
|
|
318 |
if (!currentUser) return;
|
319 |
const exerciseId = `exercise-${Date.now()}`;
|
320 |
const exerciseDiv = document.createElement('div');
|
321 |
+
exerciseDiv.className = 'card exercise';
|
322 |
exerciseDiv.setAttribute('data-exercise-id', exerciseId);
|
323 |
|
324 |
+
// **HTML SANS SUPERSET**
|
325 |
exerciseDiv.innerHTML = `
|
326 |
<div class="exercise-header">
|
327 |
<input type="text" placeholder="Nom Exercice" class="exercise-name">
|
328 |
<button class="btn btn-danger remove-exercise" style="padding: 0.3rem 0.6rem; flex-shrink: 0;">×</button>
|
329 |
</div>
|
330 |
+
<div class="form-group" style="margin-top: 0.5rem; margin-bottom: 0.5rem;">
|
331 |
<label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.9rem;">
|
332 |
<input type="checkbox" class="unilateral-checkbox"> Unilatéral
|
333 |
</label>
|
334 |
+
<!-- Checkbox Superset enlevée -->
|
|
|
|
|
335 |
</div>
|
336 |
<div class="series-container"></div>
|
337 |
<button class="btn btn-outline add-series" style="width: 100%; margin-top: 0.5rem; padding: 0.4rem;">+ Ajouter Série</button>
|
338 |
`;
|
339 |
+
exercisesContainer.appendChild(exerciseDiv);
|
340 |
|
341 |
+
// Mettre à jour la référence immédiatement
|
342 |
+
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise'));
|
343 |
|
344 |
// Écouteurs pour cet exercice
|
345 |
const removeBtn = exerciseDiv.querySelector('.remove-exercise');
|
|
|
346 |
removeBtn.addEventListener('click', () => {
|
347 |
const indexToRemove = workoutExercisesForm.indexOf(exerciseDiv);
|
348 |
if (indexToRemove > -1) {
|
349 |
+
exerciseDiv.remove();
|
350 |
+
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); // MAJ après suppression DOM
|
351 |
+
if (currentExerciseIndex >= indexToRemove) { currentExerciseIndex = Math.max(0, currentExerciseIndex - 1); }
|
352 |
+
if (currentExerciseIndex >= workoutExercisesForm.length) { currentExerciseIndex = Math.max(0, workoutExercisesForm.length - 1); }
|
353 |
+
renderActiveExerciseForm();
|
|
|
|
|
|
|
|
|
|
|
|
|
354 |
}
|
355 |
});
|
356 |
|
|
|
357 |
const addSeriesBtn = exerciseDiv.querySelector('.add-series');
|
358 |
const seriesContainer = exerciseDiv.querySelector('.series-container');
|
359 |
addSeriesBtn.addEventListener('click', () => addSeries(seriesContainer));
|
360 |
|
361 |
+
addSeries(seriesContainer);
|
362 |
|
363 |
if (navigateToNew) {
|
364 |
+
currentExerciseIndex = workoutExercisesForm.length - 1;
|
365 |
renderActiveExerciseForm();
|
366 |
} else {
|
367 |
+
updateExerciseNavButtons();
|
368 |
}
|
|
|
369 |
}
|
370 |
|
371 |
+
function addSeries(container) {
|
372 |
if (!currentUser || !container) return; const seriesId = `series-${Date.now()}`; const seriesDiv = document.createElement('div'); seriesDiv.className = 'series'; seriesDiv.setAttribute('data-series-id', seriesId);
|
373 |
seriesDiv.innerHTML = `<div class="form-row" style="align-items: flex-end; gap: 0.8rem;"> <div class="form-group" style="flex: 1.2;"> <label>Reps</label> <input type="number" class="reps" min="1" placeholder="10" style="padding: 0.4rem;"> </div> <div class="form-group" style="flex: 1.2;"> <label>Charge (kg)</label> <input type="number" class="weight" min="0" step="0.1" placeholder="20" style="padding: 0.4rem;"> </div> <div class="form-group" style="flex: 1; display: flex; align-items: center; padding-bottom: 0.6rem; min-width: 100px;"> <label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.8rem; margin-bottom: 0; white-space: nowrap;"> <input type="checkbox" class="degressive-checkbox" style="margin-right: 0.3rem;"> Dégressive </label> </div> <button class="btn btn-danger remove-series" style="padding: 0.3rem 0.6rem; margin-bottom: 0.6rem; flex-basis: 30px; flex-grow: 0; align-self: center;">×</button> </div>`;
|
374 |
container.appendChild(seriesDiv); const removeBtn = seriesDiv.querySelector('.remove-series'); removeBtn.addEventListener('click', () => seriesDiv.remove());
|
375 |
}
|
376 |
|
377 |
function navigateExerciseForm(direction) {
|
378 |
+
// S'assurer que la référence est à jour avant de naviguer
|
379 |
+
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise'));
|
380 |
const newIndex = currentExerciseIndex + direction;
|
381 |
if (newIndex >= 0 && newIndex < workoutExercisesForm.length) {
|
382 |
currentExerciseIndex = newIndex;
|
|
|
385 |
}
|
386 |
|
387 |
function renderActiveExerciseForm() {
|
388 |
+
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise'));
|
389 |
+
workoutExercisesForm.forEach(ex => ex.classList.remove('active-exercise'));
|
|
|
390 |
|
|
|
391 |
if (currentExerciseIndex >= 0 && currentExerciseIndex < workoutExercisesForm.length) {
|
392 |
const currentExDiv = workoutExercisesForm[currentExerciseIndex];
|
393 |
currentExDiv.classList.add('active-exercise');
|
394 |
+
// Logique style superset enlevée ici
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
395 |
}
|
|
|
|
|
396 |
updateExerciseNavButtons();
|
397 |
}
|
398 |
|
|
|
409 |
}
|
410 |
}
|
411 |
|
|
|
412 |
function clearNewWorkoutForm() {
|
413 |
if (!currentUser) return;
|
414 |
+
document.getElementById('workout-name').value = ''; document.getElementById('workout-duration').value = '';
|
415 |
+
setTodayDate(); exercisesContainer.innerHTML = ''; workoutExercisesForm = [];
|
416 |
+
satisfactionRange.value = 75; satisfactionValue.textContent = '75%';
|
417 |
+
currentWorkoutId = null; currentExerciseIndex = 0;
|
418 |
+
addExercise(false); // Ajoute le premier vide
|
419 |
+
renderActiveExerciseForm(); // Assure l'affichage correct
|
|
|
|
|
|
|
|
|
|
|
420 |
}
|
421 |
|
422 |
function saveWorkout() {
|
423 |
if (!currentUser) return;
|
424 |
|
425 |
+
// **CORRECTION : Rafraîchir la référence juste avant la validation**
|
426 |
+
workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise'));
|
427 |
+
|
428 |
const workoutName = document.getElementById('workout-name').value.trim();
|
429 |
const workoutDate = document.getElementById('workout-date').value;
|
430 |
const workoutDuration = parseInt(document.getElementById('workout-duration').value) || 0;
|
431 |
const satisfaction = parseInt(document.getElementById('satisfaction').value);
|
432 |
|
433 |
+
if (!workoutName) { alert("Veuillez entrer un nom pour la séance."); return; }
|
434 |
+
if (!workoutDate) { alert("Veuillez choisir une date pour la séance."); return; }
|
435 |
+
if (workoutDuration <= 0) { alert("Veuillez entrer une durée valide (en minutes)."); return; }
|
436 |
|
437 |
+
// Utiliser la référence fraîchement mise à jour
|
438 |
+
const exerciseElements = workoutExercisesForm;
|
439 |
const exercises = [];
|
440 |
+
if (exerciseElements.length === 0) { alert("Veuillez ajouter au moins un exercice à la séance."); return; }
|
441 |
|
442 |
let validationError = null;
|
443 |
+
exerciseElements.forEach((exerciseEl, index) => { // Ajout index pour debug éventuel
|
444 |
if (validationError) return;
|
|
|
|
|
|
|
445 |
|
446 |
+
const nameInput = exerciseEl.querySelector('.exercise-name');
|
447 |
+
const exerciseName = nameInput ? nameInput.value.trim() : ''; // Vérifier si l'input existe
|
448 |
+
const isUnilateral = exerciseEl.querySelector('.unilateral-checkbox').checked;
|
449 |
+
// isSupersetStart enlevé
|
450 |
+
|
451 |
+
// **Point crucial de la validation du nom**
|
452 |
+
if (!exerciseName) {
|
453 |
+
// Donner un indice (numéro de l'exercice basé sur l'ordre actuel)
|
454 |
+
validationError = `Veuillez nommer l'exercice #${index + 1}.`;
|
455 |
+
// Essayer de mettre le focus sur le champ vide si possible
|
456 |
+
if(nameInput) nameInput.focus();
|
457 |
+
return;
|
458 |
+
}
|
459 |
|
460 |
const series = [];
|
461 |
const seriesElements = exerciseEl.querySelectorAll('.series');
|
462 |
+
if (seriesElements.length === 0) { validationError = `L'exercice "${exerciseName}" doit contenir au moins une série.`; return; }
|
463 |
|
464 |
seriesElements.forEach(seriesEl => {
|
465 |
if (validationError) return;
|
466 |
const repsInput = seriesEl.querySelector('.reps'); const weightInput = seriesEl.querySelector('.weight');
|
467 |
const reps = parseInt(repsInput.value) || 0; const weight = parseFloat(weightInput.value) || 0;
|
468 |
const isDegressive = seriesEl.querySelector('.degressive-checkbox').checked;
|
469 |
+
if (reps <= 0) { validationError = `Reps invalides pour une série de "${exerciseName}".`; if(repsInput) repsInput.focus(); return; }
|
470 |
+
if (weight < 0) { validationError = `Charge invalide pour une série de "${exerciseName}".`; if(weightInput) weightInput.focus(); return; }
|
471 |
series.push({ reps, weight, isDegressive });
|
472 |
});
|
473 |
|
474 |
if (!validationError) {
|
475 |
+
// **Objet exercice SANS isSupersetStart**
|
476 |
+
exercises.push({ name: exerciseName, isUnilateral, series });
|
477 |
}
|
478 |
});
|
479 |
|
|
|
488 |
});
|
489 |
});
|
490 |
|
491 |
+
// --- Création/MAJ Objet Workout ---
|
492 |
const workout = {
|
493 |
id: currentWorkoutId || `workout-${Date.now()}`, name: workoutName, date: workoutDate, duration: workoutDuration,
|
494 |
+
exercises: exercises, // Utilise le tableau local `exercises`
|
495 |
totalTonnage: totalTonnage, satisfaction: satisfaction
|
496 |
};
|
|
|
497 |
const existingIndex = workouts.findIndex(w => w.id === workout.id);
|
498 |
if (existingIndex > -1) { workouts[existingIndex] = workout; } else { workouts.push(workout); }
|
499 |
|
500 |
saveWorkouts();
|
501 |
+
confetti({ particleCount: 150, spread: 90, origin: { y: 0.6 } }); // Confettis!
|
|
|
|
|
|
|
502 |
showPage('home-page');
|
503 |
renderWorkoutsList();
|
504 |
}
|
505 |
|
506 |
+
// --- Affichage et Stats ---
|
507 |
+
function renderWorkoutsList() {
|
508 |
if (!currentUser) return; workoutsList.innerHTML = ''; if (workouts.length === 0) { emptyWorkoutMessage.classList.remove('hidden'); updateStats(); return; } emptyWorkoutMessage.classList.add('hidden');
|
509 |
const sortedWorkouts = [...workouts].sort((a, b) => new Date(b.date) - new Date(a.date));
|
510 |
sortedWorkouts.forEach(workout => { const workoutDate = new Date(workout.date).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }); const workoutDiv = document.createElement('div'); workoutDiv.className = 'card workout-card'; workoutDiv.setAttribute('data-workout-id', workout.id); workoutDiv.innerHTML = `<div class="workout-header"><h3 style="margin-bottom: 0.5rem;">${workout.name}</h3><div class="badge">${workoutDate}</div></div><div class="workout-details" style="font-size: 0.9rem; color: #ccc; margin-top: 0.5rem;"><span>${workout.duration} min</span> | <span>${workout.exercises.length} exo${workout.exercises.length > 1 ? 's' : ''}</span> | <span>${workout.totalTonnage.toFixed(1)} kg</span> | <span>${workout.satisfaction}%</span></div>`; workoutDiv.addEventListener('click', () => displayWorkoutDetails(workout.id)); workoutsList.appendChild(workoutDiv); });
|
|
|
512 |
}
|
513 |
|
514 |
function displayWorkoutDetails(workoutId) {
|
515 |
+
if (!currentUser) return; const workout = workouts.find(w => w.id === workoutId); if (!workout) { console.error("Séance non trouvée:", workoutId); showPage('home-page'); return; }
|
516 |
+
document.getElementById('detail-workout-name').textContent = workout.name; document.getElementById('detail-date').textContent = new Date(workout.date).toLocaleDateString('fr-FR'); document.getElementById('detail-duration').textContent = workout.duration; document.getElementById('detail-tonnage').textContent = workout.totalTonnage.toFixed(1); document.getElementById('detail-satisfaction').textContent = `${workout.satisfaction}%`; document.getElementById('detail-exercises-count').textContent = workout.exercises.length;
|
517 |
+
const detailExercisesContainer = document.getElementById('detail-exercises-container'); detailExercisesContainer.innerHTML = '';
|
|
|
|
|
|
|
518 |
|
519 |
+
// **Affichage détail SANS logique Superset**
|
520 |
+
workout.exercises.forEach((exercise) => {
|
|
|
|
|
|
|
521 |
const exerciseDiv = document.createElement('div');
|
522 |
+
exerciseDiv.className = 'card detail-exercise-card'; // Juste la classe de base
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
523 |
|
524 |
let seriesHtml = '';
|
525 |
+
exercise.series.forEach((serie, sIndex) => {
|
526 |
const degressiveLabel = serie.isDegressive ? ' <span class="badge" style="font-size: 0.7rem; background-color: var(--accent-dark);">Dégr.</span>' : ''; const weightFactor = exercise.isUnilateral ? 2 : 1; const seriesTonnage = serie.reps * serie.weight * weightFactor;
|
527 |
seriesHtml += `<div class="exercise-summary"><div class="flex-between"><span>Série ${sIndex + 1}: ${serie.reps} reps × ${serie.weight} kg${degressiveLabel}</span><span style="color: #aaa;">(${seriesTonnage.toFixed(1)} kg)</span></div></div>`;
|
528 |
});
|
529 |
|
530 |
const unilateralLabel = exercise.isUnilateral ? ' <span class="badge" style="font-size: 0.7rem;">Unilat.</span>' : '';
|
531 |
+
// Pas de supersetLabel ici
|
|
|
532 |
|
533 |
exerciseDiv.innerHTML = `
|
534 |
+
<h3 style="font-size: 1.1rem; margin-bottom: 0.5rem;">${exercise.name}${unilateralLabel}</h3>
|
535 |
+
<div class="series-summary-container"> ${seriesHtml} </div>`;
|
|
|
|
|
536 |
detailExercisesContainer.appendChild(exerciseDiv);
|
537 |
});
|
538 |
|
|
|
540 |
showPage('workout-details-page');
|
541 |
}
|
542 |
|
543 |
+
function deleteWorkout() {
|
544 |
if (!currentUser || !currentWorkoutId) return; const workoutToDelete = workouts.find(w => w.id === currentWorkoutId); if (!workoutToDelete) return; const confirmDelete = confirm(`Supprimer séance "${workoutToDelete.name}" du ${new Date(workoutToDelete.date).toLocaleDateString('fr-FR')} ?`); if (!confirmDelete) return; workouts = workouts.filter(w => w.id !== currentWorkoutId); saveWorkouts(); currentWorkoutId = null; showPage('home-page'); renderWorkoutsList();
|
545 |
}
|
546 |
|
547 |
+
function updateStats() {
|
548 |
if (!currentUser) return; const workoutCount = workouts.length; document.getElementById('stats-workout-count').textContent = workoutCount; if (workoutCount === 0) { document.getElementById('stats-avg-tonnage').textContent = '0'; document.getElementById('stats-avg-satisfaction').textContent = '0%'; return; } const totalTonnageAll = workouts.reduce((sum, workout) => sum + (workout.totalTonnage || 0), 0); const avgTonnage = totalTonnageAll / workoutCount; document.getElementById('stats-avg-tonnage').textContent = avgTonnage.toFixed(1); const totalSatisfaction = workouts.reduce((sum, workout) => sum + (workout.satisfaction || 0), 0); const avgSatisfaction = totalSatisfaction / workoutCount; document.getElementById('stats-avg-satisfaction').textContent = `${Math.round(avgSatisfaction)}%`;
|
549 |
}
|
550 |
|