PageSourceSearch

https://www.aratice.fr/wp-content/themes/aratice/assets/js/home-product-slider.js?ver=1780661389

js aratice.fr collected 2026-10-08 19:26:09 UTC 17,780 bytes, 602 lines download raw bytes

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">&times;</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.