templates/home/cart.html.twig line 1

Open in your IDE?
  1. {% extends 'base_home.html.twig' %}
  2. {% block title %}Panier | MaketOu{% endblock %}
  3. {% block body %}
  4.     <!-- Start Banner Area -->
  5.     <section class="banner-area organic-breadcrumb">
  6.         <div class="container">
  7.             <div class="breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end">
  8.                 <div class="col-first">
  9.                     <h1>Panier</h1>
  10.                     <nav class="d-flex align-items-center">
  11.                         <a href="{{ path('ui_home') }}">Accueil<span class="lnr lnr-arrow-right"></span></a>
  12.                         <a href="javascript:void(0)">Panier</a>
  13.                     </nav>
  14.                 </div>
  15.             </div>
  16.         </div>
  17.     </section>
  18.     <!-- End Banner Area -->
  19.     <!--================Cart Area =================-->
  20.     <section class="cart_area">
  21.         <div class="container">
  22.             <div class="row">
  23.                 <div class="col-lg-8">
  24.                     <div class="cart_inner">
  25.                         <div class="table-responsive">
  26.                             <table class="table">
  27.                                 <thead>
  28.                                     <tr>
  29.                                         <th scope="col">Produit</th>
  30.                                         <th scope="col">Prix</th>
  31.                                         <th scope="col">Quantité</th>
  32.                                         <th scope="col">Total</th>
  33.                                         <th scope="col">Action</th>
  34.                                     </tr>
  35.                                 </thead>
  36.                                 <tbody>
  37.                                     {% for item in items %}
  38.                                     <tr>
  39.                                         <td data-label="Produit">
  40.                                             <div class="cart-item">
  41.                                                 {% set image = (item.image is defined and item.image|length > 0) ? item.image : asset('ui/img/category/s-p1.jpg') %}
  42.                                                 <img src="{{ image }}" alt="{{ item.name }}" class="cart-item-image">
  43.                                                 <div class="cart-item-info">
  44.                                                     <a href="{{ path('ui_product_show', { slug: item.slug }) }}" class="cart-item-name">{{ item.name }}</a>
  45.                                                 </div>
  46.                                             </div>
  47.                                         </td>
  48.                                         <td data-label="Prix">
  49.                                             <span class="cart-item-price">{{ item.price|number_format(2, '.', ' ') }} HTG</span>
  50.                                         </td>
  51.                                         <td data-label="Quantité">
  52.                                             <div class="quantity-controls">
  53.                                                 <button class="quantity-btn quantity-btn-decrease" id="decrease-btn-{{ item.id }}" data-product-id="{{ item.id }}" onclick="handleDecreaseOrRemove({{ item.id }})">
  54.                                                     {% if item.qty == 1 %}
  55.                                                         <i class="lnr lnr-trash"></i>
  56.                                                     {% else %}
  57.                                                         <span>−</span>
  58.                                                     {% endif %}
  59.                                                 </button>
  60.                                                 <input type="text" value="{{ item.qty }}" class="quantity-input" id="qty-{{ item.id }}" onchange="updateQuantity({{ item.id }}, this.value)">
  61.                                                 <button class="quantity-btn" onclick="changeQuantity({{ item.id }}, 1)">+</button>
  62.                                             </div>
  63.                                         </td>
  64.                                         <td data-label="Total">
  65.                                             <span class="cart-item-total" id="total-{{ item.id }}">{{ (item.price * item.qty)|number_format(2, '.', ' ') }} HTG</span>
  66.                                         </td>
  67.                                         <td data-label="Action">
  68.                                             <button class="remove-btn" onclick="removeFromCart({{ item.id }})">Supprimer</button>
  69.                                         </td>
  70.                                     </tr>
  71.                                     {% else %}
  72.                                         <tr>
  73.                                             <td colspan="5" style="text-align: center; padding: 50px;">
  74.                                                 Votre panier est vide.
  75.                                                 <br>
  76.                                                 <a href="{{ path('ui_listing') }}" style="color: #007185;">Continuer vos achats</a>
  77.                                             </td>
  78.                                         </tr>
  79.                                     {% endfor %}
  80.                                 </tbody>
  81.                             </table>
  82.                         </div>
  83.                     </div>
  84.                 </div>
  85.                 <div class="col-lg-4">
  86.                     <div class="cart-summary">
  87.                         <h3>Résumé de la commande</h3>
  88.                         <div class="cart-subtotal">
  89.                             <span id="cart-subtotal-label">Sous-total (<span id="cart-item-count">{{ items|length }}</span> article{{ items|length > 1 ? 's' : '' }})</span>
  90.                             <span id="cart-subtotal">{{ subtotal|number_format(2, '.', ' ') }} HTG</span>
  91.                         </div>
  92.                         <div class="cart-total">
  93.                             <span>Total</span>
  94.                             <span id="cart-total">{{ subtotal|number_format(2, '.', ' ') }} HTG</span>
  95.                         </div>
  96.                         <button class="checkout-btn mt-3" onclick="proceedToCheckout()">Passer la commande</button>
  97.                         <button class="continue-shopping" onclick="window.location.href='{{ path('ui_listing') }}'">Continuer vos achats</button>
  98.                     </div>
  99.                 </div>
  100.             </div>
  101.         </div>
  102.     </section>
  103.     <!--================End Cart Area =================-->
  104.     <!-- Modal personnalisé -->
  105.     <div id="customModal" class="custom-modal">
  106.         <div class="custom-modal-overlay"></div>
  107.         <div class="custom-modal-content">
  108.             <div class="custom-modal-header">
  109.                 <h3 class="custom-modal-title" id="modalTitle">Titre</h3>
  110.                 <button class="custom-modal-close" onclick="closeCustomModal()" aria-label="Fermer">
  111.                     <span>&times;</span>
  112.                 </button>
  113.             </div>
  114.             <div class="custom-modal-body" id="modalBody">
  115.                 <p id="modalMessage">Message</p>
  116.             </div>
  117.             <div class="custom-modal-footer" id="modalFooter">
  118.                 <button class="custom-modal-btn custom-modal-btn-primary" id="modalConfirmBtn" onclick="confirmModalAction()">Confirmer</button>
  119.                 <button class="custom-modal-btn custom-modal-btn-secondary" id="modalCancelBtn" onclick="closeCustomModal()">Annuler</button>
  120.             </div>
  121.         </div>
  122.     </div>
  123. {% endblock %}
  124. {% block stylesheets %}
  125. <style>
  126. .cart_area {
  127.     background-color: #f8f8f8;
  128.     padding: 20px 0;
  129. }
  130. .cart_inner {
  131.     background: white;
  132.     border-radius: 8px;
  133.     box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  134.     overflow: hidden;
  135. }
  136. .table th {
  137.     background-color: #f3f3f3;
  138.     border-bottom: 2px solid #ddd;
  139.     font-weight: 600;
  140.     color: #333;
  141.     padding: 15px;
  142. }
  143. .table td {
  144.     vertical-align: middle;
  145.     padding: 15px;
  146. }
  147. .cart-item {
  148.     display: flex;
  149.     align-items: center;
  150. }
  151. .cart-item-image {
  152.     width: 80px;
  153.     height: 80px;
  154.     object-fit: cover;
  155.     border-radius: 8px;
  156.     margin-right: 15px;
  157. }
  158. .cart-item-info {
  159.     flex-grow: 1;
  160. }
  161. .cart-item-name {
  162.     font-weight: 500;
  163.     color: #007185;
  164.     text-decoration: none;
  165.     margin-bottom: 5px;
  166. }
  167. .cart-item-name:hover {
  168.     text-decoration: underline;
  169. }
  170. .cart-item-price {
  171.     color: #333;
  172.     font-size: 18px;
  173.     font-weight: 700;
  174. }
  175. .quantity-controls {
  176.     display: flex !important;
  177.     align-items: center !important;
  178.     justify-content: center !important;
  179.     border: 1px solid #ddd !important;
  180.     border-radius: 4px !important;
  181.     width: 120px !important;
  182.     height: 40px !important;
  183.     position: relative !important;
  184.     background: white !important;
  185.     overflow: hidden !important;
  186. }
  187. .quantity-controls .quantity-btn {
  188.     background: #f8f8f8 !important;
  189.     border: none !important;
  190.     width: 35px !important;
  191.     height: 100% !important;
  192.     min-width: 35px !important;
  193.     cursor: pointer !important;
  194.     font-size: 18px !important;
  195.     font-weight: 600 !important;
  196.     color: #333 !important;
  197.     display: flex !important;
  198.     align-items: center !important;
  199.     justify-content: center !important;
  200.     padding: 0 !important;
  201.     margin: 0 !important;
  202.     line-height: 1 !important;
  203.     transition: background-color 0.2s ease !important;
  204.     position: relative !important;
  205.     z-index: 1 !important;
  206. }
  207. .quantity-controls .quantity-btn:first-child {
  208.     border-right: 1px solid #ddd !important;
  209. }
  210. .quantity-controls .quantity-btn:last-child {
  211.     border-left: 1px solid #ddd !important;
  212. }
  213. .quantity-controls .quantity-btn:hover {
  214.     background: #e7e7e7 !important;
  215.     color: #000 !important;
  216. }
  217. .quantity-controls .quantity-btn:active {
  218.     background: #d0d0d0 !important;
  219. }
  220. .quantity-controls .quantity-btn-remove {
  221.     background: #f8f8f8 !important;
  222.     color: #dc3545 !important;
  223. }
  224. .quantity-controls .quantity-btn-remove:hover {
  225.     background: #dc3545 !important;
  226.     color: white !important;
  227. }
  228. .quantity-controls .quantity-btn-remove i {
  229.     font-size: 16px !important;
  230. }
  231. .quantity-controls .quantity-btn-decrease span {
  232.     font-size: 24px !important;
  233.     font-weight: 300 !important;
  234.     line-height: 1 !important;
  235. }
  236. .quantity-controls .quantity-input {
  237.     border: none !important;
  238.     width: 50px !important;
  239.     min-width: 50px !important;
  240.     height: 100% !important;
  241.     text-align: center !important;
  242.     font-size: 15px !important;
  243.     font-weight: 500 !important;
  244.     padding: 0 !important;
  245.     margin: 0 !important;
  246.     background: white !important;
  247.     color: #333 !important;
  248.     outline: none !important;
  249.     box-shadow: none !important;
  250.     -moz-appearance: textfield !important;
  251.     position: relative !important;
  252.     z-index: 1 !important;
  253. }
  254. .quantity-controls .quantity-input::-webkit-outer-spin-button,
  255. .quantity-controls .quantity-input::-webkit-inner-spin-button {
  256.     -webkit-appearance: none !important;
  257.     margin: 0 !important;
  258. }
  259. /* Surcharger les styles externes qui pourraient interférer */
  260. .cart_inner .table tbody tr td .quantity-controls,
  261. .cart_inner .table tbody tr td .quantity-controls .quantity-btn,
  262. .cart_inner .table tbody tr td .quantity-controls .quantity-input {
  263.     position: relative !important;
  264. }
  265. .cart_inner .table tbody tr td .quantity-controls .quantity-btn:before,
  266. .cart_inner .table tbody tr td .quantity-controls .quantity-btn:after {
  267.     display: none !important;
  268.     content: none !important;
  269. }
  270. .cart-item-total {
  271.     font-size: 18px;
  272.     font-weight: 700;
  273.     color: #333;
  274. }
  275. /* Animation pour le badge du panier */
  276. @keyframes bounce {
  277.     0%, 20%, 50%, 80%, 100% {
  278.         transform: translateY(0);
  279.     }
  280.     40% {
  281.         transform: translateY(-5px);
  282.     }
  283.     60% {
  284.         transform: translateY(-3px);
  285.     }
  286. }
  287. /* Responsive Styles */
  288. @media (max-width: 991.98px) {
  289.     .cart_area {
  290.         padding: 15px 0;
  291.     }
  292.     
  293.     .cart-summary {
  294.         margin-top: 2rem;
  295.     }
  296. }
  297. @media (max-width: 767.98px) {
  298.     .cart_inner {
  299.         border-radius: 0;
  300.     }
  301.     
  302.     .table {
  303.         font-size: 0.875rem;
  304.     }
  305.     
  306.     .table th {
  307.         padding: 10px 8px;
  308.         font-size: 0.8rem;
  309.     }
  310.     
  311.     .table td {
  312.         padding: 10px 8px;
  313.     }
  314.     
  315.     .cart-item {
  316.         flex-direction: column;
  317.         align-items: flex-start;
  318.     }
  319.     
  320.     .cart-item-image {
  321.         width: 60px;
  322.         height: 60px;
  323.         margin-right: 0;
  324.         margin-bottom: 10px;
  325.     }
  326.     
  327.     .cart-item-name {
  328.         font-size: 0.9rem;
  329.     }
  330.     
  331.     .cart-item-price,
  332.     .cart-item-total {
  333.         font-size: 1rem;
  334.     }
  335.     
  336.     .quantity-controls {
  337.         width: 110px !important;
  338.         height: 38px !important;
  339.     }
  340.     
  341.     .quantity-controls .quantity-btn {
  342.         width: 32px !important;
  343.         font-size: 16px !important;
  344.     }
  345.     
  346.     .quantity-controls .quantity-input {
  347.         width: 46px !important;
  348.         font-size: 14px !important;
  349.     }
  350.     
  351.     .cart-summary {
  352.         margin-top: 1.5rem;
  353.         padding: 1.5rem;
  354.     }
  355.     
  356.     .checkout-btn,
  357.     .continue-shopping {
  358.         width: 100%;
  359.         margin-bottom: 0.5rem;
  360.     }
  361. }
  362. @media (max-width: 575.98px) {
  363.     .table thead {
  364.         display: none;
  365.     }
  366.     
  367.     .table tbody tr {
  368.         display: block;
  369.         margin-bottom: 1rem;
  370.         border: 1px solid #dee2e6;
  371.         border-radius: 8px;
  372.         padding: 1rem;
  373.     }
  374.     
  375.     .table tbody td {
  376.         display: flex;
  377.         justify-content: space-between;
  378.         align-items: center;
  379.         padding: 0.5rem 0;
  380.         border: none;
  381.         text-align: right;
  382.     }
  383.     
  384.     .table tbody td::before {
  385.         content: attr(data-label);
  386.         font-weight: bold;
  387.         text-align: left;
  388.         margin-right: 1rem;
  389.     }
  390.     
  391.     .table tbody td:first-child {
  392.         flex-direction: column;
  393.         align-items: flex-start;
  394.     }
  395.     
  396.     .cart-item-image {
  397.         width: 100%;
  398.         height: 200px;
  399.         margin-bottom: 1rem;
  400.     }
  401.     
  402.     .cart-item-name {
  403.         font-size: 1rem;
  404.         margin-bottom: 0.5rem;
  405.     }
  406.     
  407.     .quantity-controls {
  408.         width: 100% !important;
  409.         max-width: 150px !important;
  410.         margin: 0 auto !important;
  411.         justify-content: center !important;
  412.         height: 40px !important;
  413.     }
  414.     
  415.     .quantity-controls .quantity-btn {
  416.         width: 40px !important;
  417.         font-size: 18px !important;
  418.     }
  419.     
  420.     .quantity-controls .quantity-input {
  421.         width: 60px !important;
  422.         font-size: 16px !important;
  423.     }
  424.     
  425.     .remove-btn {
  426.         width: 100%;
  427.         margin-top: 0.5rem;
  428.     }
  429. }
  430. .remove-btn {
  431.     background: #ff9900;
  432.     color: white;
  433.     border: none;
  434.     padding: 8px 16px;
  435.     border-radius: 4px;
  436.     cursor: pointer;
  437.     font-size: 12px;
  438. }
  439. .remove-btn:hover {
  440.     background: #e68900;
  441. }
  442. .cart-summary {
  443.     background: white;
  444.     border-radius: 8px;
  445.     box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  446.     padding: 20px;
  447.     height: fit-content;
  448. }
  449. .cart-subtotal {
  450.     display: flex;
  451.     justify-content: space-between;
  452.     padding: 10px 0;
  453.     border-bottom: 1px solid #ddd;
  454.     margin-bottom: 15px;
  455. }
  456. .cart-total {
  457.     font-size: 20px;
  458.     font-weight: 700;
  459.     color: #333;
  460. }
  461. .checkout-btn {
  462.     background: #ffa200;
  463.     color: white;
  464.     border: none;
  465.     padding: 12px 24px;
  466.     border-radius: 4px;
  467.     cursor: pointer;
  468.     width: 100%;
  469.     font-size: 16px;
  470.     font-weight: 600;
  471. }
  472. .checkout-btn:hover {
  473.     background: #e68900;
  474. }
  475. .checkout-btn:disabled,
  476. .checkout-btn.disabled {
  477.     background: #cccccc !important;
  478.     color: #666666 !important;
  479.     cursor: pointer !important; /* Garder le curseur pointer pour indiquer que c'est cliquable */
  480.     opacity: 0.6;
  481.     pointer-events: auto !important; /* S'assurer que le clic fonctionne */
  482. }
  483. .checkout-btn:disabled:hover,
  484. .checkout-btn.disabled:hover {
  485.     background: #cccccc !important;
  486.     transform: none !important;
  487.     box-shadow: none !important;
  488. }
  489. .continue-shopping {
  490.     background: #095ad3;
  491.     color: #ffffff;
  492.     border: none;
  493.     padding: 12px 24px;
  494.     border-radius: 4px;
  495.     cursor: pointer;
  496.     width: 100%;
  497.     font-size: 16px;
  498.     font-weight: 600;
  499.     margin-top: 10px;
  500. }
  501. .continue-shopping:hover {
  502.     background: #e68900;
  503.     color: #ffffff
  504. }
  505. /* ============================================
  506.    MODAL PERSONNALISÉ
  507.    ============================================ */
  508. .custom-modal {
  509.     display: none;
  510.     position: fixed;
  511.     top: 0;
  512.     left: 0;
  513.     width: 100%;
  514.     height: 100%;
  515.     z-index: 10000;
  516.     opacity: 0;
  517.     transition: opacity 0.3s ease;
  518. }
  519. .custom-modal.show {
  520.     display: flex;
  521.     align-items: center;
  522.     justify-content: center;
  523.     opacity: 1;
  524. }
  525. .custom-modal-overlay {
  526.     position: absolute;
  527.     top: 0;
  528.     left: 0;
  529.     width: 100%;
  530.     height: 100%;
  531.     background: rgba(0, 0, 0, 0.5);
  532.     backdrop-filter: blur(4px);
  533.     -webkit-backdrop-filter: blur(4px);
  534. }
  535. .custom-modal-content {
  536.     position: relative;
  537.     background: white;
  538.     border-radius: 12px;
  539.     box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  540.     max-width: 500px;
  541.     width: 90%;
  542.     max-height: 90vh;
  543.     overflow: hidden;
  544.     transform: scale(0.9) translateY(-20px);
  545.     transition: transform 0.3s ease;
  546.     z-index: 10001;
  547. }
  548. .custom-modal.show .custom-modal-content {
  549.     transform: scale(1) translateY(0);
  550. }
  551. .custom-modal-header {
  552.     display: flex;
  553.     align-items: center;
  554.     justify-content: space-between;
  555.     padding: 20px 24px;
  556.     border-bottom: 1px solid #e0e0e0;
  557.     background: linear-gradient(135deg, #ffa200 0%, #ff9900 100%);
  558. }
  559. .custom-modal-header.error {
  560.     background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
  561. }
  562. .custom-modal-header.success {
  563.     background: linear-gradient(135deg, #28a745 0%, #218838 100%);
  564. }
  565. .custom-modal-header.warning {
  566.     background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%);
  567. }
  568. .custom-modal-header.info {
  569.     background: linear-gradient(135deg, #17a2b8 0%, #138496 100%);
  570. }
  571. .custom-modal-title {
  572.     margin: 0;
  573.     font-size: 20px;
  574.     font-weight: 600;
  575.     color: white;
  576.     display: flex;
  577.     align-items: center;
  578.     gap: 10px;
  579. }
  580. .custom-modal-title::before {
  581.     content: '';
  582.     width: 24px;
  583.     height: 24px;
  584.     display: inline-block;
  585.     background-size: contain;
  586.     background-repeat: no-repeat;
  587.     background-position: center;
  588. }
  589. .custom-modal-title.success::before {
  590.     content: '✓';
  591.     font-size: 20px;
  592.     font-weight: bold;
  593. }
  594. .custom-modal-title.error::before {
  595.     content: '✕';
  596.     font-size: 20px;
  597.     font-weight: bold;
  598. }
  599. .custom-modal-title.warning::before {
  600.     content: '⚠';
  601.     font-size: 20px;
  602.     font-weight: bold;
  603. }
  604. .custom-modal-title.info::before {
  605.     content: 'ℹ';
  606.     font-size: 20px;
  607.     font-weight: bold;
  608. }
  609. .custom-modal-close {
  610.     background: rgba(255, 255, 255, 0.15);
  611.     border: 2px solid rgba(255, 255, 255, 0.3);
  612.     color: white;
  613.     font-size: 22px;
  614.     font-weight: 300;
  615.     line-height: 1;
  616.     width: 36px;
  617.     height: 36px;
  618.     border-radius: 50%;
  619.     cursor: pointer;
  620.     display: flex;
  621.     align-items: center;
  622.     justify-content: center;
  623.     transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  624.     padding: 0;
  625.     margin: 0;
  626.     position: relative;
  627.     overflow: hidden;
  628. }
  629. .custom-modal-close::before {
  630.     content: '';
  631.     position: absolute;
  632.     top: 50%;
  633.     left: 50%;
  634.     width: 0;
  635.     height: 0;
  636.     border-radius: 50%;
  637.     background: rgba(255, 255, 255, 0.2);
  638.     transform: translate(-50%, -50%);
  639.     transition: width 0.3s ease, height 0.3s ease;
  640. }
  641. .custom-modal-close:hover {
  642.     background: rgba(255, 255, 255, 0.25);
  643.     border-color: rgba(255, 255, 255, 0.5);
  644.     transform: rotate(90deg) scale(1.1);
  645.     box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  646. }
  647. .custom-modal-close:hover::before {
  648.     width: 100%;
  649.     height: 100%;
  650. }
  651. .custom-modal-close:active {
  652.     transform: rotate(90deg) scale(0.95);
  653.     background: rgba(255, 255, 255, 0.3);
  654. }
  655. .custom-modal-close span {
  656.     position: relative;
  657.     z-index: 1;
  658.     display: block;
  659.     line-height: 1;
  660. }
  661. .custom-modal-body {
  662.     padding: 24px;
  663.     font-size: 16px;
  664.     line-height: 1.6;
  665.     color: #333;
  666.     max-height: 60vh;
  667.     overflow-y: auto;
  668. }
  669. .custom-modal-body p {
  670.     margin: 0;
  671. }
  672. .custom-modal-footer {
  673.     display: flex;
  674.     gap: 12px;
  675.     justify-content: flex-end;
  676.     padding: 16px 24px;
  677.     border-top: 1px solid #e0e0e0;
  678.     background: #f8f9fa;
  679. }
  680. .custom-modal-footer.single-button {
  681.     justify-content: center;
  682. }
  683. .custom-modal-btn {
  684.     padding: 10px 24px;
  685.     border: none;
  686.     border-radius: 6px;
  687.     font-size: 15px;
  688.     font-weight: 500;
  689.     cursor: pointer;
  690.     transition: all 0.2s ease;
  691.     min-width: 100px;
  692. }
  693. .custom-modal-btn-primary {
  694.     background: #ffa200;
  695.     color: white;
  696. }
  697. .custom-modal-btn-primary:hover {
  698.     background: #e68900;
  699.     transform: translateY(-1px);
  700.     box-shadow: 0 4px 8px rgba(255, 162, 0, 0.3);
  701. }
  702. .custom-modal-btn-secondary {
  703.     background: #6c757d;
  704.     color: white;
  705. }
  706. .custom-modal-btn-secondary:hover {
  707.     background: #5a6268;
  708.     transform: translateY(-1px);
  709.     box-shadow: 0 4px 8px rgba(108, 117, 125, 0.3);
  710. }
  711. .custom-modal-btn-success {
  712.     background: #28a745;
  713.     color: white;
  714. }
  715. .custom-modal-btn-success:hover {
  716.     background: #218838;
  717.     transform: translateY(-1px);
  718.     box-shadow: 0 4px 8px rgba(40, 167, 69, 0.3);
  719. }
  720. .custom-modal-btn-danger {
  721.     background: #dc3545;
  722.     color: white;
  723. }
  724. .custom-modal-btn-danger:hover {
  725.     background: #c82333;
  726.     transform: translateY(-1px);
  727.     box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
  728. }
  729. /* Animation pour les modals */
  730. @keyframes modalFadeIn {
  731.     from {
  732.         opacity: 0;
  733.     }
  734.     to {
  735.         opacity: 1;
  736.     }
  737. }
  738. @keyframes modalSlideIn {
  739.     from {
  740.         transform: scale(0.9) translateY(-20px);
  741.         opacity: 0;
  742.     }
  743.     to {
  744.         transform: scale(1) translateY(0);
  745.         opacity: 1;
  746.     }
  747. }
  748. .custom-modal.show .custom-modal-overlay {
  749.     animation: modalFadeIn 0.3s ease;
  750. }
  751. .custom-modal.show .custom-modal-content {
  752.     animation: modalSlideIn 0.3s ease;
  753. }
  754. /* Responsive */
  755. @media (max-width: 575.98px) {
  756.     .custom-modal-content {
  757.         width: 95%;
  758.         margin: 20px;
  759.     }
  760.     
  761.     .custom-modal-header {
  762.         padding: 16px 20px;
  763.     }
  764.     
  765.     .custom-modal-title {
  766.         font-size: 18px;
  767.     }
  768.     
  769.     .custom-modal-body {
  770.         padding: 20px;
  771.         font-size: 15px;
  772.     }
  773.     
  774.     .custom-modal-footer {
  775.         flex-direction: column;
  776.         padding: 16px 20px;
  777.     }
  778.     
  779.     .custom-modal-btn {
  780.         width: 100%;
  781.     }
  782. }
  783. </style>
  784. {% endblock %}
  785. {% block javascripts %}
  786. <script>
  787. // Variables globales pour le modal
  788. let modalConfirmCallback = null;
  789. let modalCancelCallback = null;
  790. // Fonctions pour gérer le modal personnalisé
  791. function showCustomModal(options) {
  792.     const modal = document.getElementById('customModal');
  793.     const title = document.getElementById('modalTitle');
  794.     const message = document.getElementById('modalMessage');
  795.     const footer = document.getElementById('modalFooter');
  796.     const confirmBtn = document.getElementById('modalConfirmBtn');
  797.     const cancelBtn = document.getElementById('modalCancelBtn');
  798.     const header = modal.querySelector('.custom-modal-header');
  799.     
  800.     // Réinitialiser les classes
  801.     header.className = 'custom-modal-header';
  802.     title.className = 'custom-modal-title';
  803.     footer.className = 'custom-modal-footer';
  804.     
  805.     // Définir le titre et le message
  806.     title.textContent = options.title || 'Information';
  807.     message.textContent = options.message || '';
  808.     
  809.     // Définir le type (success, error, warning, info)
  810.     const type = options.type || 'info';
  811.     header.classList.add(type);
  812.     title.classList.add(type);
  813.     
  814.     // Configurer les boutons
  815.     if (options.showCancel !== false) {
  816.         cancelBtn.style.display = 'block';
  817.         footer.classList.remove('single-button');
  818.     } else {
  819.         cancelBtn.style.display = 'none';
  820.         footer.classList.add('single-button');
  821.     }
  822.     
  823.     // Configurer le bouton de confirmation
  824.     if (options.confirmText) {
  825.         confirmBtn.textContent = options.confirmText;
  826.     }
  827.     
  828.     if (options.confirmClass) {
  829.         confirmBtn.className = 'custom-modal-btn ' + options.confirmClass;
  830.     } else {
  831.         confirmBtn.className = 'custom-modal-btn custom-modal-btn-primary';
  832.     }
  833.     
  834.     // Stocker les callbacks
  835.     modalConfirmCallback = options.onConfirm || null;
  836.     modalCancelCallback = options.onCancel || null;
  837.     
  838.     // Afficher le modal
  839.     modal.classList.add('show');
  840.     document.body.style.overflow = 'hidden';
  841. }
  842. function closeCustomModal() {
  843.     const modal = document.getElementById('customModal');
  844.     const cancelCallback = modalCancelCallback; // Sauvegarder avant réinitialisation
  845.     
  846.     modal.classList.remove('show');
  847.     document.body.style.overflow = '';
  848.     
  849.     // Réinitialiser les callbacks
  850.     modalConfirmCallback = null;
  851.     modalCancelCallback = null;
  852.     
  853.     // Appeler le callback d'annulation si présent
  854.     if (cancelCallback) {
  855.         cancelCallback();
  856.     }
  857. }
  858. function confirmModalAction() {
  859.     if (modalConfirmCallback) {
  860.         modalConfirmCallback();
  861.     }
  862.     closeCustomModal();
  863. }
  864. // Fermer le modal en cliquant sur l'overlay
  865. document.addEventListener('DOMContentLoaded', function() {
  866.     const modal = document.getElementById('customModal');
  867.     const overlay = modal.querySelector('.custom-modal-overlay');
  868.     
  869.     overlay.addEventListener('click', function() {
  870.         closeCustomModal();
  871.     });
  872.     
  873.     // Fermer avec la touche Escape
  874.     document.addEventListener('keydown', function(e) {
  875.         if (e.key === 'Escape' && modal.classList.contains('show')) {
  876.             closeCustomModal();
  877.         }
  878.     });
  879. });
  880. // Fonctions utilitaires pour différents types de modals
  881. function showAlert(title, message, type = 'info') {
  882.     showCustomModal({
  883.         title: title,
  884.         message: message,
  885.         type: type,
  886.         showCancel: false,
  887.         confirmText: 'OK'
  888.     });
  889. }
  890. function showConfirm(title, message, onConfirm, onCancel = null, type = 'warning') {
  891.     showCustomModal({
  892.         title: title,
  893.         message: message,
  894.         type: type,
  895.         showCancel: true,
  896.         confirmText: 'Confirmer',
  897.         cancelText: 'Annuler',
  898.         onConfirm: onConfirm,
  899.         onCancel: onCancel
  900.     });
  901. }
  902. function proceedToCheckout() {
  903.     // Vérifier qu'il y a des articles dans le panier
  904.     if (Object.keys(cartData).length === 0) {
  905.         showCustomModal({
  906.             title: 'Panier vide',
  907.             message: 'Votre panier est vide. Veuillez ajouter des articles avant de procéder à la commande.',
  908.             type: 'warning',
  909.             showCancel: false,
  910.             confirmText: 'OK',
  911.             onConfirm: function() {
  912.                 // Rediriger vers la page de listing pour encourager l'ajout d'articles
  913.                 window.location.href = '{{ path('ui_listing') }}';
  914.             }
  915.         });
  916.         return;
  917.     }
  918.     // Rediriger vers la page de checkout
  919.     window.location.href = '{{ path('ui_checkout') }}';
  920. }
  921. function changeQuantity(productId, delta) {
  922.     const qtyInput = document.getElementById('qty-' + productId);
  923.     if (!qtyInput) return;
  924.     
  925.     const currentQty = parseInt(qtyInput.value) || 0;
  926.     const newQty = Math.max(0, currentQty + delta);
  927.     
  928.     updateQuantity(productId, newQty);
  929. }
  930. function handleDecreaseOrRemove(productId) {
  931.     const qtyInput = document.getElementById('qty-' + productId);
  932.     if (!qtyInput) return;
  933.     
  934.     const currentQty = parseInt(qtyInput.value) || 0;
  935.     
  936.     if (currentQty === 1) {
  937.         // Si la quantité est 1, supprimer le produit
  938.         removeFromCart(productId);
  939.     } else {
  940.         // Sinon, diminuer la quantité
  941.         changeQuantity(productId, -1);
  942.     }
  943. }
  944. // Initialiser les données du panier
  945. let cartData = {}; // Stocker les données du panier côté client
  946. {% for item in items %}
  947.     {% set image = (item.image is defined and item.image) ? item.image : asset('ui/img/category/s-p1.jpg') %}
  948. cartData[{{ item.id }}] = {
  949.     id: {{ item.id }},
  950.     name: '{{ item.name }}',
  951.     price: {{ item.price }},
  952.     qty: {{ item.qty }},
  953.     image: '{{ image }}',
  954.     slug: '{{ item.slug }}'
  955. };
  956. {% endfor %}
  957. function updateQuantity(productId, newQty) {
  958.     const item = cartData[productId];
  959.     
  960.     if (!item) {
  961.         console.error('Produit non trouvé:', productId);
  962.         return;
  963.     }
  964.     
  965.     // Validation de la quantité
  966.     newQty = Math.max(0, parseInt(newQty) || 0);
  967.     
  968.     if (newQty === 0) {
  969.         removeFromCart(productId);
  970.         return;
  971.     }
  972.     
  973.     if (newQty > 99) {
  974.         showAlert('Quantité maximale', 'La quantité maximale est de 99 articles.', 'warning');
  975.         return;
  976.     }
  977.     
  978.     // Mettre à jour l'interface immédiatement (optimistic update)
  979.     updateCartDisplay(productId, newQty);
  980.     
  981.     // Envoyer la requête au serveur
  982.     fetch('{{ path("ui_cart_update") }}', {
  983.         method: 'POST',
  984.         headers: { 
  985.             'Content-Type': 'application/x-www-form-urlencoded',
  986.             'X-Requested-With': 'XMLHttpRequest'
  987.         },
  988.         body: 'productId=' + productId + '&qty=' + newQty
  989.     })
  990.     .then(response => {
  991.         console.log('🔍 Réponse HTTP:', response.status, response.statusText);
  992.         // Vérifier si la réponse est OK avant de parser
  993.         if (!response.ok) {
  994.             throw new Error('Erreur HTTP ' + response.status + ': ' + response.statusText);
  995.         }
  996.         return response.json();
  997.     })
  998.     .then(data => {
  999.         console.log('📦 Données reçues:', data);
  1000.         if (data.ok) {
  1001.             // Les données serveur confirment la mise à jour
  1002.             // Mettre à jour le sous-total avec les valeurs exactes du serveur
  1003.             if (data.subtotal !== undefined) {
  1004.                 updateSubtotalDisplay(data);
  1005.                 updateCartBadge(data.totalQty || 0);
  1006.             }
  1007.             console.log('✅ Panier mis à jour avec succès - Sous-total:', data.subtotal, 'HTG');
  1008.         } else {
  1009.             // En cas d'erreur, restaurer la valeur précédente
  1010.             const oldQty = item.qty;
  1011.             updateCartDisplay(productId, oldQty);
  1012.             showAlert('Erreur', data.message || 'Erreur lors de la mise à jour du panier.', 'error');
  1013.         }
  1014.     })
  1015.     .catch(error => {
  1016.         console.error('❌ Erreur détaillée:', error);
  1017.         console.error('❌ Type d\'erreur:', error.constructor.name);
  1018.         console.error('❌ Message d\'erreur:', error.message);
  1019.         // Restaurer la valeur précédente en cas d'erreur
  1020.         const oldQty = item.qty;
  1021.         updateCartDisplay(productId, oldQty);
  1022.         // Afficher un message d'erreur plus détaillé
  1023.         let errorMessage = 'Une erreur de connexion est survenue. Veuillez réessayer.';
  1024.         if (error.message.includes('HTTP')) {
  1025.             errorMessage = 'Erreur du serveur: ' + error.message;
  1026.         } else if (error.message.includes('JSON')) {
  1027.             errorMessage = 'Erreur de traitement des données. Veuillez recharger la page.';
  1028.         }
  1029.         showAlert('Erreur de connexion', errorMessage, 'error');
  1030.     });
  1031. }
  1032. function updateCartDisplay(productId, qty) {
  1033.     const item = cartData[productId];
  1034.     if (!item) return;
  1035.     
  1036.     // Mettre à jour les données locales
  1037.     item.qty = qty;
  1038.     
  1039.     // Mettre à jour la quantité dans l'input
  1040.     const qtyInput = document.getElementById('qty-' + productId);
  1041.     if (qtyInput) {
  1042.         qtyInput.value = qty;
  1043.     }
  1044.     
  1045.     // Mettre à jour le bouton de diminution/suppression
  1046.     const decreaseBtn = document.getElementById('decrease-btn-' + productId);
  1047.     if (decreaseBtn) {
  1048.         if (qty === 1) {
  1049.             // Changer en bouton de suppression (poubelle)
  1050.             decreaseBtn.innerHTML = '<i class="lnr lnr-trash"></i>';
  1051.             decreaseBtn.classList.add('quantity-btn-remove');
  1052.             decreaseBtn.classList.remove('quantity-btn-decrease');
  1053.         } else {
  1054.             // Changer en bouton de diminution (moins)
  1055.             decreaseBtn.innerHTML = '<span>−</span>';
  1056.             decreaseBtn.classList.add('quantity-btn-decrease');
  1057.             decreaseBtn.classList.remove('quantity-btn-remove');
  1058.         }
  1059.     }
  1060.     
  1061.     // Mettre à jour le total du produit
  1062.     const totalElement = document.getElementById('total-' + productId);
  1063.     if (totalElement) {
  1064.         const itemTotal = (item.price * qty);
  1065.         totalElement.textContent = itemTotal.toLocaleString('fr-FR', {
  1066.             minimumFractionDigits: 2,
  1067.             maximumFractionDigits: 2
  1068.         }) + ' HTG';
  1069.     }
  1070.     
  1071.     // Mettre à jour les totaux généraux
  1072.     updateCartSummary();
  1073. }
  1074. function updateCartTotals(data) {
  1075.     // Utiliser la fonction unifiée pour mettre à jour les totaux
  1076.     updateSubtotalDisplay(data);
  1077. }
  1078. function updateCartSummary() {
  1079.     // Calculer les totaux côté client depuis cartData
  1080.     let subtotal = 0;
  1081.     let totalItems = 0;
  1082.     Object.values(cartData).forEach(item => {
  1083.         subtotal += item.price * item.qty;
  1084.         totalItems += item.qty;
  1085.     });
  1086.     // Mettre à jour l'affichage du sous-total
  1087.     const subtotalElement = document.getElementById('cart-subtotal');
  1088.     if (subtotalElement) {
  1089.         subtotalElement.textContent = subtotal.toLocaleString('fr-FR', {
  1090.             minimumFractionDigits: 2,
  1091.             maximumFractionDigits: 2
  1092.         }) + ' HTG';
  1093.     }
  1094.     // Mettre à jour l'affichage du total
  1095.     const totalElement = document.getElementById('cart-total');
  1096.     if (totalElement) {
  1097.         totalElement.textContent = subtotal.toLocaleString('fr-FR', {
  1098.             minimumFractionDigits: 2,
  1099.             maximumFractionDigits: 2
  1100.         }) + ' HTG';
  1101.     }
  1102.     // Mettre à jour le compteur d'articles
  1103.     const itemCountElement = document.getElementById('cart-item-count');
  1104.     if (itemCountElement) {
  1105.         itemCountElement.textContent = totalItems;
  1106.     }
  1107.     // Mettre à jour le label du sous-total
  1108.     const subtotalLabel = document.getElementById('cart-subtotal-label');
  1109.     if (subtotalLabel) {
  1110.         const itemText = totalItems + ' article' + (totalItems > 1 ? 's' : '');
  1111.         subtotalLabel.innerHTML = 'Sous-total (<span id="cart-item-count">' + totalItems + '</span> ' + (totalItems > 1 ? 'articles' : 'article') + ')';
  1112.     }
  1113.     // Mettre à jour l'état du bouton checkout
  1114.     updateCheckoutButtonState();
  1115. }
  1116. // Fonction pour mettre à jour l'état du bouton checkout
  1117. function updateCheckoutButtonState() {
  1118.     const checkoutBtn = document.querySelector('.checkout-btn');
  1119.     const isCartEmpty = Object.keys(cartData).length === 0;
  1120.     if (checkoutBtn) {
  1121.         if (isCartEmpty) {
  1122.             checkoutBtn.classList.add('disabled');
  1123.             checkoutBtn.title = 'Votre panier est vide';
  1124.         } else {
  1125.             checkoutBtn.classList.remove('disabled');
  1126.             checkoutBtn.title = 'Procéder à la commande';
  1127.         }
  1128.     }
  1129. }
  1130. // Fonction pour mettre à jour le badge du panier
  1131. function updateCartBadge(totalQty) {
  1132.     const cartBadge = document.querySelector('.cart-badge');
  1133.     if (cartBadge) {
  1134.         cartBadge.textContent = totalQty || 0;
  1135.         console.log('🛒 Badge du panier mis à jour:', totalQty);
  1136.         // Animation du badge si la quantité change
  1137.         if (totalQty > 0) {
  1138.             cartBadge.style.animation = 'none';
  1139.             cartBadge.offsetHeight; // Trigger reflow
  1140.             cartBadge.style.animation = 'bounce 0.5s ease';
  1141.         }
  1142.     }
  1143. }
  1144. // Fonction améliorée pour mettre à jour le sous-total avec débogage
  1145. function updateSubtotalDisplay(data = null) {
  1146.     // Si des données serveur sont fournies, les utiliser (priorité absolue)
  1147.     if (data && data.subtotal !== undefined) {
  1148.         console.log('🔄 Mise à jour sous-total avec données serveur:', data.subtotal, 'HTG');
  1149.         const subtotalElement = document.getElementById('cart-subtotal');
  1150.         const totalElement = document.getElementById('cart-total');
  1151.         const formattedSubtotal = parseFloat(data.subtotal).toLocaleString('fr-FR', {
  1152.             minimumFractionDigits: 2,
  1153.             maximumFractionDigits: 2
  1154.         }) + ' HTG';
  1155.         if (subtotalElement) {
  1156.             subtotalElement.textContent = formattedSubtotal;
  1157.         }
  1158.         if (totalElement) {
  1159.             totalElement.textContent = formattedSubtotal;
  1160.         }
  1161.         // Mettre à jour le compteur d'articles
  1162.         const itemCountElement = document.getElementById('cart-item-count');
  1163.         if (itemCountElement && data.totalQty !== undefined) {
  1164.             const totalItems = data.totalQty;
  1165.             console.log('🔢 Mise à jour compteur articles:', totalItems, 'articles au total');
  1166.             const itemText = totalItems + ' article' + (totalItems > 1 ? 's' : '');
  1167.             const subtotalLabel = document.getElementById('cart-subtotal-label');
  1168.             if (subtotalLabel) {
  1169.                 subtotalLabel.innerHTML = 'Sous-total (<span id="cart-item-count">' + totalItems + '</span> ' + (totalItems > 1 ? 'articles' : 'article') + ')';
  1170.                 console.log('📊 Label mis à jour:', subtotalLabel.innerHTML);
  1171.             }
  1172.         }
  1173.         // Vérification de cohérence (debug)
  1174.         let clientSubtotal = 0;
  1175.         let clientTotalItems = 0;
  1176.         Object.values(cartData).forEach(item => {
  1177.             clientSubtotal += parseFloat(item.price) * parseInt(item.qty);
  1178.             clientTotalItems += parseInt(item.qty);
  1179.         });
  1180.         const serverSubtotal = parseFloat(data.subtotal);
  1181.         if (Math.abs(clientSubtotal - serverSubtotal) > 0.01) {
  1182.             console.warn('⚠️ Incohérence détectée:', {
  1183.                 client: clientSubtotal.toFixed(2),
  1184.                 server: serverSubtotal.toFixed(2),
  1185.                 difference: Math.abs(clientSubtotal - serverSubtotal).toFixed(2)
  1186.             });
  1187.         }
  1188.     } else {
  1189.         // Calcul de secours côté client (si pas de données serveur)
  1190.         console.log('🔄 Calcul sous-total côté client (secours)');
  1191.         let subtotal = 0;
  1192.         let totalItems = 0;
  1193.         Object.values(cartData).forEach(item => {
  1194.             subtotal += parseFloat(item.price) * parseInt(item.qty);
  1195.             totalItems += parseInt(item.qty);
  1196.         });
  1197.         const formattedSubtotal = subtotal.toLocaleString('fr-FR', {
  1198.             minimumFractionDigits: 2,
  1199.             maximumFractionDigits: 2
  1200.         }) + ' HTG';
  1201.         // Mettre à jour l'affichage du sous-total
  1202.         const subtotalElement = document.getElementById('cart-subtotal');
  1203.         if (subtotalElement) {
  1204.             subtotalElement.textContent = formattedSubtotal;
  1205.         }
  1206.         const totalElement = document.getElementById('cart-total');
  1207.         if (totalElement) {
  1208.             totalElement.textContent = formattedSubtotal;
  1209.         }
  1210.         // Mettre à jour le label du sous-total
  1211.         const subtotalLabel = document.getElementById('cart-subtotal-label');
  1212.         if (subtotalLabel) {
  1213.             const itemText = totalItems + ' article' + (totalItems > 1 ? 's' : '');
  1214.             subtotalLabel.innerHTML = 'Sous-total (<span id="cart-item-count">' + totalItems + '</span> ' + (totalItems > 1 ? 'articles' : 'article') + ')';
  1215.         }
  1216.     }
  1217. }
  1218. function removeFromCart(productId) {
  1219.     const item = cartData[productId];
  1220.     const itemName = item ? item.name : 'cet article';
  1221.     
  1222.     showConfirm(
  1223.         'Supprimer l\'article',
  1224.         `Êtes-vous sûr de vouloir supprimer "${itemName}" de votre panier ?`,
  1225.         function() {
  1226.             // Mettre à jour l'interface immédiatement
  1227.             const row = document.querySelector(`tr:has(#qty-${productId})`);
  1228.             if (row) {
  1229.                 row.style.opacity = '0.5';
  1230.                 row.style.pointerEvents = 'none';
  1231.             }
  1232.             
  1233.             performRemoveFromCart(productId, row);
  1234.         },
  1235.         null,
  1236.         'warning'
  1237.     );
  1238. }
  1239. function performRemoveFromCart(productId, row) {
  1240.     
  1241.     fetch('{{ path("ui_cart_remove") }}', {
  1242.         method: 'POST',
  1243.         headers: { 
  1244.             'Content-Type': 'application/x-www-form-urlencoded',
  1245.             'X-Requested-With': 'XMLHttpRequest'
  1246.         },
  1247.         body: 'productId=' + productId
  1248.     })
  1249.     .then(response => response.json())
  1250.     .then(data => {
  1251.         if (data.ok) {
  1252.             // Supprimer des données locales
  1253.             delete cartData[productId];
  1254.             
  1255.             // Supprimer la ligne du DOM
  1256.             const row = document.querySelector(`tr:has(#qty-${productId})`);
  1257.             if (row) {
  1258.                 row.remove();
  1259.             }
  1260.             
  1261.             // Mettre à jour les totaux
  1262.             updateCartTotals(data);
  1263.             
  1264.             // Mettre à jour le résumé du panier
  1265.             updateCartSummary();
  1266.             
  1267.             // Afficher un message de succès
  1268.             showAlert('Article supprimé', 'L\'article a été supprimé de votre panier avec succès.', 'success');
  1269.             
  1270.             // Vérifier si le panier est vide
  1271.             if (Object.keys(cartData).length === 0) {
  1272.                 setTimeout(function() {
  1273.                     location.reload();
  1274.                 }, 1500);
  1275.             }
  1276.         } else {
  1277.             // Restaurer l'affichage en cas d'erreur
  1278.             if (row) {
  1279.                 row.style.opacity = '1';
  1280.                 row.style.pointerEvents = 'auto';
  1281.             }
  1282.             showAlert('Erreur', data.message || 'Erreur lors de la suppression de l\'article.', 'error');
  1283.         }
  1284.     })
  1285.     .catch(error => {
  1286.         console.error('Erreur:', error);
  1287.         // Restaurer l'affichage en cas d'erreur réseau
  1288.         if (row) {
  1289.             row.style.opacity = '1';
  1290.             row.style.pointerEvents = 'auto';
  1291.         }
  1292.         showAlert('Erreur de connexion', 'Une erreur de connexion est survenue. Veuillez réessayer.', 'error');
  1293.     });
  1294. }
  1295. // Script pour forcer l'application des styles des boutons de quantité et initialiser l'état du bouton checkout
  1296. document.addEventListener('DOMContentLoaded', function() {
  1297.     // Initialiser l'état du bouton checkout
  1298.     updateCheckoutButtonState();
  1299.     const quantityControls = document.querySelectorAll('.quantity-controls');
  1300.     quantityControls.forEach(function(control) {
  1301.         if (control) {
  1302.             // Forcer les styles sur le conteneur
  1303.             control.style.display = 'flex';
  1304.             control.style.alignItems = 'center';
  1305.             control.style.justifyContent = 'center';
  1306.             control.style.border = '1px solid #ddd';
  1307.             control.style.borderRadius = '4px';
  1308.             control.style.width = '120px';
  1309.             control.style.height = '40px';
  1310.             control.style.position = 'relative';
  1311.             control.style.background = 'white';
  1312.             control.style.overflow = 'hidden';
  1313.             // Forcer les styles sur les boutons
  1314.             const buttons = control.querySelectorAll('.quantity-btn');
  1315.             buttons.forEach(function(btn, index) {
  1316.                 btn.style.background = '#f8f8f8';
  1317.                 btn.style.border = 'none';
  1318.                 btn.style.width = '35px';
  1319.                 btn.style.height = '100%';
  1320.                 btn.style.cursor = 'pointer';
  1321.                 btn.style.fontSize = '18px';
  1322.                 btn.style.fontWeight = '600';
  1323.                 btn.style.color = '#333';
  1324.                 btn.style.display = 'flex';
  1325.                 btn.style.alignItems = 'center';
  1326.                 btn.style.justifyContent = 'center';
  1327.                 btn.style.padding = '0';
  1328.                 btn.style.margin = '0';
  1329.                 btn.style.lineHeight = '1';
  1330.                 btn.style.position = 'relative';
  1331.                 btn.style.zIndex = '1';
  1332.                 // Ajouter les bordures entre les boutons
  1333.                 if (index === 0) {
  1334.                     btn.style.borderRight = '1px solid #ddd';
  1335.                 } else if (index === buttons.length - 1) {
  1336.                     btn.style.borderLeft = '1px solid #ddd';
  1337.                 }
  1338.             });
  1339.             // Forcer les styles sur l'input
  1340.             const input = control.querySelector('.quantity-input');
  1341.             if (input) {
  1342.                 input.style.border = 'none';
  1343.                 input.style.width = '50px';
  1344.                 input.style.height = '100%';
  1345.                 input.style.textAlign = 'center';
  1346.                 input.style.fontSize = '15px';
  1347.                 input.style.fontWeight = '500';
  1348.                 input.style.padding = '0';
  1349.                 input.style.margin = '0';
  1350.                 input.style.background = 'white';
  1351.                 input.style.color = '#333';
  1352.                 input.style.outline = 'none';
  1353.                 input.style.boxShadow = 'none';
  1354.                 input.style.position = 'relative';
  1355.                 input.style.zIndex = '1';
  1356.             }
  1357.         }
  1358.     });
  1359. });
  1360. </script>
  1361. {% endblock %}