Files

276 lines
11 KiB
JavaScript
Raw Permalink Normal View History

import './app.scss'
import * as Turbo from "@hotwired/turbo"
import '@grafikart/drop-files-element'
import {PaymentForm} from './PaymentForm.js'
import * as Sentry from "@sentry/browser";
// --- 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
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 <strong>fonctionnement</strong> et de <strong>sécurité</strong> essentiels.',
cookieLink: "Politique de cookies",
cookieButton: "Accepter",
},
en: {
notificationTitle: "🔔 Enable Notifications",
notificationText: "Receive news, promotions, and association events.",
notificationButton: "Enable",
notificationClose: "Close notification",
cookieText: 'This website only uses <strong>functional</strong> and <strong>security</strong> cookies.',
cookieLink: "Cookie Policy",
cookieButton: "Accept",
}
};
function getLanguageMessages() {
let langCode = 'fr';
const htmlLang = document.documentElement.lang;
if (htmlLang) {
const normalizedHtmlLang = htmlLang.toLowerCase().substring(0, 2);
if (normalizedHtmlLang === 'en') langCode = 'en';
}
return MESSAGES[langCode];
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function toggleMenu(button, menu) {
if (!button || !menu) return;
const isExpanded = button.getAttribute('aria-expanded') === 'true' || false;
button.setAttribute('aria-expanded', !isExpanded);
menu.classList.toggle('hidden');
}
/**
* Initialisation de l'UI
*/
function initializeUI() {
document.querySelectorAll('#mobile-menu, #userMenuDesktop, #userMenuMobile').forEach(menu => {
menu.classList.add('hidden');
});
const cartSidebar = document.getElementById('cartSidebar');
const cartBackdrop = document.getElementById('cartBackdrop');
if (cartSidebar) {
window.openCart = () => {
document.body.style.overflow = 'hidden';
if (cartBackdrop) cartBackdrop.classList.remove('hidden');
cartSidebar.classList.remove('translate-x-full');
cartSidebar.classList.add('translate-x-0');
};
window.closeCart = () => {
document.body.style.overflow = '';
cartSidebar.classList.remove('translate-x-0');
cartSidebar.classList.add('translate-x-full');
if (cartBackdrop) {
setTimeout(() => cartBackdrop.classList.add('hidden'), 300);
}
};
}
const updateCartDisplay = (count) => {
const desktopCounter = document.getElementById('cartCountDesktop');
const mobileCounter = document.getElementById('cartCountMobile');
if (desktopCounter) desktopCounter.textContent = count;
if (mobileCounter) mobileCounter.textContent = count;
};
updateCartDisplay(0);
// Initialisation spécifique au menu utilisateur (Desktop)
setupUserMenuHover();
if ('Notification' in window && Notification.permission === 'granted') {
subscribeAndSave();
}
}
/**
* 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({
userVisibleOnly: true,
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); }
}
async function promptForPermissionAndSubscribe() {
if (!('Notification' in window)) return;
try {
const permission = await Notification.requestPermission();
if (permission === 'granted') await subscribeAndSave();
} catch (error) { console.error("Permission Error:", error); }
}
function isPerformanceTestAgent() {
const ua = navigator.userAgent;
return ua.includes('Lighthouse') || ua.includes('PageSpeed');
}
function handleNotificationBanner() {
if (isPerformanceTestAgent()) return;
const BANNER_ID = 'notification-prompt-banner';
if ('Notification' in window && Notification.permission === 'granted') return;
if (document.getElementById(BANNER_ID)) return;
const M = getLanguageMessages();
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 = `
<div class="flex items-start justify-between italic">
<p class="font-black uppercase text-xs tracking-widest">${M.notificationTitle}</p>
<button id="closeNotificationBanner" class="ml-3 p-1 hover:bg-indigo-700 transition">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18"/><path d="M6 6l12 12"/></svg>
</button>
</div>
<p class="mt-1 text-[10px] font-bold opacity-80 uppercase leading-tight">${M.notificationText}</p>
<button id="activateNotifications" class="mt-3 w-full py-2 bg-yellow-400 text-black font-black uppercase italic text-xs border-2 border-black shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-1 hover:translate-y-1 transition-all">
${M.notificationButton}
</button>
`;
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();
banner.remove();
});
}
function handleCookieBanner() {
if (isPerformanceTestAgent()) return;
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 = `
<p class="text-xs font-bold uppercase italic leading-relaxed text-gray-600">${M.cookieText}</p>
<div class="mt-4 flex justify-end items-center gap-4">
<a href="/cookies" class="text-[10px] font-black uppercase underline hover:text-indigo-600">${M.cookieLink}</a>
<button id="acceptCookies" class="py-2 px-6 bg-indigo-600 text-white font-black uppercase italic text-xs border-2 border-black shadow-[4px_4px_0px_rgba(0,0,0,1)] hover:shadow-none hover:translate-x-1 hover:translate-y-1 transition-all">
${M.cookieButton}
</button>
</div>
`;
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();
});
}
// --- BOOTSTRAP ---
document.addEventListener('DOMContentLoaded', () => {
if (!customElements.get('payment-don')) {
customElements.define('payment-don', PaymentForm, {extends: 'form'});
}
initializeUI();
if (!isPerformanceTestAgent()) {
handleNotificationBanner();
handleCookieBanner();
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 });
};
}
});
document.addEventListener('turbo:load', () => {
initializeUI();
});
document.addEventListener('click', (event) => {
const target = event.target;
const mobileMenuButton = target.closest('#mobileMenuButton');
if (mobileMenuButton) {
toggleMenu(mobileMenuButton, document.getElementById('mobile-menu'));
return;
}
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();
return;
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && window.closeCart) window.closeCart();
});