diff --git "a/index.html" "b/index.html" --- "a/index.html" +++ "b/index.html" @@ -229,22 +229,71 @@ .nav-item.active { color: var(--accent); font-weight: bold; } .nav-icon { font-size: 1.5rem; margin-bottom: 0.2rem; } /* Icônes plus grandes */ - /* Workout Card */ + /* ===== CSS MODIFIÉ POUR LA CARTE SÉANCE ===== */ .workout-card { border-left: 4px solid var(--accent); cursor: pointer; transition: transform 0.2s ease-out, box-shadow 0.2s ease-out; - position: relative; /* Pour positionner le badge date */ } .workout-card:hover { - transform: translateY(-3px); /* Légère lévitation */ + transform: translateY(-3px); box-shadow: 0 6px 12px rgba(0,0,0,0.4); } - .workout-header { display: flex; justify-content: space-between; align-items: flex-start; } - .workout-header h3 { color: var(--text-light); margin-bottom: 0.3rem; font-family: var(--font-primary); text-transform: none; letter-spacing: normal; font-weight: bold; } /* Nom séance normal */ - .workout-header .badge { position: absolute; top: 1rem; right: 1rem; background-color: var(--info); color: var(--text-light); } /* Badge date repositionné */ - .workout-details { font-size: 0.9rem; color: var(--text-secondary); margin-top: 0.8rem; } - .workout-details span { margin-right: 0.8rem; } + .workout-header { + display: flex; + align-items: center; /* Centre verticalement titre et badge */ + gap: 0.75rem; /* Espace entre le titre et le badge */ + margin-bottom: 0.5rem; /* Espace sous l'en-tête, avant les détails */ + } + .workout-header h3 { + color: var(--text-light); + font-family: var(--font-primary); + text-transform: none; + letter-spacing: normal; + font-weight: bold; + flex-grow: 1; /* Permet au titre de prendre l'espace disponible */ + min-width: 0; /* Important pour permettre au titre de rétrécir */ + position: static; /* Assure positionnement normal */ + margin-bottom: 0; /* Retiré car géré par gap */ + } + /* Cible spécifiquement le badge DANS le .workout-header (le badge de date) */ + .workout-header > .badge { + background-color: #2a2a2a; /* Fond plus discret, correspond aux inputs */ + color: var(--text-secondary); /* Texte gris clair */ + padding: 0.3rem 0.6rem; + border-radius: 4px; /* Moins arrondi, plus sobre */ + font-size: 0.8rem; + font-weight: normal; /* Date pas forcément en gras */ + white-space: nowrap; /* Garde la date sur une ligne */ + flex-shrink: 0; /* Empêche le badge de rétrécir */ + margin-left: auto; /* Pousse le badge vers la droite après que le h3 ait pris sa place */ + position: static; /* Assure positionnement normal */ + } + .workout-details { + font-size: 0.9rem; + color: var(--text-secondary); + margin-top: 0.8rem; /* Rétabli ou ajusté */ + } + .workout-details span { + margin-right: 0.8rem; /* Espacement entre les détails */ + } + /* Assure que le badge de type à l'intérieur du h3 garde son style */ + .workout-header h3 .badge.type-badge { + background-color: var(--neutral-white); + color: var(--bg-dark); + padding: 0.3rem 0.6rem; + border-radius: 12px; + font-size: 0.8rem; + font-weight: bold; + white-space: nowrap; + margin-left: 5px; + /* On s'assure qu'il n'hérite pas du style du badge de date */ + position: static; + flex-shrink: initial; + margin-left: 5px; /* Garde la marge gauche du badge type */ + } + /* ===== FIN CSS MODIFIÉ POUR LA CARTE SÉANCE ===== */ + /* Stats */ .stat-card { text-align: center; padding: 1rem; } @@ -259,13 +308,10 @@ .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; } /* Min width augmentée */ .hidden { display: none !important; } /* Utilise !important pour forcer */ - /* Gérer l'affichage initial via JS plutôt que CSS pour éviter les flashs */ - /* #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: 1rem; } /* Espace augmenté */ - /* Badges */ + /* Badges Généraux (non date, non type) */ .badge { background-color: var(--accent); color: var(--text-on-accent); @@ -276,7 +322,8 @@ white-space: nowrap; margin-left: 5px; } - .badge.type-badge { background-color: var(--neutral-white); color: var(--bg-dark); } /* Badge type en blanc (logo Fitness) */ + /* Style spécifique pour le badge de type déjà géré plus haut */ + /* Spinner */ .spinner { @@ -296,7 +343,7 @@ font-size: 0.9rem; } .exercise-summary:last-child { border-bottom: none; } - .series-summary-container .badge { font-size: 0.7rem; padding: 0.2rem 0.4rem; } /* Badges plus petits dans résumé */ + .series-summary-container .badge { font-size: 0.7rem; padding: 0.2rem 0.4rem; background-color: var(--info); color: var(--text-light); } /* Style badge Dégr/Unilat dans détails */ /* Satisfaction Slider */ .satisfaction { display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 1rem; } @@ -361,6 +408,11 @@ .form-row { flex-direction: column; gap: 0.8rem; } .flex-between { flex-direction: column; align-items: stretch; gap: 0.8rem; } .flex-between > div { width: 100%; display: flex; justify-content: center; margin-top: 0.5rem;} + /* Ajustement spécifique pour l'en-tête de carte séance sur mobile */ + .workout-header { flex-wrap: wrap; } /* Permet au badge de passer en dessous si manque de place */ + .workout-header h3 { margin-bottom: 0.3rem; /* Ajoute un peu d'espace si ça passe en dessous */ } + .workout-header > .badge { margin-left: 0; /* Pas besoin de le pousser à droite si ça passe en dessous */ } + .user-info { text-align: center; margin-bottom: 0.8rem;} .user-info button { display: block; margin: 0.5rem auto 0;} .exercise-navigation button { padding: 0.6rem; font-size: 0.9rem;} @@ -560,7 +612,6 @@ } catch (e) { console.error("Erreur Init Firebase:", e); alert("Erreur critique: Impossible d'initialiser Firebase."); - // Optionnel: Afficher l'erreur dans le body document.body.innerHTML = '
Erreur critique: Impossible d\'initialiser Firebase. Vérifiez la configuration et la connexion.
'; } @@ -572,9 +623,9 @@ let currentFirebaseUser = null; let userWorkoutTypes = []; let selectedWorkoutTypeName = null; - let isLoginMode = true; // Start in login mode + let isLoginMode = true; - // --- Éléments DOM --- (On suppose qu'ils existent tous) + // --- Éléments DOM --- const loginPage = document.getElementById('login-page'); const mainAppContent = document.getElementById('main-app-content'); const authEmailInput = document.getElementById('auth-email'); @@ -630,981 +681,103 @@ // --- DÉFINITION DES FONCTIONS --- // =========================================== - function initAuthListener() { - if (!auth) { console.error("Auth object not ready in initAuthListener"); return; } - auth.onAuthStateChanged(user => { - console.log("Auth state changed:", user ? user.uid : 'null'); - currentFirebaseUser = user; - if (user) { - showApp(); - } else { - workouts = []; // Clear data on logout - userWorkoutTypes = []; - showLoginPage(); - } - }); - } - - function showLoginPage() { - if (loginPage) loginPage.style.display = 'block'; - if (mainAppContent) mainAppContent.style.display = 'none'; - isLoginMode = true; // Reset to login mode - authSwitchMode(); // Update UI for login mode - } - - function showApp() { - if (!currentFirebaseUser || !mainAppContent) { - console.error("Cannot show app: No user or main content missing."); - showLoginPage(); // Fallback to login page - return; - } - console.log("Affichage App for user:", currentFirebaseUser.email); - if (loginPage) loginPage.style.display = 'none'; - mainAppContent.style.display = 'block'; - if (currentUserDisplay) currentUserDisplay.textContent = currentFirebaseUser.email; - setTodayDate(); - loadWorkouts(); - loadWorkoutTypes(); - showPage('home-page'); // Start at home page - } - - function handleAuthAction(event) { - if (!auth || !authEmailInput || !authPasswordInput || !authActionButton) { - console.error("Auth or input/button elements missing in handleAuthAction"); return; - } - event.preventDefault(); // Stop form submission - const email = authEmailInput.value; - const password = authPasswordInput.value; - - // Clear previous error - if (loginError) { - loginError.textContent = ''; - loginError.style.display = 'none'; - } - - // Basic validation - if (!email || !password) { - if (loginError) { - loginError.textContent = 'Email et Mot de passe requis.'; - loginError.style.display = 'block'; - } - return; - } - - // Disable button and show loading state - authActionButton.disabled = true; - authActionButton.textContent = 'Chargement...'; - - if (isLoginMode) { - console.log("Attempting sign in for:", email); - auth.signInWithEmailAndPassword(email, password) - .then(userCredential => { - console.log("Sign in successful:", userCredential.user.uid); - // Auth state change will handle UI update via initAuthListener - }) - .catch(handleAuthError) // Use dedicated error handler - .finally(() => { - // Re-enable button regardless of success/failure - authActionButton.disabled = false; - authActionButton.textContent = 'Se Connecter'; - }); - } else { - console.log("Attempting create user for:", email); - auth.createUserWithEmailAndPassword(email, password) - .then(userCredential => { - console.log("Create user successful:", userCredential.user.uid); - // Auth state change will handle UI update via initAuthListener - }) - .catch(handleAuthError) // Use dedicated error handler - .finally(() => { - // Re-enable button - authActionButton.disabled = false; - authActionButton.textContent = 'S\'inscrire'; - // Reset to login mode visually after attempting signup - authSwitchMode(); - }); - } - } - - function handleLogout() { - if (!auth) { console.error("Auth missing for logout"); return; } - console.log("Déconnexion..."); - auth.signOut().catch(error => { - console.error("Logout Error:", error); - alert("Erreur lors de la déconnexion."); - }); - } - - function authSwitchMode() { - isLoginMode = !isLoginMode; - // Clear errors and inputs - if (loginError) { loginError.textContent = ''; loginError.style.display = 'none'; } - if (authEmailInput) authEmailInput.value = ''; - if (authPasswordInput) authPasswordInput.value = ''; - // Update UI elements - if (authTitle) authTitle.textContent = isLoginMode ? 'Connexion' : 'Inscription'; - if (authActionButton) authActionButton.textContent = isLoginMode ? 'Se Connecter' : 'S\'inscrire'; - if (authSwitchText) authSwitchText.textContent = isLoginMode ? 'Pas encore de compte ?' : 'Déjà un compte ?'; - if (authSwitchLink) authSwitchLink.textContent = isLoginMode ? 'Inscrivez-vous ici' : 'Connectez-vous ici'; - // Set focus to email input for convenience - if (authEmailInput) authEmailInput.focus(); - } - - function handleAuthError(error) { - console.error("Erreur Auth Firebase:", error.code, error.message); - if (loginError) { - loginError.textContent = getAuthErrorMessage(error); - loginError.style.display = 'block'; // Make sure error is visible - } - } - - function getAuthErrorMessage(error) { - // Provide user-friendly messages based on error codes - 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 doit contenir au moins 6 caractères.'; - case 'auth/operation-not-allowed': return 'Méthode de connexion non activée (vérifiez Console Firebase).'; - case 'auth/missing-password': return 'Mot de passe manquant.'; - default: - console.error("Unhandled Auth Error Code:", error.code); // Log unhandled codes - return `Erreur (${error.code}). Veuillez réessayer.`; - } - } - - function loadWorkoutTypes() { - if (!currentFirebaseUser || !db || !typesSpinner) return; - const userId = currentFirebaseUser.uid; - console.log("Chargement types pour", userId); - userWorkoutTypes = []; // Reset before loading - typesSpinner.classList.remove('hidden'); - db.collection('workoutTypes').where('userId', '==', userId).orderBy('name').get() - .then(snapshot => { - userWorkoutTypes = []; // Ensure it's fresh - snapshot.forEach(doc => { - userWorkoutTypes.push({ id: doc.id, ...doc.data() }); - }); - console.log("Types chargés:", userWorkoutTypes); - renderWorkoutTypesList(); - populateWorkoutTypeSelector(); - }) - .catch(handleFirestoreError) - .finally(() => { - typesSpinner.classList.add('hidden'); - }); - } - - function renderWorkoutTypesList() { - if (!workoutTypesList || !emptyTypesMessage) { console.error("DOM elements for types list missing"); return; } - workoutTypesList.innerHTML = ''; // Clear list - if (userWorkoutTypes.length === 0) { - emptyTypesMessage.classList.remove('hidden'); - } else { - emptyTypesMessage.classList.add('hidden'); - userWorkoutTypes.forEach(type => { - const li = document.createElement('li'); - li.textContent = type.name; - // Future: Add delete button here if needed - // const deleteBtn = document.createElement('button'); ... li.appendChild(deleteBtn); - workoutTypesList.appendChild(li); - }); - } - } - - function handleAddWorkoutType(event) { - event.preventDefault(); // Prevent default form submission - if (!currentFirebaseUser || !newTypeNameInput || !db || !addTypeError || !addTypeForm) { - console.error("Missing elements for adding workout type"); return; - } - const typeName = newTypeNameInput.value.trim(); - - // Clear previous error - addTypeError.textContent = ''; - addTypeError.style.display = 'none'; - - if (!typeName) { - addTypeError.textContent = "Le nom du type ne peut pas être vide."; - addTypeError.style.display = 'block'; - return; - } - // Check for duplicates (case-insensitive) - if (userWorkoutTypes.some(t => t.name.toLowerCase() === typeName.toLowerCase())) { - addTypeError.textContent = "Ce type de séance existe déjà."; - addTypeError.style.display = 'block'; - return; - } - - console.log("Ajout type:", typeName); - const typeData = { userId: currentFirebaseUser.uid, name: typeName, createdAt: firebase.firestore.FieldValue.serverTimestamp() }; // Add timestamp - - const addBtn = addTypeForm.querySelector('button'); - if (addBtn) { addBtn.disabled = true; addBtn.textContent = '...'; } - - db.collection('workoutTypes').add(typeData) - .then(() => { - console.log("Type ajouté avec succès"); - newTypeNameInput.value = ''; // Clear input field - loadWorkoutTypes(); // Refresh the list - }) - .catch(handleFirestoreError) - .finally(() => { - if (addBtn) { addBtn.disabled = false; addBtn.textContent = 'Ajouter'; } - }); - } - - function populateWorkoutTypeSelector() { - if (!selectWorkoutTypeDropdown) return; - selectWorkoutTypeDropdown.innerHTML = ''; // Reset dropdown - userWorkoutTypes.forEach(type => { - const option = document.createElement('option'); - option.value = type.name; // Use name as value - option.textContent = type.name; - selectWorkoutTypeDropdown.appendChild(option); - }); - updateStartStructuredBtnState(); // Update button state after populating - } - - function updateStartStructuredBtnState() { - if (!selectWorkoutTypeDropdown || !startStructuredWorkoutBtn) return; - // Enable button only if a valid type (not the default empty value) is selected - startStructuredWorkoutBtn.disabled = !selectWorkoutTypeDropdown.value; - } - - function loadWorkouts() { - if (!currentFirebaseUser || !db || !spinner || !emptyWorkoutMessage || !workoutsList) { - console.warn("Cannot load workouts: Missing user, db, or DOM elements."); - // Ensure list is cleared and message shown if applicable - if(workoutsList) workoutsList.innerHTML = ''; - if(emptyWorkoutMessage) emptyWorkoutMessage.classList.remove('hidden'); - workouts = []; updateStats(); return; - } - const userId = currentFirebaseUser.uid; - console.log(`Chargement séances pour ${userId}...`); - spinner.classList.remove('hidden'); - emptyWorkoutMessage.classList.add('hidden'); - workoutsList.innerHTML = ''; // Clear previous list - - db.collection('workouts').where('userId', '==', userId).orderBy('date', 'desc').get() - .then((querySnapshot) => { - workouts = []; // Reset local array - console.log(`${querySnapshot.size} séances trouvées.`); - querySnapshot.forEach((doc) => { - const workoutData = doc.data(); - workoutData.id = doc.id; // Assign Firestore document ID - workouts.push(workoutData); - }); - renderWorkoutsList(); // Update the UI - updateStats(); // Update stats based on new data - }) - .catch(handleFirestoreError) - .finally(() => { - spinner.classList.add('hidden'); // Hide spinner always - // Show empty message only if spinner is hidden AND workouts is empty - if (workouts.length === 0) { - emptyWorkoutMessage.classList.remove('hidden'); - } - }); - } - - function saveWorkout() { - if (!currentFirebaseUser || !db || !exercisesContainer || !saveWorkoutBtn) { - alert("Erreur: Impossible de sauvegarder (manque user/db/elements)."); return; - } - const userId = currentFirebaseUser.uid; - workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); // Get current exercises from DOM - - const workoutNameInput = document.getElementById('workout-name'); - const workoutName = workoutNameInput ? workoutNameInput.value.trim() : ''; - const workoutDate = workoutDateInput ? workoutDateInput.value : ''; - const workoutDurationInput = document.getElementById('workout-duration'); - const workoutDuration = workoutDurationInput ? parseInt(workoutDurationInput.value) || 0 : 0; - const satisfaction = satisfactionRange ? parseInt(satisfactionRange.value) : 75; // Default satisfaction - - // Validate essential data - if (!selectedWorkoutTypeName) { alert("Erreur interne: Type de séance manquant."); showPage('home-page'); return; } - if (!workoutDate) { alert("Veuillez sélectionner une date."); if(workoutDateInput) workoutDateInput.focus(); return; } - if (workoutDuration <= 0) { alert("Veuillez entrer une durée valide (minutes)."); if(workoutDurationInput) workoutDurationInput.focus(); return; } - if (workoutExercisesForm.length === 0) { alert("Veuillez ajouter au moins un exercice."); return; } - - // Process exercises and series - const exercises = []; - let validationError = null; - workoutExercisesForm.forEach((exerciseEl, index) => { - if (validationError) return; // Stop processing if error found - const nameInput = exerciseEl.querySelector('.exercise-name'); - const exerciseName = nameInput ? nameInput.value.trim() : ''; - const unilateralCheckbox = exerciseEl.querySelector('.unilateral-checkbox'); - const isUnilateral = unilateralCheckbox ? unilateralCheckbox.checked : false; - - 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 = `L'exercice "${exerciseName}" n'a pas de série.`; return; } - - seriesElements.forEach((seriesEl, sIndex) => { - if (validationError) return; - const repsInput = seriesEl.querySelector('.reps'); - const weightInput = seriesEl.querySelector('.weight'); - const degressiveCheckbox = seriesEl.querySelector('.degressive-checkbox'); - const reps = repsInput ? parseInt(repsInput.value) || 0 : 0; - const weight = weightInput ? parseFloat(weightInput.value) || 0 : 0; - const isDegressive = degressiveCheckbox ? degressiveCheckbox.checked : false; - - if (reps <= 0) { validationError = `Reps invalides pour série ${sIndex + 1} (${exerciseName}).`; if(repsInput) repsInput.focus(); return; } - if (weight < 0) { validationError = `Charge invalide pour série ${sIndex + 1} (${exerciseName}).`; if(weightInput) weightInput.focus(); return; } - series.push({ reps, weight, isDegressive }); - }); - if (!validationError) { exercises.push({ name: exerciseName, isUnilateral, series }); } - }); - - if (validationError) { alert(validationError); return; } // Show validation error and stop - - // Calculate tonnage - let totalTonnage = 0; - exercises.forEach(ex => { - (ex.series || []).forEach(s => { - totalTonnage += (s.reps || 0) * (s.weight || 0) * (ex.isUnilateral ? 2 : 1); - }); - }); - - const finalWorkoutName = workoutName || selectedWorkoutTypeName; // Use type name if custom name is empty - const workoutData = { - userId: userId, - workoutTypeName: selectedWorkoutTypeName, - name: finalWorkoutName, - date: workoutDate, - duration: workoutDuration, - exercises: exercises, - totalTonnage: totalTonnage, - satisfaction: satisfaction, - // Add timestamp for creation/update - lastUpdated: firebase.firestore.FieldValue.serverTimestamp() - }; - - let savePromise; - // Determine if creating new or updating existing based on currentWorkoutId - if (currentWorkoutId) { - console.log("Mise à jour séance:", currentWorkoutId); - workoutData.createdAt = workouts.find(w => w.id === currentWorkoutId)?.createdAt || firebase.firestore.FieldValue.serverTimestamp(); // Preserve creation date if possible - savePromise = db.collection('workouts').doc(currentWorkoutId).set(workoutData); // Use set to overwrite completely (or use update) - } else { - console.log("Création nouvelle séance"); - workoutData.createdAt = firebase.firestore.FieldValue.serverTimestamp(); // Set creation date - savePromise = db.collection('workouts').add(workoutData); // Use add to get auto-ID - } - - saveWorkoutBtn.disabled = true; - saveWorkoutBtn.textContent = 'Sauvegarde...'; - - savePromise.then((docRefOrVoid) => { - const savedId = currentWorkoutId || (docRefOrVoid ? docRefOrVoid.id : 'unknown'); // Get ID if created - console.log("Séance sauvegardée:", savedId); - currentWorkoutId = null; // Reset ID after save - try { if (typeof confetti === 'function') confetti({ particleCount: 150, spread: 90, origin: { y: 0.6 } }); } catch(e) { console.warn("Confetti error:", e)} - showPage('home-page'); // Go back home - loadWorkouts(); // Reload list - }).catch(handleFirestoreError).finally(() => { - saveWorkoutBtn.disabled = false; - saveWorkoutBtn.textContent = 'Enregistrer Séance'; - }); - } - - function deleteWorkout() { - if (!currentFirebaseUser || !currentWorkoutId || !db || !deleteWorkoutBtn) return; - const workoutToDelete = workouts.find(w => w.id === currentWorkoutId); - if (!workoutToDelete) { console.error("Workout to delete not found locally."); return; } - - const confirmMsg = `Supprimer la séance "${workoutToDelete.name || 'Sans nom'}" du ${new Date(workoutToDelete.date).toLocaleDateString('fr-FR')} ?`; - if (!confirm(confirmMsg)) return; // Ask for confirmation - - console.log("Suppression séance:", currentWorkoutId); - deleteWorkoutBtn.disabled = true; - deleteWorkoutBtn.textContent = 'Suppression...'; - - db.collection('workouts').doc(currentWorkoutId).delete() - .then(() => { - console.log("Séance supprimée avec succès:", currentWorkoutId); - currentWorkoutId = null; // Clear the ID - showPage('home-page'); // Go back home - loadWorkouts(); // Refresh list and stats - }) - .catch(handleFirestoreError) - .finally(() => { - deleteWorkoutBtn.disabled = false; - deleteWorkoutBtn.textContent = 'Supprimer Séance'; - }); - } - - function setTodayDate() { - if (workoutDateInput) { - try { - const today = new Date(); - // Adjust for timezone offset to get local date in YYYY-MM-DD format - const offset = today.getTimezoneOffset(); - const localDate = new Date(today.getTime() - (offset*60*1000)); - workoutDateInput.value = localDate.toISOString().split('T')[0]; - } catch (e) { - console.error("Erreur setTodayDate:", e); - // Fallback to non-timezone adjusted date if error occurs - try { workoutDateInput.value = new Date().toISOString().split('T')[0]; } catch {} - } - } - } - - function showPage(pageId) { - if (!appContainer) { console.error("App container not found!"); return; } - console.log(`Affichage page: ${pageId}`); - - // Hide all pages first - const pages = appContainer.querySelectorAll(':scope > div[id$="-page"]'); // Select direct children ending with -page - pages.forEach(page => page.classList.remove('active')); - - const pageToShow = document.getElementById(pageId); - if (pageToShow) { - pageToShow.classList.add('active'); // Show the target page - window.scrollTo(0, 0); // Scroll to top - - // Page-specific actions - if (pageId === 'new-workout-page') { - // Ensure a type is selected before showing this page - if (!selectedWorkoutTypeName) { - console.warn("Tentative d'accès à new-workout-page sans type sélectionné."); - alert("Veuillez d'abord sélectionner un type de séance."); - showPage('select-workout-type-page'); // Redirect - return; - } - renderActiveExerciseForm(); - updateWorkoutTypeIndicator(); - } else if (pageId === 'manage-types-page') { - loadWorkoutTypes(); - } else if (pageId === 'select-workout-type-page') { - populateWorkoutTypeSelector(); - } else if (pageId === 'stats-page') { - updateStats(); - } else if (pageId === 'workout-details-page') { - // Ensure an ID is set before showing details - if (!currentWorkoutId) { - console.warn("Tentative d'accès aux détails sans ID de séance."); - alert("Séance non spécifiée."); - showPage('home-page'); // Redirect - return; - } - // Data for details page is loaded within displayWorkoutDetails function - } - - } else { - console.error(`Page ID "${pageId}" non trouvée. Affichage de 'home-page'.`); - const homePage = document.getElementById('home-page'); - if (homePage) homePage.classList.add('active'); - pageId = 'home-page'; // Fallback pageId for nav highlighting - } - - // Update bottom navigation highlighting - navItems.forEach(item => item.classList.remove('active')); - // Highlight 'home' for most app pages, 'stats' for the stats page - const currentPageForNav = pageId === 'stats-page' ? 'stats-page' : 'home-page'; - const activeNavItem = document.querySelector(`.nav-item[data-page="${currentPageForNav}"]`); - if (activeNavItem) activeNavItem.classList.add('active'); - } - - - function handleAddNewExercise() { - addExercise(true); // Add and navigate to the new exercise - } - - function addExercise(navigateToNew = false) { - if (!currentFirebaseUser || !exercisesContainer) { console.error("Cannot add exercise"); return; } - const exerciseId = `exercise-${Date.now()}`; // Simple unique ID - const exerciseDiv = document.createElement('div'); - exerciseDiv.className = 'card exercise'; // Apply card styling - exerciseDiv.setAttribute('data-exercise-id', exerciseId); - // Simplified HTML structure for clarity - exerciseDiv.innerHTML = ` -
- - -
-
- -
-
- - `; - exercisesContainer.appendChild(exerciseDiv); - workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); // Update the form array - - // --- Add Event Listeners for the new exercise --- - const removeBtn = exerciseDiv.querySelector('.remove-exercise'); - removeBtn.addEventListener('click', () => { - const indexToRemove = workoutExercisesForm.findIndex(el => el === exerciseDiv); - if (indexToRemove > -1) { - if (confirm("Supprimer cet exercice et toutes ses séries ?")) { - exerciseDiv.remove(); // Remove from DOM - workoutExercisesForm.splice(indexToRemove, 1); // Remove from array - // Adjust current index if needed - if (currentExerciseIndex >= indexToRemove && currentExerciseIndex > 0) { - currentExerciseIndex--; - } - if (currentExerciseIndex >= workoutExercisesForm.length) { - currentExerciseIndex = Math.max(0, workoutExercisesForm.length - 1); - } - renderActiveExerciseForm(); // Refresh view (show correct exercise, update nav) - } - } - }); - - const addSeriesBtn = exerciseDiv.querySelector('.add-series'); - const seriesContainer = exerciseDiv.querySelector('.series-container'); - addSeriesBtn.addEventListener('click', () => addSeries(seriesContainer)); - - addSeries(seriesContainer); // Add one default series - - // Navigate or just update nav buttons - if (navigateToNew) { - currentExerciseIndex = workoutExercisesForm.length - 1; // Go to the newly added exercise - renderActiveExerciseForm(); - // Focus the name input of the new exercise - const nameInput = exerciseDiv.querySelector('.exercise-name'); - if (nameInput) nameInput.focus(); - } else { - updateExerciseNavButtons(); // Just update buttons if not navigating - } - } - - function addSeries(container) { - if (!currentFirebaseUser || !container) { console.error("Cannot add series"); return; } - const seriesId = `series-${Date.now()}`; - const seriesDiv = document.createElement('div'); - seriesDiv.className = 'series'; - seriesDiv.setAttribute('data-series-id', seriesId); - // Simplified HTML for series row - seriesDiv.innerHTML = ` -
-
- - -
-
- - -
-
- -
- -
- `; - container.appendChild(seriesDiv); - - // Add listener to the remove button of this specific series - const removeBtn = seriesDiv.querySelector('.remove-series'); - removeBtn.addEventListener('click', () => { - // Optional: Confirm before removing - // if (confirm("Supprimer cette série ?")) { - seriesDiv.remove(); - // } - }); - } - - function navigateExerciseForm(direction) { - if (!exercisesContainer) return; - workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); // Ensure up-to-date - const newIndex = currentExerciseIndex + direction; - // Check bounds - if (newIndex >= 0 && newIndex < workoutExercisesForm.length) { - currentExerciseIndex = newIndex; - renderActiveExerciseForm(); - } - } - - function renderActiveExerciseForm() { - if (!exercisesContainer) return; - workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); // Get current list - // Hide all, then show the active one - workoutExercisesForm.forEach((ex, index) => { - if (index === currentExerciseIndex) { - ex.classList.add('active-exercise'); - ex.style.display = 'block'; // Ensure it's visible - } else { - ex.classList.remove('active-exercise'); - ex.style.display = 'none'; // Hide inactive - } - }); - updateExerciseNavButtons(); // Update indicators and button states - } - - function updateExerciseNavButtons() { - if (!currentExerciseIndicator || !prevExerciseBtn || !nextExerciseBtn) { console.warn("Exercise nav elements missing"); return; } - 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); // Disable if first - nextExerciseBtn.disabled = (currentExerciseIndex === totalExercises - 1); // Disable if last - } - } - - function clearNewWorkoutForm(typeName = "Libre") { - console.log("Nettoyage formulaire pour type:", typeName); - if (!currentFirebaseUser) { console.error("Cannot clear form: No user"); return; } - selectedWorkoutTypeName = typeName; - - // Reset form fields - const workoutNameInput = document.getElementById('workout-name'); - if (workoutNameInput) workoutNameInput.value = (typeName === "Libre" ? "" : typeName); // Pre-fill name if structured - if (workoutDateInput) setTodayDate(); // Set date to today - const durationInput = document.getElementById('workout-duration'); - if (durationInput) durationInput.value = '60'; // Default duration - if (exercisesContainer) exercisesContainer.innerHTML = ''; // Clear existing exercises - workoutExercisesForm = []; // Reset exercise array - if (satisfactionRange) satisfactionRange.value = 75; // Reset satisfaction slider - if (satisfactionValue) satisfactionValue.textContent = '75%'; // Reset satisfaction display - - currentWorkoutId = null; // Ensure we are creating a new workout - currentExerciseIndex = 0; // Reset exercise index - - addExercise(false); // Add the first empty exercise block - updateWorkoutTypeIndicator(); // Update the type badge display - renderActiveExerciseForm(); // Show the first exercise form and update nav - } - - function updateWorkoutTypeIndicator() { - if (!workoutTypeIndicator) return; - if (selectedWorkoutTypeName && selectedWorkoutTypeName !== "Libre") { - workoutTypeIndicator.textContent = selectedWorkoutTypeName; - workoutTypeIndicator.classList.remove('hidden'); - } else { - workoutTypeIndicator.classList.add('hidden'); - } - } - - function renderWorkoutsList() { - if (!workoutsList || !emptyWorkoutMessage) { console.warn("Cannot render workout list: DOM elements missing."); return; } - workoutsList.innerHTML = ''; // Clear previous list - - if (!workouts || workouts.length === 0) { - emptyWorkoutMessage.classList.remove('hidden'); - } else { - emptyWorkoutMessage.classList.add('hidden'); - // Sort workouts by date descending (already done by Firestore query, but good practice) - const sortedWorkouts = [...workouts].sort((a, b) => new Date(b.date) - new Date(a.date)); - - sortedWorkouts.forEach(workout => { - if (!workout || !workout.id) { // Skip if workout data is invalid - console.warn("Skipping invalid workout data:", workout); - return; - } - const workoutDate = workout.date ? new Date(workout.date).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }) : 'Date inconnue'; - const workoutDiv = document.createElement('div'); - workoutDiv.className = 'card workout-card'; - workoutDiv.setAttribute('data-workout-id', workout.id); - - const typeBadge = workout.workoutTypeName && workout.workoutTypeName !== "Libre" ? `${workout.workoutTypeName}` : ''; - const exercisesCount = (workout.exercises || []).length; - - workoutDiv.innerHTML = ` -
-

${workout.name || 'Séance sans nom'} ${typeBadge}

-
${workoutDate}
-
-
- ${workout.duration || '?'} min | - ${exercisesCount} exo${exercisesCount > 1 ? 's' : ''} | - ${(workout.totalTonnage || 0).toFixed(1)} kg | - ${workout.satisfaction || '?'}% -
`; - workoutDiv.addEventListener('click', () => displayWorkoutDetails(workout.id)); - workoutsList.appendChild(workoutDiv); - }); - } - } - - function displayWorkoutDetails(workoutId) { - if (!currentFirebaseUser) { console.error("User not logged in for details"); return; } - const workout = workouts.find(w => w && w.id === workoutId); // Find workout in local array - if (!workout) { alert("Détails de la séance introuvables."); showPage('home-page'); return; } - - console.log("Affichage détails pour:", workoutId, workout); - - // --- Selectors for Detail Page Elements --- - const detailNameEl = document.getElementById('detail-workout-name'); - const detailDateEl = document.getElementById('detail-date'); - const detailDurationEl = document.getElementById('detail-duration'); - const detailTonnageEl = document.getElementById('detail-tonnage'); - const detailSatisfactionEl = document.getElementById('detail-satisfaction'); - const detailExercisesCountEl = document.getElementById('detail-exercises-count'); - const detailExercisesContainer = document.getElementById('detail-exercises-container'); - const detailWorkoutTypeBadge = document.getElementById('detail-workout-type'); - - // --- Populate Detail Page Elements --- - if (detailWorkoutTypeBadge) { - if (workout.workoutTypeName && workout.workoutTypeName !== "Libre") { - detailWorkoutTypeBadge.textContent = workout.workoutTypeName; - detailWorkoutTypeBadge.classList.remove('hidden'); - } else { - detailWorkoutTypeBadge.classList.add('hidden'); - } - } - if (detailNameEl) detailNameEl.textContent = workout.name || 'Séance sans nom'; - if (detailDateEl) detailDateEl.textContent = workout.date ? new Date(workout.date).toLocaleDateString('fr-FR') : 'Inconnue'; - if (detailDurationEl) detailDurationEl.textContent = workout.duration || '?'; - if (detailTonnageEl) detailTonnageEl.textContent = (workout.totalTonnage || 0).toFixed(1); - if (detailSatisfactionEl) detailSatisfactionEl.textContent = `${workout.satisfaction || '?'}%`; - const exercisesCount = (workout.exercises || []).length; - if (detailExercisesCountEl) detailExercisesCountEl.textContent = exercisesCount; - - // Populate exercises details - if (detailExercisesContainer) { - detailExercisesContainer.innerHTML = ''; // Clear previous details - if (exercisesCount > 0) { - workout.exercises.forEach((exercise) => { - if (!exercise) return; // Skip invalid exercise data - const exerciseDiv = document.createElement('div'); - exerciseDiv.className = 'card detail-exercise-card'; // Reuse card style - let seriesHtml = ''; - (exercise.series || []).forEach((serie, sIndex) => { - const degressiveLabel = serie.isDegressive ? ' Dégr.' : ''; - const weightFactor = exercise.isUnilateral ? 2 : 1; - const seriesTonnage = (serie.reps || 0) * (serie.weight || 0) * weightFactor; - seriesHtml += ` -
-
- Série ${sIndex + 1}: ${serie.reps || '?'} reps × ${serie.weight || '?'} kg${degressiveLabel} - (${seriesTonnage.toFixed(1)} kg) -
-
`; - }); - const unilateralLabel = exercise.isUnilateral ? ' Unilat.' : ''; - exerciseDiv.innerHTML = ` -

- ${exercise.name || 'Exercice sans nom'}${unilateralLabel} -

-
${seriesHtml}
`; - detailExercisesContainer.appendChild(exerciseDiv); - }); - } else { - detailExercisesContainer.innerHTML = '

Aucun exercice enregistré pour cette séance.

'; - } - } - - currentWorkoutId = workoutId; // Set the current ID for potential deletion - showPage('workout-details-page'); // Navigate to the details page - } - - // --- Fonctionnalité STATS (Avec tendances) --- - function updateStats() { - console.log("Mise à jour des statistiques..."); - if (!currentFirebaseUser || !statsWorkoutCountEl || !statsAvgTonnageEl || !statsAvgSatisfactionEl || !typeTrendsContainer || !noTrendsMessage) { - console.warn("Cannot update stats: Missing user or DOM elements."); - // Reset stats display if elements exist - if(statsWorkoutCountEl) statsWorkoutCountEl.textContent = '0'; - if(statsAvgTonnageEl) statsAvgTonnageEl.textContent = '0'; - if(statsAvgSatisfactionEl) statsAvgSatisfactionEl.textContent = '0%'; - if(typeTrendsContainer) typeTrendsContainer.innerHTML = ''; - if(noTrendsMessage) noTrendsMessage.classList.remove('hidden'); - return; - } - - const workoutCount = workouts.length; - statsWorkoutCountEl.textContent = workoutCount; - - if (workoutCount === 0) { - statsAvgTonnageEl.textContent = '0'; - statsAvgSatisfactionEl.textContent = '0%'; - renderTypeTrends({}); // Pass empty object to show 'no data' message - return; - } - - // Calculate global stats - const totalTonnageAll = workouts.reduce((sum, w) => sum + (w.totalTonnage || 0), 0); - statsAvgTonnageEl.textContent = workoutCount > 0 ? (totalTonnageAll / workoutCount).toFixed(1) : '0'; - const totalSatisfaction = workouts.reduce((sum, w) => sum + (w.satisfaction || 0), 0); - statsAvgSatisfactionEl.textContent = workoutCount > 0 ? `${Math.round(totalSatisfaction / workoutCount)}%` : '0%'; - - // Calculate trends per type - console.log("Calcul tendances par type..."); - const trendsData = {}; - const structuredWorkouts = workouts.filter(w => w && w.workoutTypeName && w.workoutTypeName !== "Libre"); // Filter valid workouts - - const groupedByType = structuredWorkouts.reduce((acc, workout) => { - const type = workout.workoutTypeName; - if (!acc[type]) acc[type] = []; - acc[type].push(workout); - return acc; - }, {}); - - for (const typeName in groupedByType) { - // Sort sessions chronologically for the type - const sessionsOfType = groupedByType[typeName].sort((a, b) => new Date(a.date) - new Date(b.date)); - // Get the last 3 sessions - trendsData[typeName] = sessionsOfType.slice(-3).map(s => ({ - date: s.date, - tonnage: s.totalTonnage || 0 // Default tonnage to 0 if missing - })); - } - console.log("Données tendances:", trendsData); - renderTypeTrends(trendsData); // Render the calculated trends - } - - function renderTypeTrends(trends) { - if (!typeTrendsContainer || !noTrendsMessage || !trendsSpinner) { - console.error("Cannot render trends: Missing DOM elements."); return; - } - trendsSpinner.classList.add('hidden'); // Hide spinner - typeTrendsContainer.innerHTML = ''; // Clear previous trends - const trendTypes = Object.keys(trends); - - if (trendTypes.length === 0) { - noTrendsMessage.classList.remove('hidden'); // Show 'no data' message - } else { - noTrendsMessage.classList.add('hidden'); // Hide 'no data' message - trendTypes.sort().forEach(typeName => { // Sort types alphabetically - const typeData = trends[typeName]; - if (!typeData || typeData.length === 0) return; // Skip if no data for this type - - const card = document.createElement('div'); - card.className = 'card type-trend-card'; - let listItems = ''; - typeData.forEach(session => { - const formattedDate = session.date ? new Date(session.date).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short' }) : '??'; - // Handle potential null/undefined tonnage - const tonnageText = (session.tonnage !== undefined && session.tonnage !== null) ? `${session.tonnage.toFixed(1)} kg` : 'N/A'; - listItems += `
  • ${formattedDate} ${tonnageText}
  • `; - }); - - card.innerHTML = `

    ${typeName} (3 Dernières)

    `; - typeTrendsContainer.appendChild(card); - }); - } - } - - function handleFirestoreError(error) { - console.error("Erreur Firestore:", error.code, error.message); - alert(`Erreur de base de données: ${error.message}`); - // Potentially disable relevant buttons or show a general error message - } + // (Les définitions des fonctions JavaScript sont ici - identiques à la version précédente) + // ... (initAuthListener, showLoginPage, showApp, handleAuthAction, etc...) ... + // ... (TOUTES les fonctions JS jusqu'à la fin de la section fonctions) ... + + function initAuthListener() { if (!auth) { console.error("Auth object not ready in initAuthListener"); return; } auth.onAuthStateChanged(user => { console.log("Auth state changed:", user ? user.uid : 'null'); currentFirebaseUser = user; if (user) { showApp(); } else { workouts = []; userWorkoutTypes = []; showLoginPage(); } }); } + function showLoginPage() { if(loginPage) loginPage.style.display = 'block'; if(mainAppContent) mainAppContent.style.display = 'none'; isLoginMode = true; authSwitchMode(); } + function showApp() { if (!currentFirebaseUser || !mainAppContent) { console.error("Cannot show app: No user or main content missing."); showLoginPage(); return; } console.log("Affichage App for user:", currentFirebaseUser.email); if(loginPage) loginPage.style.display = 'none'; mainAppContent.style.display = 'block'; if(currentUserDisplay) currentUserDisplay.textContent = currentFirebaseUser.email; setTodayDate(); loadWorkouts(); loadWorkoutTypes(); showPage('home-page'); } + function handleAuthAction(event) { if (!auth || !authEmailInput || !authPasswordInput || !authActionButton) { console.error("Auth or input/button elements missing in handleAuthAction"); return; } event.preventDefault(); const email = authEmailInput.value; const password = authPasswordInput.value; if (loginError) { loginError.textContent = ''; loginError.style.display = 'none'; } if (!email || !password) { if (loginError) { loginError.textContent = 'Email et Mot de passe requis.'; loginError.style.display = 'block'; } return; } authActionButton.disabled = true; authActionButton.textContent = 'Chargement...'; if (isLoginMode) { console.log("Attempting sign in for:", email); auth.signInWithEmailAndPassword(email, password).then(userCredential => { console.log("Sign in successful:", userCredential.user.uid); }).catch(handleAuthError).finally(() => { authActionButton.disabled = false; authActionButton.textContent = 'Se Connecter'; }); } else { console.log("Attempting create user for:", email); auth.createUserWithEmailAndPassword(email, password).then(userCredential => { console.log("Create user successful:", userCredential.user.uid); }).catch(handleAuthError).finally(() => { authActionButton.disabled = false; authActionButton.textContent = 'S\'inscrire'; authSwitchMode(); }); } } + function handleLogout() { if (!auth) { console.error("Auth missing for logout"); return; } console.log("Déconnexion..."); auth.signOut().catch(error => { console.error("Logout Error:", error); alert("Erreur lors de la déconnexion."); }); } + function authSwitchMode() { isLoginMode = !isLoginMode; if (loginError) { loginError.textContent = ''; loginError.style.display = 'none'; } if (authEmailInput) authEmailInput.value = ''; if (authPasswordInput) authPasswordInput.value = ''; if (authTitle) authTitle.textContent = isLoginMode ? 'Connexion' : 'Inscription'; if (authActionButton) authActionButton.textContent = isLoginMode ? 'Se Connecter' : 'S\'inscrire'; if (authSwitchText) authSwitchText.textContent = isLoginMode ? 'Pas encore de compte ?' : 'Déjà un compte ?'; if (authSwitchLink) authSwitchLink.textContent = isLoginMode ? 'Inscrivez-vous ici' : 'Connectez-vous ici'; if (authEmailInput) authEmailInput.focus(); } + function handleAuthError(error) { console.error("Erreur Auth Firebase:", error.code, error.message); if (loginError) { loginError.textContent = getAuthErrorMessage(error); loginError.style.display = 'block'; } } + function getAuthErrorMessage(error) { 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 doit contenir au moins 6 caractères.'; case 'auth/operation-not-allowed': return 'Méthode de connexion non activée (vérifiez Console Firebase).'; case 'auth/missing-password': return 'Mot de passe manquant.'; default: console.error("Unhandled Auth Error Code:", error.code); return `Erreur (${error.code}). Veuillez réessayer.`; } } + function loadWorkoutTypes() { if (!currentFirebaseUser || !db || !typesSpinner) return; const userId = currentFirebaseUser.uid; console.log("Chargement types pour", userId); userWorkoutTypes = []; typesSpinner.classList.remove('hidden'); db.collection('workoutTypes').where('userId', '==', userId).orderBy('name').get().then(snapshot => { userWorkoutTypes = []; snapshot.forEach(doc => { userWorkoutTypes.push({ id: doc.id, ...doc.data() }); }); console.log("Types chargés:", userWorkoutTypes); renderWorkoutTypesList(); populateWorkoutTypeSelector(); }).catch(handleFirestoreError).finally(() => { typesSpinner.classList.add('hidden'); }); } + function renderWorkoutTypesList() { if (!workoutTypesList || !emptyTypesMessage) { console.error("DOM elements for types list missing"); return; } workoutTypesList.innerHTML = ''; if (userWorkoutTypes.length === 0) { emptyTypesMessage.classList.remove('hidden'); } else { emptyTypesMessage.classList.add('hidden'); userWorkoutTypes.forEach(type => { const li = document.createElement('li'); li.textContent = type.name; workoutTypesList.appendChild(li); }); } } + function handleAddWorkoutType(event) { event.preventDefault(); if (!currentFirebaseUser || !newTypeNameInput || !db || !addTypeError || !addTypeForm) { console.error("Missing elements for adding workout type"); return; } const typeName = newTypeNameInput.value.trim(); addTypeError.textContent = ''; addTypeError.style.display = 'none'; if (!typeName) { addTypeError.textContent = "Le nom du type ne peut pas être vide."; addTypeError.style.display = 'block'; return; } if (userWorkoutTypes.some(t => t.name.toLowerCase() === typeName.toLowerCase())) { addTypeError.textContent = "Ce type de séance existe déjà."; addTypeError.style.display = 'block'; return; } console.log("Ajout type:", typeName); const typeData = { userId: currentFirebaseUser.uid, name: typeName, createdAt: firebase.firestore.FieldValue.serverTimestamp() }; const addBtn = addTypeForm.querySelector('button'); if (addBtn) { addBtn.disabled = true; addBtn.textContent = '...'; } db.collection('workoutTypes').add(typeData).then(() => { console.log("Type ajouté avec succès"); newTypeNameInput.value = ''; loadWorkoutTypes(); }).catch(handleFirestoreError).finally(() => { if (addBtn) { addBtn.disabled = false; addBtn.textContent = 'Ajouter'; } }); } + function populateWorkoutTypeSelector() { if (!selectWorkoutTypeDropdown) return; selectWorkoutTypeDropdown.innerHTML = ''; userWorkoutTypes.forEach(type => { const option = document.createElement('option'); option.value = type.name; option.textContent = type.name; selectWorkoutTypeDropdown.appendChild(option); }); updateStartStructuredBtnState(); } + function updateStartStructuredBtnState() { if (!selectWorkoutTypeDropdown || !startStructuredWorkoutBtn) return; startStructuredWorkoutBtn.disabled = !selectWorkoutTypeDropdown.value; } + function loadWorkouts() { if (!currentFirebaseUser || !db || !spinner || !emptyWorkoutMessage || !workoutsList) { console.warn("Cannot load workouts: Missing user, db, or DOM elements."); if(workoutsList) workoutsList.innerHTML = ''; if(emptyWorkoutMessage) emptyWorkoutMessage.classList.remove('hidden'); workouts = []; updateStats(); return; } const userId = currentFirebaseUser.uid; console.log(`Chargement séances pour ${userId}...`); spinner.classList.remove('hidden'); emptyWorkoutMessage.classList.add('hidden'); workoutsList.innerHTML = ''; db.collection('workouts').where('userId', '==', userId).orderBy('date', 'desc').get().then((querySnapshot) => { workouts = []; console.log(`${querySnapshot.size} séances trouvées.`); querySnapshot.forEach((doc) => { const workoutData = doc.data(); workoutData.id = doc.id; workouts.push(workoutData); }); renderWorkoutsList(); updateStats(); }).catch(handleFirestoreError).finally(() => { spinner.classList.add('hidden'); if (workouts.length === 0) { emptyWorkoutMessage.classList.remove('hidden'); } }); } + function saveWorkout() { if (!currentFirebaseUser || !db || !exercisesContainer || !saveWorkoutBtn) { alert("Erreur: Impossible de sauvegarder (manque user/db/elements)."); return; } const userId = currentFirebaseUser.uid; workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const workoutNameInput = document.getElementById('workout-name'); const workoutName = workoutNameInput ? workoutNameInput.value.trim() : ''; const workoutDate = workoutDateInput ? workoutDateInput.value : ''; const workoutDurationInput = document.getElementById('workout-duration'); const workoutDuration = workoutDurationInput ? parseInt(workoutDurationInput.value) || 0 : 0; const satisfaction = satisfactionRange ? parseInt(satisfactionRange.value) : 75; if (!selectedWorkoutTypeName) { alert("Erreur interne: Type de séance manquant."); showPage('home-page'); return; } if (!workoutDate) { alert("Veuillez sélectionner une date."); if(workoutDateInput) workoutDateInput.focus(); return; } if (workoutDuration <= 0) { alert("Veuillez entrer une durée valide (minutes)."); if(workoutDurationInput) workoutDurationInput.focus(); return; } if (workoutExercisesForm.length === 0) { alert("Veuillez ajouter au moins un exercice."); return; } const exercises = []; let validationError = null; workoutExercisesForm.forEach((exerciseEl, index) => { if (validationError) return; const nameInput = exerciseEl.querySelector('.exercise-name'); const exerciseName = nameInput ? nameInput.value.trim() : ''; const unilateralCheckbox = exerciseEl.querySelector('.unilateral-checkbox'); const isUnilateral = unilateralCheckbox ? unilateralCheckbox.checked : false; 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 = `L'exercice "${exerciseName}" n'a pas de série.`; return; } seriesElements.forEach((seriesEl, sIndex) => { if (validationError) return; const repsInput = seriesEl.querySelector('.reps'); const weightInput = seriesEl.querySelector('.weight'); const degressiveCheckbox = seriesEl.querySelector('.degressive-checkbox'); const reps = repsInput ? parseInt(repsInput.value) || 0 : 0; const weight = weightInput ? parseFloat(weightInput.value) || 0 : 0; const isDegressive = degressiveCheckbox ? degressiveCheckbox.checked : false; if (reps <= 0) { validationError = `Reps invalides pour série ${sIndex + 1} (${exerciseName}).`; if(repsInput) repsInput.focus(); return; } if (weight < 0) { validationError = `Charge invalide pour série ${sIndex + 1} (${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 || 0) * (s.weight || 0) * (ex.isUnilateral ? 2 : 1); }); }); const finalWorkoutName = workoutName || selectedWorkoutTypeName; const workoutData = { userId: userId, workoutTypeName: selectedWorkoutTypeName, name: finalWorkoutName, date: workoutDate, duration: workoutDuration, exercises: exercises, totalTonnage: totalTonnage, satisfaction: satisfaction, lastUpdated: firebase.firestore.FieldValue.serverTimestamp() }; let savePromise; if (currentWorkoutId) { console.log("Mise à jour séance:", currentWorkoutId); workoutData.createdAt = workouts.find(w => w.id === currentWorkoutId)?.createdAt || firebase.firestore.FieldValue.serverTimestamp(); savePromise = db.collection('workouts').doc(currentWorkoutId).set(workoutData); } else { console.log("Création nouvelle séance"); workoutData.createdAt = firebase.firestore.FieldValue.serverTimestamp(); savePromise = db.collection('workouts').add(workoutData); } saveWorkoutBtn.disabled = true; saveWorkoutBtn.textContent = 'Sauvegarde...'; savePromise.then((docRefOrVoid) => { const savedId = currentWorkoutId || (docRefOrVoid ? docRefOrVoid.id : 'unknown'); console.log("Séance sauvegardée:", savedId); currentWorkoutId = null; try { if (typeof confetti === 'function') confetti({ particleCount: 150, spread: 90, origin: { y: 0.6 } }); } catch(e) { console.warn("Confetti error:", e)} showPage('home-page'); loadWorkouts(); }).catch(handleFirestoreError).finally(() => { saveWorkoutBtn.disabled = false; saveWorkoutBtn.textContent = 'Enregistrer Séance'; }); } + function deleteWorkout() { if (!currentFirebaseUser || !currentWorkoutId || !db || !deleteWorkoutBtn) return; const workoutToDelete = workouts.find(w => w && w.id === currentWorkoutId); if (!workoutToDelete) { console.error("Workout to delete not found locally."); return; } const confirmMsg = `Supprimer la séance "${workoutToDelete.name || 'Sans nom'}" du ${new Date(workoutToDelete.date).toLocaleDateString('fr-FR')} ?`; if (!confirm(confirmMsg)) return; console.log("Suppression séance:", currentWorkoutId); deleteWorkoutBtn.disabled = true; deleteWorkoutBtn.textContent = 'Suppression...'; db.collection('workouts').doc(currentWorkoutId).delete().then(() => { console.log("Séance supprimée avec succès:", currentWorkoutId); currentWorkoutId = null; showPage('home-page'); loadWorkouts(); }).catch(handleFirestoreError).finally(() => { deleteWorkoutBtn.disabled = false; deleteWorkoutBtn.textContent = 'Supprimer Séance'; }); } + function setTodayDate() { if (workoutDateInput) { try { const today = new Date(); const offset = today.getTimezoneOffset(); const localDate = new Date(today.getTime() - (offset*60*1000)); workoutDateInput.value = localDate.toISOString().split('T')[0]; } catch (e) { console.error("Erreur setTodayDate:", e); try { workoutDateInput.value = new Date().toISOString().split('T')[0]; } catch {} } } } + function showPage(pageId) { if (!appContainer) { console.error("App container not found!"); return; } console.log(`Affichage page: ${pageId}`); const pages = appContainer.querySelectorAll(':scope > div[id$="-page"]'); pages.forEach(page => page.classList.remove('active')); const pageToShow = document.getElementById(pageId); if (pageToShow) { pageToShow.classList.add('active'); window.scrollTo(0, 0); if (pageId === 'new-workout-page') { if (!selectedWorkoutTypeName) { console.warn("!selectedWorkoutTypeName on new-workout-page"); alert("Veuillez d'abord sélectionner un type de séance."); showPage('select-workout-type-page'); return; } renderActiveExerciseForm(); updateWorkoutTypeIndicator(); } else if (pageId === 'manage-types-page') { loadWorkoutTypes(); } else if (pageId === 'select-workout-type-page') { populateWorkoutTypeSelector(); } else if (pageId === 'stats-page') { updateStats(); } else if (pageId === 'workout-details-page') { if (!currentWorkoutId) { console.warn("!currentWorkoutId on workout-details-page"); alert("Séance non spécifiée."); showPage('home-page'); return; } } } else { console.error(`Page ID "${pageId}" non trouvée. Affichage de 'home-page'.`); const homePage = document.getElementById('home-page'); if (homePage) homePage.classList.add('active'); pageId = 'home-page'; } navItems.forEach(item => item.classList.remove('active')); const currentPageForNav = pageId === 'stats-page' ? 'stats-page' : 'home-page'; const activeNavItem = document.querySelector(`.nav-item[data-page="${currentPageForNav}"]`); if (activeNavItem) activeNavItem.classList.add('active'); } + function handleAddNewExercise() { addExercise(true); } + function addExercise(navigateToNew = false) { if (!currentFirebaseUser || !exercisesContainer) { console.error("Cannot add exercise"); return; } const exerciseId = `exercise-${Date.now()}`; const exerciseDiv = document.createElement('div'); exerciseDiv.className = 'card exercise'; exerciseDiv.setAttribute('data-exercise-id', exerciseId); exerciseDiv.innerHTML = `
    `; exercisesContainer.appendChild(exerciseDiv); workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const removeBtn = exerciseDiv.querySelector('.remove-exercise'); removeBtn.addEventListener('click', () => { const indexToRemove = workoutExercisesForm.findIndex(el => el === exerciseDiv); if (indexToRemove > -1) { if (confirm("Supprimer cet exercice et toutes ses séries ?")) { exerciseDiv.remove(); workoutExercisesForm.splice(indexToRemove, 1); if (currentExerciseIndex >= indexToRemove && currentExerciseIndex > 0) { currentExerciseIndex--; } 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(); const nameInput = exerciseDiv.querySelector('.exercise-name'); if (nameInput) nameInput.focus(); } else { updateExerciseNavButtons(); } } + function addSeries(container) { if (!currentFirebaseUser || !container) { console.error("Cannot add series"); return; } const seriesId = `series-${Date.now()}`; const seriesDiv = document.createElement('div'); seriesDiv.className = 'series'; seriesDiv.setAttribute('data-series-id', seriesId); seriesDiv.innerHTML = `
    `; container.appendChild(seriesDiv); const removeBtn = seriesDiv.querySelector('.remove-series'); removeBtn.addEventListener('click', () => { seriesDiv.remove(); }); } + function navigateExerciseForm(direction) { if (!exercisesContainer) return; workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); const newIndex = currentExerciseIndex + direction; if (newIndex >= 0 && newIndex < workoutExercisesForm.length) { currentExerciseIndex = newIndex; renderActiveExerciseForm(); } } + function renderActiveExerciseForm() { if (!exercisesContainer) return; workoutExercisesForm = Array.from(exercisesContainer.querySelectorAll('.exercise')); workoutExercisesForm.forEach((ex, index) => { if (index === currentExerciseIndex) { ex.classList.add('active-exercise'); ex.style.display = 'block'; } else { ex.classList.remove('active-exercise'); ex.style.display = 'none'; } }); updateExerciseNavButtons(); } + function updateExerciseNavButtons() { if (!currentExerciseIndicator || !prevExerciseBtn || !nextExerciseBtn) { console.warn("Exercise nav elements missing"); return; } 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(typeName = "Libre") { console.log("Nettoyage formulaire pour type:", typeName); if (!currentFirebaseUser) { console.error("Cannot clear form: No user"); return; } selectedWorkoutTypeName = typeName; const workoutNameInput = document.getElementById('workout-name'); if (workoutNameInput) workoutNameInput.value = (typeName === "Libre" ? "" : typeName); if (workoutDateInput) setTodayDate(); const durationInput = document.getElementById('workout-duration'); if (durationInput) durationInput.value = '60'; if (exercisesContainer) exercisesContainer.innerHTML = ''; workoutExercisesForm = []; if (satisfactionRange) satisfactionRange.value = 75; if (satisfactionValue) satisfactionValue.textContent = '75%'; currentWorkoutId = null; currentExerciseIndex = 0; addExercise(false); updateWorkoutTypeIndicator(); renderActiveExerciseForm(); } + function updateWorkoutTypeIndicator() { if (!workoutTypeIndicator) return; if (selectedWorkoutTypeName && selectedWorkoutTypeName !== "Libre") { workoutTypeIndicator.textContent = selectedWorkoutTypeName; workoutTypeIndicator.classList.remove('hidden'); } else { workoutTypeIndicator.classList.add('hidden'); } } + function renderWorkoutsList() { if (!workoutsList || !emptyWorkoutMessage) { console.warn("Cannot render workout list: DOM elements missing."); return; } workoutsList.innerHTML = ''; if (!workouts || 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 => { if (!workout || !workout.id) { console.warn("Skipping invalid workout data:", workout); return; } const workoutDate = workout.date ? new Date(workout.date).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' }) : 'Date inconnue'; const workoutDiv = document.createElement('div'); workoutDiv.className = 'card workout-card'; workoutDiv.setAttribute('data-workout-id', workout.id); const typeBadge = workout.workoutTypeName && workout.workoutTypeName !== "Libre" ? `${workout.workoutTypeName}` : ''; const exercisesCount = (workout.exercises || []).length; workoutDiv.innerHTML = `

    ${workout.name || 'Séance sans nom'} ${typeBadge}

    ${workoutDate}
    ${workout.duration || '?'} min | ${exercisesCount} exo${exercisesCount > 1 ? 's' : ''} | ${(workout.totalTonnage || 0).toFixed(1)} kg | ${workout.satisfaction || '?'}%
    `; workoutDiv.addEventListener('click', () => displayWorkoutDetails(workout.id)); workoutsList.appendChild(workoutDiv); }); } } + function displayWorkoutDetails(workoutId) { if (!currentFirebaseUser) { console.error("User not logged in for details"); return; } const workout = workouts.find(w => w && w.id === workoutId); if (!workout) { alert("Détails de la séance introuvables."); showPage('home-page'); return; } console.log("Affichage détails pour:", workoutId, workout); const detailNameEl = document.getElementById('detail-workout-name'); const detailDateEl = document.getElementById('detail-date'); const detailDurationEl = document.getElementById('detail-duration'); const detailTonnageEl = document.getElementById('detail-tonnage'); const detailSatisfactionEl = document.getElementById('detail-satisfaction'); const detailExercisesCountEl = document.getElementById('detail-exercises-count'); const detailExercisesContainer = document.getElementById('detail-exercises-container'); const detailWorkoutTypeBadge = document.getElementById('detail-workout-type'); if (detailWorkoutTypeBadge) { if (workout.workoutTypeName && workout.workoutTypeName !== "Libre") { detailWorkoutTypeBadge.textContent = workout.workoutTypeName; detailWorkoutTypeBadge.classList.remove('hidden'); } else { detailWorkoutTypeBadge.classList.add('hidden'); } } if (detailNameEl) detailNameEl.textContent = workout.name || 'Séance sans nom'; if (detailDateEl) detailDateEl.textContent = workout.date ? new Date(workout.date).toLocaleDateString('fr-FR') : 'Inconnue'; if (detailDurationEl) detailDurationEl.textContent = workout.duration || '?'; if (detailTonnageEl) detailTonnageEl.textContent = (workout.totalTonnage || 0).toFixed(1); if (detailSatisfactionEl) detailSatisfactionEl.textContent = `${workout.satisfaction || '?'}%`; const exercisesCount = (workout.exercises || []).length; if (detailExercisesCountEl) detailExercisesCountEl.textContent = exercisesCount; if (detailExercisesContainer) { detailExercisesContainer.innerHTML = ''; if (exercisesCount > 0) { workout.exercises.forEach((exercise) => { if (!exercise) return; const exerciseDiv = document.createElement('div'); exerciseDiv.className = 'card detail-exercise-card'; let seriesHtml = ''; (exercise.series || []).forEach((serie, sIndex) => { const degressiveLabel = serie.isDegressive ? ' Dégr.' : ''; const weightFactor = exercise.isUnilateral ? 2 : 1; const seriesTonnage = (serie.reps || 0) * (serie.weight || 0) * weightFactor; seriesHtml += `
    Série ${sIndex + 1}: ${serie.reps || '?'} reps × ${serie.weight || '?'} kg${degressiveLabel}(${seriesTonnage.toFixed(1)} kg)
    `; }); const unilateralLabel = exercise.isUnilateral ? ' Unilat.' : ''; exerciseDiv.innerHTML = `

    ${exercise.name || 'Exercice sans nom'}${unilateralLabel}

    ${seriesHtml}
    `; detailExercisesContainer.appendChild(exerciseDiv); }); } else { detailExercisesContainer.innerHTML = '

    Aucun exercice enregistré.

    '; } } currentWorkoutId = workoutId; showPage('workout-details-page'); } + function updateStats() { console.log("MàJ stats..."); if (!currentFirebaseUser || !statsWorkoutCountEl || !statsAvgTonnageEl || !statsAvgSatisfactionEl || !typeTrendsContainer || !noTrendsMessage) { console.warn("Cannot update stats: Missing elements."); if(statsWorkoutCountEl) statsWorkoutCountEl.textContent = '0'; if(statsAvgTonnageEl) statsAvgTonnageEl.textContent = '0'; if(statsAvgSatisfactionEl) statsAvgSatisfactionEl.textContent = '0%'; if(typeTrendsContainer) typeTrendsContainer.innerHTML = ''; if(noTrendsMessage) noTrendsMessage.classList.remove('hidden'); return; } const workoutCount = workouts.length; statsWorkoutCountEl.textContent = workoutCount; if (workoutCount === 0) { statsAvgTonnageEl.textContent = '0'; statsAvgSatisfactionEl.textContent = '0%'; renderTypeTrends({}); return; } const totalTonnageAll = workouts.reduce((sum, w) => sum + (w.totalTonnage || 0), 0); statsAvgTonnageEl.textContent = workoutCount > 0 ? (totalTonnageAll / workoutCount).toFixed(1) : '0'; const totalSatisfaction = workouts.reduce((sum, w) => sum + (w.satisfaction || 0), 0); statsAvgSatisfactionEl.textContent = workoutCount > 0 ? `${Math.round(totalSatisfaction / workoutCount)}%` : '0%'; console.log("Calcul tendances..."); const trendsData = {}; const structuredWorkouts = workouts.filter(w => w && w.workoutTypeName && w.workoutTypeName !== "Libre"); const groupedByType = structuredWorkouts.reduce((acc, workout) => { const type = workout.workoutTypeName; if (!acc[type]) acc[type] = []; acc[type].push(workout); return acc; }, {}); for (const typeName in groupedByType) { const sessionsOfType = groupedByType[typeName].sort((a, b) => new Date(a.date) - new Date(b.date)); trendsData[typeName] = sessionsOfType.slice(-3).map(s => ({ date: s.date, tonnage: s.totalTonnage || 0 })); } console.log("Données tendances:", trendsData); renderTypeTrends(trendsData); } + function renderTypeTrends(trends) { if (!typeTrendsContainer || !noTrendsMessage || !trendsSpinner) { console.error("Cannot render trends: Missing DOM elements."); return;} trendsSpinner.classList.add('hidden'); typeTrendsContainer.innerHTML = ''; const trendTypes = Object.keys(trends); if (trendTypes.length === 0) { noTrendsMessage.classList.remove('hidden'); } else { noTrendsMessage.classList.add('hidden'); trendTypes.sort().forEach(typeName => { const typeData = trends[typeName]; if (!typeData || typeData.length === 0) return; const card = document.createElement('div'); card.className = 'card type-trend-card'; let listItems = ''; typeData.forEach(session => { const formattedDate = session.date ? new Date(session.date).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short' }) : '??'; const tonnageText = (session.tonnage !== undefined && session.tonnage !== null) ? `${session.tonnage.toFixed(1)} kg` : 'N/A'; listItems += `
  • ${formattedDate} ${tonnageText}
  • `; }); card.innerHTML = `

    ${typeName} (3 Dernières)

    `; typeTrendsContainer.appendChild(card); }); } } + function handleFirestoreError(error) { console.error("Erreur Firestore:", error.code, error.message); alert(`Erreur de base de données: ${error.message}`); } // --- ÉCOUTEURS D'ÉVÉNEMENTS --- function initEventListeners() { - console.log("initEventListeners: Attachement des écouteurs..."); - - // Auth Form - if (loginForm) loginForm.addEventListener('submit', handleAuthAction); - else console.error("!loginForm not found"); - if (authSwitchLink) authSwitchLink.addEventListener('click', authSwitchMode); - else console.error("!authSwitchLink not found"); - - // Logout - if (logoutBtn) logoutBtn.addEventListener('click', handleLogout); - else console.error("!logoutBtn not found"); - - // Navigation - navItems.forEach((item, index) => { - if (item) { - item.addEventListener('click', (e) => { - e.preventDefault(); - const targetPageId = item.getAttribute('data-page'); - if (!currentFirebaseUser && targetPageId !== 'login-page') { - showLoginPage(); // Redirect to login if not logged in - return; - } - if (targetPageId) showPage(targetPageId); - }); - } else console.error(`!navItem #${index} not found`); - }); - - // Workout Actions - if (newWorkoutBtn) newWorkoutBtn.addEventListener('click', () => { if (!currentFirebaseUser) return; showPage('select-workout-type-page'); }); - else console.error("!newWorkoutBtn not found"); - if (manageTypesBtn) manageTypesBtn.addEventListener('click', () => { if (!currentFirebaseUser) return; showPage('manage-types-page'); }); - else console.error("!manageTypesBtn not found"); - - // Type Management - if (addTypeForm) addTypeForm.addEventListener('submit', handleAddWorkoutType); - else console.error("!addTypeForm not found"); - - // Select Workout Type Page - if (selectWorkoutTypeDropdown) selectWorkoutTypeDropdown.addEventListener('change', updateStartStructuredBtnState); - else console.error("!selectWorkoutTypeDropdown not found"); - if (startStructuredWorkoutBtn) startStructuredWorkoutBtn.addEventListener('click', () => { if(selectWorkoutTypeDropdown) { const type = selectWorkoutTypeDropdown.value; if(type) { clearNewWorkoutForm(type); showPage('new-workout-page'); } else { alert("Veuillez sélectionner un type de séance.");} } }); - else console.error("!startStructuredWorkoutBtn not found"); - if (startFreeWorkoutBtn) startFreeWorkoutBtn.addEventListener('click', () => { clearNewWorkoutForm("Libre"); showPage('new-workout-page'); }); - else console.error("!startFreeWorkoutBtn not found"); - - // General Navigation - backToHomeBtns.forEach(btn => { if(btn) btn.addEventListener('click', () => showPage('home-page')); else console.error("!backToHomeBtn missing"); }); - - // New Workout Page Actions - if (cancelNewWorkoutBtn) cancelNewWorkoutBtn.addEventListener('click', () => { if(confirm("Annuler cette séance ? Les données non enregistrées seront perdues.")) showPage('home-page'); }); - else console.error("!cancelNewWorkoutBtn not found"); - if (saveWorkoutBtn) saveWorkoutBtn.addEventListener('click', saveWorkout); - else console.error("!saveWorkoutBtn not found"); - if (addExerciseBtn) addExerciseBtn.addEventListener('click', handleAddNewExercise); - else console.error("!addExerciseBtn not found"); - - // Exercise Navigation - if (prevExerciseBtn) prevExerciseBtn.addEventListener('click', () => navigateExerciseForm(-1)); - else console.error("!prevExerciseBtn not found"); - if (nextExerciseBtn) nextExerciseBtn.addEventListener('click', () => navigateExerciseForm(1)); - else console.error("!nextExerciseBtn not found"); - - // Workout Details Actions - if (deleteWorkoutBtn) deleteWorkoutBtn.addEventListener('click', deleteWorkout); - else console.error("!deleteWorkoutBtn not found"); - - // Satisfaction Slider - if (satisfactionRange) satisfactionRange.addEventListener('input', () => { if(satisfactionValue) satisfactionValue.textContent = `${satisfactionRange.value}%`; }); - else console.error("!satisfactionRange not found"); - + console.log("initEventListeners: Attachement..."); + if (loginForm) loginForm.addEventListener('submit', handleAuthAction); else console.error("!loginForm"); + if (authSwitchLink) authSwitchLink.addEventListener('click', authSwitchMode); else console.error("!authSwitchLink"); + if (logoutBtn) logoutBtn.addEventListener('click', handleLogout); else console.error("!logoutBtn"); + navItems.forEach((item) => { if (item) item.addEventListener('click', (e) => { e.preventDefault(); const targetPageId = item.getAttribute('data-page'); if (!currentFirebaseUser && targetPageId !== 'login-page') { showLoginPage(); return; } if (targetPageId) showPage(targetPageId); }); else console.error(`!navItem`); }); + if (newWorkoutBtn) newWorkoutBtn.addEventListener('click', () => { if (!currentFirebaseUser) return; showPage('select-workout-type-page'); }); else console.error("!newWorkoutBtn"); + if (manageTypesBtn) manageTypesBtn.addEventListener('click', () => { if (!currentFirebaseUser) return; showPage('manage-types-page'); }); else console.error("!manageTypesBtn"); + if (addTypeForm) addTypeForm.addEventListener('submit', handleAddWorkoutType); else console.error("!addTypeForm"); + if (selectWorkoutTypeDropdown) selectWorkoutTypeDropdown.addEventListener('change', updateStartStructuredBtnState); else console.error("!selectWorkoutTypeDropdown"); + if (startStructuredWorkoutBtn) startStructuredWorkoutBtn.addEventListener('click', () => { if (selectWorkoutTypeDropdown) { const type = selectWorkoutTypeDropdown.value; if (type) { clearNewWorkoutForm(type); showPage('new-workout-page'); } else { alert("Veuillez sélectionner un type de séance."); } } }); else console.error("!startStructuredWorkoutBtn"); + if (startFreeWorkoutBtn) startFreeWorkoutBtn.addEventListener('click', () => { clearNewWorkoutForm("Libre"); showPage('new-workout-page'); }); else console.error("!startFreeWorkoutBtn"); + backToHomeBtns.forEach(btn => { if (btn) btn.addEventListener('click', () => showPage('home-page')); else console.error("!backToHomeBtn"); }); + if (cancelNewWorkoutBtn) cancelNewWorkoutBtn.addEventListener('click', () => { if (confirm("Annuler cette séance ? Les données non enregistrées seront perdues.")) showPage('home-page'); }); else console.error("!cancelNewWorkoutBtn"); + if (saveWorkoutBtn) saveWorkoutBtn.addEventListener('click', saveWorkout); else console.error("!saveWorkoutBtn"); + if (addExerciseBtn) addExerciseBtn.addEventListener('click', handleAddNewExercise); else console.error("!addExerciseBtn"); + if (prevExerciseBtn) prevExerciseBtn.addEventListener('click', () => navigateExerciseForm(-1)); else console.error("!prevExerciseBtn"); + if (nextExerciseBtn) nextExerciseBtn.addEventListener('click', () => navigateExerciseForm(1)); else console.error("!nextExerciseBtn"); + if (deleteWorkoutBtn) deleteWorkoutBtn.addEventListener('click', deleteWorkout); else console.error("!deleteWorkoutBtn"); + if (satisfactionRange) satisfactionRange.addEventListener('input', () => { if (satisfactionValue) satisfactionValue.textContent = `${satisfactionRange.value}%`; }); else console.error("!satisfactionRange"); console.log("initEventListeners: Fin attachement."); } - // ================================================== // --- FIN DES DÉFINITIONS DE FONCTIONS --- // ================================================== // --- INITIALISATION --- - // Attend que le DOM soit entièrement chargé avant d'exécuter le JS principal document.addEventListener('DOMContentLoaded', () => { console.log("DOM chargé. Initialisation de l'application..."); - - // Vérifie que Firebase est chargé et initialisé (la variable 'auth' devrait être définie dans le try/catch initial) - if (typeof firebase !== 'undefined' && auth && db) { - console.log("Firebase prêt. Initialisation des listeners..."); - initAuthListener(); // Surveille les changements d'état de connexion - initEventListeners(); // Attache les gestionnaires d'événements aux boutons, etc. - - // Vérifie l'état de connexion initial au chargement de la page - if (auth.currentUser) { - console.log("Utilisateur déjà connecté au chargement:", auth.currentUser.uid); - currentFirebaseUser = auth.currentUser; - showApp(); // Affiche l'application principale + if (typeof firebase !== 'undefined' && typeof firebase.initializeApp === 'function' && typeof firebase.auth === 'function' && typeof firebase.firestore === 'function') { + if (!auth) auth = firebase.auth(); // Ensure auth is assigned + if (!db) db = firebase.firestore(); // Ensure db is assigned + + if (auth && db) { + console.log("Firebase prêt. Initialisation des listeners..."); + initEventListeners(); // Initialize event listeners first + initAuthListener(); // Initialize auth listener to handle state changes and initial check + // Initial check after listeners are set up + const user = auth.currentUser; + if (user) { + console.log("Utilisateur déjà connecté trouvé:", user.uid); + currentFirebaseUser = user; + showApp(); // Show app directly + } else { + console.log("Utilisateur non connecté au chargement."); + showLoginPage(); // Show login page + } } else { - console.log("Utilisateur non connecté au chargement."); - showLoginPage(); // Affiche la page de connexion + console.error("ERREUR CRITIQUE: Firebase Auth ou Firestore non initialisé correctement après tentative."); + alert("Erreur critique : Services Firebase non disponibles."); } } else { - // Si Firebase n'a pas pu s'initialiser (erreur dans le premier try/catch ou SDK non chargé) - console.error("ERREUR CRITIQUE: Firebase n'a pas pu être initialisé ou n'est pas chargé."); - // Affiche un message d'erreur clair à l'utilisateur car l'app ne peut pas fonctionner - document.body.innerHTML = '

    Erreur Critique

    Impossible de charger les services nécessaires pour l\'application. Veuillez vérifier votre connexion internet et les scripts Firebase. Si le problème persiste, contactez le support.

    '; + console.error("ERREUR CRITIQUE: Firebase SDK non chargé ou incomplet !"); + alert("Erreur critique : Impossible de charger les composants Firebase."); + document.body.innerHTML = '

    Erreur Critique

    Impossible de charger les composants nécessaires. Vérifiez votre connexion et la console (F12) pour plus de détails.

    '; } - }); // FIN DU Listener DOMContentLoaded - Important ! + }); // FIN DU Listener DOMContentLoaded - // FIN DU CODE JAVASCRIPT PRINCIPAL - + - - \ No newline at end of file + + \ No newline at end of file