diff --git a/assets/app.js b/assets/app.js index 0de7354..22650d7 100644 --- a/assets/app.js +++ b/assets/app.js @@ -4,18 +4,18 @@ import '@grafikart/drop-files-element' import {PaymentForm} from './PaymentForm' import * as Sentry from "@sentry/browser"; -// --- CLÉS DE STOCKAGE ET VAPID --- +// --- CONFIGURATION ET ETAT --- const VAPID_PUBLIC_KEY = "BKz0kdcsG6kk9KxciPpkfP8kEDAd408inZecij5kBDbQ1ZGZSNwS4KZ8FerC28LFXvgSqpDXtor3ePo0zBCdNqo"; const COOKIE_STORAGE_KEY = 'cookies_accepted'; +let userMenuTimeout; // Pour gérer le délai de fermeture du menu -// --- MESSAGES ET TRADUCTIONS --- const MESSAGES = { fr: { notificationTitle: "🔔 Activer les notifications", notificationText: "Recevez les nouvelles, les promotions et les événements de l'association.", notificationButton: "Activer", notificationClose: "Fermer la notification", - cookieText: 'Ce site utilise uniquement des cookies de fonctionnement et de sécurité essentiels. Aucune donnée personnelle n\'est collectée.', + cookieText: 'Ce site utilise uniquement des cookies de fonctionnement et de sécurité essentiels.', cookieLink: "Politique de cookies", cookieButton: "Accepter", }, @@ -24,7 +24,7 @@ const MESSAGES = { notificationText: "Receive news, promotions, and association events.", notificationButton: "Enable", notificationClose: "Close notification", - cookieText: 'This website only uses functional and security cookies. No personal data is collected.', + cookieText: 'This website only uses functional and security cookies.', cookieLink: "Cookie Policy", cookieButton: "Accept", } @@ -62,12 +62,10 @@ function toggleMenu(button, menu) { * Initialisation de l'UI */ function initializeUI() { - // Reset des menus au chargement document.querySelectorAll('#mobile-menu, #userMenuDesktop, #userMenuMobile').forEach(menu => { menu.classList.add('hidden'); }); - // Panier Latéral const cartSidebar = document.getElementById('cartSidebar'); const cartBackdrop = document.getElementById('cartBackdrop'); @@ -78,7 +76,6 @@ function initializeUI() { cartSidebar.classList.remove('translate-x-full'); cartSidebar.classList.add('translate-x-0'); }; - window.closeCart = () => { document.body.style.overflow = ''; cartSidebar.classList.remove('translate-x-0'); @@ -89,7 +86,6 @@ function initializeUI() { }; } - // Update counters const updateCartDisplay = (count) => { const desktopCounter = document.getElementById('cartCountDesktop'); const mobileCounter = document.getElementById('cartCountMobile'); @@ -98,20 +94,47 @@ function initializeUI() { }; updateCartDisplay(0); + // Initialisation spécifique au menu utilisateur (Desktop) + setupUserMenuHover(); + if ('Notification' in window && Notification.permission === 'granted') { subscribeAndSave(); } } -// --- LOGIQUE PUSH NOTIFICATIONS --- +/** + * Gestion du survol du menu utilisateur pour éviter la fermeture brutale + */ +function setupUserMenuHover() { + const btn = document.getElementById('userMenuButtonDesktop'); + const menu = document.getElementById('userMenuDesktop'); + + if (!btn || !menu) return; + + const open = () => { + clearTimeout(userMenuTimeout); + menu.classList.remove('hidden'); + }; + + const close = () => { + userMenuTimeout = setTimeout(() => { + menu.classList.add('hidden'); + }, 200); // Délai de 200ms pour laisser le temps de passer du bouton au menu + }; + + btn.addEventListener('mouseenter', open); + btn.addEventListener('mouseleave', close); + menu.addEventListener('mouseenter', open); + menu.addEventListener('mouseleave', close); +} + +// --- LOGIQUE PUSH & BANNIÈRES (Inchangé) --- async function subscribeAndSave() { if (!('Notification' in window) || Notification.permission !== 'granted') return; if (!('serviceWorker' in navigator)) return; - try { const registration = await navigator.serviceWorker.ready; let subscription = await registration.pushManager.getSubscription(); - if (!subscription) { const applicationServerKey = urlBase64ToUint8Array(VAPID_PUBLIC_KEY); subscription = await registration.pushManager.subscribe({ @@ -119,15 +142,12 @@ async function subscribeAndSave() { applicationServerKey: applicationServerKey }); } - await fetch('/notificationSub', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ subscription: subscription.toJSON() }) }); - } catch (error) { - console.error("Push Error:", error); - } + } catch (error) { console.error("Push Error:", error); } } async function promptForPermissionAndSubscribe() { @@ -135,18 +155,14 @@ async function promptForPermissionAndSubscribe() { try { const permission = await Notification.requestPermission(); if (permission === 'granted') await subscribeAndSave(); - } catch (error) { - console.error("Permission Error:", error); - } + } catch (error) { console.error("Permission Error:", error); } } -// --- OPTIMISATION PERFORMANCE --- function isPerformanceTestAgent() { const ua = navigator.userAgent; return ua.includes('Lighthouse') || ua.includes('PageSpeed'); } -// --- BANNIÈRES --- function handleNotificationBanner() { if (isPerformanceTestAgent()) return; const BANNER_ID = 'notification-prompt-banner'; @@ -157,7 +173,6 @@ function handleNotificationBanner() { const banner = document.createElement('div'); banner.id = BANNER_ID; banner.className = `fixed bottom-4 left-4 z-50 p-4 max-w-xs bg-indigo-600 text-white rounded-xl shadow-2xl transition-all duration-500 transform opacity-0 translate-y-full md:left-8 md:bottom-8 border-2 border-black`; - banner.innerHTML = `

${M.notificationTitle}

@@ -170,13 +185,11 @@ function handleNotificationBanner() { ${M.notificationButton} `; - document.body.appendChild(banner); setTimeout(() => { banner.classList.remove('opacity-0', 'translate-y-full'); banner.classList.add('opacity-100', 'translate-y-0'); }, 100); - document.getElementById('closeNotificationBanner').addEventListener('click', () => banner.remove()); document.getElementById('activateNotifications').addEventListener('click', async () => { await promptForPermissionAndSubscribe(); @@ -189,12 +202,10 @@ function handleCookieBanner() { if (localStorage.getItem(COOKIE_STORAGE_KEY) === 'true') return; const BANNER_ID = 'cookie-banner'; if (document.getElementById(BANNER_ID)) return; - const M = getLanguageMessages(); const banner = document.createElement('div'); banner.id = BANNER_ID; banner.className = `fixed bottom-4 right-4 z-50 p-6 max-w-sm bg-white border-4 border-black shadow-[10px_10px_0px_rgba(0,0,0,1)] transition-all duration-500 transform opacity-0 translate-y-full`; - banner.innerHTML = `

${M.cookieText}

@@ -204,78 +215,58 @@ function handleCookieBanner() {
`; - document.body.appendChild(banner); setTimeout(() => { banner.classList.remove('opacity-0', 'translate-y-full'); banner.classList.add('opacity-100', 'translate-y-0'); }, 200); - document.getElementById('acceptCookies').addEventListener('click', () => { localStorage.setItem(COOKIE_STORAGE_KEY, 'true'); banner.remove(); }); } -// --- DOM READY & TURBO --- +// --- BOOTSTRAP --- document.addEventListener('DOMContentLoaded', () => { if (!customElements.get('payment-don')) { customElements.define('payment-don', PaymentForm, {extends: 'form'}); } initializeUI(); - if (!isPerformanceTestAgent()) { handleNotificationBanner(); handleCookieBanner(); - - // Chatwoot var BASE_URL_WOOT = "https://chat.esy-web.dev"; let script = document.createElement('script'); script.setAttribute('src', BASE_URL_WOOT + "/packs/js/sdk.js"); script.defer = true; document.head.append(script); script.onload = () => { - window.chatwootSDK.run({ - websiteToken: '6uFX3g3qybyvSt3PAQUMgkm4', - baseUrl: BASE_URL_WOOT - }); + window.chatwootSDK.run({ websiteToken: '6uFX3g3qybyvSt3PAQUMgkm4', baseUrl: BASE_URL_WOOT }); }; } - const env = document.querySelector('meta[name="env"]'); - if (env && env.getAttribute('content') === "prod") { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw.js'); - } + if (env && env.getAttribute('content') === "prod" && 'serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js'); } }); document.addEventListener('turbo:load', () => { initializeUI(); - handleNotificationBanner(); - handleCookieBanner(); }); -// --- DÉLÉGATION D'ÉVÉNEMENTS --- document.addEventListener('click', (event) => { const target = event.target; - - // Menu Mobile const mobileMenuButton = target.closest('#mobileMenuButton'); if (mobileMenuButton) { - const menu = document.getElementById('mobile-menu'); - toggleMenu(mobileMenuButton, menu); + toggleMenu(mobileMenuButton, document.getElementById('mobile-menu')); return; } - - // Panier const openCartBtn = target.closest('#openCartDesktop, #openCartMobile'); if (openCartBtn && window.openCart) { event.preventDefault(); window.openCart(); return; } - const closeCartBtn = target.closest('#closeCartButton') || target === document.getElementById('cartBackdrop'); if (closeCartBtn && window.closeCart) { window.closeCart(); diff --git a/assets/app.scss b/assets/app.scss index b061f7d..8a92674 100644 --- a/assets/app.scss +++ b/assets/app.scss @@ -9,3 +9,8 @@ .epage{ color: orangered; } + +#userMenuDesktop { + margin-top: -4px; /* Remonte légèrement le menu pour toucher le bouton */ + padding-top: 10px; /* Ajoute du padding interne pour garder la zone réactive */ +} diff --git a/templates/about.twig b/templates/about.twig index ca24fd7..88df36d 100644 --- a/templates/about.twig +++ b/templates/about.twig @@ -42,7 +42,7 @@ {% endblock %} {% block body %} -
+
{# --- HEADER HERO --- #}
@@ -274,7 +274,7 @@
-
+