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 = '-- Sélectionner Type -- '; // 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 = `
-
-
-
- Unilatéral ?
-
-
-
- + Ajouter Série
- `;
- 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.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 = '-- Sélectionner Type -- '; 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 = ` Unilatéral ?
+ Ajouter Série `; 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.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
-
+
-