Spaces:
Running
Running
File size: 37,001 Bytes
ddd7255 013977a 840f62b 5af9065 013977a 5af9065 ddd7255 013977a ddd7255 013977a ddd7255 5af9065 013977a 5af9065 013977a 5af9065 ddd7255 013977a ddd7255 013977a 5af9065 013977a 5af9065 013977a 1a9859c 5af9065 840f62b 5af9065 013977a 5af9065 013977a ddd7255 5af9065 013977a 5af9065 013977a 5af9065 013977a 5af9065 013977a 5af9065 013977a 5af9065 013977a 5af9065 ddd7255 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LiftTrack - Firebase Edition (Debug)</title> <!-- Titre mis à jour -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/confetti.browser.min.js"></script>
<!-- SDK Firebase (v8) -->
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<style>
/* CSS Identique */
:root { /* ... */ --bg-dark: #121212; --bg-card: #1e1e1e; --text-light: #e0e0e0; --accent: #4CAF50; --accent-dark: #3a8a3d; --danger: #f44336; } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: var(--bg-dark); color: var(--text-light); min-height: 100vh; padding-bottom: 80px; } .container { width: 100%; max-width: 800px; margin: 0 auto; padding: 1rem; } header { padding: 1rem 0; text-align: center; border-bottom: 1px solid #333; margin-bottom: 1rem; } h1, h2, h3 { color: var(--accent); } .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; } .btn:hover { background-color: var(--accent-dark); } .btn:disabled { background-color: #555; cursor: not-allowed; } .btn-outline { background-color: transparent; color: var(--accent); border: 1px solid var(--accent); } .btn-outline:disabled { color: #555; border-color: #555; } .btn-danger { background-color: var(--danger); } 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); } input[type="checkbox"] { width: auto; margin-right: 0.5rem; vertical-align: middle; } .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); } .form-group { margin-bottom: 1rem; } .form-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; flex-wrap: wrap; } .form-row > * { flex: 1; margin-bottom: 0; min-width: 100px; } label { display: block; margin-bottom: 0.3rem; color: #bbb; font-size: 0.9rem; } .exercise { border-left: 3px solid var(--accent); padding-left: 1rem; margin-bottom: 1.5rem; display: none; } .exercise.active-exercise { display: block; animation: fadeIn 0.3s ease-in-out; } .exercise-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; gap: 0.5rem;} .exercise-header input {margin-bottom: 0;} .series-container { margin-left: 0.5rem; margin-top: 0.5rem; } .series { background-color: #252525; padding: 0.7rem; border-radius: 4px; margin-bottom: 0.5rem; } .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); z-index: 10; } .nav-item { text-align: center; color: #888; text-decoration: none; font-size: 0.85rem; transition: color 0.2s; padding: 0 0.5rem;} .nav-item.active { color: var(--accent); } .nav-icon { font-size: 1.4rem; margin-bottom: 0.2rem; } .workout-card { border-left: 3px solid var(--accent); cursor: pointer; transition: transform 0.2s; } .workout-card:hover { transform: translateX(5px); } .workout-header { display: flex; justify-content: space-between; align-items: flex-start; } .stat-card { text-align: center; padding: 1rem; } .stat-value { font-size: 1.8rem; color: var(--accent); font-weight: bold; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 1rem; } .hidden { display: none; } #login-page { display: block; } #main-app-content { display: none; } #app-container > div:not(.active) { display: none !important; } .flex-between { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem;} .badge { background-color: var(--accent); color: white; padding: 0.2rem 0.5rem; border-radius: 10px; font-size: 0.8rem; white-space: nowrap; } .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; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .exercise-summary { margin: 0.3rem 0; padding: 0.3rem 0; border-bottom: 1px solid #333; font-size: 0.9rem;} .exercise-summary:last-child { border-bottom: none; } .satisfaction { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 1rem; } .satisfaction-value { font-size: 2rem; color: var(--accent); margin-top: 0.5rem; } .exercise-navigation { display: flex; justify-content: space-between; margin-top: 1rem; margin-bottom: 1rem; } #login-page .card { max-width: 400px; margin: 2rem auto; } #login-page h2 { text-align: center; margin-bottom: 1.5rem; } #login-error { color: var(--danger); text-align: center; margin-top: 0.5rem; font-size: 0.9rem; display: none; min-height: 1.2em; /* Pour éviter saut de layout */ } #login-form button { margin-top: 0.5rem;} .auth-switch { text-align: center; margin-top: 1.5rem; font-size: 0.9rem; } .auth-switch a { color: var(--accent); cursor: pointer; text-decoration: underline; } .user-info { text-align: right; margin-bottom: 1rem; font-size: 0.9rem; color: #bbb;} .user-info strong { color: var(--text-light); } .user-info button { margin-left: 0.5rem; padding: 0.2rem 0.5rem; font-size: 0.8rem; } @media (max-width: 600px) { .form-row { flex-direction: column; gap: 0; } .container { padding: 0.5rem; } h1 { font-size: 1.5rem; } .flex-between { flex-direction: column; align-items: stretch; } .flex-between > div { width: 100%; display: flex; justify-content: flex-end; margin-top: 0.5rem;} .user-info { text-align: center; margin-bottom: 0.5rem;} .user-info button { display: block; margin: 0.5rem auto 0;} .exercise-navigation button { padding: 0.5rem; font-size: 0.9rem;} } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
</style>
</head>
<body>
<!-- Structure HTML identique -->
<div class="container"> <header> <h1>LiftTrack</h1> <p>Suivi de vos séances de musculation</p> </header> <div id="login-page"> <div class="card"> <h2 id="auth-title">Connexion</h2> <form id="login-form"> <div class="form-group"> <label for="auth-email">Email</label> <input type="email" id="auth-email" placeholder="[email protected]" required> </div> <div class="form-group"> <label for="auth-password">Mot de passe</label> <input type="password" id="auth-password" placeholder="********" required> </div> <p id="login-error"></p> <button type="submit" id="auth-action-btn" class="btn" style="width: 100%;">Se Connecter</button> </form> <div class="auth-switch"> <span id="auth-switch-text">Pas encore de compte ?</span> <a id="auth-switch-link">Inscrivez-vous ici</a> </div> </div> </div> <div id="main-app-content"> <div class="user-info"> Connecté: <strong id="current-user-display"></strong> <button id="logout-btn" class="btn btn-outline">Déconnexion</button> </div> <div id="app-container"> <div id="home-page" class="active"> <div class="flex-between"> <h2>Mes séances</h2> <button id="new-workout-btn" class="btn">Nouvelle séance</button> </div> <div id="workouts-list" class="workout-list" style="margin-top: 1rem;"> <div class="spinner hidden"></div> <p id="empty-workout-message" class="hidden" style="text-align: center; margin-top: 2rem;"> Aucune séance enregistrée. </p> </div> </div> <div id="new-workout-page"> <div class="flex-between"> <h2>Nouvelle séance <span id="current-exercise-indicator" style="font-size: 0.9rem; color: #ccc;"></span></h2> <div> <button id="cancel-new-workout-btn" class="btn btn-outline" style="margin-right: 0.5rem;">Annuler</button> <button id="save-workout-btn" class="btn">Enregistrer Séance</button> </div> </div> <div class="card"> <div class="form-group"> <label for="workout-name">Nom de la séance</label> <input type="text" id="workout-name" placeholder="Ex: Push, Legs, Full Body..."> </div> <div class="form-row"> <div class="form-group"> <label for="workout-date">Date</label> <input type="date" id="workout-date"> </div> <div class="form-group"> <label for="workout-duration">Durée (min)</label> <input type="number" id="workout-duration" min="1" placeholder="60"> </div> </div> </div> <h3 style="margin: 1rem 0;">Exercices</h3> <div class="exercise-navigation card"> <button id="prev-exercise-btn" class="btn btn-outline">< Précédent</button> <span style="align-self: center; color: #ccc;">Exercice Actif</span> <button id="next-exercise-btn" class="btn btn-outline">Suivant ></button> </div> <div id="exercises-container"> </div> <button id="add-exercise-btn" class="btn btn-outline" style="width: 100%; margin-top: 1rem;">+ Ajouter un Nouvel Exercice</button> <div class="card" style="margin-top: 2rem;"> <div class="form-group"> <label for="satisfaction">Niveau de satisfaction (1-100%)</label> <input type="range" id="satisfaction" min="1" max="100" value="75"> <div class="satisfaction"> <span>Satisfaction</span> <div class="satisfaction-value">75%</div> </div> </div> </div> </div> <div id="workout-details-page"> <div class="flex-between"> <h2 id="detail-workout-name">Détail Séance</h2> <button id="back-to-home" class="btn btn-outline">Retour</button> </div> <div class="card"> <div class="workout-details-info"> <div class="form-row"> <p><strong>Date:</strong> <span id="detail-date"></span></p> <p><strong>Durée:</strong> <span id="detail-duration"></span> min</p> </div> </div> </div> <div class="stats-grid" style="margin-top: 1rem;"> <div class="card stat-card"> <div class="stat-value" id="detail-tonnage">0</div> <div>Tonnage Total (kg)</div> </div> <div class="card stat-card"> <div class="stat-value" id="detail-satisfaction">0%</div> <div>Satisfaction</div> </div> <div class="card stat-card"> <div class="stat-value" id="detail-exercises-count">0</div> <div>Exercices</div> </div> </div> <h3 style="margin: 1.5rem 0 1rem;">Exercices Réalisés</h3> <div id="detail-exercises-container"> </div> <button id="delete-workout-btn" class="btn btn-danger" style="width: 100%; margin-top: 2rem;">Supprimer cette séance</button> </div> <div id="stats-page"> <h2>Statistiques</h2> <div class="stats-grid"> <div class="card stat-card"> <div class="stat-value" id="stats-workout-count">0</div> <div>Séances Totales</div> </div> <div class="card stat-card"> <div class="stat-value" id="stats-avg-tonnage">0</div> <div>Tonnage Moyen</div> </div> <div class="card stat-card"> <div class="stat-value" id="stats-avg-satisfaction">0%</div> <div>Satisfaction Moyenne</div> </div> </div> <h3 style="margin: 1.5rem 0 1rem;">Tendances Récentes</h3> <div class="card"> <p style="text-align: center; margin: 1rem 0; color: #888;"> (La section des tendances récentes n'est pas encore implémentée.) </p> </div> </div> </div> <nav class="nav-bottom"> <a href="#" class="nav-item active" data-page="home-page"> <div class="nav-icon">📋</div> <div>Séances</div> </a> <a href="#" class="nav-item" data-page="stats-page"> <div class="nav-icon">📊</div> <div>Stats</div> </a> </nav> </div>
<script>
// =============== AJOUT FIREBASE ===============
const firebaseConfig = { apiKey: "AIzaSyAkWvrRyXgrC7zbTtoh_GppsHMrz2rF7WM", authDomain: "lifttrackapp.firebaseapp.com", projectId: "lifttrackapp", storageBucket: "lifttrackapp.appspot.com", messagingSenderId: "594426771796", appId: "1:594426771796:web:789bef037ca0016c54b0c1", measurementId: "G-MXLFK0H160" };
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const db = firebase.firestore();
console.log("Firebase Initialisé !");
// =============== FIN AJOUT FIREBASE ===============
// --- Variables d'État ---
let workouts = []; let currentWorkoutId = null; let currentExerciseIndex = 0; let workoutExercisesForm = []; let currentFirebaseUser = null;
// --- Éléments DOM ---
const loginPage = document.getElementById('login-page'); const mainAppContent = document.getElementById('main-app-content'); const authEmailInput = document.getElementById('auth-email'); const authPasswordInput = document.getElementById('auth-password'); const loginForm = document.getElementById('login-form'); const authActionButton = document.getElementById('auth-action-btn'); const authSwitchLink = document.getElementById('auth-switch-link'); const authTitle = document.getElementById('auth-title'); const authSwitchText = document.getElementById('auth-switch-text'); const loginError = document.getElementById('login-error'); const currentUserDisplay = document.getElementById('current-user-display'); const logoutBtn = document.getElementById('logout-btn'); const spinner = document.querySelector('#workouts-list .spinner'); const appContainer = document.getElementById('app-container'); const navItems = document.querySelectorAll('.nav-item'); const newWorkoutBtn = document.getElementById('new-workout-btn'); const saveWorkoutBtn = document.getElementById('save-workout-btn'); const cancelNewWorkoutBtn = document.getElementById('cancel-new-workout-btn'); const addExerciseBtn = document.getElementById('add-exercise-btn'); const exercisesContainer = document.getElementById('exercises-container'); const workoutsList = document.getElementById('workouts-list'); const backToHomeBtn = document.getElementById('back-to-home'); const deleteWorkoutBtn = document.getElementById('delete-workout-btn'); const satisfactionRange = document.getElementById('satisfaction'); const satisfactionValue = document.querySelector('.satisfaction-value'); const emptyWorkoutMessage = document.getElementById('empty-workout-message'); const workoutDateInput = document.getElementById('workout-date'); const prevExerciseBtn = document.getElementById('prev-exercise-btn'); const nextExerciseBtn = document.getElementById('next-exercise-btn'); const currentExerciseIndicator = document.getElementById('current-exercise-indicator');
let isLoginMode = true;
// --- Initialisation & Auth State ---
document.addEventListener('DOMContentLoaded', () => { initAuthListener(); initEventListeners(); });
function initAuthListener() {
auth.onAuthStateChanged(user => {
console.log("Auth state changed. User:", user ? user.uid : 'null'); // DEBUG
if (user) { currentFirebaseUser = user; showApp(); } else { currentFirebaseUser = null; workouts = []; renderWorkoutsList(); showLoginPage(); }
});
}
// --- Gestion Affichage Login/App ---
function showLoginPage() { console.log("Affichage: Page Login"); loginPage.style.display = 'block'; mainAppContent.style.display = 'none'; }
function showApp() { if (!currentFirebaseUser) { console.log("showApp annulé: pas d'utilisateur"); return; } console.log("Affichage: App Principale"); loginPage.style.display = 'none'; mainAppContent.style.display = 'block'; currentUserDisplay.textContent = currentFirebaseUser.email; setTodayDate(); loadWorkouts(); showPage('home-page'); }
// --- Authentification Firebase ---
function handleAuthAction(event) { event.preventDefault(); const email = authEmailInput.value; const password = authPasswordInput.value; loginError.textContent = ''; if (!email || !password) { loginError.textContent = 'Email et mot de passe requis.'; return; } authActionButton.disabled = true; authActionButton.textContent = 'Chargement...'; if (isLoginMode) { console.log("Tentative de connexion..."); auth.signInWithEmailAndPassword(email, password) .catch((error) => { console.error("Erreur connexion:", error); loginError.textContent = getAuthErrorMessage(error); }) .finally(() => { authActionButton.disabled = false; authActionButton.textContent = 'Se Connecter'; }); } else { console.log("Tentative d'inscription..."); auth.createUserWithEmailAndPassword(email, password) .catch((error) => { console.error("Erreur inscription:", error); loginError.textContent = getAuthErrorMessage(error); }) .finally(() => { authActionButton.disabled = false; authSwitchMode(); authActionButton.textContent = 'S\'inscrire'; }); } }
function handleLogout() { console.log("Tentative de déconnexion..."); auth.signOut().catch((error) => { console.error("Erreur déconnexion:", error); alert("Erreur déconnexion."); }); }
function authSwitchMode() { isLoginMode = !isLoginMode; loginError.textContent = ''; if (isLoginMode) { authTitle.textContent = 'Connexion'; authActionButton.textContent = 'Se Connecter'; authSwitchText.textContent = 'Pas encore de compte ?'; authSwitchLink.textContent = 'Inscrivez-vous ici'; } else { authTitle.textContent = 'Inscription'; authActionButton.textContent = 'S\'inscrire'; authSwitchText.textContent = 'Déjà un compte ?'; authSwitchLink.textContent = 'Connectez-vous ici'; } }
function getAuthErrorMessage(error) { /* ... (identique) ... */ switch (error.code) { case 'auth/invalid-email': return 'Format d\'email invalide.'; case 'auth/user-disabled': return 'Ce compte utilisateur a été désactivé.'; case 'auth/user-not-found': return 'Aucun utilisateur trouvé avec cet email.'; case 'auth/wrong-password': return 'Mot de passe incorrect.'; case 'auth/email-already-in-use': return 'Cet email est déjà utilisé par un autre compte.'; case 'auth/weak-password': return 'Le mot de passe est trop faible (minimum 6 caractères).'; case 'auth/operation-not-allowed': return 'Connexion par email/mot de passe non activée.'; default: return 'Une erreur est survenue. Veuillez réessayer.'; } }
// --- Persistance Firestore ---
function loadWorkouts() { /* ... (identique, avec logs) ... */ if (!currentFirebaseUser) { console.log("Chargement annulé: aucun utilisateur connecté."); workouts = []; renderWorkoutsList(); return; } const userId = currentFirebaseUser.uid; console.log(`Chargement des séances pour l'utilisateur ${userId}...`); if (spinner) spinner.classList.remove('hidden'); emptyWorkoutMessage.classList.add('hidden'); workoutsList.innerHTML = ''; workouts = []; db.collection('workouts') .where('userId', '==', userId) .orderBy('date', 'desc') .get() .then((querySnapshot) => { console.log(`Trouvé ${querySnapshot.size} séances dans Firestore.`); querySnapshot.forEach((doc) => { const workoutData = doc.data(); workoutData.firestoreId = doc.id; workouts.push(workoutData); }); renderWorkoutsList(); }) .catch((error) => { console.error("Erreur chargement Firestore: ", error); alert("Erreur chargement séances."); }) .finally(() => { if (spinner) spinner.classList.add('hidden'); }); }
// SAVE WORKOUT EST IDENTIQUE À LA VERSION PRÉCÉDENTE (utilise Firestore)
function saveWorkout() { if (!currentFirebaseUser) { alert("Vous devez être connecté pour enregistrer."); return; } const userId = currentFirebaseUser.uid; workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const workoutName = document.getElementById('workout-name').value.trim(); const workoutDate = document.getElementById('workout-date').value; const workoutDuration = parseInt(document.getElementById('workout-duration').value) || 0; const satisfaction = parseInt(document.getElementById('satisfaction').value); if (!workoutName || !workoutDate || workoutDuration <= 0) { alert("Données séance invalides."); return; } const exerciseElements = workoutExercisesForm; const exercises = []; if (exerciseElements.length === 0) { alert("Ajoutez au moins un exercice."); return; } let validationError = null; exerciseElements.forEach((exerciseEl, index) => { if (validationError) return; const nameInput = exerciseEl.querySelector('.exercise-name'); const exerciseName = nameInput ? nameInput.value.trim() : ''; const isUnilateral = exerciseEl.querySelector('.unilateral-checkbox').checked; if (!exerciseName) { validationError = `Nommez l'exercice #${index + 1}.`; if(nameInput) nameInput.focus(); return; } const series = []; const seriesElements = exerciseEl.querySelectorAll('.series'); if (seriesElements.length === 0) { validationError = `Exercice "${exerciseName}" sans série.`; return; } seriesElements.forEach(seriesEl => { if (validationError) return; const repsInput = seriesEl.querySelector('.reps'); const weightInput = seriesEl.querySelector('.weight'); const reps = parseInt(repsInput.value) || 0; const weight = parseFloat(weightInput.value) || 0; const isDegressive = seriesEl.querySelector('.degressive-checkbox').checked; if (reps <= 0) { validationError = `Reps invalides pour "${exerciseName}".`; if(repsInput) repsInput.focus(); return; } if (weight < 0) { validationError = `Charge invalide pour "${exerciseName}".`; if(weightInput) weightInput.focus(); return; } series.push({ reps, weight, isDegressive }); }); if (!validationError) { exercises.push({ name: exerciseName, isUnilateral, series }); } }); if (validationError) { alert(validationError); return; } let totalTonnage = 0; exercises.forEach(ex => ex.series.forEach(s => totalTonnage += s.reps * s.weight * (ex.isUnilateral ? 2 : 1))); const workout = { id: currentWorkoutId || `workout-${Date.now()}`, userId: userId, name: workoutName, date: workoutDate, duration: workoutDuration, exercises: exercises, totalTonnage: totalTonnage, satisfaction: satisfaction }; console.log("Tentative sauvegarde séance:", workout); saveWorkoutBtn.disabled = true; saveWorkoutBtn.textContent = 'Sauvegarde...'; db.collection('workouts').doc(workout.id).set(workout) .then(() => { console.log("Séance sauvegardée Firestore:", workout.id); const existingIndex = workouts.findIndex(w => w.id === workout.id); if (existingIndex > -1) { workouts[existingIndex] = workout; } else { workouts.push(workout); } confetti({ particleCount: 150, spread: 90, origin: { y: 0.6 } }); showPage('home-page'); renderWorkoutsList(); }) .catch((error) => { console.error("Erreur sauvegarde Firestore: ", error); alert("Erreur sauvegarde séance."); }) .finally(() => { saveWorkoutBtn.disabled = false; saveWorkoutBtn.textContent = 'Enregistrer Séance'; }); }
// DELETE WORKOUT EST IDENTIQUE À LA VERSION PRÉCÉDENTE (utilise Firestore)
function deleteWorkout() { if (!currentFirebaseUser || !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; console.log("Tentative suppression séance:", currentWorkoutId); deleteWorkoutBtn.disabled = true; deleteWorkoutBtn.textContent = 'Suppression...'; db.collection('workouts').doc(currentWorkoutId).delete() .then(() => { console.log("Séance supprimée Firestore:", currentWorkoutId); workouts = workouts.filter(w => w.id !== currentWorkoutId); currentWorkoutId = null; showPage('home-page'); renderWorkoutsList(); }) .catch((error) => { console.error("Erreur suppression Firestore:", error); alert("Erreur suppression séance."); }) .finally(() => { deleteWorkoutBtn.disabled = false; deleteWorkoutBtn.textContent = 'Supprimer cette séance'; }); }
// --- Écouteurs d'Événements (Setup Initial) ---
function initEventListeners() {
// Auth
loginForm.addEventListener('submit', handleAuthAction);
authSwitchLink.addEventListener('click', authSwitchMode);
logoutBtn.addEventListener('click', handleLogout);
// App Navigation & Actions
navItems.forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); if (!currentFirebaseUser) return; const targetPageId = item.getAttribute('data-page'); showPage(targetPageId); /* ... MAJ nav active ... */ }); });
// **AJOUT DES LOGS DANS LE LISTENER DU BOUTON**
newWorkoutBtn.addEventListener('click', () => {
console.log("Clic sur 'Nouvelle séance'"); // <<< LOG 1
if (!currentFirebaseUser) {
console.error("'Nouvelle séance' bloqué: Utilisateur non connecté (currentFirebaseUser est null)"); // <<< LOG 2
alert("Veuillez vous reconnecter pour ajouter une séance."); // Message utilisateur
return;
}
try {
console.log("Utilisateur connecté, préparation formulaire..."); // <<< LOG 3
currentWorkoutId = null;
clearNewWorkoutForm(); // Cette fonction appelle addExercise et renderActiveExerciseForm
console.log("Formulaire nettoyé (clearNewWorkoutForm exécuté)."); // <<< LOG 4
showPage('new-workout-page'); // Cette fonction appelle triggerFlameAnimation et renderActiveExerciseForm
console.log("Affichage page nouvelle séance demandé (showPage exécuté)."); // <<< LOG 5
} catch (error) {
console.error("Erreur dans le listener de 'Nouvelle séance':", error); // <<< LOG ERREUR
alert("Une erreur est survenue lors de l'ouverture du formulaire.");
}
});
cancelNewWorkoutBtn.addEventListener('click', () => showPage('home-page'));
saveWorkoutBtn.addEventListener('click', saveWorkout);
addExerciseBtn.addEventListener('click', handleAddNewExercise);
prevExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, -1));
nextExerciseBtn.addEventListener('click', navigateExerciseForm.bind(null, 1));
backToHomeBtn.addEventListener('click', () => showPage('home-page'));
deleteWorkoutBtn.addEventListener('click', deleteWorkout);
satisfactionRange.addEventListener('input', () => { satisfactionValue.textContent = `${satisfactionRange.value}%`; });
}
// --- Fonctions Utilitaires et d'Affichage (non modifiées) ---
// (triggerFlameAnimation, handleAddNewExercise, addExercise, addSeries, navigateExerciseForm, renderActiveExerciseForm, updateExerciseNavButtons, clearNewWorkoutForm, renderWorkoutsList, displayWorkoutDetails, updateStats SONT IDENTIQUES)
function triggerFlameAnimation() { const duration = 1 * 1000; const animationEnd = Date.now() + duration; const defaults = { startVelocity: 30, spread: 360, ticks: 60, zIndex: 0 }; function randomInRange(min, max) { return Math.random() * (max - min) + min; } const interval = setInterval(function() { const timeLeft = animationEnd - Date.now(); if (timeLeft <= 0) { return clearInterval(interval); } const particleCount = 50 * (timeLeft / duration); confetti(Object.assign({}, defaults, { particleCount, origin: { x: randomInRange(0.3, 0.7), y: Math.random() - 0.2 }, angle: randomInRange(240, 300), spread: randomInRange(50, 90), gravity: 0.5, drift: randomInRange(-1, 1), colors: ['#ff6f00', '#ff8f00', '#ffa000', '#ffcc00'] })); }, 250); }
function handleAddNewExercise() { addExercise(true); }
function addExercise(navigateToNew = false) { if (!currentFirebaseUser) return; const exerciseId = `exercise-${Date.now()}`; const exerciseDiv = document.createElement('div'); exerciseDiv.className = 'card exercise'; exerciseDiv.setAttribute('data-exercise-id', exerciseId); exerciseDiv.innerHTML = `<div class="exercise-header"> <input type="text" placeholder="Nom Exercice" class="exercise-name"> <button class="btn btn-danger remove-exercise" style="padding: 0.3rem 0.6rem; flex-shrink: 0;">×</button> </div> <div class="form-group" style="margin-top: 0.5rem; margin-bottom: 0.5rem;"> <label style="display: inline-flex; align-items: center; color: #bbb; font-size: 0.9rem;"> <input type="checkbox" class="unilateral-checkbox"> Unilatéral </label> </div> <div class="series-container"></div> <button class="btn btn-outline add-series" style="width: 100%; margin-top: 0.5rem; padding: 0.4rem;">+ Ajouter Série</button>`; exercisesContainer.appendChild(exerciseDiv); workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const removeBtn = exerciseDiv.querySelector('.remove-exercise'); removeBtn.addEventListener('click', () => { const indexToRemove = workoutExercisesForm.indexOf(exerciseDiv); if (indexToRemove > -1) { exerciseDiv.remove(); workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); if (currentExerciseIndex >= indexToRemove) { currentExerciseIndex = Math.max(0, currentExerciseIndex - 1); } if (currentExerciseIndex >= workoutExercisesForm.length) { currentExerciseIndex = Math.max(0, workoutExercisesForm.length - 1); } renderActiveExerciseForm(); } }); const addSeriesBtn = exerciseDiv.querySelector('.add-series'); const seriesContainer = exerciseDiv.querySelector('.series-container'); addSeriesBtn.addEventListener('click', () => addSeries(seriesContainer)); addSeries(seriesContainer); if (navigateToNew) { currentExerciseIndex = workoutExercisesForm.length - 1; renderActiveExerciseForm(); } else { updateExerciseNavButtons(); } }
function addSeries(container) { if (!currentFirebaseUser || !container) return; const seriesId = `series-${Date.now()}`; const seriesDiv = document.createElement('div'); seriesDiv.className = 'series'; seriesDiv.setAttribute('data-series-id', seriesId); 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>`; container.appendChild(seriesDiv); const removeBtn = seriesDiv.querySelector('.remove-series'); removeBtn.addEventListener('click', () => seriesDiv.remove()); }
function navigateExerciseForm(direction) { workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const newIndex = currentExerciseIndex + direction; if (newIndex >= 0 && newIndex < workoutExercisesForm.length) { currentExerciseIndex = newIndex; renderActiveExerciseForm(); } }
function renderActiveExerciseForm() { workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); workoutExercisesForm.forEach(ex => ex.classList.remove('active-exercise')); if (currentExerciseIndex >= 0 && currentExerciseIndex < workoutExercisesForm.length) { workoutExercisesForm[currentExerciseIndex].classList.add('active-exercise'); } updateExerciseNavButtons(); }
function updateExerciseNavButtons() { const totalExercises = workoutExercisesForm.length; if (totalExercises === 0) { currentExerciseIndicator.textContent = "(Aucun exercice)"; prevExerciseBtn.disabled = true; nextExerciseBtn.disabled = true; } else { currentExerciseIndicator.textContent = `(${currentExerciseIndex + 1}/${totalExercises})`; prevExerciseBtn.disabled = currentExerciseIndex === 0; nextExerciseBtn.disabled = currentExerciseIndex === totalExercises - 1; } }
function clearNewWorkoutForm() { console.log("Début clearNewWorkoutForm"); if (!currentFirebaseUser) return; document.getElementById('workout-name').value = ''; document.getElementById('workout-duration').value = ''; setTodayDate(); exercisesContainer.innerHTML = ''; workoutExercisesForm = []; satisfactionRange.value = 75; satisfactionValue.textContent = '75%'; currentWorkoutId = null; currentExerciseIndex = 0; addExercise(false); console.log("Fin clearNewWorkoutForm (après addExercise)"); renderActiveExerciseForm(); }
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 = ''; } } }
function renderWorkoutsList() { if (!currentFirebaseUser) { workoutsList.innerHTML = ''; emptyWorkoutMessage.classList.remove('hidden'); updateStats(); return; } workoutsList.innerHTML = ''; if (workouts.length === 0) { emptyWorkoutMessage.classList.remove('hidden'); } else { emptyWorkoutMessage.classList.add('hidden'); const sortedWorkouts = [...workouts].sort((a, b) => new Date(b.date) - new Date(a.date)); 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); }); } updateStats(); }
function displayWorkoutDetails(workoutId) { if (!currentFirebaseUser) return; const workout = workouts.find(w => w.id === workoutId); if (!workout) { console.error("Séance non trouvée:", workoutId); showPage('home-page'); return; } 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; const detailExercisesContainer = document.getElementById('detail-exercises-container'); detailExercisesContainer.innerHTML = ''; workout.exercises.forEach((exercise) => { const exerciseDiv = document.createElement('div'); exerciseDiv.className = 'card detail-exercise-card'; let seriesHtml = ''; exercise.series.forEach((serie, sIndex) => { 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; 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>`; }); const unilateralLabel = exercise.isUnilateral ? ' <span class="badge" style="font-size: 0.7rem;">Unilat.</span>' : ''; exerciseDiv.innerHTML = `<h3 style="font-size: 1.1rem; margin-bottom: 0.5rem;">${exercise.name}${unilateralLabel}</h3> <div class="series-summary-container"> ${seriesHtml} </div>`; detailExercisesContainer.appendChild(exerciseDiv); }); currentWorkoutId = workoutId; showPage('workout-details-page'); }
function updateStats() { if (!currentFirebaseUser) 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)}%`; }
</script>
</body>
</html> |