import './reserve.scss'; import { UtmEvent, UtmAccount } from "./tools/UtmEvent.js"; import { CookieBanner } from "./tools/CookieBanner.js"; import { FlowReserve } from "./tools/FlowReserve.js"; import { FlowDatePicker } from "./tools/FlowDatePicker.js"; import { FlowAddToCart } from "./tools/FlowAddToCart.js"; import * as Turbo from "@hotwired/turbo"; import { onLCP, onINP, onCLS } from 'web-vitals'; import AOS from 'aos'; import 'aos/dist/aos.css'; // --- CONFIGURATION & ÉTAT --- const CONFIG = { sentryDsn: "https://803814be6540031b1c37bf92ba9c0f79@sentry.esy-web.dev/24", vitalsUrl: '/web-vitals' }; // --- UTILS --- const isLighthouse = () => { if (typeof navigator === 'undefined' || !navigator.userAgent) return false; return ['lighthouse', 'pagespeed', 'headless', 'webdriver'].some(p => navigator.userAgent.toLowerCase().includes(p) ); }; // --- PERFORMANCE (Web Vitals) --- const sendToAnalytics = (metric) => { if (isLighthouse()) return; const body = JSON.stringify({ ...metric, path: window.location.pathname }); if (navigator.sendBeacon) { navigator.sendBeacon(CONFIG.vitalsUrl, body); } else { fetch(CONFIG.vitalsUrl, { body, method: 'POST', keepalive: true }); } }; const initVitals = () => { onLCP(sendToAnalytics); onINP(sendToAnalytics); onCLS(sendToAnalytics); }; // --- SERVICES (Sentry) --- const toggleSentry = async (status) => { if (isLighthouse()) return; try { const Sentry = await import("@sentry/browser"); if (status === 'accepted') { if (!window.SentryInitialized) { window.sentryClient = Sentry.init({ dsn: CONFIG.sentryDsn, tunnel: "/sentry-tunnel", integrations: [Sentry.browserTracingIntegration()], tracesSampleRate: 1.0, }); window.SentryInitialized = true; } else if (window.sentryClient) { window.sentryClient.getOptions().enabled = true; } } else if (status === 'refused' && window.SentryInitialized) { if (window.sentryClient) window.sentryClient.getOptions().enabled = false; } } catch (e) { console.warn("Sentry error", e); } }; // --- UI : ACCESSIBILITÉ (Text-to-Speech) --- const initAccessibility = () => { if (document.getElementById('accessibility-toggle')) return; const btn = document.createElement('button'); btn.id = 'accessibility-toggle'; // Positionné en bas à gauche, fond gris ardoise par défaut btn.className = ` fixed bottom-24 left-8 z-50 p-4 rounded-2xl shadow-xl border-2 border-white/10 transition-all duration-300 hover:scale-110 active:scale-95 `; const updateBtnStyle = () => { const isActive = localStorage.getItem('ldk_tts_active') === 'true'; btn.innerHTML = isActive ? '🔊' : '🔇'; btn.classList.toggle('bg-[#f39e36]', isActive); // Orange si actif btn.classList.toggle('bg-slate-900', !isActive); // Noir si inactif btn.classList.toggle('text-white', true); }; updateBtnStyle(); document.body.appendChild(btn); btn.onclick = () => { const currentState = localStorage.getItem('ldk_tts_active') === 'true'; localStorage.setItem('ldk_tts_active', !currentState); updateBtnStyle(); if (currentState) window.speechSynthesis.cancel(); }; // Écouteur global sur le document pour la lecture au clic document.addEventListener('click', (e) => { if (localStorage.getItem('ldk_tts_active') !== 'true') return; const target = e.target.closest('p, h1, h2, h3, h4, span, label, li'); if (target && target.innerText.trim().length > 0) { window.speechSynthesis.cancel(); const utterance = new SpeechSynthesisUtterance(target.innerText); utterance.lang = 'fr-FR'; // Feedback visuel Ludik Event target.classList.add('outline', 'outline-2', 'outline-[#f39e36]', 'outline-offset-2'); setTimeout(() => target.classList.remove('outline', 'outline-2', 'outline-[#f39e36]', 'outline-offset-2'), 1200); window.speechSynthesis.speak(utterance); } }); }; // --- UI : LOADERS & IMAGES --- const initImageLoader = () => { const images = document.querySelectorAll('main img:not(.loaded)'); images.forEach(img => { if (img.complete) { img.classList.add('loaded'); return; } const parent = img.parentElement; if (!parent) return; parent.classList.add('relative', 'overflow-hidden', 'bg-slate-50'); const loader = document.createElement('div'); loader.className = 'absolute inset-0 flex items-center justify-center z-10 bg-slate-50 transition-opacity duration-500'; loader.innerHTML = `
`; parent.insertBefore(loader, img); img.classList.add('opacity-0', 'transition-opacity', 'duration-700'); img.onload = () => { img.classList.replace('opacity-0', 'opacity-100'); img.classList.add('loaded'); loader.classList.add('opacity-0'); setTimeout(() => loader.remove(), 500); }; img.onerror = () => { loader.innerHTML = '⚠️'; }; }); }; const initTurboLoader = () => { if (document.getElementById('turbo-loader')) return; const loaderEl = document.createElement('div'); loaderEl.id = 'turbo-loader'; loaderEl.className = 'fixed inset-0 z-[9999] flex items-center justify-center bg-white/80 backdrop-blur-sm transition-opacity duration-300 opacity-0 pointer-events-none'; loaderEl.innerHTML = `
LDK
`; document.body.appendChild(loaderEl); document.addEventListener("turbo:click", () => { loaderEl.classList.replace('opacity-0', 'opacity-100'); loaderEl.classList.remove('pointer-events-none'); }); const hide = () => { loaderEl.classList.replace('opacity-100', 'opacity-0'); loaderEl.classList.add('pointer-events-none'); }; document.addEventListener("turbo:load", hide); document.addEventListener("turbo:render", hide); }; // --- UI : BACK TO TOP --- const initBackToTop = () => { if (document.getElementById('back-to-top')) return; const btn = document.createElement('button'); btn.id = 'back-to-top'; btn.className = ` fixed bottom-24 right-8 z-50 p-4 bg-[#f39e36] text-white rounded-2xl shadow-xl border-2 border-white/20 opacity-0 translate-y-10 pointer-events-none transition-all duration-500 hover:bg-[#fc0e50] hover:-translate-y-2 group `; btn.innerHTML = ` `; document.body.appendChild(btn); const handleScroll = () => { const isVisible = window.scrollY > 400; btn.classList.toggle('opacity-100', isVisible); btn.classList.toggle('translate-y-0', isVisible); btn.classList.toggle('opacity-0', !isVisible); btn.classList.toggle('translate-y-10', !isVisible); btn.classList.toggle('pointer-events-none', !isVisible); }; window.addEventListener('scroll', handleScroll, { passive: true }); btn.onclick = () => window.scrollTo({ top: 0, behavior: 'smooth' }); }; // --- LOGIQUE MÉTIER --- const initMobileMenu = () => { const btn = document.getElementById('menu-button'); const menu = document.getElementById('mobile-menu'); console.log(btn,menu) if (!btn || !menu) return; btn.onclick = () => { const open = menu.classList.toggle('hidden'); btn.setAttribute('aria-expanded', !open); }; }; const initCatalogueSearch = () => { const filters = document.querySelectorAll('.filter-btn'); const products = document.querySelectorAll('.product-item'); const emptyMsg = document.getElementById('empty-msg'); filters.forEach(btn => { btn.onclick = () => { const category = btn.dataset.filter.toLowerCase(); let count = 0; filters.forEach(f => f.classList.remove('bg-slate-900', 'text-black')); btn.classList.add('bg-slate-900', 'text-black'); products.forEach(item => { const isVisible = category === 'all' || (item.dataset.category || '').toLowerCase().includes(category); item.classList.toggle('hidden', !isVisible); if (isVisible) count++; }); emptyMsg?.classList.toggle('hidden', count > 0); }; }); }; const initRegisterLogic = () => { const siretContainer = document.getElementById('siret-container'); const typeRadios = document.querySelectorAll('input[name="type"]'); if (!siretContainer) return; const toggle = () => { const isBiz = document.querySelector('input[name="type"]:checked')?.value === 'buisness'; siretContainer.classList.toggle('hidden', !isBiz); siretContainer.querySelector('input')?.toggleAttribute('required', isBiz); }; typeRadios.forEach(r => r.addEventListener('change', toggle)); toggle(); }; // --- INITIALISATION --- const registerComponents = () => { const comps = [['utm-event', UtmEvent], ['utm-account', UtmAccount], ['cookie-banner', CookieBanner]]; comps.forEach(([name, cl]) => { if (!customElements.get(name)) customElements.define(name, cl); }); if(!customElements.get('flow-reserve')) customElements.define('flow-reserve',FlowReserve,{extends:'a'}) if(!customElements.get('flow-datepicker')) customElements.define('flow-datepicker',FlowDatePicker) if(!customElements.get('flow-add-to-cart')) customElements.define('flow-add-to-cart',FlowAddToCart) }; document.addEventListener('DOMContentLoaded', () => { initVitals(); initTurboLoader(); registerComponents(); const consent = sessionStorage.getItem('ldk_cookie'); if (consent) toggleSentry(consent); window.addEventListener('cookieAccepted', () => toggleSentry('accepted')); window.addEventListener('cookieRefused', () => toggleSentry('refused')); }); document.addEventListener('turbo:load', () => { initVitals(); initImageLoader(); initBackToTop(); initAccessibility(); initMobileMenu(); initRegisterLogic(); initCatalogueSearch(); // Initialize AOS AOS.init({ duration: 800, once: true, }); const payContainer = document.getElementById('payment-check-container'); if (payContainer?.dataset.autoRedirect) { setTimeout(() => { if (document.getElementById('payment-check-container')) { Turbo.visit(payContainer.dataset.autoRedirect); } }, 10000); } }); document.addEventListener("turbo:before-cache", () => { document.querySelectorAll('.product-item').forEach(i => i.classList.remove('hidden')); document.getElementById('empty-msg')?.classList.add('hidden'); });