1/** 2 * Aratice Home Product Slider JavaScript 3 * Initialisation et gestion du slider Swiper.js pour la page d'accueil 4 */ 5 6(function ($) { 7 "use strict"; 8 9 /** 10 * Afficher une notification en haut à droite 11 */ 12 function showNotification(message) { 13 // Supprimer les notifications existantes 14 $(".ahps-notification").remove(); 15 16 // Créer la notification 17 const $notification = $(` 18 <div class="ahps-notification"> 19 <span class="ahps-notification-icon">â</span> 20 <span class="ahps-notification-text">${message}</span> 21 <button class="ahps-notification-close">×</button> 22 </div> 23 `); 24 25 // Ajouter au DOM 26 $("body").append($notification); 27 28 // Afficher avec animation 29 setTimeout(() => { 30 $notification.addClass("show"); 31 }, 100); 32 33 // Auto-masquer après 4 secondes 34 setTimeout(() => { 35 hideNotification($notification); 36 }, 4000); 37 38 // Fermer au clic 39 $notification.find(".ahps-notification-close").on("click", () => { 40 hideNotification($notification); 41 }); 42 } 43 44 /** 45 * Masquer une notification 46 */ 47 function hideNotification($notification) { 48 $notification.removeClass("show"); 49 setTimeout(() => { 50 $notification.remove(); 51 }, 300); 52 } 53 54 /** 55 * Initialiser le slider de produits 56 */ 57 function initProductsSlider() { 58 $(".ahps-swiper").each(function () { 59 const $slider = $(this); 60 const config = $slider.data("config") || {}; 61 62 // Configuration Swiper correcte 63 const finalConfig = { 64 slidesPerView: 1, // Par défaut 1 65 spaceBetween: 20, 66 loop: config.loop !== false, // Loop par défaut 67 autoplay: config.autoplay || false, 68 navigation: config.navigation || { 69 nextEl: ".ahps-nav-next", 70 prevEl: ".ahps-nav-prev", 71 }, 72 pagination: false, // Désactiver la pagination 73 breakpoints: { 74 1024: { 75 slidesPerView: 4, // Desktop: 4 produits 76 spaceBetween: 20, 77 }, 78 768: { 79 slidesPerView: 2, // Tablette: 2 produits 80 spaceBetween: 15, 81 }, 82 480: { 83 slidesPerView: 1, // Mobile: 1 produit 84 spaceBetween: 10, 85 }, 86 }, 87 on: { 88 init: function () { 89 // Animation d'entrée des slides 90 $slider.find(".swiper-slide").addClass("swiper-slide-animate"); 91 // S'assurer que les images sont visibles 92 $slider.find(".apg-product-img").first().addClass("active"); 93 94 // Masquer la pagination si elle existe 95 $slider.find(".ahps-pagination").hide(); 96 }, 97 }, 98 }; 99 100 // Initialiser Swiper 101 const swiper = new Swiper(this, finalConfig); 102 103 // Gestion des événements personnalisés 104 handleSliderEvents($slider, swiper); 105 }); 106 } 107 108 /** 109 * Gérer les événements du slider 110 */ 111 function handleSliderEvents($slider, swiper) { 112 // Pause autoplay au hover 113 $slider.on("mouseenter", function () { 114 if (swiper.autoplay && swiper.autoplay.running) { 115 swiper.autoplay.pause(); 116 } 117 }); 118 119 $slider.on("mouseleave", function () { 120 if (swiper.autoplay && swiper.autoplay.paused) { 121 swiper.autoplay.resume(); 122 } 123 }); 124 125 // Désactiver la gestion des images multiples dans le slider 126 // Les images restent statiques pour éviter les problèmes de visibilité 127 128 // Gestion des boutons "Ajouter au devis" - Utilise QuoteStorage comme products-grid 129 $slider.on("click", ".apg-add-to-quote", function (e) { 130 e.preventDefault(); 131 132 const $button = $(this); 133 const productId = $button.attr("data-product-id"); 134 135 if (!productId) { 136 console.error("ID produit manquant"); 137 return; 138 } 139 140 // Vérifier que QuoteStorage est disponible 141 if (!window.QuoteStorage) { 142 console.error("QuoteStorage non disponible!"); 143 if (typeof window.araticeShowNotification === "function") { 144 window.araticeShowNotification( 145 "Erreur: Système de devis non chargé", 146 "error" 147 ); 148 } 149 return; 150 } 151 152 // Extraire les données du produit depuis la card 153 const $card = $button.closest(".apg-product-card"); 154 const productData = { 155 productId: productId, 156 title: 157 $card.data("product-title") || 158 $card.find(".apg-product-title a").text().trim(), 159 sku: $card.data("product-sku") || "", 160 imageUrl: 161 $card.data("product-image") || 162 $card.find(".apg-product-img.active").attr("src"), 163 priceHT: parseFloat($card.data("product-price-ht")) || 0, 164 priceTTC: parseFloat($card.data("product-price-ttc")) || 0, 165 originalPriceHT: 166 parseFloat($card.data("product-original-price-ht")) || 167 parseFloat($card.data("product-price-ht")) || 168 0, 169 originalPriceTTC: 170 parseFloat($card.data("product-original-price-ttc")) || 171 parseFloat($card.data("product-price-ttc")) || 172 0, 173 userDiscount: parseFloat($card.data("product-user-discount")) || 0, 174 permalink: 175 $card.data("product-permalink") || 176 $card.find(".apg-product-title a").attr("href"), 177 quantity: 1, 178 }; 179 180 // Vérifier que les données essentielles sont présentes 181 if (!productData.title || productData.priceHT === 0) { 182 console.error("Données produit incomplètes:", productData); 183 if (typeof window.araticeShowNotification === "function") { 184 window.araticeShowNotification( 185 "Erreur: Impossible de récupérer les informations du produit", 186 "error" 187 ); 188 } 189 return; 190 } 191 192 // Animation du bouton 193 $button 194 .prop("disabled", true) 195 .html('<span class="apg-spinner apg-spinner-small"></span> Ajout...'); 196 197 // Utiliser QuoteStorage pour ajouter le produit
198 try { 199 const result = window.QuoteStorage.addProduct(productData); 200 201 if (result.success) { 202 // Notification de succès - Désactivée car gérée par quote-notifications.js 203 // const message = 204 // result.action === "quantity_updated" 205 // ? "Quantité mise à jour dans le devis" 206 // : "Produit ajouté au devis avec succès !"; 207 208 // showNotification(message); 209 210 // Récupérer les totaux 211 const totals = window.QuoteStorage.calculateTotals(); 212 213 // Déclencher un événement pour mettre à jour le panier 214 $(document).trigger("aratice:product_added_to_quote", [ 215 productId, 216 { product: result.item, totals }, 217 ]); 218 219 // Remplacer le bouton par le contrôle de quantité 220 replaceButtonWithQuantityControl($button, productId, 1); 221 } else { 222 // Erreur 223 console.error("â Ãchec de l'ajout au localStorage"); 224 if (typeof window.araticeShowNotification === "function") { 225 window.araticeShowNotification( 226 "Erreur lors de l'ajout au devis", 227 "error" 228 ); 229 } 230 $button 231 .html('<span class="apg-btn-icon">+</span> Ajouter au devis') 232 .prop("disabled", false); 233 } 234 } catch (error) { 235 console.error("Erreur QuoteStorage:", error); 236 if (typeof window.araticeShowNotification === "function") { 237 window.araticeShowNotification( 238 "Erreur lors de l'ajout au devis", 239 "error" 240 ); 241 } 242 $button 243 .html('<span class="apg-btn-icon">+</span> Ajouter au devis') 244 .prop("disabled", false); 245 } 246 }); 247 248 // Désactiver le drag & drop dans le slider - On utilise uniquement Swiper 249 $slider.on("dragstart", ".apg-product-card", function (e) { 250 e.preventDefault(); 251 return false; 252 }); 253 254 $slider.on("drag", ".apg-product-card", function (e) { 255 e.preventDefault(); 256 return false; 257 }); 258 259 // Gérer les boutons +/- de quantité dans le slider 260 $slider.on("click", ".apg-card-qty-plus", function (e) { 261 e.preventDefault(); 262 const productId = $(this).data("product-id"); 263 updateProductQuantity(productId, 1); 264 }); 265 266 $slider.on("click", ".apg-card-qty-minus", function (e) { 267 e.preventDefault(); 268 const productId = $(this).data("product-id"); 269 updateProductQuantity(productId, -1); 270 }); 271 272 // Clic sur la quantité pour édition manuelle dans le slider 273 $slider.on("click", ".apg-card-qty-value", function (e) { 274 handleCardQuantityClick($(this)); 275 }); 276 277 // Gestion de l'input de quantité dans le slider 278 $slider.on("blur", ".apg-card-qty-input", function (e) { 279 handleCardQuantityInputBlur($(this)); 280 }); 281 282 $slider.on("keypress", ".apg-card-qty-input", function (e) { 283 if (e.which === 13) { 284 // Touche Entrée 285 e.preventDefault(); 286 $(this).blur(); 287 } 288 }); 289 } 290 291 /** 292 * Remplacer le bouton "Ajouter au devis" par un contrôle de quantité 293 */ 294 function replaceButtonWithQuantityControl($btn, productId, quantity) { 295 const $actionsContainer = $btn.closest(".apg-product-actions"); 296 297 const quantityControlHtml = ` 298 <div class="apg-card-quantity-control" data-product-id="${productId}"> 299 <button class="apg-card-qty-btn apg-card-qty-minus" data-product-id="${productId}">â</button> 300 <span class="apg-card-qty-value" data-product-id="${productId}" title="Cliquez pour modifier">${quantity}</span> 301 <button class="apg-card-qty-btn apg-card-qty-plus" data-product-id="${productId}">+</button> 302 </div> 303 `; 304 305 // Remplacer le bouton par le contrôle 306 $actionsContainer.html(quantityControlHtml); 307 308 // Animation d'apparition 309 $actionsContainer.find(".apg-card-quantity-control").hide().fadeIn(300); 310 } 311 312 /** 313 * Mettre à jour la quantité d'un produit dans le slider 314 */ 315 function updateProductQuantity(productId, delta) { 316 const item = window.QuoteStorage.getItem(productId); 317 318 if (!item) { 319 console.error("Produit non trouvé dans le devis:", productId); 320 return; 321 } 322 323 const newQuantity = item.quantity + delta; 324 325 if (newQuantity < 1) { 326 // Supprimer le produit du devis 327 if (confirm("Voulez-vous retirer ce produit du devis ?")) { 328 window.QuoteStorage.removeProduct(productId); 329 330 // Remettre le bouton "Ajouter au devis" 331 const $control = $( 332 `.apg-card-quantity-control[data-product-id="${productId}"]` 333 ); 334 const $actionsContainer = $control.closest(".apg-product-actions"); 335 336 const addButtonHtml = `
337 <button class="apg-add-to-quote" data-product-id="${productId}"> 338 <span class="apg-btn-icon">+</span> 339 Ajouter au devis 340 </button> 341 `; 342 343 $actionsContainer.html(addButtonHtml); 344 345 // Notification désactivée - Gérée par quote-notifications.js 346 // showNotification("Produit retiré du devis"); 347 348 // Déclencher l'événement de mise à jour 349 $(document).trigger("aratice:quote_updated"); 350 } 351 } else { 352 // Mettre à jour la quantité 353 window.QuoteStorage.updateQuantity(productId, newQuantity); 354 355 // Mettre à jour l'affichage 356 $(`.apg-card-qty-value[data-product-id="${productId}"]`).text( 357 newQuantity 358 ); 359 360 // Déclencher l'événement de mise à jour 361 $(document).trigger("aratice:quote_updated"); 362 } 363 } 364 365 /** 366 * Gérer le clic sur la quantité pour permettre l'édition manuelle 367 */ 368 function handleCardQuantityClick($qtyValue) { 369 const productId = $qtyValue.data("product-id"); 370 const currentQty = parseInt($qtyValue.text()); 371 372 // Créer un input pour édition 373 const $input = $( 374 '<input type="number" class="apg-card-qty-input" min="1" />' 375 ); 376 $input.val(currentQty); 377 $input.data("product-id", productId); 378 379 // Remplacer le span par l'input 380 $qtyValue.replaceWith($input); 381 382 // Sélectionner le contenu 383 $input.focus().select(); 384 } 385 386 /** 387 * Gérer la perte de focus de l'input de quantité 388 */ 389 function handleCardQuantityInputBlur($input) { 390 const productId = $input.data("product-id"); 391 const newQty = parseInt($input.val()) || 1; 392 393 // Mettre à jour la quantité dans le localStorage 394 window.QuoteStorage.updateQuantity(productId, newQty); 395 396 // Recréer le span 397 const $qtyValue = $( 398 '<span class="apg-card-qty-value" title="Cliquez pour modifier"></span>' 399 ); 400 $qtyValue.text(newQty); 401 $qtyValue.data("product-id", productId); 402 403 // Remplacer l'input par le span 404 $input.replaceWith($qtyValue); 405 406 // Déclencher l'événement de mise à jour 407 $(document).trigger("aratice:quote_updated"); 408 } 409 410 /** 411 * Vérifier les produits déjà dans le devis au chargement 412 */ 413 function checkProductsInQuote() { 414 if (!window.QuoteStorage) return; 415 416 const items = window.QuoteStorage.getItems(); 417 418 items.forEach((item) => { 419 const productId = item.productId; 420 const $btn = $(`.apg-add-to-quote[data-product-id="${productId}"]`); 421 422 if ($btn.length > 0) { 423 // Remplacer par le contrôle de quantité 424 replaceButtonWithQuantityControl($btn, productId, item.quantity); 425 } 426 }); 427 } 428 429 /** 430 * Gérer le redimensionnement de la fenêtre 431 */ 432 function handleResize() { 433 $(".ahps-swiper").each(function () { 434 if (this.swiper) { 435 this.swiper.update(); 436 } 437 }); 438 } 439 440 /** 441 * Initialiser les animations 442 */ 443 function initAnimations() { 444 // Observer pour les animations d'entrée 445 if ("IntersectionObserver" in window) { 446 const observer = new IntersectionObserver( 447 (entries) => { 448 entries.forEach((entry) => { 449 if (entry.isIntersecting) { 450 entry.target.classList.add("ahps-animate-in"); 451 } 452 }); 453 }, 454 { 455 threshold: 0.1, 456 rootMargin: "0px 0px -50px 0px", 457 } 458 ); 459 460 $(".ahps-products-slider-wrapper").each(function () { 461 observer.observe(this); 462 }); 463 } 464 } 465 466 /** 467 * Gérer les erreurs de chargement des images 468 */ 469 function handleImageErrors() { 470 $(".ahps-swiper .apg-product-img").on("error", function () { 471 const $img = $(this); 472 const $card = $img.closest(".apg-product-card"); 473 474 // Remplacer par l'image placeholder 475 $img.attr( 476 "src", 477 wc_placeholder_img_src || 478 "/wp-content/plugins/woocommerce/assets/images/placeholder.png" 479 ); 480 $img.attr("alt", "Image non disponible"); 481 482 // Ajouter une classe pour le style 483 $card.addClass("apg-no-image"); 484 }); 485 } 486 487 /** 488 * Initialiser les contrôles de navigation 489 */ 490 function initNavigationControls() { 491 // Navigation au clavier 492 $(".ahps-swiper").on("keydown", function (e) { 493 const $slider = $(this); 494 const swiper = $slider[0].swiper; 495 496 if (!swiper) return; 497 498 switch (e.keyCode) { 499 case 37: // Flèche gauche 500 e.preventDefault(); 501 swiper.slidePrev(); 502 break; 503 case 39: // Flèche droite 504 e.preventDefault(); 505 swiper.slideNext(); 506 break; 507 } 508 }); 509 510 // Focus management 511 $(".ahps-nav").on("click", function () { 512 $(this).focus(); 513 }); 514 } 515 516 /** 517 * Gérer la visibilité des contrôles 518 */ 519 function handleControlsVisibility() { 520 $(".ahps-swiper").each(function () { 521 const $slider = $(this); 522 const swiper = $slider[0].swiper; 523 524 if (!swiper) return; 525 526 const $nav = $slider.find(".ahps-nav"); 527 const $pagination = $slider.find(".ahps-pagination"); 528 529 // Masquer les contrôles si pas assez de slides 530 if (swiper.slides.length <= swiper.params.slidesPerView) { 531 $nav.hide(); 532 $pagination.hide(); 533 } else { 534 $nav.show(); 535 $pagination.show(); 536 } 537 }); 538 } 539 540 /** 541 * Initialisation principale 542 */
543 function init() { 544 // Attendre que Swiper soit disponible 545 if (typeof Swiper === "undefined") { 546 return; 547 } 548 549 // Initialiser le slider 550 initProductsSlider(); 551 552 // Initialiser les animations 553 initAnimations(); 554 555 // Gérer les erreurs d'images 556 handleImageErrors(); 557 558 // Initialiser les contrôles 559 initNavigationControls(); 560 561 // Gérer la visibilité des contrôles 562 handleControlsVisibility(); 563 564 // Vérifier les produits déjà dans le devis 565 checkProductsInQuote(); 566 567 // Ãcouter les mises à jour du devis 568 $(document).on("aratice:quote_updated", function () { 569 checkProductsInQuote(); 570 }); 571 572 // Ãcouter l'ajout de produit depuis d'autres sources 573 $(document).on( 574 "aratice:product_added_to_quote", 575 function (e, productId, data) { 576 const $btn = $(`.apg-add-to-quote[data-product-id="${productId}"]`); 577 if ($btn.length > 0 && data && data.product) { 578 replaceButtonWithQuantityControl( 579 $btn, 580 productId, 581 data.product.quantity || 1 582 ); 583 } 584 } 585 ); 586 } 587 588 // Ãvénements 589 $(document).ready(init); 590 $(window).on("resize", handleResize); 591 592 // Réinitialiser après les mises à jour AJAX 593 $(document).on("elementor/popup/show", init); 594 $(document).on("elementor/frontend/init", init); 595 596 // Exposer les fonctions globalement pour debug 597 window.AraticeProductsSlider = { 598 init: init, 599 initProductsSlider: initProductsSlider, 600 handleResize: handleResize, 601 }; 602})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.