2026-01-19 21:08:04 +01:00
|
|
|
import './reserve.scss';
|
2026-01-23 09:25:11 +01:00
|
|
|
import { UtmEvent, UtmAccount } from "./tools/UtmEvent.js";
|
2026-01-27 20:24:02 +01:00
|
|
|
import { CookieBanner } from "./tools/CookieBanner.js";
|
2026-01-22 22:17:32 +01:00
|
|
|
import * as Turbo from "@hotwired/turbo";
|
|
|
|
|
|
2026-01-23 09:25:11 +01:00
|
|
|
// --- DÉTECTION BOT / PERFORMANCE ---
|
2026-01-22 22:53:05 +01:00
|
|
|
const isLighthouse = () => {
|
2026-01-23 09:25:11 +01:00
|
|
|
if (typeof navigator === 'undefined' || !navigator.userAgent) return false;
|
2026-01-22 22:53:05 +01:00
|
|
|
const userAgent = navigator.userAgent.toLowerCase();
|
2026-01-23 09:25:11 +01:00
|
|
|
const patterns = ['chrome-lighthouse', 'google/lighthouse', 'lighthouse', 'pagespeed', 'headless', 'webdriver'];
|
|
|
|
|
return patterns.some(pattern => userAgent.includes(pattern));
|
2026-01-22 22:53:05 +01:00
|
|
|
};
|
2026-01-22 22:17:32 +01:00
|
|
|
|
2026-01-27 20:24:02 +01:00
|
|
|
// --- GESTION DYNAMIQUE DE 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: "https://803814be6540031b1c37bf92ba9c0f79@sentry.esy-web.dev/24",
|
|
|
|
|
tunnel: "/sentry-tunnel",
|
|
|
|
|
integrations: [Sentry.browserTracingIntegration()],
|
|
|
|
|
tracesSampleRate: 1.0,
|
|
|
|
|
});
|
|
|
|
|
window.SentryInitialized = true;
|
|
|
|
|
console.log("✔️ Sentry initialisé et activé");
|
|
|
|
|
} else {
|
|
|
|
|
// Réactivation si déjà chargé
|
|
|
|
|
if (window.sentryClient) window.sentryClient.getOptions().enabled = true;
|
|
|
|
|
console.log("✔️ Sentry ré-activé");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status === 'refused' && window.SentryInitialized) {
|
|
|
|
|
// Désactivation sans décharger le script
|
|
|
|
|
if (window.sentryClient) window.sentryClient.getOptions().enabled = false;
|
|
|
|
|
console.log("🛑 Sentry désactivé (Client muet)");
|
2026-01-22 22:17:32 +01:00
|
|
|
}
|
2026-01-27 20:24:02 +01:00
|
|
|
} catch (e) {
|
|
|
|
|
console.warn("Sentry toggle failed", e);
|
2026-01-22 22:17:32 +01:00
|
|
|
}
|
|
|
|
|
};
|
2026-01-19 21:08:04 +01:00
|
|
|
|
2026-01-27 20:44:41 +01:00
|
|
|
const initImageLoader = () => {
|
|
|
|
|
// On cible uniquement les images à l'intérieur de la balise <main>
|
|
|
|
|
const mainContainer = document.querySelector('main');
|
|
|
|
|
if (!mainContainer) return;
|
|
|
|
|
|
|
|
|
|
const images = mainContainer.querySelectorAll('img:not(.loaded)');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log(images);
|
|
|
|
|
images.forEach(img => {
|
|
|
|
|
// Sécurité : si l'image est déjà chargée (cache), on marque et on skip
|
|
|
|
|
if (img.complete) {
|
|
|
|
|
img.classList.add('loaded');
|
|
|
|
|
img.style.opacity = '1';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1. Préparation du parent (doit être relatif pour le loader absolu)
|
|
|
|
|
const parent = img.parentElement;
|
|
|
|
|
if (!parent) return;
|
|
|
|
|
parent.classList.add('relative', 'overflow-hidden', 'bg-gray-50');
|
|
|
|
|
|
|
|
|
|
// 2. Création du Loader (Spinner Tailwind)
|
|
|
|
|
const loader = document.createElement('div');
|
|
|
|
|
loader.id = `loader-${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
|
loader.className = 'absolute inset-0 flex items-center justify-center z-10 bg-gray-50 transition-opacity duration-500';
|
|
|
|
|
loader.innerHTML = `
|
|
|
|
|
<div class="flex flex-col items-center gap-2">
|
|
|
|
|
<div class="w-7 h-7 border-3 border-slate-200 border-t-slate-800 rounded-full animate-spin"></div>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
parent.insertBefore(loader, img);
|
|
|
|
|
|
|
|
|
|
// 3. État initial de l'image (invisible)
|
|
|
|
|
img.classList.add('opacity-0', 'transition-opacity', 'duration-700');
|
|
|
|
|
|
|
|
|
|
// 4. Gestionnaire de fin de chargement
|
|
|
|
|
img.onload = () => {
|
|
|
|
|
img.classList.replace('opacity-0', 'opacity-100');
|
|
|
|
|
img.classList.add('loaded');
|
|
|
|
|
loader.classList.add('opacity-0');
|
|
|
|
|
setTimeout(() => loader.remove(), 500);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Gestion de l'erreur
|
|
|
|
|
img.onerror = () => {
|
|
|
|
|
loader.innerHTML = '<span class="text-[10px] text-gray-400 font-medium uppercase">Erreur</span>';
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
};
|
2026-01-27 20:24:02 +01:00
|
|
|
// --- LOGIQUE DU LOADER TURBO ---
|
2026-01-20 13:51:23 +01:00
|
|
|
const initLoader = () => {
|
2026-01-23 09:25:11 +01:00
|
|
|
if (document.getElementById('turbo-loader')) return;
|
|
|
|
|
|
2026-01-27 20:24:02 +01:00
|
|
|
const loaderEl = document.createElement('div');
|
|
|
|
|
loaderEl.id = 'turbo-loader';
|
|
|
|
|
loaderEl.className = 'fixed inset-0 z-[9999] flex items-center justify-center bg-white transition-opacity duration-300 opacity-0 pointer-events-none';
|
|
|
|
|
loaderEl.innerHTML = `
|
|
|
|
|
<div class="relative flex items-center justify-center">
|
|
|
|
|
<div class="absolute w-24 h-24 border-4 border-[#f39e36] border-t-transparent rounded-full animate-spin"></div>
|
|
|
|
|
<img src="/provider/images/favicon.webp" class="w-12 h-12 relative z-10 animate-pulse" alt="Logo">
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
document.body.appendChild(loaderEl);
|
2026-01-23 09:25:11 +01:00
|
|
|
|
|
|
|
|
document.addEventListener("turbo:click", () => {
|
|
|
|
|
loaderEl.classList.replace('opacity-0', 'opacity-100');
|
|
|
|
|
loaderEl.classList.remove('pointer-events-none');
|
|
|
|
|
});
|
2026-01-20 13:51:23 +01:00
|
|
|
|
|
|
|
|
const hideLoader = () => {
|
|
|
|
|
setTimeout(() => {
|
2026-01-23 09:25:11 +01:00
|
|
|
loaderEl.classList.replace('opacity-100', 'opacity-0');
|
|
|
|
|
loaderEl.classList.add('pointer-events-none');
|
2026-01-20 13:51:23 +01:00
|
|
|
}, 300);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.addEventListener("turbo:load", hideLoader);
|
|
|
|
|
document.addEventListener("turbo:render", hideLoader);
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-27 20:24:02 +01:00
|
|
|
// --- LOGIQUE INTERFACE (Menu, Filtres, Redirect, Register) ---
|
2026-01-19 21:08:04 +01:00
|
|
|
const initMobileMenu = () => {
|
|
|
|
|
const btn = document.getElementById('menu-button');
|
|
|
|
|
const menu = document.getElementById('mobile-menu');
|
|
|
|
|
if (btn && menu) {
|
2026-01-23 09:25:11 +01:00
|
|
|
btn.onclick = null;
|
|
|
|
|
btn.addEventListener('click', () => {
|
2026-01-19 21:08:04 +01:00
|
|
|
const isExpanded = btn.getAttribute('aria-expanded') === 'true';
|
|
|
|
|
btn.setAttribute('aria-expanded', !isExpanded);
|
|
|
|
|
menu.classList.toggle('hidden');
|
2026-01-23 09:25:11 +01:00
|
|
|
});
|
2026-01-19 21:08:04 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-20 13:51:23 +01:00
|
|
|
const initCatalogueSearch = () => {
|
|
|
|
|
const filters = document.querySelectorAll('.filter-btn');
|
|
|
|
|
const products = document.querySelectorAll('.product-item');
|
|
|
|
|
const emptyMsg = document.getElementById('empty-msg');
|
2026-01-23 09:25:11 +01:00
|
|
|
if (filters.length === 0) return;
|
2026-01-20 13:51:23 +01:00
|
|
|
filters.forEach(btn => {
|
|
|
|
|
btn.onclick = () => {
|
|
|
|
|
const category = btn.getAttribute('data-filter').toLowerCase();
|
|
|
|
|
let count = 0;
|
2026-01-27 20:24:02 +01:00
|
|
|
filters.forEach(f => {
|
|
|
|
|
f.classList.replace('bg-slate-900', 'bg-white');
|
|
|
|
|
f.classList.replace('text-white', 'text-slate-500');
|
|
|
|
|
});
|
2026-01-23 09:25:11 +01:00
|
|
|
btn.classList.replace('bg-white', 'bg-slate-900');
|
|
|
|
|
btn.classList.replace('text-slate-500', 'text-white');
|
2026-01-20 13:51:23 +01:00
|
|
|
products.forEach(item => {
|
2026-01-27 20:24:02 +01:00
|
|
|
const itemCat = (item.getAttribute('data-category') || '').toLowerCase();
|
2026-01-23 09:25:11 +01:00
|
|
|
const isVisible = category === 'all' || itemCat.includes(category);
|
|
|
|
|
item.style.display = isVisible ? 'block' : 'none';
|
|
|
|
|
if (isVisible) count++;
|
2026-01-20 13:51:23 +01:00
|
|
|
});
|
2026-01-23 09:25:11 +01:00
|
|
|
if (emptyMsg) count === 0 ? emptyMsg.classList.remove('hidden') : emptyMsg.classList.add('hidden');
|
2026-01-20 13:51:23 +01:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-23 09:25:11 +01:00
|
|
|
const initAutoRedirect = () => {
|
|
|
|
|
const container = document.getElementById('payment-check-container');
|
|
|
|
|
if (container && container.dataset.autoRedirect) {
|
|
|
|
|
const url = container.dataset.autoRedirect;
|
|
|
|
|
setTimeout(() => {
|
2026-01-27 20:24:02 +01:00
|
|
|
if (document.getElementById('payment-check-container')) Turbo.visit(url);
|
2026-01-23 09:25:11 +01:00
|
|
|
}, 10000);
|
|
|
|
|
}
|
2026-01-27 20:24:02 +01:00
|
|
|
};
|
|
|
|
|
|
2026-01-23 09:25:11 +01:00
|
|
|
const initRegisterLogic = () => {
|
|
|
|
|
const siretContainer = document.getElementById('siret-container');
|
|
|
|
|
const typeRadios = document.querySelectorAll('input[name="type"]');
|
|
|
|
|
if (!siretContainer || typeRadios.length === 0) return;
|
|
|
|
|
const updateSiretVisibility = () => {
|
|
|
|
|
const selectedType = document.querySelector('input[name="type"]:checked')?.value;
|
|
|
|
|
if (selectedType === 'buisness') {
|
|
|
|
|
siretContainer.classList.remove('hidden');
|
|
|
|
|
siretContainer.querySelector('input')?.setAttribute('required', 'required');
|
|
|
|
|
} else {
|
|
|
|
|
siretContainer.classList.add('hidden');
|
|
|
|
|
siretContainer.querySelector('input')?.removeAttribute('required');
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-01-27 20:24:02 +01:00
|
|
|
typeRadios.forEach(radio => radio.addEventListener('change', updateSiretVisibility));
|
2026-01-23 09:25:11 +01:00
|
|
|
updateSiretVisibility();
|
|
|
|
|
};
|
2026-01-27 20:24:02 +01:00
|
|
|
|
2026-01-23 09:25:11 +01:00
|
|
|
// --- INITIALISATION GLOBALE ---
|
2026-01-19 21:08:04 +01:00
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
2026-01-20 13:51:23 +01:00
|
|
|
initLoader();
|
2026-01-27 20:44:41 +01:00
|
|
|
initImageLoader();
|
2026-01-27 20:24:02 +01:00
|
|
|
// Enregistrement Custom Elements
|
2026-01-22 22:17:32 +01:00
|
|
|
if (!customElements.get('utm-event')) customElements.define('utm-event', UtmEvent);
|
|
|
|
|
if (!customElements.get('utm-account')) customElements.define('utm-account', UtmAccount);
|
2026-01-27 20:24:02 +01:00
|
|
|
if (!customElements.get('cookie-banner')) customElements.define('cookie-banner', CookieBanner);
|
|
|
|
|
|
|
|
|
|
// Initialisation Sentry basée sur le choix existant
|
|
|
|
|
const currentConsent = sessionStorage.getItem('ldk_cookie');
|
|
|
|
|
if (currentConsent) toggleSentry(currentConsent);
|
|
|
|
|
|
|
|
|
|
// Écouteurs pour changements de choix cookies
|
|
|
|
|
window.addEventListener('cookieAccepted', () => toggleSentry('accepted'));
|
|
|
|
|
window.addEventListener('cookieRefused', () => toggleSentry('refused'));
|
2026-01-19 21:08:04 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('turbo:load', () => {
|
|
|
|
|
initMobileMenu();
|
2026-01-20 13:51:23 +01:00
|
|
|
initCatalogueSearch();
|
2026-01-22 20:15:21 +01:00
|
|
|
initAutoRedirect();
|
2026-01-23 09:25:11 +01:00
|
|
|
initRegisterLogic();
|
2026-01-27 20:44:41 +01:00
|
|
|
initImageLoader();
|
2026-01-20 13:51:23 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener("turbo:before-cache", () => {
|
|
|
|
|
document.querySelectorAll('.product-item').forEach(i => i.style.display = 'block');
|
2026-01-23 09:25:11 +01:00
|
|
|
const emptyMsg = document.getElementById('empty-msg');
|
|
|
|
|
if (emptyMsg) emptyMsg.classList.add('hidden');
|
2026-01-19 21:08:04 +01:00
|
|
|
});
|