Files
e-cosplay/assets/app.js
Serreau Jovann a280eb29a4 ```
 feat(dons): Ajoute la fonctionnalité de dons avec Stripe et reçus PDF.

Ajoute une page de dons avec formulaire, intégration Stripe, webhooks,
génération de reçus PDF et envoi de mails de confirmation. Ajoute aussi
gestion des erreurs 404/500.
```
2025-11-18 20:48:34 +01:00

208 lines
8.4 KiB
JavaScript

import './app.scss'
import * as Turbo from "@hotwired/turbo"
import {PaymentForm} from './PaymentForm'
/**
* Fonction générique pour basculer la visibilité d'un menu déroulant.
* @param {HTMLElement} button - Le bouton qui déclenche l'action.
* @param {HTMLElement} menu - Le menu à afficher/masquer.
*/
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');
}
/**
* Fonction d'initialisation pour les composants qui DOIVENT être réinitialisés
* après un chargement Turbo (comme les compteurs d'articles, les états initiaux).
* Le menu mobile et le panier sont gérés par délégation d'événements.
*/
function initializeUI() {
// Réinitialisation des états des menus cachés après un chargement Turbo,
// au cas où ils étaient ouverts lors de la navigation précédente.
document.querySelectorAll('#mobile-menu, #userMenuDesktop, #userMenuMobile').forEach(menu => {
if (!menu.classList.contains('hidden')) {
menu.classList.add('hidden');
}
});
document.querySelectorAll('#mobileMenuButton, #userMenuButtonDesktop, #userMenuButtonMobile').forEach(button => {
button.setAttribute('aria-expanded', 'false');
});
// --- 2. Gestion du Panier Latéral (Off-Canvas) ---
const cartSidebar = document.getElementById('cartSidebar');
const cartBackdrop = document.getElementById('cartBackdrop');
const closeCartButton = document.getElementById('closeCartButton');
// Mettez les fonctions ici pour qu'elles soient toujours définies si les éléments existent
if (cartSidebar && cartBackdrop && closeCartButton) {
function openCart() {
document.body.style.overflow = 'hidden';
cartBackdrop.classList.remove('hidden');
cartSidebar.classList.remove('translate-x-full');
cartSidebar.classList.add('translate-x-0');
}
function closeCart() {
document.body.style.overflow = '';
cartSidebar.classList.remove('translate-x-0');
cartSidebar.classList.add('translate-x-full');
setTimeout(() => {
cartBackdrop.classList.add('hidden');
}, 300);
}
// Stocker les fonctions dans une variable globale accessible par l'écouteur du document
window.openCart = openCart;
window.closeCart = closeCart;
} else {
// Sécurité si les éléments du panier n'existent pas
window.openCart = null;
window.closeCart = null;
}
// --- 3. Logique Panier Mock (Affichage du compteur) ---
function updateCartDisplay(count) {
const desktopCounter = document.getElementById('cartCountDesktop');
const mobileCounter = document.getElementById('cartCountMobile');
if (desktopCounter) desktopCounter.textContent = count;
if (mobileCounter) mobileCounter.textContent = count;
}
// Simuler un panier non-vide au chargement (Mettre 0 pour un panier vide réel)
updateCartDisplay(0);
}
// --- INITIALISATION DES COMPOSANTS APRÈS TURBO/CHARGEMENT ---
document.addEventListener('DOMContentLoaded', ()=>{
customElements.define('payment-don',PaymentForm,{extends:'form'})
initializeUI()
const env = document.querySelector('meta[name="env"]')
if(env.getAttribute('content') == "prod") {
if (typeof navigator.serviceWorker !== 'undefined') {
navigator.serviceWorker.register('sw.js')
}
}
});
document.addEventListener('turbo:load', initializeUI);
// ====================================================================
// --- DÉLÉGATION D'ÉVÉNEMENTS (Gestion des clics une seule fois) ---
// ====================================================================
document.addEventListener('click', (event) => {
const target = event.target;
// --- 1. GESTION DU MENU MOBILE (Burger) ---
const mobileMenuButton = document.getElementById('mobileMenuButton');
const mobileMenu = document.getElementById('mobile-menu');
// On vérifie si la cible cliquée est le bouton ou un de ses enfants
if (mobileMenuButton && mobileMenu && (target === mobileMenuButton || mobileMenuButton.contains(target))) {
event.preventDefault();
toggleMenu(mobileMenuButton, mobileMenu);
return;
}
// --- 2. GESTION DU MENU UTILISATEUR (Dropdown) ---
const userMenuButtonDesktop = document.getElementById('userMenuButtonDesktop');
const userMenuDesktop = document.getElementById('userMenuDesktop');
const userMenuButtonMobile = document.getElementById('userMenuButtonMobile');
const userMenuMobile = document.getElementById('userMenuMobile');
// Ouverture/Fermeture du menu utilisateur Desktop
if (userMenuButtonDesktop && userMenuDesktop && (target === userMenuButtonDesktop || userMenuButtonDesktop.contains(target))) {
event.preventDefault();
// S'assurer que les autres menus sont fermés
userMenuMobile.classList.add('hidden');
toggleMenu(userMenuButtonDesktop, userMenuDesktop);
return;
}
// Ouverture/Fermeture du menu utilisateur Mobile
if (userMenuButtonMobile && userMenuMobile && (target === userMenuButtonMobile || userMenuButtonMobile.contains(target))) {
event.preventDefault();
// S'assurer que les autres menus sont fermés
userMenuDesktop.classList.add('hidden');
toggleMenu(userMenuButtonMobile, userMenuMobile);
return;
}
// Fermeture des menus s'il y a un clic en dehors
const isClickInsideDesktopMenu = userMenuDesktop && (userMenuDesktop.contains(target) || userMenuButtonDesktop.contains(target));
const isClickInsideMobileMenu = userMenuMobile && (userMenuMobile.contains(target) || userMenuButtonMobile.contains(target));
if (userMenuDesktop && userMenuButtonDesktop && !isClickInsideDesktopMenu) {
userMenuDesktop.classList.add('hidden');
userMenuButtonDesktop.setAttribute('aria-expanded', 'false');
}
if (userMenuMobile && userMenuButtonMobile && !isClickInsideMobileMenu) {
userMenuMobile.classList.add('hidden');
userMenuButtonMobile.setAttribute('aria-expanded', 'false');
}
// --- 3. GESTION DE L'OUVERTURE ET FERMETURE DU PANIER ---
const openCartDesktop = document.getElementById('openCartDesktop');
const openCartMobile = document.getElementById('openCartMobile');
const closeCartButton = document.getElementById('closeCartButton');
const cartBackdrop = document.getElementById('cartBackdrop');
// Ouverture (Desktop ou Mobile)
if (window.openCart && (
(openCartDesktop && (target === openCartDesktop || openCartDesktop.contains(target))) ||
(openCartMobile && (target === openCartMobile || openCartMobile.contains(target)))
)) {
event.preventDefault();
window.openCart();
return;
}
// Fermeture (Bouton interne)
if (window.closeCart && closeCartButton && (target === closeCartButton || closeCartButton.contains(target))) {
event.preventDefault();
window.closeCart();
return;
}
// Fermeture (Cliquer sur le fond/backdrop)
if (window.closeCart && target === cartBackdrop) {
window.closeCart();
return;
}
});
// --- GESTION GLOBALE DE LA TOUCHE ESC (Une seule fois) ---
document.addEventListener('keydown', (event) => {
const cartSidebar = document.getElementById('cartSidebar');
// Fermer le panier
if (cartSidebar && window.closeCart && event.key === 'Escape' && !cartSidebar.classList.contains('translate-x-full')) {
window.closeCart();
return;
}
// Fermer les menus utilisateur
const userMenuDesktop = document.getElementById('userMenuDesktop');
const userMenuButtonDesktop = document.getElementById('userMenuButtonDesktop');
const userMenuMobile = document.getElementById('userMenuMobile');
const userMenuButtonMobile = document.getElementById('userMenuButtonMobile');
if (event.key === 'Escape') {
if (userMenuDesktop && !userMenuDesktop.classList.contains('hidden')) {
toggleMenu(userMenuButtonDesktop, userMenuDesktop);
return;
}
if (userMenuMobile && !userMenuMobile.classList.contains('hidden')) {
toggleMenu(userMenuButtonMobile, userMenuMobile);
return;
}
}
});