templates/home/single-product.html.twig line 1

Open in your IDE?
  1. {% extends 'base_home.html.twig' %}
  2. {% block title %}
  3.     {{ product.name }}
  4.     | MaketOu
  5. {% endblock %}
  6. {% block stylesheets %}
  7.     <style>
  8.         /* Styles pour l'affichage de la boutique */
  9.         .shop-info {
  10.             padding: 10px 0;
  11.             border-bottom: 1px solid #f0f0f0;
  12.             margin-bottom: 15px;
  13.         }
  14.         .shop-link {
  15.             color: #ffa200;
  16.             text-decoration: none;
  17.             font-weight: 500;
  18.         }
  19.         .shop-link:hover {
  20.             color: #e8910a;
  21.             text-decoration: underline;
  22.         }
  23.         /* Styles pour les statistiques du produit */
  24.         .product-stats {
  25.             background: #f8f9fa;
  26.             padding: 15px;
  27.             border-radius: 8px;
  28.             border: 1px solid #e9ecef;
  29.         }
  30.         .product-stats .stat-item {
  31.             text-align: center;
  32.         }
  33.         .product-stats .stat-number {
  34.             display: block;
  35.             font-size: 1.2rem;
  36.             font-weight: bold;
  37.             color: #ffa200;
  38.         }
  39.         .product-stats .stat-label {
  40.             color: #666;
  41.             font-size: 0.8rem;
  42.             text-transform: uppercase;
  43.             letter-spacing: 0.5px;
  44.         }
  45.         /* Message de confirmation */
  46.         #cart-message {
  47.             border: none;
  48.             background: #d4edda;
  49.             color: #155724;
  50.             border-radius: 6px;
  51.             padding: 12px 16px;
  52.             margin-top: 15px;
  53.         }
  54.         /* Amélioration des boutons */
  55.         .card_area .primary-btn {
  56.             background: #ffa200;
  57.             border: none;
  58.             padding: 12px 24px;
  59.             border-radius: 6px;
  60.             color: white;
  61.             font-weight: 500;
  62.             transition: all 0.3s ease;
  63.         }
  64.         .card_area .primary-btn:hover {
  65.             background: #e8910a;
  66.             transform: translateY(-2px);
  67.             box-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  68.         }
  69.         .icon_btn:hover {
  70.             background: #ffa200;
  71.             color: white;
  72.             border-color: #ffa200;
  73.         }
  74.         /* Styles pour la galerie moderne de produits */
  75.         .product-gallery-modern {
  76.             display: flex;
  77.             gap: 15px;
  78.             position: relative;
  79.         }
  80.         .thumbnails-container {
  81.             position: relative;
  82.             display: flex;
  83.             flex-direction: column;
  84.             align-items: center;
  85.             width: 80px;
  86.             flex-shrink: 0;
  87.         }
  88.         /* Cacher les boutons jusqu'au survol */
  89.         .thumbnails-container .thumbnail-nav-btn {
  90.             opacity: 0;
  91.             visibility: hidden;
  92.             transition: opacity 0.2s ease, visibility 0.2s ease;
  93.         }
  94.         .thumbnails-container:hover .thumbnail-nav-btn {
  95.             opacity: 1;
  96.             visibility: visible;
  97.         }
  98.         .product-thumbnails {
  99.             display: flex;
  100.             flex-direction: column;
  101.             gap: 10px;
  102.             width: 100%;
  103.             height: 400px;
  104.             overflow: hidden;
  105.             position: relative;
  106.         }
  107.         .thumbnails-wrapper {
  108.             display: flex;
  109.             flex-direction: column;
  110.             gap: 10px;
  111.             transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  112.             will-change: transform;
  113.         }
  114.         .thumbnail-nav-btn {
  115.             width: 30px;
  116.             height: 30px;
  117.             border-radius: 50%;
  118.             background: white;
  119.             border: 2px solid #e0e0e0;
  120.             display: flex !important;
  121.             align-items: center;
  122.             justify-content: center;
  123.             cursor: pointer;
  124.             transition: all 0.3s ease;
  125.             margin: 0;
  126.             color: #666;
  127.             z-index: 10;
  128.             box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  129.             position: relative;
  130.             flex-shrink: 0;
  131.         }
  132.         .thumbnail-nav-btn:hover {
  133.             background: #ffa200;
  134.             border-color: #ffa200;
  135.             color: white;
  136.             transform: scale(1.1);
  137.             box-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  138.         }
  139.         .thumbnail-nav-btn:active {
  140.             transform: scale(0.95);
  141.         }
  142.         .thumbnail-nav-btn.disabled {
  143.             opacity: 0.3;
  144.             cursor: not-allowed;
  145.             pointer-events: none;
  146.             filter: grayscale(1);
  147.         }
  148.         .thumbnail-nav-btn i {
  149.             font-size: 20px;
  150.             font-weight: bold;
  151.         }
  152.         .thumbnail-item {
  153.             width: 80px;
  154.             height: 80px;
  155.             min-width: 80px;
  156.             min-height: 80px;
  157.             max-width: 80px;
  158.             max-height: 80px;
  159.             border: 2px solid #e0e0e0;
  160.             border-radius: 8px;
  161.             overflow: hidden;
  162.             cursor: pointer;
  163.             transition: border-color 0.3s ease, border-width 0.3s ease, box-shadow 0.3s ease;
  164.             position: relative;
  165.             background: #f8f9fa;
  166.             border-bottom: 2px solid #e0e0e0;
  167.             flex-shrink: 0;
  168.             display: flex;
  169.             align-items: center;
  170.             justify-content: center;
  171.         }
  172.         .thumbnail-item:hover {
  173.             border-color: #ffa200;
  174.             border-bottom-color: #ffa200;
  175.             border-width: 3px;
  176.             box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2);
  177.         }
  178.         .thumbnail-item.active {
  179.             border-color: #ffa200 !important;
  180.             border-bottom-color: #ffa200 !important;
  181.             border-width: 3px !important;
  182.             box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2) !important;
  183.         }
  184.         .thumbnail-item::after,
  185.         .thumbnail-item::before {
  186.             display: none !important;
  187.             content: none !important;
  188.         }
  189.         .thumbnail-item img {
  190.             width: 100%;
  191.             height: 100%;
  192.             object-fit: cover !important;
  193.             object-position: center !important;
  194.             transition: transform 0.3s ease;
  195.         }
  196.         .thumbnail-item:hover img {
  197.             transform: scale(1.05);
  198.         }
  199.         .thumbnail-item.active img {
  200.             transform: scale(1.05);
  201.         }
  202.         .product-main-image-wrapper {
  203.             flex: 1;
  204.             position: relative;
  205.             background: #fff;
  206.             height: 400px;
  207.             border-radius: 12px;
  208.             overflow: visible;
  209.             border: 1px solid #e0e0e0;
  210.             z-index: 1;
  211.         }
  212.         .product-main-image {
  213.             position: relative;
  214.             width: 100%;
  215.             padding-top: 100%;
  216.             overflow: visible;
  217.             background: #f8f9fa;
  218.             display: flex;
  219.             align-items: center;
  220.             justify-content: center;
  221.             z-index: 1;
  222.         }
  223.         .product-main-image img {
  224.             position: absolute;
  225.             top: 0;
  226.             left: 0;
  227.             width: 100%;
  228.             height: 100%;
  229.             object-fit: contain !important;
  230.             object-position: center !important;
  231.             transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease;
  232.             opacity: 1;
  233.         }
  234.         .product-main-image img.loading {
  235.             opacity: 0.5;
  236.         }
  237.         .product-main-image:hover img {
  238.             transform: scale(1.08);
  239.         }
  240.         .product-main-image img.fade-in {
  241.             animation: fadeInImage 0.4s ease;
  242.         }
  243.         @keyframes fadeInImage {
  244.             from {
  245.                 opacity: 0;
  246.                 transform: scale(0.95);
  247.             }
  248.             to {
  249.                 opacity: 1;
  250.                 transform: scale(1);
  251.             }
  252.         }
  253.         .image-overlay-icons {
  254.             position: absolute !important;
  255.             top: 20px !important;
  256.             right: 20px !important;
  257.             display: flex !important;
  258.             flex-direction: column !important;
  259.             gap: 12px !important;
  260.             z-index: 10 !important;
  261.             opacity: 0 !important;
  262.             transition: opacity 0.3s ease, transform 0.3s ease !important;
  263.             transform: translateX(10px) !important;
  264.         }
  265.         .product-main-image-wrapper:hover .image-overlay-icons {
  266.             opacity: 1 !important;
  267.             transform: translateX(0) !important;
  268.         }
  269.         .product-main-image-wrapper .icon-btn,
  270.         .image-overlay-icons .icon-btn {
  271.             width: 48px !important;
  272.             height: 48px !important;
  273.             min-width: 48px !important;
  274.             min-height: 48px !important;
  275.             max-width: 48px !important;
  276.             max-height: 48px !important;
  277.             border-radius: 50% !important;
  278.             background: rgba(255, 255, 255, 0.95) !important;
  279.             backdrop-filter: blur(10px) !important;
  280.             border: 2px solid rgba(255, 255, 255, 0.8) !important;
  281.             display: flex !important;
  282.             align-items: center !important;
  283.             justify-content: center !important;
  284.             cursor: pointer !important;
  285.             transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  286.             box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
  287.             color: #333 !important;
  288.             position: relative !important;
  289.             overflow: hidden !important;
  290.             padding: 0 !important;
  291.             margin: 0 !important;
  292.             font-size: inherit !important;
  293.             font-weight: normal !important;
  294.             line-height: 1 !important;
  295.             text-align: center !important;
  296.             text-decoration: none !important;
  297.             white-space: nowrap !important;
  298.             vertical-align: middle !important;
  299.             font-family: inherit !important;
  300.             text-transform: none !important;
  301.             letter-spacing: normal !important;
  302.         }
  303.         .product-main-image-wrapper .icon-btn::before,
  304.         .image-overlay-icons .icon-btn::before {
  305.             content: '' !important;
  306.             position: absolute !important;
  307.             top: 50% !important;
  308.             left: 50% !important;
  309.             width: 0 !important;
  310.             height: 0 !important;
  311.             border-radius: 50% !important;
  312.             background: rgba(255, 162, 0, 0.2) !important;
  313.             transform: translate(-50%, -50%) !important;
  314.             transition: width 0.4s ease, height 0.4s ease !important;
  315.         }
  316.         .product-main-image-wrapper .icon-btn:hover::before,
  317.         .image-overlay-icons .icon-btn:hover::before {
  318.             width: 100% !important;
  319.             height: 100% !important;
  320.         }
  321.         .product-main-image-wrapper .icon-btn:hover,
  322.         .image-overlay-icons .icon-btn:hover {
  323.             background: #ffa200 !important;
  324.             border-color: #ffa200 !important;
  325.             color: white !important;
  326.             transform: scale(1.15) rotate(5deg) !important;
  327.             box-shadow: 0 6px 20px rgba(255, 162, 0, 0.5) !important;
  328.         }
  329.         .product-main-image-wrapper .icon-btn:active,
  330.         .image-overlay-icons .icon-btn:active {
  331.             transform: scale(1.05) rotate(0deg) !important;
  332.         }
  333.         .product-main-image-wrapper .icon-btn i,
  334.         .image-overlay-icons .icon-btn i {
  335.             font-size: 20px !important;
  336.             position: relative !important;
  337.             z-index: 1 !important;
  338.             transition: transform 0.3s ease !important;
  339.             margin: 0 !important;
  340.             display: block !important;
  341.         }
  342.         .product-main-image-wrapper .icon-btn:hover i,
  343.         .image-overlay-icons .icon-btn:hover i {
  344.             transform: scale(1.1) !important;
  345.         }
  346.         .product-main-image-wrapper .favorite-btn.active,
  347.         .image-overlay-icons .favorite-btn.active {
  348.             background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%) !important;
  349.             border-color: #ff6b6b !important;
  350.             color: white !important;
  351.             box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4) !important;
  352.         }
  353.         .product-main-image-wrapper .favorite-btn.active:hover,
  354.         .image-overlay-icons .favorite-btn.active:hover {
  355.             background: linear-gradient(135deg, #ee5a6f 0%, #dd4a5f 100%) !important;
  356.             transform: scale(1.15) rotate(-5deg) !important;
  357.         }
  358.         .product-main-image-wrapper .image-counter,
  359.         .image-counter {
  360.             position: absolute !important;
  361.             bottom: 20px !important;
  362.             right: 20px !important;
  363.             background: rgba(0, 0, 0, 0.75) !important;
  364.             backdrop-filter: blur(8px) !important;
  365.             color: white !important;
  366.             padding: 8px 16px !important;
  367.             border-radius: 25px !important;
  368.             font-size: 14px !important;
  369.             font-weight: 600 !important;
  370.             box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3) !important;
  371.             z-index: 5 !important;
  372.             display: flex !important;
  373.             align-items: center !important;
  374.             gap: 6px !important;
  375.         }
  376.         .product-main-image-wrapper .image-counter span,
  377.         .image-counter span {
  378.             font-weight: 700 !important;
  379.             color: #ffa200 !important;
  380.         }
  381.         .product-main-image-wrapper .image-indicators,
  382.         #image-indicators {
  383.             position: absolute !important;
  384.             bottom: 20px !important;
  385.             left: 50% !important;
  386.             transform: translateX(-50%) !important;
  387.             display: flex !important;
  388.             align-items: center !important;
  389.             justify-content: center !important;
  390.             gap: 10px !important;
  391.             background: rgba(0, 0, 0, 0.4) !important;
  392.             backdrop-filter: blur(8px) !important;
  393.             padding: 8px 16px !important;
  394.             border-radius: 25px !important;
  395.             z-index: 5 !important;
  396.         }
  397.         .product-main-image-wrapper .indicator,
  398.         #image-indicators .indicator,
  399.         .image-indicators .indicator {
  400.             width: 12px !important;
  401.             height: 12px !important;
  402.             min-width: 12px !important;
  403.             min-height: 12px !important;
  404.             max-width: 12px !important;
  405.             max-height: 12px !important;
  406.             border-radius: 50% !important;
  407.             border: 2px solid rgba(255, 255, 255, 0.6) !important;
  408.             background: rgba(255, 255, 255, 0.3) !important;
  409.             cursor: pointer !important;
  410.             transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  411.             padding: 0 !important;
  412.             margin: 0 !important;
  413.             position: relative !important;
  414.             display: block !important;
  415.             text-align: center !important;
  416.             text-decoration: none !important;
  417.             white-space: nowrap !important;
  418.             vertical-align: middle !important;
  419.             font-size: 0 !important;
  420.             line-height: 0 !important;
  421.             box-sizing: border-box !important;
  422.         }
  423.         .product-main-image-wrapper .indicator::before,
  424.         #image-indicators .indicator::before,
  425.         .image-indicators .indicator::before {
  426.             content: '' !important;
  427.             position: absolute !important;
  428.             top: 50% !important;
  429.             left: 50% !important;
  430.             transform: translate(-50%, -50%) scale(0) !important;
  431.             width: 20px !important;
  432.             height: 20px !important;
  433.             border-radius: 50% !important;
  434.             background: rgba(255, 162, 0, 0.3) !important;
  435.             transition: transform 0.3s ease !important;
  436.         }
  437.         .product-main-image-wrapper .indicator:hover,
  438.         #image-indicators .indicator:hover,
  439.         .image-indicators .indicator:hover {
  440.             border-color: #ffa200 !important;
  441.             background: rgba(255, 162, 0, 0.6) !important;
  442.             transform: scale(1.2) !important;
  443.         }
  444.         .product-main-image-wrapper .indicator:hover::before,
  445.         #image-indicators .indicator:hover::before,
  446.         .image-indicators .indicator:hover::before {
  447.             transform: translate(-50%, -50%) scale(1) !important;
  448.         }
  449.         .product-main-image-wrapper .indicator.active,
  450.         #image-indicators .indicator.active,
  451.         .image-indicators .indicator.active {
  452.             border-color: #ffa200 !important;
  453.             background: #ffa200 !important;
  454.             box-shadow: 0 0 0 3px rgba(255, 162, 0, 0.3), 0 2px 8px rgba(255, 162, 0, 0.5) !important;
  455.             transform: scale(1.3) !important;
  456.         }
  457.         .product-main-image-wrapper .indicator.active::before,
  458.         #image-indicators .indicator.active::before,
  459.         .image-indicators .indicator.active::before {
  460.             transform: translate(-50%, -50%) scale(1) !important;
  461.             background: rgba(255, 162, 0, 0.4) !important;
  462.         }
  463.         /* Modal de zoom d'image avec transitions modernes */
  464.         .image-zoom-modal {
  465.             display: none;
  466.             position: fixed;
  467.             z-index: 9999;
  468.             left: 0;
  469.             top: 0;
  470.             width: 100%;
  471.             height: 100%;
  472.             background: rgba(0, 0, 0, 0);
  473.             overflow: auto;
  474.             opacity: 0;
  475.             transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), background 0.3s ease;
  476.             backdrop-filter: blur(0px);
  477.         }
  478.         .image-zoom-modal.active {
  479.             display: flex;
  480.             align-items: center;
  481.             justify-content: center;
  482.             opacity: 1;
  483.             background: rgba(0, 0, 0, 0.95);
  484.             backdrop-filter: blur(10px);
  485.         }
  486.         .zoom-modal-content {
  487.             position: relative;
  488.             max-width: 90%;
  489.             max-height: 90%;
  490.             margin: auto;
  491.             opacity: 0;
  492.             transform: scale(0.8);
  493.             transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1), transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  494.         }
  495.         .image-zoom-modal.active .zoom-modal-content {
  496.             opacity: 1;
  497.             transform: scale(1);
  498.         }
  499.         .zoom-modal-content img {
  500.             width: 100%;
  501.             height: auto;
  502.             max-height: 90vh;
  503.             object-fit: contain !important;
  504.             object-position: center !important;
  505.             border-radius: 8px;
  506.             box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  507.             opacity: 0;
  508.             transition: opacity 0.3s ease;
  509.         }
  510.         .zoom-modal-content img.loaded {
  511.             opacity: 1;
  512.         }
  513.         /* Loader moderne */
  514.         .zoom-loader {
  515.             position: absolute;
  516.             top: 50%;
  517.             left: 50%;
  518.             transform: translate(-50%, -50%);
  519.             z-index: 1;
  520.             display: flex;
  521.             flex-direction: column;
  522.             align-items: center;
  523.             justify-content: center;
  524.         }
  525.         .zoom-loader-spinner {
  526.             width: 60px;
  527.             height: 60px;
  528.             border: 4px solid rgba(255, 255, 255, 0.1);
  529.             border-top-color: #ffa200;
  530.             border-radius: 50%;
  531.             animation: spin 1s linear infinite;
  532.         }
  533.         @keyframes spin {
  534.             to {
  535.                 transform: rotate(360deg);
  536.             }
  537.         }
  538.         .zoom-loader-text {
  539.             color: white;
  540.             margin-top: 15px;
  541.             text-align: center;
  542.             font-size: 14px;
  543.             font-weight: 500;
  544.         }
  545.         .close-zoom {
  546.             position: absolute;
  547.             top: 30px;
  548.             right: 30px;
  549.             color: white;
  550.             font-size: 32px;
  551.             font-weight: 300;
  552.             cursor: pointer;
  553.             z-index: 10000;
  554.             width: 50px;
  555.             height: 50px;
  556.             display: flex;
  557.             align-items: center;
  558.             justify-content: center;
  559.             background: rgba(255, 255, 255, 0.1);
  560.             border: 2px solid rgba(255, 255, 255, 0.2);
  561.             border-radius: 50%;
  562.             transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  563.             opacity: 0;
  564.             transform: scale(0.8);
  565.         }
  566.         .image-zoom-modal.active .close-zoom {
  567.             opacity: 1;
  568.             transform: scale(1);
  569.             transition-delay: 0.2s;
  570.         }
  571.         .close-zoom:hover {
  572.             background: rgba(255, 162, 0, 0.9);
  573.             border-color: #ffa200;
  574.             transform: scale(1.1) rotate(90deg);
  575.             box-shadow: 0 4px 20px rgba(255, 162, 0, 0.4);
  576.         }
  577.         
  578.         /* Styles pour le contrôle de quantité amélioré */
  579.         .product_count {
  580.             display: flex !important;
  581.             align-items: center !important;
  582.             gap: 15px !important;
  583.             margin-bottom: 24px !important;
  584.             position: relative !important;
  585.         }
  586.         
  587.         .product_count label {
  588.             font-size: 14px !important;
  589.             color: #777777 !important;
  590.             font-family: "Roboto", sans-serif !important;
  591.             font-weight: normal !important;
  592.             margin-bottom: 0 !important;
  593.             white-space: nowrap !important;
  594.             padding-right: 10px !important;
  595.         }
  596.         
  597.         .quantity-controls-wrapper {
  598.             display: flex !important;
  599.             align-items: center !important;
  600.             border: 1px solid #eeeeee !important;
  601.             border-radius: 4px !important;
  602.             overflow: hidden !important;
  603.             background: #fff !important;
  604.             position: relative !important;
  605.         }
  606.         
  607.         .quantity-btn {
  608.             display: flex !important;
  609.             align-items: center !important;
  610.             justify-content: center !important;
  611.             width: 40px !important;
  612.             height: 40px !important;
  613.             border: none !important;
  614.             background: #f8f9fa !important;
  615.             color: #666 !important;
  616.             cursor: pointer !important;
  617.             transition: all 0.3s ease !important;
  618.             font-size: 16px !important;
  619.             padding: 0 !important;
  620.             margin: 0 !important;
  621.             position: relative !important;
  622.             top: auto !important;
  623.             bottom: auto !important;
  624.             right: auto !important;
  625.             left: auto !important;
  626.         }
  627.         
  628.         .quantity-btn:hover {
  629.             background: #e9ecef !important;
  630.             color: #ffa200 !important;
  631.         }
  632.         
  633.         .quantity-btn:active {
  634.             background: #dee2e6 !important;
  635.             transform: scale(0.95) !important;
  636.         }
  637.         
  638.         .quantity-btn i {
  639.             font-size: 14px !important;
  640.         }
  641.         
  642.         .quantity-input {
  643.             width: 60px !important;
  644.             height: 40px !important;
  645.             border: none !important;
  646.             border-left: 1px solid #eeeeee !important;
  647.             border-right: 1px solid #eeeeee !important;
  648.             text-align: center !important;
  649.             font-size: 14px !important;
  650.             font-weight: 500 !important;
  651.             padding: 0 !important;
  652.             margin: 0 !important;
  653.             outline: none !important;
  654.             background: #fff !important;
  655.             position: relative !important;
  656.         }
  657.         
  658.         .quantity-input:focus {
  659.             border-left-color: #ffa200 !important;
  660.             border-right-color: #ffa200 !important;
  661.         }
  662.         
  663.         /* Surcharger les styles existants pour les boutons */
  664.         .product_count .quantity-btn {
  665.             position: relative !important;
  666.             display: flex !important;
  667.             align-items: center !important;
  668.             justify-content: center !important;
  669.             width: 40px !important;
  670.             height: 40px !important;
  671.             border: none !important;
  672.             background: #f8f9fa !important;
  673.             color: #666 !important;
  674.             cursor: pointer !important;
  675.             transition: all 0.3s ease !important;
  676.             font-size: 16px !important;
  677.             padding: 0 !important;
  678.             margin: 0 !important;
  679.             top: auto !important;
  680.             bottom: auto !important;
  681.             right: auto !important;
  682.             left: auto !important;
  683.         }
  684.         
  685.         .product_count .quantity-btn:hover {
  686.             background: #e9ecef !important;
  687.             color: #ffa200 !important;
  688.         }
  689.         
  690.         .product_count .quantity-btn:active {
  691.             background: #dee2e6 !important;
  692.             transform: scale(0.95) !important;
  693.         }
  694.         
  695.         .product_count .quantity-btn i,
  696.         .product_count .quantity-btn .quantity-icon {
  697.             font-size: 20px !important;
  698.             font-weight: 300 !important;
  699.             line-height: 1 !important;
  700.             display: inline-block !important;
  701.         }
  702.         
  703.         .product_count .quantity-btn .quantity-icon {
  704.             font-size: 24px !important;
  705.             font-weight: 300 !important;
  706.             line-height: 1 !important;
  707.         }
  708.         
  709.         /* Désactiver les boutons si nécessaire */
  710.         .quantity-btn:disabled {
  711.             opacity: 0.5 !important;
  712.             cursor: not-allowed !important;
  713.         }
  714.         
  715.         .quantity-btn:disabled:hover {
  716.             background: #f8f9fa !important;
  717.             color: #666 !important;
  718.         }
  719.         
  720.         /* Surcharger les styles pour le wrapper */
  721.         .product_count .quantity-controls-wrapper {
  722.             display: flex !important;
  723.             align-items: center !important;
  724.             border: 1px solid #eeeeee !important;
  725.             border-radius: 4px !important;
  726.             overflow: hidden !important;
  727.             background: #fff !important;
  728.             position: relative !important;
  729.         }
  730.         
  731.         /* Surcharger tous les styles de main.css pour les boutons dans product_count */
  732.         .product_count button.quantity-btn,
  733.         .product_count .quantity-btn {
  734.             display: flex !important;
  735.             position: relative !important;
  736.             top: auto !important;
  737.             bottom: auto !important;
  738.             right: auto !important;
  739.             left: auto !important;
  740.             width: 40px !important;
  741.             height: 40px !important;
  742.             background: #f8f9fa !important;
  743.             color: #666 !important;
  744.             border: none !important;
  745.             box-shadow: none !important;
  746.         }
  747.         
  748.         .product_count button.quantity-btn:hover,
  749.         .product_count .quantity-btn:hover {
  750.             background: #e9ecef !important;
  751.             color: #ffa200 !important;
  752.         }
  753.         
  754.         /* Surcharger les styles pour l'input */
  755.         .product_count .quantity-input,
  756.         .product_count input.quantity-input {
  757.             width: 60px !important;
  758.             height: 40px !important;
  759.             border: none !important;
  760.             border-left: 1px solid #eeeeee !important;
  761.             border-right: 1px solid #eeeeee !important;
  762.             text-align: center !important;
  763.             padding: 0 !important;
  764.             padding-left: 0 !important;
  765.             position: relative !important;
  766.         }
  767.         /* Boutons de navigation sur l'image principale */
  768.         .main-image-nav {
  769.             position: absolute;
  770.             top: 50%;
  771.             left: 0;
  772.             right: 0;
  773.             transform: translateY(-50%);
  774.             display: flex;
  775.             justify-content: space-between;
  776.             padding: 0 12px;
  777.             pointer-events: none !important;
  778.             z-index: 10 !important;
  779.         }
  780.         .main-image-nav .main-image-nav-btn,
  781.         .main-image-nav-btn {
  782.             pointer-events: all !important;
  783.             z-index: 1000 !important;
  784.             position: relative !important;
  785.             cursor: pointer !important;
  786.             -webkit-user-select: none !important;
  787.             -moz-user-select: none !important;
  788.             -ms-user-select: none !important;
  789.             user-select: none !important;
  790.             user-select: none !important;
  791.             -webkit-user-select: none !important;
  792.             -moz-user-select: none !important;
  793.             -ms-user-select: none !important;
  794.             width: 48px !important;
  795.             height: 48px !important;
  796.             min-width: 48px !important;
  797.             min-height: 48px !important;
  798.             max-width: 48px !important;
  799.             max-height: 48px !important;
  800.             border-radius: 50% !important;
  801.             background: rgba(255, 255, 255, 0.95) !important;
  802.             backdrop-filter: blur(10px) !important;
  803.             border: 2px solid rgba(255, 255, 255, 0.8) !important;
  804.             display: flex !important;
  805.             align-items: center !important;
  806.             justify-content: center !important;
  807.             box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important;
  808.             color: #333 !important;
  809.             transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  810.             cursor: pointer !important;
  811.             overflow: hidden !important;
  812.             padding: 0 !important;
  813.             margin: 0 !important;
  814.             font-size: inherit !important;
  815.             font-weight: normal !important;
  816.             line-height: 1 !important;
  817.             text-align: center !important;
  818.             text-decoration: none !important;
  819.             white-space: nowrap !important;
  820.             vertical-align: middle !important;
  821.             font-family: inherit !important;
  822.             text-transform: none !important;
  823.             letter-spacing: normal !important;
  824.             -webkit-tap-highlight-color: transparent !important;
  825.             touch-action: manipulation !important;
  826.         }
  827.         .main-image-nav .main-image-nav-btn::before,
  828.         .main-image-nav-btn::before {
  829.             content: '' !important;
  830.             position: absolute !important;
  831.             top: 50% !important;
  832.             left: 50% !important;
  833.             width: 0 !important;
  834.             height: 0 !important;
  835.             border-radius: 50% !important;
  836.             background: rgba(255, 162, 0, 0.2) !important;
  837.             transform: translate(-50%, -50%) !important;
  838.             transition: width 0.4s ease, height 0.4s ease !important;
  839.         }
  840.         .main-image-nav .main-image-nav-btn:hover::before,
  841.         .main-image-nav-btn:hover::before {
  842.             width: 100% !important;
  843.             height: 100% !important;
  844.         }
  845.         .main-image-nav .main-image-nav-btn:hover,
  846.         .main-image-nav-btn:hover {
  847.             background: #ffa200 !important;
  848.             border-color: #ffa200 !important;
  849.             color: #fff !important;
  850.             transform: scale(1.15) !important;
  851.             box-shadow: 0 6px 20px rgba(255, 162, 0, 0.5) !important;
  852.         }
  853.         .main-image-nav .main-image-nav-btn:active,
  854.         .main-image-nav-btn:active {
  855.             transform: scale(1.05) !important;
  856.         }
  857.         .main-image-nav .main-image-nav-btn i,
  858.         .main-image-nav-btn i {
  859.             position: relative !important;
  860.             z-index: 1 !important;
  861.             font-size: 20px !important;
  862.             transition: transform 0.3s ease !important;
  863.             margin: 0 !important;
  864.             display: block !important;
  865.         }
  866.         .main-image-nav .main-image-nav-btn:hover i,
  867.         .main-image-nav-btn:hover i {
  868.             transform: scale(1.2) !important;
  869.         }
  870.         .main-image-nav .main-image-nav-btn.disabled,
  871.         .main-image-nav-btn.disabled {
  872.             opacity: 0.4 !important;
  873.             cursor: not-allowed !important;
  874.             pointer-events: none !important;
  875.         }
  876.         /* Modal personnalisé pour remplacer les alert */
  877.         .custom-alert-modal .modal-content {
  878.             border-radius: 12px;
  879.             border: none;
  880.             box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  881.         }
  882.         .custom-alert-modal .modal-header {
  883.             border-bottom: none;
  884.             padding: 1.5rem 1.5rem 0.5rem;
  885.             background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  886.             color: white;
  887.             border-radius: 12px 12px 0 0;
  888.         }
  889.         .custom-alert-modal .modal-header .btn-close {
  890.             filter: brightness(0) invert(1);
  891.             opacity: 0.8;
  892.         }
  893.         .custom-alert-modal .modal-header .btn-close:hover {
  894.             opacity: 1;
  895.         }
  896.         .custom-alert-modal .modal-body {
  897.             padding: 1.5rem;
  898.             text-align: center;
  899.         }
  900.         .custom-alert-modal .modal-body .alert-icon {
  901.             font-size: 3rem;
  902.             margin-bottom: 1rem;
  903.         }
  904.         .custom-alert-modal .modal-body .alert-icon.success {
  905.             color: #28a745;
  906.         }
  907.         .custom-alert-modal .modal-body .alert-icon.error {
  908.             color: #dc3545;
  909.         }
  910.         .custom-alert-modal .modal-body .alert-icon.warning {
  911.             color: #ffc107;
  912.         }
  913.         .custom-alert-modal .modal-body .alert-icon.info {
  914.             color: #17a2b8;
  915.         }
  916.         .custom-alert-modal .modal-body .alert-message {
  917.             font-size: 1.1rem;
  918.             color: #333;
  919.             margin-bottom: 1rem;
  920.             line-height: 1.6;
  921.         }
  922.         .custom-alert-modal .modal-footer {
  923.             border-top: none;
  924.             padding: 0.5rem 1.5rem 1.5rem;
  925.             justify-content: center;
  926.         }
  927.         .custom-alert-modal .modal-footer .btn {
  928.             min-width: 120px;
  929.             padding: 0.6rem 1.5rem;
  930.             border-radius: 8px;
  931.             font-weight: 500;
  932.         }
  933.         /* Responsive pour single-product */
  934.         @media(max-width: 991.98px) {
  935.             .product-gallery-modern {
  936.                 flex-direction: column;
  937.             }
  938.             .thumbnails-container {
  939.                 flex-direction: row;
  940.                 width: 100%;
  941.                 overflow-x: auto;
  942.                 padding: 10px 0;
  943.             }
  944.             .thumbnail-item {
  945.                 flex-shrink: 0;
  946.             }
  947.             .main-image-container {
  948.                 width: 100%;
  949.             }
  950.         }
  951.         @media(max-width: 768px) {
  952.             .product-gallery-modern {
  953.                 flex-direction: column;
  954.             }
  955.             .thumbnails-container {
  956.                 width: 100%;
  957.                 flex-direction: row;
  958.                 align-items: center;
  959.             }
  960.             .product-thumbnails {
  961.                 flex-direction: row;
  962.                 width: 100%;
  963.                 max-height: 100px;
  964.                 overflow-x: auto;
  965.                 overflow-y: hidden;
  966.             }
  967.             .thumbnail-item {
  968.                 flex-shrink: 0;
  969.             }
  970.             .thumbnail-nav-btn {
  971.                 margin: 0 5px;
  972.             }
  973.             .thumbnail-nav-up {
  974.                 order: 1;
  975.             }
  976.             .product-thumbnails {
  977.                 order: 2;
  978.             }
  979.             .thumbnail-nav-down {
  980.                 order: 3;
  981.             }
  982.             .product-main-image-wrapper .image-overlay-icons,
  983.             .image-overlay-icons {
  984.                 opacity: 1 !important;
  985.                 top: 10px !important;
  986.                 right: 10px !important;
  987.             }
  988.             .product-main-image-wrapper .icon-btn,
  989.             .image-overlay-icons .icon-btn {
  990.                 width: 40px !important;
  991.                 height: 40px !important;
  992.                 min-width: 40px !important;
  993.                 min-height: 40px !important;
  994.                 max-width: 40px !important;
  995.                 max-height: 40px !important;
  996.             }
  997.             .product-main-image-wrapper .icon-btn i,
  998.             .image-overlay-icons .icon-btn i {
  999.                 font-size: 18px !important;
  1000.             }
  1001.             .product-main-image-wrapper .image-indicators,
  1002.             #image-indicators {
  1003.                 bottom: 15px !important;
  1004.                 padding: 6px 12px !important;
  1005.                 gap: 8px !important;
  1006.             }
  1007.             .product-main-image-wrapper .indicator,
  1008.             #image-indicators .indicator {
  1009.                 width: 10px !important;
  1010.                 height: 10px !important;
  1011.                 min-width: 10px !important;
  1012.                 min-height: 10px !important;
  1013.                 max-width: 10px !important;
  1014.                 max-height: 10px !important;
  1015.             }
  1016.             .product-main-image-wrapper .image-counter,
  1017.             .image-counter {
  1018.                 bottom: 15px !important;
  1019.                 right: 15px !important;
  1020.                 padding: 6px 12px !important;
  1021.                 font-size: 12px !important;
  1022.             }
  1023.             .main-image-nav .main-image-nav-btn,
  1024.             .main-image-nav-btn {
  1025.                 width: 40px !important;
  1026.                 height: 40px !important;
  1027.                 min-width: 40px !important;
  1028.                 min-height: 40px !important;
  1029.                 max-width: 40px !important;
  1030.                 max-height: 40px !important;
  1031.             }
  1032.             .main-image-nav .main-image-nav-btn i,
  1033.             .main-image-nav-btn i {
  1034.                 font-size: 18px !important;
  1035.             }
  1036.         }
  1037.         @media(max-width: 576px) {
  1038.             .product-main-image-wrapper .image-indicators,
  1039.             #image-indicators {
  1040.                 bottom: 10px !important;
  1041.                 padding: 5px 10px !important;
  1042.                 gap: 6px !important;
  1043.             }
  1044.             .product-main-image-wrapper .indicator,
  1045.             #image-indicators .indicator {
  1046.                 width: 8px !important;
  1047.                 height: 8px !important;
  1048.                 min-width: 8px !important;
  1049.                 min-height: 8px !important;
  1050.                 max-width: 8px !important;
  1051.                 max-height: 8px !important;
  1052.             }
  1053.             .product-main-image-wrapper .image-counter,
  1054.             .image-counter {
  1055.                 bottom: 10px !important;
  1056.                 right: 10px !important;
  1057.                 padding: 5px 10px !important;
  1058.                 font-size: 11px !important;
  1059.             }
  1060.             .product-main-image-wrapper .icon-btn,
  1061.             .image-overlay-icons .icon-btn {
  1062.                 width: 36px !important;
  1063.                 height: 36px !important;
  1064.                 min-width: 36px !important;
  1065.                 min-height: 36px !important;
  1066.                 max-width: 36px !important;
  1067.                 max-height: 36px !important;
  1068.             }
  1069.             .product-main-image-wrapper .icon-btn i,
  1070.             .image-overlay-icons .icon-btn i {
  1071.                 font-size: 16px !important;
  1072.             }
  1073.             .main-image-nav .main-image-nav-btn,
  1074.             .main-image-nav-btn {
  1075.                 width: 36px !important;
  1076.                 height: 36px !important;
  1077.                 min-width: 36px !important;
  1078.                 min-height: 36px !important;
  1079.                 max-width: 36px !important;
  1080.                 max-height: 36px !important;
  1081.             }
  1082.             .main-image-nav .main-image-nav-btn i,
  1083.             .main-image-nav-btn i {
  1084.                 font-size: 16px !important;
  1085.             }
  1086.         }
  1087.         
  1088.         /* Styles finaux pour forcer l'affichage des boutons de quantité */
  1089.         .product_count .quantity-controls-wrapper button.quantity-btn,
  1090.         .product_count button.quantity-btn-decrease,
  1091.         .product_count button.quantity-btn-increase {
  1092.             display: flex !important;
  1093.             visibility: visible !important;
  1094.             opacity: 1 !important;
  1095.             position: relative !important;
  1096.             top: auto !important;
  1097.             bottom: auto !important;
  1098.             right: auto !important;
  1099.             left: auto !important;
  1100.             width: 40px !important;
  1101.             height: 40px !important;
  1102.             background: #f8f9fa !important;
  1103.             color: #666 !important;
  1104.             border: none !important;
  1105.             box-shadow: none !important;
  1106.             margin: 0 !important;
  1107.             padding: 0 !important;
  1108.         }
  1109.         
  1110.         .product_count .quantity-controls-wrapper {
  1111.             display: flex !important;
  1112.             visibility: visible !important;
  1113.             opacity: 1 !important;
  1114.         }
  1115.     </style>
  1116. {% endblock %}
  1117. {% block body %}
  1118.     <!-- Start Banner Area -->
  1119.     <section class="banner-area organic-breadcrumb">
  1120.         <div class="container">
  1121.             <div class="breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end">
  1122.                 <div class="col-first">
  1123.                     <h1>Product Details Page</h1>
  1124.                     <nav class="d-flex align-items-center">
  1125.                         <a href="index.html">Home<span class="lnr lnr-arrow-right"></span>
  1126.                         </a>
  1127.                         <a href="#">Shop<span class="lnr lnr-arrow-right"></span>
  1128.                         </a>
  1129.                         <a href="single-product.html">product-details</a>
  1130.                     </nav>
  1131.                 </div>
  1132.             </div>
  1133.         </div>
  1134.     </section>
  1135.     <!-- End Banner Area -->
  1136.     <!--================Single Product Area =================-->
  1137.     <div class="product_image_area">
  1138.         <div class="container">
  1139.             <div class="row s_product_inner">
  1140.                 <div class="col-lg-6">
  1141.                     <div
  1142.                         class="product-gallery-modern">
  1143.                         <!-- Colonne de miniatures à gauche avec navigation -->
  1144.                         <div class="thumbnails-container">
  1145.                             <button class="thumbnail-nav-btn thumbnail-nav-up" id="thumbnail-nav-up" title="Image précédente">
  1146.                                 <i class="lnr lnr-chevron-up"></i>
  1147.                             </button>
  1148.                             <div class="product-thumbnails" id="product-thumbnails">
  1149.                                 <div class="thumbnails-wrapper" id="thumbnails-wrapper">
  1150.                                     {% set allProductImages = product.images|default([]) %}
  1151.                                     {% if product.variants is defined %}
  1152.                                         {% for variant in product.variants %}
  1153.                                             {% if variant.isActive and variant.images is defined %}
  1154.                                                 {% for variantImg in variant.images %}
  1155.                                                     {% set allProductImages = allProductImages|merge([variantImg]) %}
  1156.                                                 {% endfor %}
  1157.                                             {% endif %}
  1158.                                         {% endfor %}
  1159.                                     {% endif %}
  1160.                                     {% if allProductImages|length > 0 %}
  1161.                                         {% for img in allProductImages %}
  1162.                                             <div class="thumbnail-item {% if loop.first %}active permanently-active{% endif %}" data-image="{{ asset(img) }}" data-index="{{ loop.index0 }}">
  1163.                                                 <img src="{{ asset(img) }}" alt="{{ product.name }} - Image {{ loop.index }}">
  1164.                                             </div>
  1165.                                         {% endfor %}
  1166.                                     {% else %}
  1167.                                         <div class="thumbnail-item active permanently-active" data-image="{{ asset('ui/img/category/s-p1.jpg') }}" data-index="0">
  1168.                                             <img src="{{ asset('ui/img/category/s-p1.jpg') }}" alt="{{ product.name }}">
  1169.                                         </div>
  1170.                                     {% endif %}
  1171.                                 </div>
  1172.                             </div>
  1173.                             <button class="thumbnail-nav-btn thumbnail-nav-down" id="thumbnail-nav-down" title="Image suivante">
  1174.                                 <i class="lnr lnr-chevron-down"></i>
  1175.                             </button>
  1176.                         </div>
  1177.                         <!-- Grande image principale -->
  1178.                         <div class="product-main-image-wrapper">
  1179.                             <div class="product-main-image">
  1180.                                 {% set allProductImages = product.images|default([]) %}
  1181.                                 {% if product.variants is defined %}
  1182.                                     {% for variant in product.variants %}
  1183.                                         {% if variant.isActive and variant.images is defined %}
  1184.                                             {% for variantImg in variant.images %}
  1185.                                                 {% set allProductImages = allProductImages|merge([variantImg]) %}
  1186.                                             {% endfor %}
  1187.                                         {% endif %}
  1188.                                     {% endfor %}
  1189.                                 {% endif %}
  1190.                                 {% if allProductImages|length > 0 %}
  1191.                                     <img id="main-product-image" src="{{ asset(allProductImages[0]) }}" alt="{{ product.name }}">
  1192.                                 {% else %}
  1193.                                     <img id="main-product-image" src="{{ asset('ui/img/category/s-p1.jpg') }}" alt="{{ product.name }}">
  1194.                                 {% endif %}
  1195.                                 <!-- Overlay avec icônes -->
  1196.                                 <div class="image-overlay-icons">
  1197.                                     <button class="icon-btn zoom-btn" onclick="openImageZoom()" title="Agrandir l'image">
  1198.                                         <i class="lnr lnr-magnifier"></i>
  1199.                                     </button>
  1200.                                     <button class="icon-btn favorite-btn" {% if app.user %} onclick="toggleWishlist({{ product.id }}, this); return false;" {% else %} onclick="showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;" {% endif %} title="Ajouter aux favoris" data-product-id="{{ product.id }}">
  1201.                                         <i class="lnr lnr-heart"></i>
  1202.                                     </button>
  1203.                                 </div>
  1204.                                 <!-- Navigation sur l'image principale -->
  1205.                                 <div class="main-image-nav">
  1206.                                     <button type="button" class="main-image-nav-btn" id="main-image-prev" title="Image précédente">
  1207.                                         <i class="lnr lnr-chevron-left"></i>
  1208.                                     </button>
  1209.                                     <button type="button" class="main-image-nav-btn" id="main-image-next" title="Image suivante">
  1210.                                         <i class="lnr lnr-chevron-right"></i>
  1211.                                     </button>
  1212.                                 </div>
  1213.                             </div>
  1214.                             <!-- Indicateur d'image active -->
  1215.                             <div class="image-counter">
  1216.                                 <span id="current-image-index">1</span>
  1217.                                 /
  1218.                                 {% set allProductImages = product.images|default([]) %}
  1219.                                 {% if product.variants is defined %}
  1220.                                     {% for variant in product.variants %}
  1221.                                         {% if variant.isActive and variant.images is defined %}
  1222.                                             {% for variantImg in variant.images %}
  1223.                                                 {% set allProductImages = allProductImages|merge([variantImg]) %}
  1224.                                             {% endfor %}
  1225.                                         {% endif %}
  1226.                                     {% endfor %}
  1227.                                 {% endif %}
  1228.                                 <span id="total-images">{{ (allProductImages|length) ?: 1 }}</span>
  1229.                             </div>
  1230.                             <!-- Indicateurs d'images (points) -->
  1231.                             <div class="image-indicators" id="image-indicators">
  1232.                                 {% set allProductImages = product.images|default([]) %}
  1233.                                 {% if product.variants is defined %}
  1234.                                     {% for variant in product.variants %}
  1235.                                         {% if variant.isActive and variant.images is defined %}
  1236.                                             {% for variantImg in variant.images %}
  1237.                                                 {% set allProductImages = allProductImages|merge([variantImg]) %}
  1238.                                             {% endfor %}
  1239.                                         {% endif %}
  1240.                                     {% endfor %}
  1241.                                 {% endif %}
  1242.                                 {% for i in 0..((allProductImages|length) ?: 1) - 1 %}
  1243.                                 <button class="indicator {{ i == 0 ? 'active' : '' }}" data-index="{{ i }}" title="Image {{ i + 1 }}"></button>
  1244.                                 {% endfor %}
  1245.                             </div>
  1246.                         </div>
  1247.                     </div>
  1248.                 </div>
  1249.                 <div class="col-lg-5 offset-lg-1">
  1250.                     <div class="s_product_text">
  1251.                         <h3>{{ product.name }}</h3>
  1252.                         <h2>
  1253.                             <span id="main-unit-price">{{ product.price|number_format(2, '.', ' ') }}</span>
  1254.                             HTG
  1255.                         </h2>
  1256.                         <ul class="list">
  1257.                             <li>
  1258.                                 <a class="active" href="#">
  1259.                                     <span>Category</span>
  1260.                                     :
  1261.                                     {{ product.category.name }}</a>
  1262.                             </li>
  1263.                             <li>
  1264.                                 <a href="#">
  1265.                                     <span>Disponibilité</span>
  1266.                                     :
  1267.                                     {{ product.stockStatus }}</a>
  1268.                             </li>
  1269.                         </ul>
  1270.                         <div class="product-description-preview">{{ product.description|striptags|length > 150 ? product.description|striptags|slice(0, 150) ~ '...' : product.description|striptags }}</div>
  1271.                         <!-- Sélection de variantes (Couleurs, Tailles, etc.) -->
  1272.                         {% if product.variants is defined and product.variants|length > 0 %}
  1273.                             <div class="product-variants mb-4" id="product-variants" data-product-id="{{ product.id }}">
  1274.                                 {% set variantAttributes = {} %}
  1275.                                 {% for variant in product.variants %}
  1276.                                     {% if variant.isActive %}
  1277.                                         {% for attrValue in variant.attributeValues %}
  1278.                                             {% set attrName = attrValue.attribute.name|lower %}
  1279.                                             {% if variantAttributes[attrName] is not defined %}
  1280.                                                 {% set variantAttributes = variantAttributes|merge({(attrName): {'attribute': attrValue.attribute, 'values': []}}) %}
  1281.                                             {% endif %}
  1282.                                             {% if attrValue not in variantAttributes[attrName].values %}
  1283.                                                 {% set variantAttributes = variantAttributes|merge({(attrName): variantAttributes[attrName]|merge({'values': variantAttributes[attrName].values|merge([attrValue])})}) %}
  1284.                                             {% endif %}
  1285.                                         {% endfor %}
  1286.                                     {% endif %}
  1287.                                 {% endfor %}
  1288.                                 {% for attrName, attrData in variantAttributes %}
  1289.                                     <div class="variant-selector mb-3" data-attribute="{{ attrData.attribute.slug }}">
  1290.                                         <label class="variant-label mb-2">
  1291.                                             <strong>{{ attrData.attribute.name }}:</strong>
  1292.                                             <span class="selected-variant-value" id="selected-{{ attrData.attribute.slug }}"></span>
  1293.                                         </label>
  1294.                                         <div class="variant-options d-flex flex-wrap gap-2">
  1295.                                             {% for attrValue in attrData.values %}
  1296.                                                 {% if attrValue.isActive %}
  1297.                                                     {% if attrData.attribute.type == 'color' %}
  1298.                                                         <button type="button" 
  1299.                                                                 class="variant-option variant-color-option {% if loop.first %}selected{% endif %}" 
  1300.                                                                 data-value-id="{{ attrValue.id }}"
  1301.                                                                 data-value="{{ attrValue.value }}"
  1302.                                                                 data-attribute="{{ attrData.attribute.slug }}"
  1303.                                                                 title="{{ attrValue.value }}"
  1304.                                                                 style="{% if attrValue.colorCode %}background-color: {{ attrValue.colorCode }};{% endif %} width: 40px; height: 40px; border-radius: 50%; border: 2px solid {% if loop.first %}#007bff{% else %}#ddd{% endif %}; cursor: pointer; position: relative;">
  1305.                                                             {% if loop.first %}
  1306.                                                                 <span class="check-icon" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: {% if attrValue.colorCode and attrValue.colorCode|is_dark_color %}white{% else %}black{% endif %}; font-size: 18px;">✓</span>
  1307.                                                             {% endif %}
  1308.                                                         </button>
  1309.                                                     {% else %}
  1310.                                                         <button type="button" 
  1311.                                                                 class="variant-option variant-text-option btn btn-outline-secondary {% if loop.first %}active{% endif %}" 
  1312.                                                                 data-value-id="{{ attrValue.id }}"
  1313.                                                                 data-value="{{ attrValue.value }}"
  1314.                                                                 data-attribute="{{ attrData.attribute.slug }}">
  1315.                                                             {{ attrValue.value }}
  1316.                                                         </button>
  1317.                                                     {% endif %}
  1318.                                                 {% endif %}
  1319.                                             {% endfor %}
  1320.                                         </div>
  1321.                                     </div>
  1322.                                 {% endfor %}
  1323.                                 <!-- Affichage du stock et du prix de la variante sélectionnée -->
  1324.                                 <div class="variant-info mt-3 p-3 bg-light rounded" id="variant-info" style="display: none;">
  1325.                                     <div class="d-flex justify-content-between align-items-center mb-2">
  1326.                                         <span><strong>Prix:</strong></span>
  1327.                                         <span class="variant-price text-primary fw-bold" id="variant-price"></span>
  1328.                                     </div>
  1329.                                     <div class="d-flex justify-content-between align-items-center">
  1330.                                         <span><strong>Stock:</strong></span>
  1331.                                         <span class="variant-stock" id="variant-stock"></span>
  1332.                                     </div>
  1333.                                     <input type="hidden" id="selected-variant-id" value="">
  1334.                                     <input type="hidden" id="selected-variant-sku" value="">
  1335.                                 </div>
  1336.                             </div>
  1337.                         {% endif %}
  1338.                         <!-- Informations sur la boutique -->
  1339.                         <div class="shop-info mb-3">
  1340.                             <small class="text-muted">
  1341.                                 <i class="lnr lnr-store"></i>
  1342.                                 Vendu par :
  1343.                                 {% if product.shop is defined and product.shop %}
  1344.                                     <a href="{{ path('ui_shop_show', {'slug': product.shop.slug}) }}" class="shop-link">
  1345.                                         {{ product.shop.name }}
  1346.                                     </a>
  1347.                                 {% else %}
  1348.                                     <span class="text-muted">Boutique inconnue</span>
  1349.                                 {% endif %}
  1350.                             </small>
  1351.                         </div>
  1352.                         <div class="product_count">
  1353.                             <label for="qty">Quantité :</label>
  1354.                             <div class="quantity-controls-wrapper">
  1355.                                 <button onclick="var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst > 1 ) result.value--;return false;" class="quantity-btn quantity-btn-decrease" type="button" title="Diminuer">
  1356.                                     <span class="quantity-icon">−</span>
  1357.                                 </button>
  1358.                                 <input type="text" name="qty" id="sst" maxlength="12" value="1" title="Quantité:" class="input-text qty quantity-input" max="{{ product.stock ?? 99 }}">
  1359.                                 <button onclick="var result = document.getElementById('sst'); var sst = result.value; if( !isNaN( sst ) && sst < {{ product.stock ?? 99 }}) result.value++;return false;" class="quantity-btn quantity-btn-increase" type="button" title="Augmenter">
  1360.                                     <span class="quantity-icon">+</span>
  1361.                                 </button>
  1362.                             </div>
  1363.                         </div>
  1364.                         <input
  1365.                         type="hidden" id="unit-price-input" value="{{ product.price|number_format(2, '.', '') }}"/>
  1366.                         {# Prix de gros (bulk pricing) - Affiché uniquement si activé par le vendeur #}
  1367.                         {% if product.hasTierPricing and product.tierPrices is defined and product.tierPrices|length > 0 %}
  1368.                         {% set tierPrices = [] %}
  1369.                             {% for tier in product.tierPrices %}
  1370.                                 {% set tierPrices = tierPrices|merge([{ 'min': tier.min, 'price': tier.price }]) %}
  1371.                             {% endfor %}
  1372.                             <div id="bulk-pricing" class="mt-4 p-3 bg-light rounded" data-tiers='{{ tierPrices|json_encode|e('html_attr') }}'>
  1373.                                 <div class="d-flex align-items-center mb-3">
  1374.                                     <i class="lnr lnr-tag text-primary me-2" style="font-size: 1.2rem;"></i>
  1375.                                     <strong class="me-2">Prix de gros disponible</strong>
  1376.                                     <span id="unit-price-value" class="badge bg-primary ms-2 text-white p-2">{{ product.price|number_format(2, '.', ' ') }} HTG</span>
  1377.                             </div>
  1378.                             <div class="table-responsive">
  1379.                                     <table class="table table-sm mb-3" style="border:1px solid #dee2e6; background: white;">
  1380.                                         <thead class="table-primary">
  1381.                                         <tr>
  1382.                                                 <th style="font-weight: 600;">Quantité minimale</th>
  1383.                                                 <th style="font-weight: 600;">Prix unitaire (HTG)</th>
  1384.                                                 <th style="font-weight: 600;">Économie</th>
  1385.                                         </tr>
  1386.                                     </thead>
  1387.                                     <tbody id="bulk-tier-rows">
  1388.                                         {% for tier in tierPrices|sort((a,b)=> a.min <=> b.min) %}
  1389.                                                 {% set savings = ((product.price - tier.price) / product.price * 100)|round(1) %}
  1390.                                                 <tr data-min="{{ tier.min }}" class="{% if loop.first %}table-active{% endif %}">
  1391.                                                     <td><strong>≥ {{ tier.min }}</strong></td>
  1392.                                                     <td><strong class="text-primary">{{ tier.price|number_format(2, '.', ' ') }}</strong></td>
  1393.                                                     <td>
  1394.                                                         {% if savings > 0 %}
  1395.                                                             <span class="badge bg-success text-white p-2">-{{ savings }}%</span>
  1396.                                                         {% else %}
  1397.                                                             <span class="text-muted">-</span>
  1398.                                                         {% endif %}
  1399.                                                     </td>
  1400.                                             </tr>
  1401.                                         {% endfor %}
  1402.                                     </tbody>
  1403.                                 </table>
  1404.                             </div>
  1405.                                 <div class="d-flex flex-wrap gap-3 align-items-baseline mb-2 p-2 bg-white rounded">
  1406.                                 <div>
  1407.                                     <strong>Total:</strong>
  1408.                                         <span id="total-price-value" class="text-primary fs-5">{{ product.price|number_format(2, '.', ' ') }}</span>
  1409.                                     HTG
  1410.                                 </div>
  1411.                                 <div class="text-success">
  1412.                                     <strong>Économies:</strong>
  1413.                                     <span id="savings-amount">0.00</span>
  1414.                                     HTG
  1415.                                                                                                                                                                                     (<span id="savings-percent">0</span>%)
  1416.                                 </div>
  1417.                             </div>
  1418.                                 <small class="text-muted d-block mt-2">
  1419.                                     <i class="lnr lnr-info-circle me-1"></i>
  1420.                                     Le prix et le total s'adaptent automatiquement selon la quantité sélectionnée.
  1421.                                 </small>
  1422.                         </div>
  1423.                         {% endif %}
  1424.                         <!-- Stock disponible -->
  1425.                         {% if product.stock is defined %}
  1426.                             <small class="text-muted">Stock disponible :
  1427.                                 {{ product.stock }}
  1428.                                 unités</small>
  1429.                         {% endif %}
  1430.                         <!-- Statistiques du produit -->
  1431.                         {% if productStats is defined %}
  1432.                             <div class="product-stats mt-3">
  1433.                                 <div class="row text-center">
  1434.                                     <div class="col-4">
  1435.                                         <div class="stat-item">
  1436.                                             <span class="stat-number">{{ productStats.totalViews }}</span>
  1437.                                             <small class="stat-label">Vues</small>
  1438.                                         </div>
  1439.                                     </div>
  1440.                                     <div class="col-4">
  1441.                                         <div class="stat-item">
  1442.                                             <span class="stat-number">{{ productStats.salesCount }}</span>
  1443.                                             <small class="stat-label">Ventes</small>
  1444.                                         </div>
  1445.                                     </div>
  1446.                                     <div class="col-4">
  1447.                                         <div class="stat-item">
  1448.                                             <span class="stat-number">{{ productStats.conversionRate }}%</span>
  1449.                                             <small class="stat-label">Conversion</small>
  1450.                                         </div>
  1451.                                     </div>
  1452.                                 </div>
  1453.                             </div>
  1454.                         {% endif %}
  1455.                         <div class="card_area d-flex align-items-center">
  1456.                             <a class="primary-btn btn-add-to-cart" href="javascript:void(0);" id="add-to-cart-btn" data-product-id="{{ product.id }}" data-qty="1">
  1457.                                 <i class="lnr lnr-cart"></i>
  1458.                                 Ajouter au panier
  1459.                             </a>
  1460.                             <a class="icon_btn wishlist-btn" href="#" title="Ajouter aux favoris" data-product-id="{{ product.id }}" {% if app.user %} onclick="toggleWishlist({{ product.id }}, this); return false;" {% else %} onclick="showCustomAlert('Vous devez être connecté pour ajouter aux favoris', 'warning'); return false;" {% endif %}>
  1461.                                 <i class="lnr lnr-heart"></i>
  1462.                             </a>
  1463.                             <a class="icon_btn comparison-btn" href="#" title="Comparer" data-product-id="{{ product.id }}" {% if app.user %} onclick="toggleComparison({{ product.id }}, this); return false;" {% else %} onclick="showCustomAlert('Vous devez être connecté pour comparer des produits', 'warning'); return false;" {% endif %}>
  1464.                                 <i class="lnr lnr-sync"></i>
  1465.                             </a>
  1466.                         </div>
  1467.                         <!-- Message de confirmation -->
  1468.                         <div id="cart-message" class="alert alert-success mt-3" style="display: none;">
  1469.                             <i class="lnr lnr-checkmark-circle"></i>
  1470.                             Produit ajouté au panier avec succès !
  1471.                         </div>
  1472.                         <!-- ... existing code ... -->
  1473.                     </div>
  1474.                 </div>
  1475.             </div>
  1476.         </div>
  1477.     </div>
  1478.     <!--================End Single Product Area =================-->
  1479.     <!--================Product Description Area =================-->
  1480.     <section class="product_description_area">
  1481.         <div class="container">
  1482.             <ul class="nav nav-tabs" id="myTab" role="tablist">
  1483.                 <li class="nav-item">
  1484.                     <a class="nav-link active" id="home-tab" data-bs-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Description</a>
  1485.                 </li>
  1486.                 <li class="nav-item">
  1487.                     <a class="nav-link" id="profile-tab" data-bs-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Spécifications</a>
  1488.                 </li>
  1489.                 <li class="nav-item">
  1490.                     <a class="nav-link" id="contact-tab" data-bs-toggle="tab" href="#contact" role="tab" aria-controls="contact" aria-selected="false">Commentaires</a>
  1491.                 </li>
  1492.                 <li class="nav-item">
  1493.                     <a class="nav-link" id="review-tab" data-bs-toggle="tab" href="#review" role="tab" aria-controls="review" aria-selected="false">Avis ({{ product.reviewCount ?? 0 }})</a>
  1494.                 </li>
  1495.             </ul>
  1496.             <div class="tab-content" id="myTabContent">
  1497.                 <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
  1498.                     <div class="description">
  1499.                         {% if product.description %}
  1500.                             <div class="product-description-content">
  1501.                                 {{ product.description|raw }}
  1502.                             </div>
  1503.                         {% else %}
  1504.                             <p class="text-muted">Aucune description disponible pour ce produit.</p>
  1505.                         {% endif %}
  1506.                     </div>
  1507.                 </div>
  1508.                 <div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">
  1509.                     <div class="specification-table">
  1510.                         {% if product.weight or product.length or product.width or product.height or product.attributes %}
  1511.                             <div class="table-responsive">
  1512.                                 <table class="table">
  1513.                                     <tbody>
  1514.                                         {% if product.sku %}
  1515.                                             <tr>
  1516.                                                 <td>
  1517.                                                     <h5>Référence (SKU)</h5>
  1518.                                                 </td>
  1519.                                                 <td>
  1520.                                                     <h5>{{ product.sku }}</h5>
  1521.                                                 </td>
  1522.                                             </tr>
  1523.                                         {% endif %}
  1524.                                         {% if product.barcode %}
  1525.                                             <tr>
  1526.                                                 <td>
  1527.                                                     <h5>Code-barres</h5>
  1528.                                                 </td>
  1529.                                                 <td>
  1530.                                                     <h5>{{ product.barcode }}</h5>
  1531.                                                 </td>
  1532.                                             </tr>
  1533.                                         {% endif %}
  1534.                                         {% if product.brand %}
  1535.                                             <tr>
  1536.                                                 <td>
  1537.                                                     <h5>Marque</h5>
  1538.                                                 </td>
  1539.                                                 <td>
  1540.                                                     <h5>{{ product.brand.name }}</h5>
  1541.                                                 </td>
  1542.                                             </tr>
  1543.                                         {% endif %}
  1544.                                         {% if product.category %}
  1545.                                             <tr>
  1546.                                                 <td>
  1547.                                                     <h5>Catégorie</h5>
  1548.                                                 </td>
  1549.                                                 <td>
  1550.                                                     <h5>{{ product.category.name }}</h5>
  1551.                                                 </td>
  1552.                                             </tr>
  1553.                                         {% endif %}
  1554.                                         {% if product.weight %}
  1555.                                             <tr>
  1556.                                                 <td>
  1557.                                                     <h5>Poids</h5>
  1558.                                                 </td>
  1559.                                                 <td>
  1560.                                                     <h5>{{ product.weight }}
  1561.                                                         kg</h5>
  1562.                                                 </td>
  1563.                                             </tr>
  1564.                                         {% endif %}
  1565.                                         {% if product.length or product.width or product.height %}
  1566.                                             <tr>
  1567.                                                 <td>
  1568.                                                     <h5>Dimensions</h5>
  1569.                                                 </td>
  1570.                                                 <td>
  1571.                                                     <h5>
  1572.                                                         {% if product.length %}
  1573.                                                             {{ product.length }}cm
  1574.                                                         {% endif %}
  1575.                                                         {% if product.width %}
  1576.                                                             ×
  1577.                                                             {{ product.width }}cm
  1578.                                                         {% endif %}
  1579.                                                         {% if product.height %}
  1580.                                                             ×
  1581.                                                             {{ product.height }}cm
  1582.                                                         {% endif %}
  1583.                                                     </h5>
  1584.                                                 </td>
  1585.                                             </tr>
  1586.                                         {% endif %}
  1587.                                         {% if product.stock is not null %}
  1588.                                             <tr>
  1589.                                                 <td>
  1590.                                                     <h5>Stock disponible</h5>
  1591.                                                 </td>
  1592.                                                 <td>
  1593.                                                     <h5>{{ product.stock }}
  1594.                                                         unité(s)</h5>
  1595.                                                 </td>
  1596.                                             </tr>
  1597.                                         {% endif %}
  1598.                                         {% if product.details is defined and product.details|length > 0 %}
  1599.                                             {% for detail in product.details %}
  1600.                                                 {% if detail.isActive %}
  1601.                                                     <tr>
  1602.                                                         <td>
  1603.                                                             <h5>{{ detail.label }}</h5>
  1604.                                                         </td>
  1605.                                                         <td>
  1606.                                                             <h5>{{ detail.value|raw }}</h5>
  1607.                                                         </td>
  1608.                                                     </tr>
  1609.                                                 {% endif %}
  1610.                                             {% endfor %}
  1611.                                         {% endif %}
  1612.                                         {% if product.stockStatus %}
  1613.                                             <tr>
  1614.                                                 <td>
  1615.                                                     <h5>Statut</h5>
  1616.                                                 </td>
  1617.                                                 <td>
  1618.                                                     <h5>
  1619.                                                         {% if product.stockStatus == 'in_stock' %}
  1620.                                                             <span class="badge bg-success">En stock</span>
  1621.                                                         {% elseif product.stockStatus == 'out_of_stock' %}
  1622.                                                             <span class="badge bg-danger">Rupture de stock</span>
  1623.                                                         {% elseif product.stockStatus == 'backorder' %}
  1624.                                                             <span class="badge bg-warning">Sur commande</span>
  1625.                                                         {% else %}
  1626.                                                             {{ product.stockStatus }}
  1627.                                                         {% endif %}
  1628.                                                     </h5>
  1629.                                                 </td>
  1630.                                             </tr>
  1631.                                         {% endif %}
  1632.                                         {% if product.isDigital %}
  1633.                                             <tr>
  1634.                                                 <td>
  1635.                                                     <h5>Type</h5>
  1636.                                                 </td>
  1637.                                                 <td>
  1638.                                                     <h5>
  1639.                                                         <span class="badge bg-info">Produit digital</span>
  1640.                                                     </h5>
  1641.                                                 </td>
  1642.                                             </tr>
  1643.                                         {% endif %}
  1644.                                         {% if product.attributes is not empty %}
  1645.                                             {% for key, value in product.attributes %}
  1646.                                                 <tr>
  1647.                                                     <td>
  1648.                                                         <h5>{{ key|replace({'_': ' '})|title }}</h5>
  1649.                                                     </td>
  1650.                                                     <td>
  1651.                                                         <h5>{{ value }}</h5>
  1652.                                                     </td>
  1653.                                                 </tr>
  1654.                                             {% endfor %}
  1655.                                         {% endif %}
  1656.                                     </tbody>
  1657.                                 </table>
  1658.                             </div>
  1659.                         {% else %}
  1660.                             <p class="text-muted">Aucune spécification disponible pour ce produit.</p>
  1661.                         {% endif %}
  1662.                     </div>
  1663.                 </div>
  1664.                 <div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab">
  1665.                     <div class="comment-wrapper">
  1666.                         <div class="alert alert-info">
  1667.                             <i class="lnr lnr-info-circle"></i>
  1668.                             La fonctionnalité de commentaires sera bientôt disponible. Vous pourrez laisser des commentaires et poser des questions sur ce produit.
  1669.                         </div>
  1670.                         {% if app.user %}
  1671.                             <div class="review_box mt-4">
  1672.                                 <h4>Poser une question</h4>
  1673.                                 <form class="row contact_form" method="post" novalidate="novalidate">
  1674.                                     <div class="col-md-12">
  1675.                                         <div class="form-group">
  1676.                                             <textarea class="form-control" name="comment" id="comment" rows="3" placeholder="Votre question ou commentaire..." required></textarea>
  1677.                                         </div>
  1678.                                     </div>
  1679.                                     <div class="col-md-12 text-right">
  1680.                                         <button type="submit" class="btn primary-btn">Envoyer</button>
  1681.                                     </div>
  1682.                                 </form>
  1683.                             </div>
  1684.                         {% else %}
  1685.                             <p class="text-center">
  1686.                                 <a href="{{ path('ui_app_login') }}" class="primary-btn">Connectez-vous</a>
  1687.                                 pour poser une question ou laisser un commentaire.
  1688.                             </p>
  1689.                         {% endif %}
  1690.                     </div>
  1691.                 </div>
  1692.                 <div class="tab-pane fade" id="review" role="tabpanel" aria-labelledby="review-tab">
  1693.                     <div class="review-wrapper">
  1694.                         <div class="row">
  1695.                             <div class="col-lg-6">
  1696.                                 <div class="row total_rate">
  1697.                                     <div class="col-6">
  1698.                                         <div class="box_total">
  1699.                                             <h5>Note globale</h5>
  1700.                                             <h4>{{ product.averageRating ? product.averageRating|number_format(1, '.', ',') : '0.0' }}</h4>
  1701.                                             <h6>({{ product.reviewCount ?? 0 }}
  1702.                                                 Avis)</h6>
  1703.                                         </div>
  1704.                                     </div>
  1705.                                     <div class="col-6">
  1706.                                         <div class="rating_list">
  1707.                                             <h3>Basé sur
  1708.                                                 {{ product.reviewCount ?? 0 }}
  1709.                                                 avis</h3>
  1710.                                             <div class="rating-summary">
  1711.                                                 {% if product.averageRating %}
  1712.                                                     <div class="rating-display mb-3">
  1713.                                                         {% for i in 1..5 %}
  1714.                                                             <i class="fa fa-star{% if i <= product.averageRating %}{% else %}-o{% endif %}" style="color: #ffa200; font-size: 1.2rem;"></i>
  1715.                                                         {% endfor %}
  1716.                                                         <span class="ms-2" style="font-size: 1.1rem; font-weight: bold;">{{ product.averageRating|number_format(1, '.', ',') }}
  1717.                                                             / 5.0</span>
  1718.                                                     </div>
  1719.                                                 {% else %}
  1720.                                                     <p class="text-muted">Aucune note disponible</p>
  1721.                                                 {% endif %}
  1722.                                             </div>
  1723.                                         </div>
  1724.                                     </div>
  1725.                                 </div>
  1726.                                 <div class="review_list">
  1727.                                     {% if product.reviewCount and product.reviewCount > 0 %}
  1728.                                         <div class="alert alert-info">
  1729.                                             <i class="lnr lnr-info-circle"></i>
  1730.                                             Le système d'affichage détaillé des avis sera disponible prochainement. 
  1731.                                                                                                                                                                                                                                                                         Note actuelle :
  1732.                                             {{ product.averageRating|number_format(1, '.', ',') }}
  1733.                                             / 5.0 basée sur
  1734.                                             {{ product.reviewCount }}
  1735.                                             avis.
  1736.                                         </div>
  1737.                                     {% else %}
  1738.                                         <div class="alert alert-info">
  1739.                                             <i class="lnr lnr-info-circle"></i>
  1740.                                             Aucun avis pour ce produit pour le moment. Soyez le premier à laisser un avis après votre achat !
  1741.                                         </div>
  1742.                                     {% endif %}
  1743.                                 </div>
  1744.                             </div>
  1745.                             <div class="col-lg-6">
  1746.                                 <div class="review_box">
  1747.                                     <h4>Laisser un avis</h4>
  1748.                                     {% if app.user %}
  1749.                                         <p class="text-muted">Vous pouvez laisser un avis après avoir acheté ce produit.</p>
  1750.                                         <form class="row contact_form" method="post" novalidate="novalidate">
  1751.                                             <div class="col-md-12">
  1752.                                                 <div class="form-group">
  1753.                                                     <label>Votre note</label>
  1754.                                                     <div class="rating-input mb-3" style="display: flex; gap: 5px; flex-direction: row-reverse; justify-content: flex-end;">
  1755.                                                         {% for i in 5..1 %}
  1756.                                                             <input type="radio" name="rating" id="rating{{ i }}" value="{{ i }}" required style="display: none;">
  1757.                                                             <label for="rating{{ i }}" class="star-label" style="cursor: pointer;">
  1758.                                                                 <i class="fa fa-star-o" style="color: #ccc; font-size: 1.5rem;"></i>
  1759.                                                             </label>
  1760.                                                         {% endfor %}
  1761.                                                     </div>
  1762.                                                 </div>
  1763.                                             </div>
  1764.                                             <div class="col-md-12">
  1765.                                                 <div class="form-group">
  1766.                                                     <textarea class="form-control" name="review" id="review" rows="5" placeholder="Votre avis..." required onfocus="this.placeholder = ''" onblur="this.placeholder = 'Votre avis...'"></textarea>
  1767.                                                 </div>
  1768.                                             </div>
  1769.                                             <div class="col-md-12 text-right">
  1770.                                                 <button type="submit" class="btn primary-btn">Publier l'avis</button>
  1771.                                             </div>
  1772.                                         </form>
  1773.                                     {% else %}
  1774.                                         <p class="text-center">
  1775.                                             <a href="{{ path('ui_app_login') }}" class="primary-btn">Connectez-vous</a>
  1776.                                             pour laisser un avis sur ce produit.
  1777.                                         </p>
  1778.                                     {% endif %}
  1779.                                 </div>
  1780.                             </div>
  1781.                         </div>
  1782.                     </div>
  1783.                 </div>
  1784.             </div>
  1785.         </div>
  1786.     </section>
  1787.     <!--================End Product Description Area =================-->
  1788.     <script>
  1789.         // Données des variantes du produit
  1790.         const productVariants = {{ product.variants|length > 0 ? product.variants|map(v => {
  1791.             'id': v.id,
  1792.             'sku': v.sku,
  1793.             'price': v.price,
  1794.             'compareAtPrice': v.compareAtPrice,
  1795.             'stock': v.stock,
  1796.             'stockStatus': v.stockStatus,
  1797.             'isActive': v.isActive,
  1798.             'images': v.images,
  1799.             'attributeValues': v.attributeValues|map(av => {
  1800.                 'attributeId': av.attribute.id,
  1801.                 'attributeSlug': av.attribute.slug,
  1802.                 'valueId': av.id,
  1803.                 'value': av.value
  1804.             })|to_array
  1805.         })|json_encode|raw : '[]' }};
  1806.         // Gestion de la sélection de variantes
  1807.         let selectedAttributes = {};
  1808.         let currentVariant = null;
  1809.         // Initialiser les onglets Bootstrap
  1810. document.addEventListener('DOMContentLoaded', function () { // Activer les onglets Bootstrap 5
  1811. const triggerTabList = document.querySelectorAll('#myTab button[data-bs-toggle="tab"]');
  1812. triggerTabList.forEach(triggerEl => {
  1813. const tabTrigger = new bootstrap.Tab(triggerEl);
  1814. triggerEl.addEventListener('click', event => {
  1815. event.preventDefault();
  1816. tabTrigger.show();
  1817. });
  1818. });
  1819. // Gestion des étoiles pour les avis
  1820. const ratingInputs = document.querySelectorAll('.rating-input input[type="radio"]');
  1821. const starLabels = document.querySelectorAll('.rating-input .star-label');
  1822. ratingInputs.forEach((input, index) => {
  1823. input.addEventListener('change', function () {
  1824. const rating = parseInt(this.value);
  1825. starLabels.forEach((label) => { // Trouver l'input associé à ce label
  1826. const labelInput = label.previousElementSibling;
  1827. if (labelInput && labelInput.type === 'radio') {
  1828. const starValue = parseInt(labelInput.value);
  1829. const star = label.querySelector('i');
  1830. if (starValue <= rating) {
  1831. star.style.color = '#ffa200';
  1832. star.classList.remove('fa-star-o');
  1833. star.classList.add('fa-star');
  1834. } else {
  1835. star.style.color = '#ccc';
  1836. star.classList.remove('fa-star');
  1837. star.classList.add('fa-star-o');
  1838. }
  1839. }
  1840. });
  1841. });
  1842. });
  1843. // Pré-remplir les étoiles au survol
  1844. starLabels.forEach((label) => {
  1845. label.addEventListener('mouseenter', function () { // Trouver l'input associé à ce label
  1846. const input = label.previousElementSibling;
  1847. if (input && input.type === 'radio') {
  1848. const rating = parseInt(input.value);
  1849. starLabels.forEach((l) => {
  1850. const lInput = l.previousElementSibling;
  1851. if (lInput && lInput.type === 'radio') {
  1852. const starValue = parseInt(lInput.value);
  1853. const star = l.querySelector('i');
  1854. if (starValue <= rating) {
  1855. star.style.color = '#ffa200';
  1856. star.classList.remove('fa-star-o');
  1857. star.classList.add('fa-star');
  1858. } else {
  1859. star.style.color = '#ccc';
  1860. star.classList.remove('fa-star');
  1861. star.classList.add('fa-star-o');
  1862. }
  1863. }
  1864. });
  1865. }
  1866. });
  1867. });
  1868. const ratingContainer = document.querySelector('.rating-input');
  1869. if (ratingContainer) {
  1870. ratingContainer.addEventListener('mouseleave', function () {
  1871. const checkedInput = document.querySelector('.rating-input input[type="radio"]:checked');
  1872. if (checkedInput) {
  1873. checkedInput.dispatchEvent(new Event('change'));
  1874. } else {
  1875. starLabels.forEach(label => {
  1876. const star = label.querySelector('i');
  1877. star.style.color = '#ccc';
  1878. });
  1879. }
  1880. });
  1881. }
  1882. });
  1883.     // Gestion de la sélection de variantes
  1884.     if (typeof productVariants !== 'undefined' && productVariants.length > 0) {
  1885.         // Initialiser avec la première variante active
  1886.         const firstVariant = productVariants.find(v => v.isActive);
  1887.         if (firstVariant) {
  1888.             firstVariant.attributeValues.forEach(av => {
  1889.                 selectedAttributes[av.attributeSlug] = av.valueId;
  1890.             });
  1891.             updateVariantDisplay(firstVariant);
  1892.         } else {
  1893.             // Si aucune variante active, afficher le prix de base
  1894.             const priceEl = document.getElementById('main-unit-price');
  1895.             if (priceEl) {
  1896.                 priceEl.textContent = '{{ product.price|number_format(2, '.', ' ') }}';
  1897.             }
  1898.         }
  1899.         // Écouter les clics sur les options de variantes
  1900.         document.querySelectorAll('.variant-option').forEach(option => {
  1901.             option.addEventListener('click', function() {
  1902.                 const attributeSlug = this.getAttribute('data-attribute');
  1903.                 const valueId = parseInt(this.getAttribute('data-value-id'));
  1904.                 const value = this.getAttribute('data-value');
  1905.                 // Mettre à jour la sélection visuelle
  1906.                 document.querySelectorAll(`[data-attribute="${attributeSlug}"]`).forEach(opt => {
  1907.                     opt.classList.remove('selected', 'active');
  1908.                     if (opt.classList.contains('variant-color-option')) {
  1909.                         opt.style.borderColor = '#ddd';
  1910.                         const checkIcon = opt.querySelector('.check-icon');
  1911.                         if (checkIcon) checkIcon.remove();
  1912.                     }
  1913.                 });
  1914.                 this.classList.add('selected', 'active');
  1915.                 if (this.classList.contains('variant-color-option')) {
  1916.                     this.style.borderColor = '#007bff';
  1917.                     const checkIcon = document.createElement('span');
  1918.                     checkIcon.className = 'check-icon';
  1919.                     const bgColor = this.style.backgroundColor || '';
  1920.                     const isDark = bgColor && getContrastColor(bgColor) < 128;
  1921.                     checkIcon.style.cssText = 'position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: ' + (isDark ? 'white' : 'black') + '; font-size: 18px;';
  1922.                     checkIcon.textContent = '✓';
  1923.                     this.appendChild(checkIcon);
  1924.                 }
  1925.                 // Mettre à jour les attributs sélectionnés
  1926.                 selectedAttributes[attributeSlug] = valueId;
  1927.                 const selectedSpan = document.getElementById(`selected-${attributeSlug}`);
  1928.                 if (selectedSpan) {
  1929.                     selectedSpan.textContent = value;
  1930.                 }
  1931.                 // Trouver la variante correspondante
  1932.                 const matchingVariant = findMatchingVariant();
  1933.                 if (matchingVariant) {
  1934.                     updateVariantDisplay(matchingVariant);
  1935.                 } else {
  1936.                     // Aucune variante correspondante trouvée - afficher le produit de base
  1937.                     resetToBaseProduct();
  1938.                 }
  1939.             });
  1940.         });
  1941.     }
  1942.     function findMatchingVariant() {
  1943.         if (!productVariants || productVariants.length === 0) return null;
  1944.         
  1945.         return productVariants.find(variant => {
  1946.             if (!variant.isActive) return false;
  1947.             
  1948.             const variantAttributes = {};
  1949.             variant.attributeValues.forEach(av => {
  1950.                 variantAttributes[av.attributeSlug] = av.valueId;
  1951.             });
  1952.             // Vérifier si tous les attributs sélectionnés correspondent
  1953.             for (const [attrSlug, valueId] of Object.entries(selectedAttributes)) {
  1954.                 if (!variantAttributes[attrSlug] || variantAttributes[attrSlug] !== valueId) {
  1955.                     return false;
  1956.                 }
  1957.             }
  1958.             // Vérifier que le nombre d'attributs correspond
  1959.             return Object.keys(variantAttributes).length === Object.keys(selectedAttributes).length;
  1960.         });
  1961.     }
  1962.     function updateVariantDisplay(variant) {
  1963.         currentVariant = variant;
  1964.         
  1965.         // Mettre à jour le prix
  1966.         const priceElement = document.getElementById('main-unit-price');
  1967.         const variantPriceElement = document.getElementById('variant-price');
  1968.         if (priceElement) {
  1969.             priceElement.textContent = variant.price.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  1970.         }
  1971.         if (variantPriceElement) {
  1972.             variantPriceElement.textContent = variant.price.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' HTG';
  1973.         }
  1974.         // Mettre à jour le stock
  1975.         const stockElement = document.getElementById('variant-stock');
  1976.         if (stockElement) {
  1977.             if (variant.stock > 0) {
  1978.                 stockElement.textContent = variant.stock + ' disponible(s)';
  1979.                 stockElement.className = 'variant-stock text-success';
  1980.             } else {
  1981.                 stockElement.textContent = 'Rupture de stock';
  1982.                 stockElement.className = 'variant-stock text-danger';
  1983.             }
  1984.         }
  1985.         // Mettre à jour les champs cachés
  1986.         const variantIdInput = document.getElementById('selected-variant-id');
  1987.         const variantSkuInput = document.getElementById('selected-variant-sku');
  1988.         if (variantIdInput) variantIdInput.value = variant.id;
  1989.         if (variantSkuInput) variantSkuInput.value = variant.sku;
  1990.         // Mettre à jour les images si disponibles
  1991.         if (variant.images && variant.images.length > 0) {
  1992.             const mainImage = document.getElementById('main-product-image');
  1993.             if (mainImage && variant.images[0]) {
  1994.                 mainImage.src = variant.images[0];
  1995.             }
  1996.         }
  1997.         // Afficher les informations de la variante
  1998.         const variantInfo = document.getElementById('variant-info');
  1999.         if (variantInfo) variantInfo.style.display = 'block';
  2000.         // Mettre à jour la quantité maximale
  2001.         const qtyInput = document.getElementById('sst');
  2002.         if (qtyInput) {
  2003.             qtyInput.max = variant.stock;
  2004.         }
  2005.     }
  2006.     function getContrastColor(hexColor) {
  2007.         // Convertir hex en RGB et calculer la luminosité
  2008.         const rgb = hexColor.match(/\d+/g);
  2009.         if (rgb && rgb.length >= 3) {
  2010.             const r = parseInt(rgb[0]);
  2011.             const g = parseInt(rgb[1]);
  2012.         const b = parseInt(rgb[2]);
  2013.         return (r * 299 + g * 587 + b * 114) / 1000;
  2014.     }
  2015.     return 128;
  2016. }
  2017. function resetToBaseProduct() {
  2018.     currentVariant = null;
  2019.     
  2020.     // Réinitialiser les attributs sélectionnés
  2021.     selectedAttributes = {};
  2022.     
  2023.     // Masquer les informations de variante
  2024.     const variantInfo = document.getElementById('variant-info');
  2025.     if (variantInfo) variantInfo.style.display = 'none';
  2026.     
  2027.     // Réinitialiser le prix au prix de base
  2028.     const priceEl = document.getElementById('main-unit-price');
  2029.     if (priceEl) priceEl.textContent = '{{ product.price|number_format(2, '.', ' ') }}';
  2030.     
  2031.     // Réinitialiser les champs cachés
  2032.     const variantIdInput = document.getElementById('selected-variant-id');
  2033.     const variantSkuInput = document.getElementById('selected-variant-sku');
  2034.     if (variantIdInput) variantIdInput.value = '';
  2035.     if (variantSkuInput) variantSkuInput.value = '';
  2036.     
  2037.     // Réinitialiser les sélections visuelles
  2038.     document.querySelectorAll('.variant-option').forEach(opt => {
  2039.         opt.classList.remove('selected', 'active');
  2040.         if (opt.classList.contains('variant-color-option')) {
  2041.             opt.style.borderColor = '#ddd';
  2042.             const checkIcon = opt.querySelector('.check-icon');
  2043.             if (checkIcon) checkIcon.remove();
  2044.         }
  2045.     });
  2046.     
  2047.     // Réinitialiser les labels de sélection
  2048.     document.querySelectorAll('.selected-variant-value').forEach(span => {
  2049.         span.textContent = '';
  2050.     });
  2051.     
  2052.     // Réinitialiser les images au produit de base
  2053.     const mainImage = document.getElementById('main-product-image');
  2054.     if (mainImage && typeof productImages !== 'undefined' && productImages.length > 0) {
  2055.         mainImage.src = productImages[0];
  2056.     }
  2057.     
  2058.     // Réinitialiser la quantité maximale
  2059.     const qtyInput = document.getElementById('sst');
  2060.     if (qtyInput) {
  2061.         qtyInput.max = {{ product.stock }};
  2062.     }
  2063. }
  2064. </script>
  2065.     {% if youMightAlsoLike|length > 0 %}
  2066.         <section class="related-product-area section_gap_bottom py-5" style="background: linear-gradient(to bottom, #f8f9fa 0%, #ffffff 100%);">
  2067.             <div class="container">
  2068.                 <div class="row justify-content-center">
  2069.                     <div class="col-lg-8 text-center">
  2070.                         <div class="section-title">
  2071.                             <h2 class="fw-bold mb-3" style="font-size: 2.5rem; color: #2c3e50;">Vous pourriez aussi aimer</h2>
  2072.                             <p class="text-muted" style="font-size: 1.1rem;">Produits sélectionnés pour vous en fonction de vos préférences</p>
  2073.                         </div>
  2074.                     </div>
  2075.                 </div>
  2076.                 <div class="row g-4">
  2077.                     {% for relatedProduct in youMightAlsoLike %}
  2078.                         <div class="col-6 col-md-4 col-lg-3 mb-3">
  2079.                             <div class="modern-product-card">
  2080.                                 <div class="product-image-wrapper">
  2081.                                     <a href="{{ path('ui_product_show', { slug: relatedProduct.slug }) }}" class="product-image-link">
  2082.                                         {% if relatedProduct.images is defined and relatedProduct.images|length > 0 %}
  2083.                                             <img src="{{ asset(relatedProduct.images[0]) }}" alt="{{ relatedProduct.name }}" class="product-image">
  2084.                                         {% else %}
  2085.                                             <img src="{{ asset('ui/img/product/p1.jpg') }}" alt="{{ relatedProduct.name }}" class="product-image">
  2086.                                         {% endif %}
  2087.                                         <div class="product-overlay">
  2088.                                             <span class="view-product-btn">
  2089.                                                 <i class="ti ti-eye"></i> Voir
  2090.                                             </span>
  2091.                                         </div>
  2092.                                     </a>
  2093.                                     <div class="product-badge">
  2094.                                         {% if relatedProduct.compareAtPrice and relatedProduct.compareAtPrice > relatedProduct.price %}
  2095.                                             <span class="badge-discount">
  2096.                                                 -{{ ((relatedProduct.compareAtPrice - relatedProduct.price) / relatedProduct.compareAtPrice * 100)|round }}%
  2097.                                             </span>
  2098.                                         {% endif %}
  2099.                                     </div>
  2100.                                 </div>
  2101.                                 <div class="product-info">
  2102.                                     <h6 class="product-title">
  2103.                                         <a href="{{ path('ui_product_show', { slug: relatedProduct.slug }) }}">{{ relatedProduct.name }}</a>
  2104.                                     </h6>
  2105.                                     {% if relatedProduct.shop %}
  2106.                                         <div class="product-shop">
  2107.                                             <i class="ti ti-store"></i>
  2108.                                             <a href="{{ path('ui_shop_show', {'slug': relatedProduct.shop.slug}) }}" class="shop-link">{{ relatedProduct.shop.name }}</a>
  2109.                                         </div>
  2110.                                     {% endif %}
  2111.                                     <div class="product-price-wrapper">
  2112.                                         <span class="product-price">{{ relatedProduct.price|number_format(0, '.', ' ') }} HTG</span>
  2113.                                         {% if relatedProduct.compareAtPrice and relatedProduct.compareAtPrice > relatedProduct.price %}
  2114.                                             <span class="product-compare-price">{{ relatedProduct.compareAtPrice|number_format(0, '.', ' ') }} HTG</span>
  2115.                                         {% endif %}
  2116.                                     </div>
  2117.                                     <div class="product-actions">
  2118.                                         <a href="javascript:void(0)" class="btn-add-to-cart" data-product-id="{{ relatedProduct.id }}" data-qty="1" title="Ajouter au panier">
  2119.                                             <i class="ti ti-shopping-cart"></i>
  2120.                                             <span>Ajouter</span>
  2121.                                         </a>
  2122.                                     </div>
  2123.                                 </div>
  2124.                             </div>
  2125.                         </div>
  2126.                     {% endfor %}
  2127.                 </div>
  2128.             </div>
  2129.         </section>
  2130.         <style>
  2131.             .modern-product-card {
  2132.                 background: #ffffff;
  2133.                 border-radius: 16px;
  2134.                 overflow: hidden;
  2135.                 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  2136.                 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  2137.                 height: 100%;
  2138.                 display: flex;
  2139.                 flex-direction: column;
  2140.                 position: relative;
  2141.             }
  2142.             .modern-product-card:hover {
  2143.                 transform: translateY(-8px);
  2144.                 box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
  2145.             }
  2146.             .product-image-wrapper {
  2147.                 position: relative;
  2148.                 width: 100%;
  2149.                 height: 250px;
  2150.                 overflow: hidden;
  2151.                 background: #f8f9fa;
  2152.             }
  2153.             .product-image {
  2154.                 width: 100%;
  2155.                 height: 100%;
  2156.                 object-fit: cover !important;
  2157.                 object-position: center !important;
  2158.                 transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  2159.             }
  2160.             .modern-product-card:hover .product-image {
  2161.                 transform: scale(1.1);
  2162.             }
  2163.             .product-overlay {
  2164.                 position: absolute;
  2165.                 top: 0;
  2166.                 left: 0;
  2167.                 right: 0;
  2168.                 bottom: 0;
  2169.                 background: rgba(0, 0, 0, 0.5);
  2170.                 display: flex;
  2171.                 align-items: center;
  2172.                 justify-content: center;
  2173.                 opacity: 0;
  2174.                 transition: opacity 0.3s ease;
  2175.             }
  2176.             .modern-product-card:hover .product-overlay {
  2177.                 opacity: 1;
  2178.             }
  2179.             .view-product-btn {
  2180.                 color: white;
  2181.                 padding: 12px 24px;
  2182.                 background: rgba(255, 255, 255, 0.2);
  2183.                 backdrop-filter: blur(10px);
  2184.                 border-radius: 8px;
  2185.                 font-weight: 600;
  2186.                 display: inline-flex;
  2187.                 align-items: center;
  2188.                 gap: 8px;
  2189.                 transition: all 0.3s ease;
  2190.             }
  2191.             .view-product-btn:hover {
  2192.                 background: rgba(255, 255, 255, 0.3);
  2193.                 transform: scale(1.05);
  2194.             }
  2195.             .product-badge {
  2196.                 position: absolute;
  2197.                 top: 12px;
  2198.                 right: 12px;
  2199.                 z-index: 2;
  2200.             }
  2201.             .badge-discount {
  2202.                 background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
  2203.                 color: white;
  2204.                 padding: 6px 12px;
  2205.                 border-radius: 20px;
  2206.                 font-size: 0.75rem;
  2207.                 font-weight: 700;
  2208.                 box-shadow: 0 2px 8px rgba(238, 90, 111, 0.4);
  2209.             }
  2210.             .product-info {
  2211.                 padding: 20px;
  2212.                 flex-grow: 1;
  2213.                 display: flex;
  2214.                 flex-direction: column;
  2215.             }
  2216.             .product-title {
  2217.                 margin: 0 0 8px 0;
  2218.                 font-size: 1rem;
  2219.                 font-weight: 600;
  2220.                 line-height: 1.4;
  2221.                 height: 2.8em;
  2222.                 overflow: hidden;
  2223.                 display: -webkit-box;
  2224.                 -webkit-line-clamp: 2;
  2225.                 -webkit-box-orient: vertical;
  2226.             }
  2227.             .product-title a {
  2228.                 color: #2c3e50;
  2229.                 text-decoration: none;
  2230.                 transition: color 0.3s ease;
  2231.             }
  2232.             .product-title a:hover {
  2233.                 color: #ffa200;
  2234.             }
  2235.             .product-shop {
  2236.                 font-size: 0.85rem;
  2237.                 color: #6c757d;
  2238.                 margin-bottom: 12px;
  2239.                 display: flex;
  2240.                 align-items: center;
  2241.                 gap: 6px;
  2242.             }
  2243.             .product-shop i {
  2244.                 font-size: 0.9rem;
  2245.             }
  2246.             .shop-link {
  2247.                 color: #6c757d;
  2248.                 text-decoration: none;
  2249.                 transition: color 0.3s ease;
  2250.             }
  2251.             .shop-link:hover {
  2252.                 color: #ffa200;
  2253.             }
  2254.             .product-price-wrapper {
  2255.                 margin-bottom: 16px;
  2256.                 display: flex;
  2257.                 align-items: center;
  2258.                 gap: 8px;
  2259.                 flex-wrap: wrap;
  2260.             }
  2261.             .product-price {
  2262.                 font-size: 1.25rem;
  2263.                 font-weight: 700;
  2264.                 color: #ffa200;
  2265.             }
  2266.             .product-compare-price {
  2267.                 font-size: 0.9rem;
  2268.                 color: #adb5bd;
  2269.                 text-decoration: line-through;
  2270.             }
  2271.             .product-actions {
  2272.                 margin-top: auto;
  2273.             }
  2274.             /* Styles pour les boutons de la section "Vous pourriez aussi aimer" uniquement */
  2275.             .modern-product-card .btn-add-to-cart {
  2276.                 width: 100%;
  2277.                 padding: 12px;
  2278.                 background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  2279.                 color: white;
  2280.                 border: none;
  2281.                 border-radius: 10px;
  2282.                 font-weight: 600;
  2283.                 display: flex;
  2284.                 align-items: center;
  2285.                 justify-content: center;
  2286.                 gap: 8px;
  2287.                 text-decoration: none;
  2288.                 transition: all 0.3s ease;
  2289.                 cursor: pointer;
  2290.             }
  2291.             .modern-product-card .btn-add-to-cart:hover {
  2292.                 background: linear-gradient(135deg, #e8910a 0%, #d6820a 100%);
  2293.                 transform: translateY(-2px);
  2294.                 box-shadow: 0 4px 12px rgba(255, 162, 0, 0.4);
  2295.                 color: white;
  2296.             }
  2297.             /* Le bouton principal garde son design original */
  2298.             .card_area .btn-add-to-cart {
  2299.                 width: auto;
  2300.                 display: inline-flex;
  2301.             }
  2302.             .modern-product-card .btn-add-to-cart:active {
  2303.                 transform: translateY(0);
  2304.             }
  2305.             .modern-product-card .btn-add-to-cart i {
  2306.                 font-size: 1.1rem;
  2307.             }
  2308.             /* Responsive adjustments */
  2309.             @media (max-width: 768px) {
  2310.                 .product-image-wrapper {
  2311.                     height: 200px;
  2312.                 }
  2313.                 .product-info {
  2314.                     padding: 16px;
  2315.                 }
  2316.                 .product-title {
  2317.                     font-size: 0.95rem;
  2318.                 }
  2319.                 .product-price {
  2320.                     font-size: 1.1rem;
  2321.                 }
  2322.                 .section-title h2 {
  2323.                     font-size: 2rem !important;
  2324.                 }
  2325.             }
  2326.             @media (max-width: 576px) {
  2327.                 .product-image-wrapper {
  2328.                     height: 190px;
  2329.                 }
  2330.                 .product-info {
  2331.                     padding: 14px;
  2332.                 }
  2333.                 .btn-add-to-cart {
  2334.                     padding: 10px;
  2335.                     font-size: 0.9rem;
  2336.                 }
  2337.             }
  2338.             /* Ensure fixed height for cards */
  2339.             .col-6.col-md-4.col-lg-3 {
  2340.                 display: flex;
  2341.             }
  2342.             .modern-product-card {
  2343.                 width: 100%;
  2344.             }
  2345.             /* Loading state for add to cart button */
  2346.             .btn-add-to-cart.loading {
  2347.                 opacity: 0.7;
  2348.                 pointer-events: none;
  2349.             }
  2350.             .btn-add-to-cart.loading i {
  2351.                 animation: spin 1s linear infinite;
  2352.             }
  2353.             @keyframes spin {
  2354.                 from { transform: rotate(0deg); }
  2355.                 to { transform: rotate(360deg); }
  2356.             }
  2357.             /* Success state - vert pour tous les boutons */
  2358.             .btn-add-to-cart.success {
  2359.                 background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  2360.             }
  2361.             /* Le bouton principal garde son style original même en success */
  2362.             .card_area .btn-add-to-cart.success {
  2363.                 background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
  2364.             }
  2365.     {% endif %}
  2366.     <script src="/ui/js/vendor/jquery-2.2.4.min.js"></script>
  2367.     <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
  2368.     <script src="/ui/js/vendor/bootstrap.min.js"></script>
  2369.     <script src="/ui/js/jquery.ajaxchimp.min.js"></script>
  2370.     <script src="/ui/js/jquery.nice-select.min.js"></script>
  2371.     <script src="/ui/js/jquery.sticky.js"></script>
  2372.     <script src="/ui/js/nouislider.min.js"></script>
  2373.     <script src="/ui/js/jquery.magnific-popup.min.js"></script>
  2374.     <script src="/ui/js/owl.carousel.min.js"></script>
  2375.     <!--gmaps Js-->
  2376.     <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE"></script>
  2377.     <script src="/ui/js/gmaps.min.js"></script>
  2378.     <script src="/ui/js/main.js"></script>
  2379.     
  2380.     <!-- Product Image Navigation - Chargé en dernier pour éviter les conflits -->
  2381.     <script src="/ui/js/product-image-navigation.js"></script>
  2382.     
  2383.     <script>
  2384. // Variables globales pour la navigation des images (utilisées par le script externe)
  2385. window.productImages = [];
  2386. window.currentImageIndex = 0;
  2387. let mainImageElement, currentIndexElement, indicatorsElements;
  2388. // Test pour vérifier que les fonctions sont définies
  2389. window.testImageNavigation = function() {
  2390. console.log('Testing image navigation functions...');
  2391. if (window.productImageNav) {
  2392. console.log('productImageNav object:', window.productImageNav);
  2393. console.log('nextImage function:', typeof window.productImageNav.nextImage);
  2394. console.log('prevImage function:', typeof window.productImageNav.prevImage);
  2395. console.log('changeMainImageByIndex function:', typeof window.productImageNav.changeMainImageByIndex);
  2396. console.log('State:', window.productImageNav.getState());
  2397. } else {
  2398. console.warn('productImageNav not available');
  2399. }
  2400. };
  2401. // Fonction pour nettoyer le cache PWA et désenregistrer le Service Worker
  2402. window.clearPWACache = function() {
  2403. console.log('🧹 Nettoyage du cache PWA...');
  2404. // Désenregistrer tous les Service Workers
  2405. if ('serviceWorker' in navigator) {
  2406. navigator.serviceWorker.getRegistrations().then(function(registrations) {
  2407. for(let registration of registrations) {
  2408. console.log('Désenregistrement du Service Worker:', registration.scope);
  2409. registration.unregister().then(function(boolean) {
  2410. console.log('Service Worker désenregistré:', boolean);
  2411. });
  2412. }
  2413. });
  2414. }
  2415. // Vider tous les caches
  2416. if ('caches' in window) {
  2417. caches.keys().then(function(names) {
  2418. for (let name of names) {
  2419. console.log('Suppression du cache:', name);
  2420. caches.delete(name);
  2421. }
  2422. });
  2423. }
  2424. // Forcer le rechargement de la page après nettoyage
  2425. setTimeout(function() {
  2426. console.log('🔄 Rechargement de la page...');
  2427. window.location.reload(true);
  2428. }, 1000);
  2429. };
  2430. console.log('💡 Pour nettoyer le cache PWA, tapez: clearPWACache() dans la console');
  2431. // Fonction pour vérifier l'état du cache PWA
  2432. window.checkPWACache = function() {
  2433. console.log('🔍 Vérification de l\'état PWA...');
  2434. if ('serviceWorker' in navigator) {
  2435. navigator.serviceWorker.getRegistrations().then(function(registrations) {
  2436. console.log('Service Workers enregistrés:', registrations.length);
  2437. registrations.forEach(function(registration, index) {
  2438. console.log(`SW ${index + 1}:`, registration.scope, registration.active ? 'ACTIF' : 'INACTIF');
  2439. });
  2440. });
  2441. } else {
  2442. console.log('Service Worker non supporté');
  2443. }
  2444. if ('caches' in window) {
  2445. caches.keys().then(function(names) {
  2446. console.log('Caches disponibles:', names.length);
  2447. names.forEach(function(name) {
  2448. console.log('- Cache:', name);
  2449. });
  2450. });
  2451. } else {
  2452. console.log('Cache API non supporté');
  2453. }
  2454. };
  2455. console.log('💡 Pour vérifier l\'état PWA, tapez: checkPWACache() dans la console');
  2456. // Nettoyage automatique du cache PWA pour les tests (à supprimer en production)
  2457. if (window.location.search.includes('clearcache')) {
  2458. console.log('🧹 Nettoyage automatique du cache PWA demandé via URL');
  2459. clearPWACache();
  2460. }
  2461. // NOTE: La navigation des images principales est gérée par product-image-navigation.js
  2462. // Les fonctions sont exposées via window.productImageNav pour le débogage
  2463. // NOTE: La navigation des images principales est gérée par product-image-navigation.js
  2464. // Le script externe initialise automatiquement les images depuis les thumbnails
  2465. // Pas besoin de réinitialiser ici pour éviter les conflits
  2466. // Gestion du carrousel de miniatures avec taille fixe carrée
  2467. const navUpBtn = document.getElementById('thumbnail-nav-up');
  2468. const navDownBtn = document.getElementById('thumbnail-nav-down');
  2469. const thumbnailsContainer = document.getElementById('product-thumbnails');
  2470. const thumbnailsWrapper = document.getElementById('thumbnails-wrapper');
  2471. const thumbnailItems = Array.from(document.querySelectorAll('.thumbnail-item'));
  2472. let currentThumbnailOffset = 0;
  2473. const thumbnailHeight = 90;
  2474. // 80px (hauteur) + 10px (gap)
  2475. // Calculer la hauteur disponible pour les miniatures dynamiquement
  2476. const thumbnailsEl = document.getElementById('product-thumbnails');
  2477. let containerHeight = thumbnailsEl ? thumbnailsEl.clientHeight : 520; // Hauteur par défaut si non disponible
  2478. let visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  2479. // Recalculer après le chargement pour avoir la vraie hauteur
  2480. setTimeout(() => {
  2481. if (thumbnailsEl) {
  2482. containerHeight = thumbnailsEl.clientHeight;
  2483. visibleThumbnails = Math.floor(containerHeight / thumbnailHeight);
  2484. updateThumbnailsPosition();
  2485. }
  2486. }, 100);
  2487. function getCurrentIndex() {
  2488. const current = document.querySelector('.thumbnail-item.permanently-active') || document.querySelector('.thumbnail-item.active');
  2489. if (! current)
  2490. return 0;
  2491. const idxAttr = current.getAttribute('data-index');
  2492. if (idxAttr !== null) {
  2493.     return parseInt(idxAttr, 10);
  2494. }
  2495. const index = thumbnailItems.indexOf(current);
  2496. return index >= 0 ? index : 0;
  2497. }
  2498. function selectThumbnailByIndex(newIndex) {
  2499. if (! thumbnailItems.length) 
  2500. return;
  2501. const total = thumbnailItems.length;
  2502. if (newIndex < 0) 
  2503. newIndex = 0;
  2504. if (newIndex > total - 1) 
  2505. newIndex = total - 1;
  2506. // Reset classes
  2507. thumbnailItems.forEach(t => t.classList.remove('active', 'permanently-active'));
  2508. const target = thumbnailItems[newIndex];
  2509. if (! target) 
  2510. return;
  2511. target.classList.add('active', 'permanently-active');
  2512. // Utiliser la fonction du script externe si disponible
  2513. if (window.productImageNav && typeof window.productImageNav.changeMainImageByIndex === 'function') {
  2514. window.productImageNav.changeMainImageByIndex(newIndex);
  2515. } else {
  2516. // Fallback : changer l'image directement
  2517. const imageUrl = target.getAttribute('data-image');
  2518. const mainImg = document.getElementById('main-product-image');
  2519. if (mainImg && imageUrl) {
  2520. mainImg.src = imageUrl;
  2521. }
  2522. }
  2523. // Ajuster le carrousel pour voir la miniature sélectionnée
  2524. ensureThumbnailVisible(newIndex);
  2525. }
  2526. function ensureThumbnailVisible(index) {
  2527. if (! thumbnailsWrapper || ! thumbnailItems.length) 
  2528. return;
  2529. const targetTop = index * thumbnailHeight;
  2530. const visibleTop = currentThumbnailOffset * thumbnailHeight;
  2531. const visibleBottom = visibleTop + containerHeight;
  2532. // Si la miniature est au-dessus de la zone visible
  2533. if (targetTop < visibleTop) {
  2534. currentThumbnailOffset = index;
  2535. updateThumbnailsPosition();
  2536. }
  2537. // Si la miniature est en-dessous de la zone visible else if (targetTop + thumbnailHeight > visibleBottom) {
  2538. currentThumbnailOffset = Math.max(0, index - visibleThumbnails + 1);
  2539. updateThumbnailsPosition();
  2540. }
  2541. }
  2542. function updateThumbnailsPosition() {
  2543. if (!thumbnailsWrapper) 
  2544. return;
  2545. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2546. currentThumbnailOffset = Math.max(0, Math.min(currentThumbnailOffset, maxOffset));
  2547. thumbnailsWrapper.style.transform = `translateY(-${
  2548. currentThumbnailOffset * thumbnailHeight
  2549. }px)`;
  2550. updateNavButtons();
  2551. }
  2552. function updateNavButtons() {
  2553. if (!thumbnailItems.length) 
  2554. return;
  2555. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2556. const canScrollUp = currentThumbnailOffset > 0;
  2557. const canScrollDown = currentThumbnailOffset < maxOffset;
  2558. if (navUpBtn) {
  2559. navUpBtn.classList.toggle('disabled', ! canScrollUp);
  2560. }
  2561. if (navDownBtn) {
  2562. navDownBtn.classList.toggle('disabled', ! canScrollDown);
  2563. }
  2564. }
  2565. // Navigation avec les boutons
  2566. if(navUpBtn) {
  2567. navUpBtn.addEventListener('click', function (e) {
  2568. e.preventDefault();
  2569. if (currentThumbnailOffset > 0) {
  2570. currentThumbnailOffset --;
  2571. updateThumbnailsPosition();
  2572. }
  2573. });
  2574. }
  2575. if(navDownBtn) {
  2576. navDownBtn.addEventListener('click', function (e) {
  2577. e.preventDefault();
  2578. const maxOffset = Math.max(0, thumbnailItems.length - visibleThumbnails);
  2579. if (currentThumbnailOffset < maxOffset) {
  2580. currentThumbnailOffset ++;
  2581. updateThumbnailsPosition();
  2582. }
  2583. });
  2584. }
  2585. // Initialiser le carrousel
  2586. if(thumbnailsWrapper && thumbnailItems.length > 0) {
  2587. updateThumbnailsPosition();
  2588. // S'assurer que la première miniature est visible
  2589. ensureThumbnailVisible(0);
  2590. // Sélectionner la première image par défaut si aucune n'est sélectionnée
  2591. if (!document.querySelector('.thumbnail-item.permanently-active') && !document.querySelector('.thumbnail-item.active')) {
  2592.     selectThumbnailByIndex(0);
  2593. }
  2594. // Initialiser l'index actuel via le script externe si disponible
  2595. // Le script externe product-image-navigation.js gère déjà l'initialisation
  2596. if (window.productImageNav && typeof window.productImageNav.getState === 'function') {
  2597. const state = window.productImageNav.getState();
  2598. if (state.productImages && state.productImages.length > 0) {
  2599. window.productImageNav.changeMainImageByIndex(0);
  2600. }
  2601. }
  2602. }
  2603. // NOTE: La navigation des images principales est maintenant gérée par product-image-navigation.js
  2604. // Ce fichier externe évite les conflits avec cart-modal.js et autres scripts
  2605. }, 100); // Fin du setTimeout
  2606. }); // Fin du DOMContentLoaded
  2607. // --- Prix de gros : calcul du prix unitaire dynamique ---
  2608. (function () {
  2609. const bulk = document.getElementById('bulk-pricing');
  2610. const qtyInputEl = document.getElementById('sst');
  2611. const unitPriceSpan = document.getElementById('unit-price-value');
  2612. const mainUnitPriceSpan = document.getElementById('main-unit-price');
  2613. const totalPriceSpan = document.getElementById('total-price-value');
  2614. const savingsAmountSpan = document.getElementById('savings-amount');
  2615. const savingsPercentSpan = document.getElementById('savings-percent');
  2616. const unitPriceInput = document.getElementById('unit-price-input');
  2617. const tierRowsTbody = document.getElementById('bulk-tier-rows');
  2618. if (! bulk || ! qtyInputEl || ! unitPriceSpan || ! mainUnitPriceSpan || ! unitPriceInput) 
  2619. return;
  2620. let tiers = [];
  2621. try {
  2622. tiers = JSON.parse(bulk.getAttribute('data-tiers') || '[]');
  2623. } catch (e) {
  2624. tiers = [];
  2625. }
  2626. if (!Array.isArray(tiers) || tiers.length === 0) 
  2627. return;
  2628. tiers.sort(function (a, b) {
  2629. return(a.min || 0) - (b.min || 0);
  2630. });
  2631. const basePrice = (function () {
  2632. const one = tiers.find(t => (t.min || 0) <= 1);
  2633. return one ? parseFloat(one.price) : parseFloat(tiers[0].price);
  2634. })();
  2635. function getUnitPriceForQty(qty) {
  2636. let candidate = tiers[0] || null;
  2637. for (let i = 0; i < tiers.length; i++) {
  2638. if (qty >= (tiers[i].min || 1)) {
  2639. candidate = tiers[i];
  2640. }
  2641. }
  2642. return candidate ? parseFloat(candidate.price) : (tiers[0] ? parseFloat(tiers[0].price) : basePrice);
  2643. }
  2644. function formatPrice(val) {
  2645. return(parseFloat(val) || 0).toFixed(2);
  2646. }
  2647. function highlightActiveTier(qty) {
  2648. if (! tierRowsTbody) 
  2649. return;
  2650. const rows = tierRowsTbody.querySelectorAll('tr');
  2651. rows.forEach(r => r.classList.remove('table-warning'));
  2652. let activeRow = null;
  2653. rows.forEach(r => {
  2654. const m = parseInt(r.getAttribute('data-min'), 10) || 1;
  2655. if (qty >= m) 
  2656. activeRow = r;
  2657. });
  2658. if (activeRow) 
  2659. activeRow.classList.add('table-warning');
  2660. }
  2661. function refreshPrices() {
  2662. const qty = Math.max(1, parseInt(qtyInputEl.value, 10) || 1);
  2663. const unit = getUnitPriceForQty(qty);
  2664. const total = unit * qty;
  2665. const saveAmount = Math.max(0, (basePrice - unit) * qty);
  2666. const savePercent = Math.max(0, 100 * (basePrice - unit) / basePrice);
  2667. unitPriceSpan.textContent = formatPrice(unit) + ' HTG';
  2668. mainUnitPriceSpan.textContent = formatPrice(unit);
  2669. if (totalPriceSpan) 
  2670. totalPriceSpan.textContent = formatPrice(total);
  2671. if (savingsAmountSpan) 
  2672. savingsAmountSpan.textContent = formatPrice(saveAmount);
  2673. if (savingsPercentSpan) 
  2674. savingsPercentSpan.textContent = Math.round(savePercent);
  2675. unitPriceInput.value = formatPrice(unit);
  2676. highlightActiveTier(qty);
  2677. }
  2678. qtyInputEl.addEventListener('input', refreshPrices);
  2679. qtyInputEl.addEventListener('change', refreshPrices);
  2680. setTimeout(refreshPrices, 50);
  2681. setInterval(refreshPrices, 350);
  2682. })();
  2683. // --- /Prix de gros ---
  2684. // Améliorer les boutons de quantité avec gestion de l'état disabled
  2685. (function() {
  2686. const qtyInput = document.getElementById('sst');
  2687. const decreaseBtn = document.querySelector('.quantity-btn-decrease');
  2688. const increaseBtn = document.querySelector('.quantity-btn-increase');
  2689. if (!qtyInput || !decreaseBtn || !increaseBtn) return;
  2690. const maxStock = parseInt(qtyInput.getAttribute('max') || '99', 10);
  2691. function updateButtonStates() {
  2692. const currentValue = parseInt(qtyInput.value, 10) || 1;
  2693. // Désactiver le bouton decrease si la valeur est 1
  2694. if (currentValue <= 1) {
  2695. decreaseBtn.disabled = true;
  2696. } else {
  2697. decreaseBtn.disabled = false;
  2698. }
  2699. // Désactiver le bouton increase si la valeur est au maximum
  2700. if (currentValue >= maxStock) {
  2701. increaseBtn.disabled = true;
  2702. } else {
  2703. increaseBtn.disabled = false;
  2704. }
  2705. }
  2706. // Mettre à jour l'état des boutons au chargement
  2707. updateButtonStates();
  2708. // Mettre à jour l'état des boutons lors des changements
  2709. qtyInput.addEventListener('input', updateButtonStates);
  2710. qtyInput.addEventListener('change', updateButtonStates);
  2711. // Mettre à jour l'état après les clics sur les boutons
  2712. decreaseBtn.addEventListener('click', function() {
  2713. setTimeout(updateButtonStates, 10);
  2714. });
  2715. increaseBtn.addEventListener('click', function() {
  2716. setTimeout(updateButtonStates, 10);
  2717. });
  2718. })();
  2719. // Injection du prix unitaire dans l'ajout au panier
  2720. (function () {
  2721. const addToCartBtn = document.getElementById('add-to-cart-btn');
  2722. if (! addToCartBtn) 
  2723. return;
  2724. addToCartBtn.addEventListener('click', function () {
  2725. const unitPriceInput = document.getElementById('unit-price-input');
  2726. if (unitPriceInput) {
  2727. addToCartBtn.setAttribute('data-unit-price', unitPriceInput.value);
  2728. }
  2729. });
  2730. })();
  2731. // Fonction pour ouvrir le modal de zoom avec loader
  2732. function openImageZoom () {
  2733. const mainImage = document.getElementById('main-product-image');
  2734. const zoomModal = document.getElementById('image-zoom-modal');
  2735. const zoomImage = document.getElementById('zoom-modal-image');
  2736. const zoomLoader = document.getElementById('zoom-loader');
  2737. if (mainImage && zoomModal && zoomImage) { // Afficher le loader
  2738. if (zoomLoader) {
  2739. zoomLoader.style.display = 'flex';
  2740. }
  2741. // Masquer l'image et réinitialiser
  2742. zoomImage.classList.remove('loaded');
  2743. zoomImage.style.opacity = '0';
  2744. // Ouvrir le modal avec animation
  2745. zoomModal.classList.add('active');
  2746. document.body.style.overflow = 'hidden';
  2747. // Charger l'image
  2748. const imageUrl = mainImage.src;
  2749. const tempImage = new Image();
  2750. tempImage.onload = function () {
  2751. zoomImage.src = imageUrl;
  2752. // Masquer le loader et afficher l'image avec transition
  2753. setTimeout(function () {
  2754. if (zoomLoader) {
  2755. zoomLoader.style.display = 'none';
  2756. }
  2757. zoomImage.classList.add('loaded');
  2758. zoomImage.style.opacity = '1';
  2759. }, 200);
  2760. };
  2761. tempImage.onerror = function () {
  2762. if (zoomLoader) {
  2763. zoomLoader.style.display = 'none';
  2764. }
  2765. zoomImage.src = imageUrl;
  2766. zoomImage.classList.add('loaded');
  2767. };
  2768. tempImage.src = imageUrl;
  2769. }
  2770. }
  2771. // Fonction pour fermer le modal de zoom avec animation
  2772. function closeImageZoom () {
  2773. const zoomModal = document.getElementById('image-zoom-modal');
  2774. const zoomImage = document.getElementById('zoom-modal-image');
  2775. const zoomLoader = document.getElementById('zoom-loader');
  2776. if (zoomModal) { // Animation de fermeture
  2777. if (zoomImage) {
  2778. zoomImage.style.opacity = '0';
  2779. zoomImage.classList.remove('loaded');
  2780. }
  2781. setTimeout(function () {
  2782. zoomModal.classList.remove('active');
  2783. document.body.style.overflow = 'auto';
  2784. // Réinitialiser le loader pour la prochaine ouverture
  2785. if (zoomLoader) {
  2786. zoomLoader.style.display = 'none';
  2787. }
  2788. }, 200);
  2789. }
  2790. }
  2791. // Fermer le modal en cliquant en dehors
  2792. document.addEventListener('DOMContentLoaded', function () {
  2793. const zoomModal = document.getElementById('image-zoom-modal');
  2794. if (zoomModal) {
  2795. zoomModal.addEventListener('click', function (e) {
  2796. if (e.target === zoomModal) {
  2797. closeImageZoom();
  2798. }
  2799. });
  2800. }
  2801. });
  2802.     </script>
  2803.     <!-- Modal de zoom d'image -->
  2804.     <div id="image-zoom-modal" class="image-zoom-modal">
  2805.         <span class="close-zoom" onclick="closeImageZoom()" title="Fermer">&times;</span>
  2806.         <div
  2807.             class="zoom-modal-content">
  2808.             <!-- Loader -->
  2809.             <div class="zoom-loader" id="zoom-loader" style="display: none;">
  2810.                 <div class="zoom-loader-spinner"></div>
  2811.                 <div class="zoom-loader-text">Chargement...</div>
  2812.             </div>
  2813.             <!-- Image -->
  2814.             <img id="zoom-modal-image" src="" alt="{{ product.name }}">
  2815.         </div>
  2816.     </div>
  2817.     {% if dropshipReferral %}
  2818.         <!-- Modal d'affiliation -->
  2819.         <div id="dropship-modal" class="dropship-referral-modal" style="display: none;">
  2820.             <div class="dropship-modal-overlay"></div>
  2821.             <div class="dropship-modal-content">
  2822.                 <button type="button" class="dropship-modal-close" onclick="closeDropshipModal()" aria-label="Fermer">
  2823.                     &times;
  2824.                 </button>
  2825.                 <div class="dropship-modal-header">
  2826.                     <div class="dropship-icon">
  2827.                         <i class="fas fa-gift"></i>
  2828.                     </div>
  2829.                     <h3>Produit recommandé par
  2830.                         {{ dropshipReferral.affiliateName }}</h3>
  2831.                     <p class="dropship-subtitle">Vous avez été dirigé vers ce produit par un de nos partenaires</p>
  2832.                 </div>
  2833.                 <div class="dropship-modal-body">
  2834.                     <div class="dropship-product-info">
  2835.                         {% if product.images|length > 0 %}
  2836.                             <img src="{{ asset(product.images[0]) }}" alt="{{ product.name }}" class="dropship-product-image" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
  2837.                             <div class="dropship-product-image-placeholder" style="display: none; width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; align-items: center; justify-content: center; color: #999;">
  2838.                                 <i class="fas fa-image" style="font-size: 32px;"></i>
  2839.                             </div>
  2840.                         {% else %}
  2841.                             <div class="dropship-product-image-placeholder" style="width: 100px; height: 100px; background: #f0f0f0; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #999;">
  2842.                                 <i class="fas fa-image" style="font-size: 32px;"></i>
  2843.                             </div>
  2844.                         {% endif %}
  2845.                         <div class="dropship-product-details">
  2846.                             <h4>{{ product.name }}</h4>
  2847.                             <div class="dropship-product-price">
  2848.                                 <span class="price">{{ product.price|number_format(2, ',', ' ') }}
  2849.                                     HTG</span>
  2850.                             </div>
  2851.                         </div>
  2852.                     </div>
  2853.                     <div class="dropship-message">
  2854.                         <p>
  2855.                             <strong>{{ dropshipReferral.affiliateName }}</strong>
  2856.                             vous recommande ce produit. Souhaitez-vous l'ajouter à votre panier ?</p>
  2857.                     </div>
  2858.                 </div>
  2859.                 <div class="dropship-modal-footer">
  2860.                     <button type="button" class="btn btn-secondary" onclick="closeDropshipModal()">
  2861.                         Parcourir d'abord
  2862.                     </button>
  2863.                     <button type="button" class="btn btn-primary dropship-add-to-cart" onclick="addToCartFromDropship()">
  2864.                         <i class="fas fa-shopping-cart"></i>
  2865.                         Ajouter au panier
  2866.                     </button>
  2867.                 </div>
  2868.             </div>
  2869.         </div>
  2870.         <style>
  2871.             .dropship-referral-modal {
  2872.                 position: fixed;
  2873.                 top: 0;
  2874.                 left: 0;
  2875.                 width: 100%;
  2876.                 height: 100%;
  2877.                 z-index: 10000;
  2878.                 display: flex;
  2879.                 align-items: center;
  2880.                 justify-content: center;
  2881.             }
  2882.             .dropship-modal-overlay {
  2883.                 position: absolute;
  2884.                 top: 0;
  2885.                 left: 0;
  2886.                 width: 100%;
  2887.                 height: 100%;
  2888.                 background: rgba(0, 0, 0, 0.6);
  2889.                 backdrop-filter: blur(4px);
  2890.             }
  2891.             .dropship-modal-content {
  2892.                 position: relative;
  2893.                 background: white;
  2894.                 border-radius: 16px;
  2895.                 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  2896.                 max-width: 500px;
  2897.                 width: 90%;
  2898.                 max-height: 90vh;
  2899.                 overflow-y: auto;
  2900.                 z-index: 10001;
  2901.                 animation: dropshipModalSlideIn 0.3s ease-out;
  2902.             }
  2903.             @keyframes dropshipModalSlideIn {
  2904.                 from {
  2905.                     opacity: 0;
  2906.                     transform: translateY(-30px) scale(0.95);
  2907.                 }
  2908.                 to {
  2909.                     opacity: 1;
  2910.                     transform: translateY(0) scale(1);
  2911.                 }
  2912.             }
  2913.             .dropship-modal-close {
  2914.                 position: absolute;
  2915.                 top: 15px;
  2916.                 right: 15px;
  2917.                 background: none;
  2918.                 border: none;
  2919.                 font-size: 28px;
  2920.                 color: #666;
  2921.                 cursor: pointer;
  2922.                 width: 35px;
  2923.                 height: 35px;
  2924.                 display: flex;
  2925.                 align-items: center;
  2926.                 justify-content: center;
  2927.                 border-radius: 50%;
  2928.                 transition: all 0.2s;
  2929.                 z-index: 10;
  2930.             }
  2931.             .dropship-modal-close:hover {
  2932.                 background: #f0f0f0;
  2933.                 color: #333;
  2934.             }
  2935.             .dropship-modal-header {
  2936.                 padding: 30px 30px 20px;
  2937.                 text-align: center;
  2938.                 border-bottom: 1px solid #f0f0f0;
  2939.             }
  2940.             .dropship-icon {
  2941.                 width: 70px;
  2942.                 height: 70px;
  2943.                 margin: 0 auto 15px;
  2944.                 background: linear-gradient(135deg, #ffa200 0%, #ff8c00 100%);
  2945.                 border-radius: 50%;
  2946.                 display: flex;
  2947.                 align-items: center;
  2948.                 justify-content: center;
  2949.                 font-size: 32px;
  2950.                 color: white;
  2951.                 box-shadow: 0 4px 15px rgba(255, 162, 0, 0.3);
  2952.             }
  2953.             .dropship-modal-header h3 {
  2954.                 margin: 0 0 8px;
  2955.                 font-size: 22px;
  2956.                 font-weight: 600;
  2957.                 color: #333;
  2958.             }
  2959.             .dropship-subtitle {
  2960.                 margin: 0;
  2961.                 color: #666;
  2962.                 font-size: 14px;
  2963.             }
  2964.             .dropship-modal-body {
  2965.                 padding: 25px 30px;
  2966.             }
  2967.             .dropship-product-info {
  2968.                 display: flex;
  2969.                 gap: 15px;
  2970.                 margin-bottom: 20px;
  2971.                 padding: 15px;
  2972.                 background: #f8f9fa;
  2973.                 border-radius: 10px;
  2974.             }
  2975.             .dropship-product-image {
  2976.                 width: 80px;
  2977.                 height: 80px;
  2978.                 object-fit: cover;
  2979.                 border-radius: 8px;
  2980.                 flex-shrink: 0;
  2981.             }
  2982.             .dropship-product-details {
  2983.                 flex: 1;
  2984.             }
  2985.             .dropship-product-details h4 {
  2986.                 margin: 0 0 8px;
  2987.                 font-size: 16px;
  2988.                 font-weight: 600;
  2989.                 color: #333;
  2990.             }
  2991.             .dropship-product-price {
  2992.                 margin-top: 5px;
  2993.             }
  2994.             .dropship-product-price .price {
  2995.                 font-size: 20px;
  2996.                 font-weight: 700;
  2997.                 color: #ffa200;
  2998.             }
  2999.             .dropship-message {
  3000.                 padding: 15px;
  3001.                 background: #fff9e6;
  3002.                 border-left: 4px solid #ffa200;
  3003.                 border-radius: 6px;
  3004.             }
  3005.             .dropship-message p {
  3006.                 margin: 0;
  3007.                 color: #666;
  3008.                 line-height: 1.6;
  3009.             }
  3010.             .dropship-modal-footer {
  3011.                 padding: 20px 30px 30px;
  3012.                 display: flex;
  3013.                 gap: 12px;
  3014.                 justify-content: flex-end;
  3015.                 border-top: 1px solid #f0f0f0;
  3016.             }
  3017.             .dropship-modal-footer .btn {
  3018.                 padding: 12px 24px;
  3019.                 border-radius: 8px;
  3020.                 font-weight: 500;
  3021.                 border: none;
  3022.                 cursor: pointer;
  3023.                 transition: all 0.2s;
  3024.                 font-size: 15px;
  3025.             }
  3026.             .dropship-modal-footer .btn-secondary {
  3027.                 background: #f0f0f0;
  3028.                 color: #666;
  3029.             }
  3030.             .dropship-modal-footer .btn-secondary:hover {
  3031.                 background: #e0e0e0;
  3032.             }
  3033.             .dropship-modal-footer .btn-primary {
  3034.                 background: #ffa200;
  3035.                 color: white;
  3036.             }
  3037.             .dropship-modal-footer .btn-primary:hover {
  3038.                 background: #e8910a;
  3039.                 transform: translateY(-2px);
  3040.                 box-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
  3041.             }
  3042.             .dropship-modal-footer .btn-primary:disabled {
  3043.                 opacity: 0.6;
  3044.                 cursor: not-allowed;
  3045.                 transform: none;
  3046.             }
  3047.         </style>
  3048.         <script>
  3049.             // Afficher le modal automatiquement au chargement de la page
  3050. document.addEventListener('DOMContentLoaded', function () {
  3051. const dropshipModal = document.getElementById('dropship-modal');
  3052. if (dropshipModal) {
  3053. setTimeout(function () {
  3054. dropshipModal.style.display = 'flex';
  3055. document.body.style.overflow = 'hidden';
  3056. }, 500); // Délai de 500ms pour une meilleure UX
  3057. }
  3058. });
  3059. function closeDropshipModal () {
  3060. const dropshipModal = document.getElementById('dropship-modal');
  3061. if (dropshipModal) {
  3062. dropshipModal.style.display = 'none';
  3063. document.body.style.overflow = 'auto';
  3064. }
  3065. }
  3066. function addToCartFromDropship () {
  3067. const addBtn = document.querySelector('.dropship-add-to-cart');
  3068. if (! addBtn) 
  3069. return;
  3070. // Désactiver le bouton pendant le traitement
  3071. addBtn.disabled = true;
  3072. addBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Ajout en cours...';
  3073. const productId = {{ product.id }};
  3074. const qty = parseInt(document.getElementById('sst') ?. value || '1');
  3075. // Utiliser fetch pour ajouter au panier
  3076. fetch('{{ path("ui_cart_add") }}', {
  3077. method: 'POST',
  3078. headers: {
  3079. 'Content-Type': 'application/x-www-form-urlencoded',
  3080. 'X-Requested-With': 'XMLHttpRequest'
  3081. },
  3082. body: new URLSearchParams(
  3083. {productId: productId, qty: qty}
  3084. )
  3085. }).then(response => response.json()).then(data => {
  3086. if (data.ok) { // Afficher un message de succès
  3087. const modalBody = document.querySelector('.dropship-modal-body');
  3088. if (modalBody) {
  3089. modalBody.innerHTML = `
  3090.                                 <div style="text-align: center; padding: 30px 20px;">
  3091.                                     <div style="width: 80px; height: 80px; margin: 0 auto 20px; background: #d4edda; border-radius: 50%; display: flex; align-items: center; justify-content: center;">
  3092.                                         <i class="fas fa-check" style="font-size: 40px; color: #28a745;"></i>
  3093.                                     </div>
  3094.                                     <h3 style="color: #28a745; margin-bottom: 10px;">Produit ajouté avec succès !</h3>
  3095.                                     <p style="color: #666; margin-bottom: 20px;">Merci d'avoir utilisé le lien de recommandation de <strong>{{ dropshipReferral.affiliateName }}</strong></p>
  3096.                                     <p style="color: #666; font-size: 14px;">Vous pouvez continuer vos achats ou aller directement au panier.</p>
  3097.                                 </div>
  3098.                             `;
  3099. }
  3100. const modalFooter = document.querySelector('.dropship-modal-footer');
  3101. if (modalFooter) {
  3102. modalFooter.innerHTML = `
  3103.                                 <button type="button" class="btn btn-secondary" onclick="closeDropshipModal()">
  3104.                                     Continuer les achats
  3105.                                 </button>
  3106.                                 <a href="{{ path('cart') }}" class="btn btn-primary" style="text-decoration: none; display: inline-block;">
  3107.                                     <i class="fas fa-shopping-cart"></i> Voir le panier
  3108.                                 </a>
  3109.                             `;
  3110. }
  3111. // Mettre à jour le compteur du panier si présent
  3112. const cartCount = document.querySelector('.cart-count, .cart_count, [data-cart-count]');
  3113. if (cartCount) {
  3114. cartCount.textContent = data.totalQty || 0;
  3115. }
  3116. } else {
  3117. showCustomAlert('Erreur lors de l\'ajout au panier. Veuillez réessayer.', 'error');
  3118. addBtn.disabled = false;
  3119. addBtn.innerHTML = '<i class="fas fa-shopping-cart"></i> Ajouter au panier';
  3120. }
  3121. }).catch(error => {
  3122. console.error('Erreur:', error);
  3123. showCustomAlert('Une erreur est survenue. Veuillez réessayer.', 'error');
  3124. addBtn.disabled = false;
  3125. addBtn.innerHTML = '<i class="fas fa-shopping-cart"></i> Ajouter au panier';
  3126. });
  3127. }
  3128. // Fermer le modal en cliquant sur l'overlay
  3129. document.addEventListener('DOMContentLoaded', function () {
  3130. const dropshipModal = document.getElementById('dropship-modal');
  3131. if (dropshipModal) {
  3132. const overlay = dropshipModal.querySelector('.dropship-modal-overlay');
  3133. if (overlay) {
  3134. overlay.addEventListener('click', function () {
  3135. closeDropshipModal();
  3136. });
  3137. }
  3138. }
  3139. });
  3140.         </script>
  3141.     {% endif %}
  3142.     <!-- Modal personnalisé pour remplacer les alert -->
  3143.     <div class="modal fade custom-alert-modal" id="customAlertModal" tabindex="-1" aria-labelledby="customAlertModalLabel" aria-hidden="true">
  3144.         <div class="modal-dialog modal-dialog-centered">
  3145.             <div class="modal-content">
  3146.                 <div class="modal-header">
  3147.                     <h5 class="modal-title text-white" id="customAlertModalLabel">Notification</h5>
  3148.                     <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  3149.                 </div>
  3150.                 <div class="modal-body">
  3151.                     <div class="alert-icon" id="alertIcon">
  3152.                         <i class="lnr lnr-info-circle"></i>
  3153.                     </div>
  3154.                     <p class="alert-message" id="alertMessage"></p>
  3155.                 </div>
  3156.                 <div class="modal-footer">
  3157.                     <button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
  3158.                 </div>
  3159.             </div>
  3160.         </div>
  3161.     </div>
  3162.     <script>
  3163.         // Fonction pour remplacer alert()
  3164. function showCustomAlert (message, type = 'info') {
  3165. const modal = new bootstrap.Modal(document.getElementById('customAlertModal'));
  3166. const alertMessage = document.getElementById('alertMessage');
  3167. const alertIcon = document.getElementById('alertIcon');
  3168. const modalTitle = document.getElementById('customAlertModalLabel');
  3169. alertMessage.textContent = message;
  3170. // Définir l'icône et le titre selon le type
  3171. let iconClass = 'lnr lnr-info-circle';
  3172. let title = 'Information';
  3173. switch (type) {
  3174. case 'success': iconClass = 'lnr lnr-checkmark-circle';
  3175. title = 'Succès';
  3176. alertIcon.className = 'alert-icon success';
  3177. break;
  3178. case 'error':
  3179. case 'danger': iconClass = 'lnr lnr-warning';
  3180. title = 'Erreur';
  3181. alertIcon.className = 'alert-icon error';
  3182. break;
  3183. case 'warning': iconClass = 'lnr lnr-warning';
  3184. title = 'Attention';
  3185. alertIcon.className = 'alert-icon warning';
  3186. break;
  3187. default: iconClass = 'lnr lnr-info-circle';
  3188. title = 'Information';
  3189. alertIcon.className = 'alert-icon info';
  3190. }
  3191. alertIcon.innerHTML = `<i class="${iconClass}"></i>`;
  3192. modalTitle.textContent = title;
  3193. modal.show();
  3194. }
  3195. // Remplacer window.alert par showCustomAlert
  3196. window.alert = showCustomAlert;
  3197.     </script>
  3198. {% endblock %}