✨ feat(templates): [FR] Améliore l'UI et ajoute des traductions pour plus de contenu dynamique.
```
280 lines
11 KiB
JavaScript
280 lines
11 KiB
JavaScript
import './app.scss'
|
|
import * as Turbo from "@hotwired/turbo"
|
|
import '@grafikart/drop-files-element'
|
|
import {PaymentForm} from './PaymentForm'
|
|
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 });
|
|
};
|
|
}
|
|
const env = document.querySelector('meta[name="env"]');
|
|
if (env && env.getAttribute('content') === "prod" && 'serviceWorker' in navigator) {
|
|
navigator.serviceWorker.register('/sw.js');
|
|
}
|
|
});
|
|
|
|
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();
|
|
});
|