templates/base_home.html.twig line 2164

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html lang="fr" class="no-js">
  3.     <head>
  4.         <!-- Mobile Specific Meta -->
  5.         <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
  6.         <meta name="mobile-web-app-capable" content="yes">
  7.         <meta name="apple-mobile-web-app-capable" content="yes">
  8.         <meta name="apple-mobile-web-app-status-bar-style" content="default">
  9.         <meta name="apple-mobile-web-app-title" content="MaketOu">
  10.         <meta name="theme-color" content="#ffa200">
  11.         <meta name="msapplication-TileColor" content="#ffa200">
  12.         <meta
  13.         name="msapplication-config" content="{{ asset('favicon/browserconfig.xml') }}">
  14.         <!-- Favicon-->
  15.         <link rel="shortcut icon" href="{{ asset('favicon/favicon-16x16.png') }}">
  16.         <link rel="apple-touch-icon" sizes="180x180" href="{{ asset('favicon/apple-touch-icon.png') }}">
  17.         <link rel="icon" type="image/png" sizes="32x32" href="{{ asset('favicon/favicon-32x32.png') }}">
  18.         <link
  19.         rel="icon" type="image/png" sizes="16x16" href="{{ asset('favicon/favicon-16x16.png') }}">
  20.         <!-- PWA Manifest -->
  21.         <link
  22.         rel="manifest" href="{{ asset('manifest.json') }}">
  23.         <!-- Author Meta -->
  24.         <meta
  25.         name="author" content="Foulgor Tech">
  26.         <!-- Meta Description -->
  27.         <meta
  28.         name="description" content="">
  29.         <!-- Meta Keyword -->
  30.         <meta
  31.         name="keywords" content="">
  32.         <!-- meta character set -->
  33.         <meta
  34.         charset="UTF-8">
  35.         <!-- Site Title -->
  36.         <title>
  37.             {% block title %}MaketOu
  38.             {% endblock %}
  39.         </title>
  40.         <!--
  41.                                 CSS
  42.                                 ============================================= -->
  43.         <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"/>
  44.         <link rel="stylesheet" href="{{ asset('ui/css/linearicons.css') }}">
  45.         <link rel="stylesheet" href="{{ asset('ui/css/font-awesome.min.css') }}">
  46.         <link rel="stylesheet" href="{{ asset('ui/css/themify-icons.css') }}">
  47.         <link rel="stylesheet" href="{{ asset('ui/css/bootstrap.css') }}">
  48.         <link rel="stylesheet" href="{{ asset('ui/css/owl.carousel.css') }}">
  49.         <link rel="stylesheet" href="{{ asset('ui/css/nice-select.css') }}">
  50.         <link rel="stylesheet" href="{{ asset('ui/css/nouislider.min.css') }}">
  51.         <link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.css') }}"/>
  52.         <link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.skinFlat.css') }}"/>
  53.         <link rel="stylesheet" href="{{ asset('ui/css/magnific-popup.css') }}">
  54.         <link rel="stylesheet" href="{{ asset('ui/css/main.css') }}">
  55.         <link rel="stylesheet" href="{{ asset('ui/css/cart-modal.css') }}">
  56.         <link rel="stylesheet" href="{{ asset('ui/css/buttons-unified.css') }}">
  57.         <link rel="stylesheet" href="{{ asset('ui/css/design-system.css') }}">
  58.         <link rel="stylesheet" href="{{ asset('ui/css/dark-mode-forced.css') }}">
  59.         <link rel="stylesheet" href="{{ asset('ui/css/dark-mode-complete.css') }}">
  60.         <link rel="stylesheet" href="{{ asset('ui/css/navbar-icons-enhanced.css') }}">
  61.         <link rel="stylesheet" href="{{ asset('ui/css/dark-mode-pages-specific.css') }}">
  62.         <meta
  63.         charset="UTF-8">
  64.         {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
  65.         <style>
  66.             /* Styles pour l'icône de notification */
  67.             .notification-icon {
  68.                 position: relative;
  69.                 display: inline-block;
  70.                 color: #333;
  71.                 text-decoration: none;
  72.                 transition: color 0.3s ease;
  73.             }
  74.             .notification-icon:hover {
  75.                 color: #007bff;
  76.             }
  77.             .notification-badge {
  78.                 position: absolute;
  79.                 top: -4px;
  80.                 right: -2px;
  81.                 background: #dc3545 !important;
  82.                 color: white !important;
  83.                 border-radius: 50%;
  84.                 width: 18px;
  85.                 height: 18px;
  86.                 font-size: 8px !important;
  87.                 display: flex !important;
  88.                 align-items: center !important;
  89.                 justify-content: center !important;
  90.                 font-weight: bold !important;
  91.                 animation: pulse 2s infinite;
  92.                 z-index: 10;
  93.                 line-height: 1 !important;
  94.             }
  95.             /* Forcer le texte en blanc pour tous les éléments du badge - avec spécificité maximale */
  96.             .notification-badge,
  97.             .notification-badge.text-white,
  98.             .notification-badge *,
  99.             #notificationBadge,
  100.             #notificationBadge.text-white,
  101.             #notificationBadge *,
  102.             span.notification-badge,
  103.             span.notification-badge.text-white,
  104.             span#notificationBadge,
  105.             span#notificationBadge.text-white {
  106.                 color: white !important;
  107.                 text-color: white !important;
  108.                 -webkit-text-fill-color: white !important;
  109.             }
  110.             /* S'assurer que le contenu texte est bien en blanc */
  111.             .notification-badge::before,
  112.             .notification-badge::after {
  113.                 color: white !important;
  114.             }
  115.             /* Forcer la couleur pour tous les sélecteurs possibles avec spécificité maximale */
  116.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge,
  117.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge.text-white,
  118.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span.notification-badge,
  119.             .notification-icon .notification-badge,
  120.             .notification-icon .notification-badge.text-white,
  121.             .notification-icon span.notification-badge,
  122.             a.notification-icon .notification-badge,
  123.             a.notification-icon span.notification-badge {
  124.                 color: white !important;
  125.                 text-color: white !important;
  126.                 -webkit-text-fill-color: white !important;
  127.             }
  128.             /* Forcer la couleur pour tous les enfants directs */
  129.             .notification-icon > .notification-badge,
  130.             .notification-icon > span.notification-badge {
  131.                 color: white !important;
  132.             }
  133.             @keyframes pulse {
  134.                 0% {
  135.                     transform: scale(1);
  136.                 }
  137.                 50% {
  138.                     transform: scale(1.1);
  139.                 }
  140.                 100% {
  141.                     transform: scale(1);
  142.                 }
  143.             }
  144.             .notification-icon:hover .notification-badge {
  145.                 animation: none;
  146.             }
  147.             /* Container pour les icônes - toujours alignées horizontalement */
  148.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
  149.                 display: flex !important;
  150.                 flex-direction: row !important;
  151.                 align-items: center !important;
  152.                 justify-content: center !important;
  153.                 gap: 15px !important;
  154.                 list-style: none !important;
  155.                 margin: 0 !important;
  156.                 padding: 0 !important;
  157.                 width: auto !important;
  158.             }
  159.             /* Surcharger les styles existants pour les li contenant le wrapper */
  160.             .header_area .navbar .nav.navbar-nav.navbar-right li:has(.navbar-icons-wrapper) {
  161.                 margin-left: 0 !important;
  162.                 margin-right: 0 !important;
  163.                 display: flex !important;
  164.                 align-items: center !important;
  165.                 justify-content: center !important;
  166.             }
  167.             /* Styles pour les icônes */
  168.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart,
  169.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon,
  170.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon,
  171.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .dark-mode-toggle,
  172.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
  173.                 display: flex !important;
  174.                 align-items: center !important;
  175.                 justify-content: center !important;
  176.                 width: 40px !important;
  177.                 height: 40px !important;
  178.                 min-width: 40px !important;
  179.                 min-height: 40px !important;
  180.                 color: #333 !important;
  181.                 text-decoration: none !important;
  182.                 transition: all 0.3s ease !important;
  183.                 border-radius: 50% !important;
  184.                 position: relative !important;
  185.                 margin: 0 !important;
  186.                 padding: 0 !important;
  187.             }
  188.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart:hover,
  189.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon:hover,
  190.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon:hover,
  191.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .dark-mode-toggle:hover,
  192.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search:hover {
  193.                 background-color: #f0f0f0 !important;
  194.                 color: #ffa200 !important;
  195.                 transform: scale(1.1) !important;
  196.             }
  197.             /* Surcharger le line-height de main.css */
  198.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart span,
  199.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span,
  200.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search span {
  201.                 font-size: 18px !important;
  202.                 line-height: 1 !important;
  203.                 display: flex !important;
  204.                 align-items: center !important;
  205.                 justify-content: center !important;
  206.                 color: inherit !important;
  207.                 font-weight: normal !important;
  208.             }
  209.             /* Icône de comparaison en noir comme le panier */
  210.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon {
  211.                 color: #333 !important;
  212.             }
  213.             /* L'icône elle-même (lnr-sync) doit être noire, mais pas le badge */
  214.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon > span.lnr-sync,
  215.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon span:not(.comparison-badge) {
  216.                 color: #333 !important;
  217.                 font-size: 18px !important;
  218.                 line-height: 1 !important;
  219.                 display: flex !important;
  220.                 align-items: center !important;
  221.                 justify-content: center !important;
  222.                 font-weight: normal !important;
  223.             }
  224.             /* Le badge doit rester blanc */
  225.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon .comparison-badge {
  226.                 color: white !important;
  227.             }
  228.             .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
  229.                 background: transparent !important;
  230.                 border: 0 !important;
  231.                 cursor: pointer !important;
  232.                 padding: 0 !important;
  233.             }
  234.             /* Alignement des icônes navbar sur mobile */
  235.             @media(max-width: 991.98px) {
  236.                 /* Container navbar-right en colonne sur mobile */
  237.                 .header_area .navbar .nav.navbar-nav.navbar-right {
  238.                     display: flex !important;
  239.                     flex-direction: column !important;
  240.                     align-items: center !important;
  241.                     justify-content: center !important;
  242.                     width: 100% !important;
  243.                     gap: 0 !important;
  244.                     margin: 0 !important;
  245.                     padding: 10px 0 !important;
  246.                 }
  247.                 /* Le container d'icônes reste horizontal */
  248.                 .header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
  249.                     width: 100% !important;
  250.                     justify-content: center !important;
  251.                     display: flex !important;
  252.                     flex-direction: row !important;
  253.                 }
  254.                 /* Le bouton compte en bas - pleine largeur */
  255.                 .header_area .navbar .nav.navbar-nav.navbar-right li:last-child {
  256.                     width: 100% !important;
  257.                     display: flex !important;
  258.                     justify-content: center !important;
  259.                     margin-top: 12px !important;
  260.                     padding-top: 12px !important;
  261.                     border-top: 1px solid #e0e0e0 !important;
  262.                 }
  263.                 /* Bouton compte responsive */
  264.                 .header_area .navbar .nav.navbar-nav.navbar-right .btn-account {
  265.                     width: 100%;
  266.                     max-width: 200px;
  267.                     padding: 10px 20px;
  268.                     font-size: 14px;
  269.                 }
  270.             }
  271.             /* Styles pour les messages flash - Design moderne */
  272.             .flash-message {
  273.                 animation: slideInDownFlash 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  274.                 position: relative;
  275.                 overflow: visible !important;
  276.                 border: none !important;
  277.                 padding: 16px 20px !important;
  278.                 border-radius: 12px !important;
  279.                 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
  280.                 display: flex !important;
  281.                 align-items: center;
  282.                 gap: 12px;
  283.                 margin-bottom: 15px;
  284.                 backdrop-filter: blur(10px);
  285.                 transition: all 0.3s ease;
  286.             }
  287.             /* S'assurer que les icônes Font Awesome sont visibles */
  288.             .flash-message i.fa {
  289.                 display: inline-block !important;
  290.                 visibility: visible !important;
  291.                 opacity: 1 !important;
  292.                 width: auto !important;
  293.                 height: auto !important;
  294.                 font-family: "FontAwesome" !important;
  295.                 font-style: normal !important;
  296.                 font-weight: normal !important;
  297.                 text-rendering: auto !important;
  298.                 -webkit-font-smoothing: antialiased !important;
  299.                 -moz-osx-font-smoothing: grayscale !important;
  300.                 line-height: 1 !important;
  301.                 flex-shrink: 0 !important;
  302.             }
  303.             /* Forcer l'affichage des pseudo-éléments Font Awesome */
  304.             .flash-message i.fa::before {
  305.                 display: inline-block !important;
  306.                 visibility: visible !important;
  307.                 opacity: 1 !important;
  308.                 content: attr(data-icon) !important;
  309.             }
  310.             /* Styles spécifiques pour chaque icône */
  311.             .flash-message i.fa-check-circle::before {
  312.                 content: "\f058" !important;
  313.             }
  314.             .flash-message i.fa-exclamation-circle::before {
  315.                 content: "\f06a" !important;
  316.             }
  317.             .flash-message i.fa-exclamation-triangle::before {
  318.                 content: "\f071" !important;
  319.             }
  320.             .flash-message i.fa-info-circle::before {
  321.                 content: "\f05a" !important;
  322.             }
  323.             .flash-message:hover {
  324.                 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2) !important;
  325.                 transform: translateY(-2px);
  326.             }
  327.             .flash-message::before {
  328.                 content: '';
  329.                 position: absolute;
  330.                 left: 0;
  331.                 top: 0;
  332.                 bottom: 0;
  333.                 width: 5px;
  334.                 background: linear-gradient(180deg, currentColor 0%, rgba(255,255,255,0.3) 100%);
  335.                 border-radius: 12px 0 0 12px;
  336.             }
  337.             .flash-message.alert-success {
  338.                 background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%) !important;
  339.                 color: #155724 !important;
  340.                 border-left: 5px solid #28a745 !important;
  341.             }
  342.             .flash-message.alert-success i.fa {
  343.                 color: #28a745 !important;
  344.                 font-size: 1.3rem !important;
  345.                 display: inline-block !important;
  346.                 margin-right: 8px;
  347.                 vertical-align: middle;
  348.             }
  349.             .flash-message.alert-danger {
  350.                 background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%) !important;
  351.                 color: #721c24 !important;
  352.                 border-left: 5px solid #dc3545 !important;
  353.             }
  354.             .flash-message.alert-danger i.fa {
  355.                 color: #dc3545 !important;
  356.                 font-size: 1.3rem !important;
  357.                 display: inline-block !important;
  358.                 margin-right: 8px;
  359.                 vertical-align: middle;
  360.             }
  361.             .flash-message.alert-warning {
  362.                 background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%) !important;
  363.                 color: #856404 !important;
  364.                 border-left: 5px solid #ffc107 !important;
  365.             }
  366.             .flash-message.alert-warning i.fa {
  367.                 color: #ffa200 !important;
  368.                 font-size: 1.3rem !important;
  369.                 display: inline-block !important;
  370.                 margin-right: 8px;
  371.                 vertical-align: middle;
  372.             }
  373.             .flash-message.alert-info {
  374.                 background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%) !important;
  375.                 color: #0c5460 !important;
  376.                 border-left: 5px solid #17a2b8 !important;
  377.             }
  378.             .flash-message.alert-info i.fa {
  379.                 color: #17a2b8 !important;
  380.                 font-size: 1.3rem !important;
  381.                 display: inline-block !important;
  382.                 margin-right: 8px;
  383.                 vertical-align: middle;
  384.             }
  385.             .flash-message strong {
  386.                 flex: 1;
  387.                 font-weight: 600;
  388.                 font-size: 0.95rem;
  389.                 line-height: 1.5;
  390.             }
  391.             .flash-message .btn-close {
  392.                 opacity: 0.7;
  393.                 padding: 8px !important;
  394.                 margin: -8px -8px -8px auto !important;
  395.                 transition: all 0.2s ease;
  396.                 border-radius: 50% !important;
  397.                 background: rgba(0, 0, 0, 0.05) !important;
  398.                 width: 32px !important;
  399.                 height: 32px !important;
  400.                 display: flex !important;
  401.                 align-items: center !important;
  402.                 justify-content: center !important;
  403.                 border: none !important;
  404.                 cursor: pointer !important;
  405.                 position: relative !important;
  406.             }
  407.             /* Ajouter une icône X visible dans le bouton */
  408.             .flash-message .btn-close::before {
  409.                 content: '×' !important;
  410.                 font-size: 24px !important;
  411.                 line-height: 1 !important;
  412.                 color: currentColor !important;
  413.                 font-weight: bold !important;
  414.                 display: block !important;
  415.                 position: absolute !important;
  416.                 top: 50% !important;
  417.                 left: 50% !important;
  418.                 transform: translate(-50%, -50%) !important;
  419.             }
  420.             .flash-message .btn-close:hover {
  421.                 opacity: 1 !important;
  422.                 background: rgba(0, 0, 0, 0.15) !important;
  423.                 transform: rotate(90deg) !important;
  424.             }
  425.             .flash-message .btn-close:hover::before {
  426.                 transform: translate(-50%, -50%) rotate(-90deg) !important;
  427.             }
  428.             @keyframes slideInDownFlash {
  429.                 from {
  430.                     opacity: 0;
  431.                     transform: translateY(-30px) scale(0.95);
  432.                 }
  433.                 to {
  434.                     opacity: 1;
  435.                     transform: translateY(0) scale(1);
  436.                 }
  437.             }
  438.             #flash-messages-container {
  439.                 margin-top: 20px;
  440.                 margin-bottom: 0;
  441.             }
  442.             @media(max-width: 768px) {
  443.                 .flash-message {
  444.                     padding: 14px 16px !important;
  445.                     font-size: 0.9rem;
  446.                 }
  447.                 .flash-message i {
  448.                     font-size: 1.2rem !important;
  449.                 }
  450.             }
  451.             /* Styles pour l'icône de comparaison */
  452.             .comparison-icon {
  453.                 position: relative;
  454.                 display: inline-block;
  455.                 color: #333;
  456.                 text-decoration: none;
  457.                 transition: all 0.3s ease;
  458.             }
  459.             .comparison-icon:hover {
  460.                 color: #ffa200;
  461.             }
  462.             .comparison-badge {
  463.                 position: absolute;
  464.                 top: -4px;
  465.                 right: -2px;
  466.                 background: #095ad3 !important;
  467.                 color: white !important;
  468.                 border-radius: 50%;
  469.                 width: 18px;
  470.                 height: 18px;
  471.                 font-size: 8px !important;
  472.                 display: flex !important;
  473.                 align-items: center !important;
  474.                 justify-content: center !important;
  475.                 font-weight: bold !important;
  476.                 animation: pulse 2s infinite;
  477.                 z-index: 10;
  478.                 line-height: 1 !important;
  479.             }
  480.             /* Correction de l'alignement des boutons actifs dans prd-bottom */
  481.             /* Ne s'applique qu'aux boutons actifs pour éviter de casser le comportement hover */
  482.             .single-product .product-details .prd-bottom .social-info.active {
  483.                 vertical-align: middle !important;
  484.                 position: relative !important;
  485.                 top: 0 !important;
  486.                 transform: none !important;
  487.                 margin: 0 !important;
  488.                 padding: 0 !important;
  489.                 line-height: normal !important;
  490.                 width: 35px !important;
  491.                 height: auto !important;
  492.                 min-height: auto !important;
  493.                 max-height: none !important;
  494.             }
  495.             .single-product .product-details .prd-bottom .social-info.wishlist-btn.active,
  496.             .single-product .product-details .prd-bottom .social-info.comparison-btn.active {
  497.                 vertical-align: middle !important;
  498.                 position: relative !important;
  499.                 top: 0 !important;
  500.                 transform: none !important;
  501.                 margin: 0 !important;
  502.                 padding: 0 !important;
  503.                 line-height: normal !important;
  504.                 width: 35px !important;
  505.                 height: auto !important;
  506.                 min-height: auto !important;
  507.                 max-height: none !important;
  508.             }
  509.             .single-product .product-details .prd-bottom .social-info.active .hover-text {
  510.                 margin: 0 !important;
  511.                 padding: 0 !important;
  512.                 line-height: normal !important;
  513.                 position: absolute !important;
  514.                 top: 0 !important;
  515.                 background: #ffa200 !important;
  516.                 left: -40px !important;
  517.             }
  518.             /* S'assurer qu'aucune marge supplémentaire n'est ajoutée quand un bouton est actif */
  519.             .single-product .product-details .prd-bottom .social-info.active span,
  520.             .single-product .product-details .prd-bottom .social-info.active p {
  521.                 margin: 0 !important;
  522.                 padding: 0 !important;
  523.             }
  524.             /* Empêcher le hover de changer la largeur quand le bouton est actif */
  525.             .single-product .product-details .prd-bottom .social-info.active:hover {
  526.                 width: 35px !important;
  527.             }
  528.             .comparison-icon:hover .comparison-badge {
  529.                 animation: none;
  530.             }
  531.             /* Styles pour les mega-menus - style comme la barre de recherche */
  532.             .mega-menu {
  533.                 width: 100%;
  534.                 max-width: 1200px;
  535.                 left: 50%;
  536.                 transform: translateX(-50%);
  537.                 padding: 25px;
  538.                 border-radius: 12px;
  539.                 box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
  540.                 border: none;
  541.                 background: white;
  542.                 display: none;
  543.                 position: absolute;
  544.                 top: 100%;
  545.                 z-index: 9999 !important;
  546.                 margin-top: 10px;
  547.                 opacity: 0;
  548.                 visibility: hidden;
  549.                 transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
  550.             }
  551.             .mega-menu.active {
  552.                 display: block;
  553.                 opacity: 1;
  554.                 visibility: visible;
  555.                 transform: translateX(-50%) translateY(0);
  556.             }
  557.             .nav-item.dropdown {
  558.                 position: relative !important;
  559.             }
  560.             @media(min-width: 768px) {
  561.                 .nav-item.dropdown {
  562.                     position: relative !important;
  563.                 }
  564.                 .mega-menu {
  565.                     position: absolute !important;
  566.                     left: 50% !important;
  567.                     transform: translateX(-50%) !important;
  568.                     width: auto !important;
  569.                     min-width: 600px !important;
  570.                 }
  571.             }
  572.             /* Correction supplémentaire pour l'affichage sur la page d'accueil */
  573.             body.home .nav-item.dropdown,
  574.             .nav-item.dropdown {
  575.                 position: relative !important;
  576.             }
  577.             body.home .mega-menu,
  578.             .mega-menu {
  579.                 position: absolute !important;
  580.                 left: 50% !important;
  581.                 transform: translateX(-50%) translateY(0) !important;
  582.                 width: auto !important;
  583.                 min-width: 600px !important;
  584.                 max-width: 90% !important;
  585.                 top: 100% !important;
  586.                 margin-top: 10px !important;
  587.             }
  588.             /* Assurer que les mega-menus sont toujours visibles au survol */
  589.             .nav-item.dropdown:hover .mega-menu {
  590.                 opacity: 1 !important;
  591.                 visibility: visible !important;
  592.                 display: block !important;
  593.                 transform: translateX(-50%) translateY(0) !important;
  594.             }
  595.             .mega-menu .dropdown-item {
  596.                 padding: 0;
  597.                 border: none;
  598.             }
  599.             .mega-menu .dropdown-header {
  600.                 font-weight: 700;
  601.                 color: #2c3e50;
  602.                 margin-bottom: 18px;
  603.                 padding-bottom: 10px;
  604.                 border-bottom: 3px solid #3498db;
  605.                 font-size: 16px;
  606.                 text-transform: uppercase;
  607.                 letter-spacing: 0.5px;
  608.             }
  609.             .mega-menu .dropdown-item a {
  610.                 display: block;
  611.                 padding: 10px 15px;
  612.                 color: #555;
  613.                 text-decoration: none;
  614.                 border-radius: 6px;
  615.                 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  616.                 position: relative;
  617.                 overflow: hidden;
  618.             }
  619.             .mega-menu .dropdown-item a::before {
  620.                 content: '';
  621.                 position: absolute;
  622.                 top: 0;
  623.                 left: -100%;
  624.                 width: 100%;
  625.                 height: 100%;
  626.                 background: linear-gradient(90deg, transparent, rgba(52, 152, 219, 0.1), transparent);
  627.                 transition: left 0.5s;
  628.             }
  629.             .mega-menu .dropdown-item a:hover::before {
  630.                 left: 100%;
  631.             }
  632.             .mega-menu .dropdown-item a:hover {
  633.                 background: linear-gradient(135deg, #f8f9fa, #e3f2fd);
  634.                 color: #2980b9;
  635.                 transform: translateX(8px);
  636.                 box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2);
  637.             }
  638.             .mega-menu .dropdown-item a i {
  639.                 width: 22px;
  640.                 text-align: center;
  641.                 margin-right: 8px;
  642.                 color: #3498db;
  643.                 transition: color 0.3s ease;
  644.             }
  645.             .mega-menu .dropdown-item a:hover i {
  646.                 color: #2980b9;
  647.             }
  648.             /* Animation d'apparition améliorée */
  649.             @keyframes slideInDown {
  650.                 from {
  651.                     opacity: 0;
  652.                     transform: translateY(-20px);
  653.                 }
  654.                 to {
  655.                     opacity: 1;
  656.                     transform: translateY(0);
  657.                 }
  658.             }
  659.             .mega-menu {
  660.                 animation: slideInDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  661.             }
  662.             /* Effet de survol sur le lien principal */
  663.             .nav-item.dropdown .nav-link {
  664.                 position: relative;
  665.                 transition: all 0.3s ease;
  666.             }
  667.             .nav-item.dropdown .nav-link::after {
  668.                 content: '';
  669.                 position: absolute;
  670.                 bottom: -2px;
  671.                 left: 50%;
  672.                 width: 0;
  673.                 height: 2px;
  674.                 background: linear-gradient(90deg, #3498db, #2980b9);
  675.                 transition: all 0.3s ease;
  676.                 transform: translateX(-50%);
  677.             }
  678.             .nav-item.dropdown:hover .nav-link::after {
  679.                 width: 100%;
  680.             }
  681.             .nav-item.dropdown:hover .nav-link {
  682.                 color: #2980b9;
  683.             }
  684.             @keyframes fadeInDown {
  685.                 from {
  686.                     opacity: 0;
  687.                     transform: translateY(-10px);
  688.                 }
  689.                 to {
  690.                     opacity: 1;
  691.                     transform: translateY(0);
  692.                 }
  693.             }
  694.             /* Responsive */
  695.             @media(max-width: 768px) {
  696.                 .mega-menu {
  697.                     width: 100%;
  698.                     left: 0;
  699.                     padding: 15px;
  700.                 }
  701.                 .mega-menu .row {
  702.                     margin: 0;
  703.                 }
  704.                 .mega-menu .col-md-3 {
  705.                     margin-bottom: 20px;
  706.                 }
  707.             }
  708.             /* ============================================
  709.             .footer-area {
  710.                 background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
  711.                 position: relative;
  712.                 overflow: hidden;
  713.                 padding: 50px 0 20px;
  714.             }
  715.             .footer-pattern {
  716.                 position: absolute;
  717.                 top: 0;
  718.                 left: 0;
  719.                 right: 0;
  720.                 bottom: 0;
  721.                 background-image: radial-gradient(circle at 20% 50%, rgba(255, 162, 0, 0.1) 0%, transparent 50%), radial-gradient(circle at 80% 80%, rgba(255, 162, 0, 0.08) 0%, transparent 50%);
  722.                 pointer-events: none;
  723.             }
  724.             .footer-area .container {
  725.                 position: relative;
  726.                 z-index: 1;
  727.             }
  728.             /* Widgets */
  729.             .single-footer-widget {
  730.                 color: rgba(255, 255, 255, 0.9);
  731.             }
  732.             .footer-brand {
  733.                 font-size: 1.6rem;
  734.                 font-weight: 700;
  735.                 background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  736.                 -webkit-background-clip: text;
  737.                 -webkit-text-fill-color: transparent;
  738.                 background-clip: text;
  739.                 margin-bottom: 12px;
  740.                 display: inline-block;
  741.             }
  742.             .footer-widget-title {
  743.                 color: #fff;
  744.                 font-size: 1rem;
  745.                 font-weight: 600;
  746.                 margin-bottom: 18px;
  747.                 position: relative;
  748.                 padding-bottom: 8px;
  749.             }
  750.             .footer-widget-title::after {
  751.                 content: '';
  752.                 position: absolute;
  753.                 bottom: 0;
  754.                 left: 0;
  755.                 width: 40px;
  756.                 height: 3px;
  757.                 background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  758.                 border-radius: 2px;
  759.             }
  760.             .footer-description {
  761.                 color: rgba(255, 255, 255, 0.7);
  762.                 line-height: 1.6;
  763.                 font-size: 0.9rem;
  764.                 margin-bottom: 0;
  765.             }
  766.             /* Liste de liens */
  767.             .footer-list {
  768.                 list-style: none;
  769.                 padding: 0;
  770.                 margin: 0;
  771.             }
  772.             .footer-list li {
  773.                 margin-bottom: 10px;
  774.             }
  775.             .footer-list li a {
  776.                 color: rgba(255, 255, 255, 0.7);
  777.                 text-decoration: none;
  778.                 transition: all 0.3s ease;
  779.                 font-size: 0.9rem;
  780.                 display: inline-flex;
  781.                 align-items: center;
  782.             }
  783.             .footer-list li a i {
  784.                 font-size: 0.8rem;
  785.                 opacity: 0.6;
  786.                 transition: all 0.3s ease;
  787.             }
  788.             .footer-list li a:hover {
  789.                 color: #ffa200;
  790.                 transform: translateX(5px);
  791.             }
  792.             .footer-list li a:hover i {
  793.                 opacity: 1;
  794.                 color: #ffa200;
  795.             }
  796.             /* Newsletter */
  797.             .footer-newsletter-text {
  798.                 color: rgba(255, 255, 255, 0.7);
  799.                 font-size: 0.85rem;
  800.                 margin-bottom: 15px;
  801.                 line-height: 1.5;
  802.             }
  803.             .footer-newsletter-wrapper {
  804.                 margin-bottom: 15px;
  805.             }
  806.             .newsletter-input-group {
  807.                 display: flex;
  808.                 align-items: center;
  809.                 background: rgba(255, 255, 255, 0.08);
  810.                 border-radius: 30px;
  811.                 padding: 4px 4px 4px 15px;
  812.                 backdrop-filter: blur(10px);
  813.                 border: 1px solid rgba(255, 255, 255, 0.15);
  814.                 transition: all 0.3s ease;
  815.                 gap: 8px;
  816.             }
  817.             .newsletter-input-group:focus-within {
  818.                 background: rgba(255, 255, 255, 0.12);
  819.                 border-color: rgba(255, 162, 0, 0.6);
  820.                 box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.15);
  821.             }
  822.             .newsletter-icon {
  823.                 color: rgba(255, 255, 255, 0.5);
  824.                 font-size: 0.9rem;
  825.                 flex-shrink: 0;
  826.             }
  827.             .newsletter-input-group:focus-within .newsletter-icon {
  828.                 color: #ffa200;
  829.             }
  830.             .newsletter-input {
  831.                 flex: 1;
  832.                 background: transparent;
  833.                 border: none;
  834.                 color: #fff;
  835.                 padding: 10px 8px;
  836.                 font-size: 0.9rem;
  837.                 min-width: 0;
  838.             }
  839.             .newsletter-input::placeholder {
  840.                 color: rgba(255, 255, 255, 0.4);
  841.                 font-size: 0.85rem;
  842.             }
  843.             .newsletter-input:focus {
  844.                 outline: none;
  845.                 box-shadow: none;
  846.             }
  847.             .newsletter-btn {
  848.                 background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
  849.                 border: none;
  850.                 color: white;
  851.                 width: 42px;
  852.                 height: 42px;
  853.                 min-width: 42px;
  854.                 min-height: 42px;
  855.                 border-radius: 50%;
  856.                 display: flex;
  857.                 align-items: center;
  858.                 justify-content: center;
  859.                 cursor: pointer;
  860.                 transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  861.                 flex-shrink: 0;
  862.                 box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4), 0 0 0 0 rgba(255, 162, 0, 0.4);
  863.                 font-size: 0.9rem;
  864.                 position: relative;
  865.                 overflow: hidden;
  866.             }
  867.             .newsletter-btn::before {
  868.                 content: '';
  869.                 position: absolute;
  870.                 top: 50%;
  871.                 left: 50%;
  872.                 width: 0;
  873.                 height: 0;
  874.                 border-radius: 50%;
  875.                 background: rgba(255, 255, 255, 0.3);
  876.                 transform: translate(-50%, -50%);
  877.                 transition: width 0.6s ease, height 0.6s ease;
  878.             }
  879.             .newsletter-btn:hover::before {
  880.                 width: 100%;
  881.                 height: 100%;
  882.             }
  883.             .newsletter-btn:hover {
  884.                 transform: translateX(3px) scale(1.05);
  885.                 box-shadow: 0 6px 20px rgba(255, 162, 0, 0.5), 0 0 0 8px rgba(255, 162, 0, 0.1);
  886.                 background: linear-gradient(135deg, #e8910a 0%, #ffa200 100%);
  887.             }
  888.             .newsletter-btn:active {
  889.                 transform: translateX(3px) scale(0.98);
  890.                 box-shadow: 0 3px 10px rgba(255, 162, 0, 0.4);
  891.             }
  892.             .newsletter-btn:focus {
  893.                 outline: none;
  894.                 box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4), 0 0 0 3px rgba(255, 162, 0, 0.3);
  895.             }
  896.             .newsletter-btn-icon,
  897.             .newsletter-btn-loader {
  898.                 position: relative;
  899.                 z-index: 2;
  900.                 display: flex;
  901.                 align-items: center;
  902.                 justify-content: center;
  903.                 transition: all 0.3s ease;
  904.             }
  905.             .newsletter-btn:hover .newsletter-btn-icon {
  906.                 transform: translateX(2px) rotate(-10deg);
  907.             }
  908.             .newsletter-btn.loading .newsletter-btn-icon {
  909.                 opacity: 0;
  910.                 transform: scale(0);
  911.             }
  912.             .newsletter-btn.loading .newsletter-btn-loader {
  913.                 display: flex !important;
  914.             }
  915.             .newsletter-btn.success {
  916.                 background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
  917.                 box-shadow: 0 4px 15px rgba(40, 167, 69, 0.4);
  918.             }
  919.             .newsletter-btn.success .newsletter-btn-icon i::before {
  920.                 content: "\f00c";
  921.             }
  922.             /* Trust badges */
  923.             .footer-trust-badges {
  924.                 display: flex;
  925.                 flex-direction: row;
  926.                 gap: 15px;
  927.                 flex-wrap: wrap;
  928.             }
  929.             .trust-item {
  930.                 display: flex;
  931.                 align-items: center;
  932.                 gap: 6px;
  933.                 color: rgba(255, 255, 255, 0.6);
  934.                 font-size: 0.8rem;
  935.             }
  936.             .trust-item i {
  937.                 color: #ffa200;
  938.                 font-size: 0.9rem;
  939.             }
  940.             /* Social icons */
  941.             .footer-social {
  942.                 margin-top: 0 !important;
  943.                 position: relative !important;
  944.                 width: 100% !important;
  945.                 clear: both !important;
  946.                 float: none !important;
  947.             }
  948.             .footer-social h6 {
  949.                 color: #fff;
  950.                 font-size: 0.95rem;
  951.                 font-weight: 600;
  952.                 margin-bottom: 12px !important;
  953.                 display: block !important;
  954.                 width: 100% !important;
  955.                 position: relative !important;
  956.                 clear: both !important;
  957.                 float: none !important;
  958.             }
  959.             .footer-social-icons {
  960.                 display: flex !important;
  961.                 gap: 12px !important;
  962.                 flex-wrap: wrap !important;
  963.                 align-items: center !important;
  964.                 justify-content: flex-start !important;
  965.                 width: 100% !important;
  966.                 position: relative !important;
  967.                 clear: both !important;
  968.                 float: none !important;
  969.                 margin: 0 !important;
  970.                 padding: 0 !important;
  971.             }
  972.             .social-icon {
  973.                 width: 38px !important;
  974.                 height: 38px !important;
  975.                 min-width: 38px !important;
  976.                 min-height: 38px !important;
  977.                 display: inline-flex !important;
  978.                 align-items: center !important;
  979.                 justify-content: center !important;
  980.                 background: rgba(255, 255, 255, 0.1) !important;
  981.                 border-radius: 50% !important;
  982.                 color: rgba(255, 255, 255, 0.8) !important;
  983.                 text-decoration: none !important;
  984.                 transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  985.                 font-size: 16px !important;
  986.                 line-height: 1 !important;
  987.                 backdrop-filter: blur(10px) !important;
  988.                 border: 1px solid rgba(255, 255, 255, 0.1) !important;
  989.                 flex-shrink: 0 !important;
  990.                 position: relative !important;
  991.                 float: none !important;
  992.                 margin: 0 !important;
  993.                 padding: 0 !important;
  994.                 vertical-align: middle !important;
  995.             }
  996.             .social-icon i {
  997.                 font-size: 16px !important;
  998.                 line-height: 1 !important;
  999.                 display: inline-block !important;
  1000.                 transition: transform 0.3s ease !important;
  1001.                 position: relative !important;
  1002.                 z-index: 1 !important;
  1003.                 margin: 0 !important;
  1004.                 padding: 0 !important;
  1005.             }
  1006.             .social-icon:hover {
  1007.                 background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%) !important;
  1008.                 transform: translateY(-5px) scale(1.1) !important;
  1009.                 color: white !important;
  1010.                 border-color: transparent !important;
  1011.                 box-shadow: 0 8px 20px rgba(255, 162, 0, 0.4) !important;
  1012.             }
  1013.             .social-icon:hover i {
  1014.                 transform: scale(1.1) !important;
  1015.             }
  1016.             /* Footer divider */
  1017.             .footer-divider {
  1018.                 height: 1px;
  1019.                 background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
  1020.                 margin: 30px 0 20px;
  1021.             }
  1022.             /* Footer bottom */
  1023.             .footer-bottom {
  1024.                 padding-top: 15px;
  1025.             }
  1026.             .footer-text {
  1027.                 color: rgba(255, 255, 255, 0.6);
  1028.                 font-size: 0.9rem;
  1029.                 margin: 0;
  1030.                 text-align: center;
  1031.             }
  1032.             /* Responsive */
  1033.             @media(max-width: 991.98px) {
  1034.                 .footer-area {
  1035.                     padding: 40px 0 20px;
  1036.                 }
  1037.                 .single-footer-widget {
  1038.                     margin-bottom: 30px;
  1039.                 }
  1040.                 .footer-social {
  1041.                     margin-top: 15px;
  1042.                 }
  1043.             }
  1044.             @media(max-width: 767.98px) {
  1045.                 .footer-area {
  1046.                     padding: 35px 0 18px;
  1047.                 }
  1048.                 .footer-brand {
  1049.                     font-size: 1.4rem;
  1050.                 }
  1051.                 .footer-widget-title {
  1052.                     font-size: 0.95rem;
  1053.                 }
  1054.                 .newsletter-input-group {
  1055.                     padding: 3px 3px 3px 12px;
  1056.                 }
  1057.                 .newsletter-input {
  1058.                     padding: 8px 6px;
  1059.                     font-size: 0.85rem;
  1060.                 }
  1061.                 .newsletter-btn {
  1062.                     width: 38px;
  1063.                     height: 38px;
  1064.                     min-width: 38px;
  1065.                     min-height: 38px;
  1066.                     font-size: 0.85rem;
  1067.                 }
  1068.                 .footer-trust-badges {
  1069.                     flex-direction: row;
  1070.                     flex-wrap: wrap;
  1071.                     justify-content: flex-start;
  1072.                     gap: 12px;
  1073.                 }
  1074.             }
  1075.             @media(max-width: 575.98px) {
  1076.                 .footer-area {
  1077.                     padding: 40px 0 20px;
  1078.                 }
  1079.                 .footer-brand {
  1080.                     font-size: 1.3rem;
  1081.                 }
  1082.                 .social-icon {
  1083.                     width: 38px;
  1084.                     height: 38px;
  1085.                     font-size: 16px;
  1086.                 }
  1087.                 .social-icon i {
  1088.                     font-size: 16px;
  1089.                 }
  1090.             }
  1091.         </style>
  1092.         {% block stylesheets %}
  1093.             {{ encore_entry_link_tags('app') }}
  1094.         {% endblock %}
  1095.         <!-- Script pour forcer l'application des styles CSS après chargement -->
  1096.         <script>
  1097.             document.addEventListener('DOMContentLoaded', function () { // Forcer le masquage du toggle sur grands écrans
  1098. if (window.innerWidth >= 992) {
  1099. const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
  1100. togglers.forEach(function (toggler) {
  1101. if (toggler) {
  1102. toggler.style.display = 'none';
  1103. toggler.style.visibility = 'hidden';
  1104. toggler.style.opacity = '0';
  1105. toggler.style.width = '0';
  1106. toggler.style.height = '0';
  1107. toggler.style.padding = '0';
  1108. toggler.style.margin = '0';
  1109. toggler.style.border = 'none';
  1110. toggler.style.position = 'absolute';
  1111. toggler.style.left = '-9999px';
  1112. }
  1113. });
  1114. const iconBars = document.querySelectorAll('.navbar-toggler .icon-bar, .header_area .navbar .navbar-toggler .icon-bar, .navbar-toggler-icon');
  1115. iconBars.forEach(function (bar) {
  1116. if (bar) {
  1117. bar.style.display = 'none';
  1118. bar.style.visibility = 'hidden';
  1119. }
  1120. });
  1121. }
  1122. // Alignement des icônes navbar sur mobile
  1123. const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
  1124. navbarRight.forEach(function (nav) {
  1125. if (nav && window.innerWidth<= 991.98) {
  1126.                     // Mobile: colonne avec icônes alignées horizontalement
  1127.                     nav.style.display = 'flex';
  1128.                     nav.style.flexDirection = 'column';
  1129.                     nav.style.alignItems = 'center';
  1130.                     nav.style.textAlign = 'center';
  1131.                     
  1132.                     // Le container d'icônes reste horizontal
  1133.                     const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
  1134.                     if (iconsContainer) {
  1135.                         iconsContainer.style.display = 'flex';
  1136.                         iconsContainer.style.flexDirection = 'row';
  1137.                         iconsContainer.style.justifyContent = 'center';
  1138.                     }
  1139.                     
  1140.                     // Le bouton compte en bas
  1141.                     const accountBtn = nav.querySelector('li:last-child');
  1142.                     if (accountBtn) {
  1143.                         accountBtn.style.width = '100%';
  1144.                         accountBtn.style.display = 'flex';
  1145.                         accountBtn.style.justifyContent = 'center';
  1146.                         accountBtn.style.marginTop = '12px';
  1147.                         accountBtn.style.paddingTop = '12px';
  1148.                         accountBtn.style.borderTop = '1px solid #e0e0e0';
  1149.                     }
  1150.                 } else if (nav && window.innerWidth> 991.98) { // Desktop: alignement horizontal normal
  1151. nav.style.display = 'flex';
  1152. nav.style.flexDirection = 'row';
  1153. nav.style.alignItems = 'center';
  1154. nav.style.justifyContent = 'flex-end';
  1155. }
  1156. });
  1157. });
  1158. // Réappliquer lors du redimensionnement
  1159. window.addEventListener('resize', function () {
  1160. if (window.innerWidth >= 992) {
  1161. const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
  1162. togglers.forEach(function (toggler) {
  1163. if (toggler) {
  1164. toggler.style.display = 'none';
  1165. toggler.style.visibility = 'hidden';
  1166. }
  1167. });
  1168. }
  1169. // Réappliquer l'alignement des icônes navbar
  1170. const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
  1171. navbarRight.forEach(function (nav) {
  1172. if (nav && window.innerWidth<= 991.98) {
  1173.                     // Mobile: colonne avec icônes alignées horizontalement
  1174.                     nav.style.display = 'flex';
  1175.                     nav.style.flexDirection = 'column';
  1176.                     nav.style.alignItems = 'center';
  1177.                     nav.style.textAlign = 'center';
  1178.                     
  1179.                     // Le container d'icônes reste horizontal
  1180.                     const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
  1181.                     if (iconsContainer) {
  1182.                         iconsContainer.style.display = 'flex';
  1183.                         iconsContainer.style.flexDirection = 'row';
  1184.                         iconsContainer.style.justifyContent = 'center';
  1185.                     }
  1186.                     
  1187.                     const accountBtn = nav.querySelector('li:last-child');
  1188.                     if (accountBtn) {
  1189.                         accountBtn.style.width = '100%';
  1190.                         accountBtn.style.display = 'flex';
  1191.                         accountBtn.style.justifyContent = 'center';
  1192.                         accountBtn.style.marginTop = '12px';
  1193.                         accountBtn.style.paddingTop = '12px';
  1194.                         accountBtn.style.borderTop = '1px solid #e0e0e0';
  1195.                     }
  1196.                 } else if (nav && window.innerWidth> 991.98) { // Desktop: alignement horizontal normal
  1197. nav.style.display = 'flex';
  1198. nav.style.flexDirection = 'row';
  1199. nav.style.alignItems = 'center';
  1200. nav.style.justifyContent = 'flex-end';
  1201. }
  1202. });
  1203. });
  1204.         </script>
  1205.     </head>
  1206.     <body
  1207.         {% if current_menu is defined and current_menu == "listing" %} id="category" {% endif %}
  1208.         style="transition: background-color 0.3s ease, color 0.3s ease;"
  1209.         class="theme-transition">
  1210.         <!-- Start Header Area -->
  1211.         <header class="header_area sticky-header">
  1212.             <div class="main_menu">
  1213.                 <nav class="navbar navbar-expand-lg navbar-light main_box">
  1214.                     <div
  1215.                         class="container">
  1216.                         <!-- Brand and toggle get grouped for better mobile display -->
  1217.                         <a class="navbar-brand logo_h" href="{{ path('ui_home') }}"><img style="width: auto;height: 60px" src="{{ asset('logo.png') }}" alt=""></a>
  1218.                         <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
  1219.                             <span class="icon-bar"></span>
  1220.                             <span class="icon-bar"></span>
  1221.                             <span class="icon-bar"></span>
  1222.                         </button>
  1223.                         <!-- Collect the nav links, forms, and other content for toggling -->
  1224.                         <div class="collapse navbar-collapse offset" id="navbarSupportedContent">
  1225.                             <ul class="nav navbar-nav menu_nav ml-auto">
  1226.                                 <li class="nav-item {% if current_menu is defined and current_menu == " home" %}active{% endif %}">
  1227.                                     <a class="nav-link" href="{{ path('ui_home') }}">Accueil</a>
  1228.                                 </li>
  1229.                                 <li class="nav-item {% if current_menu is defined and current_menu == " shops" %}active{% endif %}">
  1230.                                     <a class="nav-link" href="{{ path('ui_shops_list') }}">Boutiques</a>
  1231.                                 </li>
  1232.                                 <li class="nav-item {% if current_menu is defined and current_menu == " listing" %}active{% endif %}">
  1233.                                     <a class="nav-link" href="{{ path('ui_listing') }}">Produits</a>
  1234.                                 </li>
  1235.                                 <li class="nav-item {% if current_menu is defined and current_menu == " contact" %}active{% endif %}">
  1236.                                     <a class="nav-link" href="{{ path('ui_contact') }}">Contact</a>
  1237.                                 </li>
  1238.                             </ul>
  1239.                             <ul class="nav navbar-nav navbar-right">
  1240.                                 <li class="nav-item">
  1241.                                     <div class="navbar-icons-wrapper">
  1242.                                         <a href="{{ path('ui_cart') }}" class="cart {% if current_menu is defined and current_menu == " cart" %}active{% endif %}" aria-label="Panier">
  1243.                                             <span class="ti-bag"></span>
  1244.                                         </a>
  1245.                                         {% if app.user %}
  1246.                                             <a href="{{ path('notifications_list') }}" class="notification-icon" title="Notifications" aria-label="Notifications">
  1247.                                                 <span class="ti-bell"></span>
  1248.                                                 <span class="notification-badge text-white" id="notificationBadge" style="display: none;color: white;">0</span>
  1249.                                             </a>
  1250.                                             <a href="{{ path('ui_product_comparison') }}" class="comparison-icon" title="Comparaison" aria-label="Comparaison">
  1251.                                                 <span class="lnr lnr-sync"></span>
  1252.                                                 <span class="comparison-badge text-white" id="comparisonBadge" style="display: none;color: white;">0</span>
  1253.                                             </a>
  1254.                                         {% endif %}
  1255.                                         {{ include('components/_dark_mode_toggle.html.twig') }}
  1256.                                         <button class="search" type="button" aria-label="Recherche">
  1257.                                             <span class="lnr lnr-magnifier" id="search"></span>
  1258.                                         </button>
  1259.                                     </div>
  1260.                                 </li>
  1261.                                 <li class="nav-item">
  1262.                                     {% if app.user %}
  1263.                                         <a class="nav-link btn btn-primary btn-account text-white" href="{{ path('ui_account_index') }}">
  1264.                                             <i class="ti-user"></i>
  1265.                                             Mon compte
  1266.                                         </a>
  1267.                                     {% else %}
  1268.                                         <a class="nav-link btn btn-primary btn-account text-white" href="{{ path('ui_app_login') }}">
  1269.                                             <i class="ti-lock"></i>
  1270.                                             Se connecter
  1271.                                         </a>
  1272.                                     {% endif %}
  1273.                                 </li>
  1274.                             </ul>
  1275.                         </div>
  1276.                     </div>
  1277.                 </nav>
  1278.             </div>
  1279.             <div class="search_input" id="search_input_box">
  1280.                 <div class="container">
  1281.                     <form class="d-flex justify-content-between" id="search_form" action="{{ path('ui_listing') }}" method="get">
  1282.                         <input type="text" class="form-control" id="search_input" name="q" placeholder="Rechercher un produit, une boutique, une catégorie..." autocomplete="off" value="{{ app.request.query.get('q', '') }}">
  1283.                         <button type="button" class="btn" id="search_submit_btn">
  1284.                             <i class="lnr lnr-magnifier"></i>
  1285.                         </button>
  1286.                         {% if app.request.query.get('q') %}
  1287.                             <button type="button" class="btn btn-clear-search" id="clear_search_btn" title="Effacer la recherche">
  1288.                                 <i class="lnr lnr-cross"></i>
  1289.                             </button>
  1290.                         {% endif %}
  1291.                         <span class="lnr lnr-cross" id="close_search" title="Fermer la recherche"></span>
  1292.                     </form>
  1293.                     <div id="search_results" class="search-results-container"></div>
  1294.                 </div>
  1295.             </div>
  1296.             <script>
  1297.                 window.searchSuggestUrl = "{{ path('ui_api_search_suggest') }}";
  1298.                 window.listingUrl = "{{ path('ui_listing') }}";
  1299.             </script>
  1300.         </header>
  1301.         <!-- End Header Area -->
  1302.         <!-- Flash Messages -->
  1303.         <div class="container mt-3" id="flash-messages-container" style="position: relative; z-index: 1050;">
  1304.             <div class="row">
  1305.                 <div class="col-12">
  1306.                     {% for message in app.flashes('success') %}
  1307.                         <div class="alert alert-success alert-dismissible fade show flash-message" role="alert">
  1308.                             <i class="fa fa-check-circle"></i>
  1309.                             <strong>{{ message }}</strong>
  1310.                             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1311.                         </div>
  1312.                     {% endfor %}
  1313.                     {% for message in app.flashes('error') %}
  1314.                         <div class="alert alert-danger alert-dismissible fade show flash-message" role="alert">
  1315.                             <i class="fa fa-exclamation-circle"></i>
  1316.                             <strong>{{ message }}</strong>
  1317.                             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1318.                         </div>
  1319.                     {% endfor %}
  1320.                     {% for message in app.flashes('warning') %}
  1321.                         <div class="alert alert-warning alert-dismissible fade show flash-message" role="alert">
  1322.                             <i class="fa fa-exclamation-triangle"></i>
  1323.                             <strong>{{ message }}</strong>
  1324.                             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1325.                         </div>
  1326.                     {% endfor %}
  1327.                     {% for message in app.flashes('info') %}
  1328.                         <div class="alert alert-info alert-dismissible fade show flash-message" role="alert">
  1329.                             <i class="fa fa-info-circle"></i>
  1330.                             <strong>{{ message }}</strong>
  1331.                             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  1332.                         </div>
  1333.                     {% endfor %}
  1334.                 </div>
  1335.             </div>
  1336.         </div>
  1337.         {% block body %}{% endblock %}
  1338.         <!-- start footer Area -->
  1339.         <footer class="footer-area section_gap">
  1340.             <div class="footer-pattern"></div>
  1341.             <div class="container">
  1342.                 <div class="row">
  1343.                     <div class="col-lg-3 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1344.                         <div class="single-footer-widget">
  1345.                             <div class="footer-logo mb-3">
  1346.                                 <h4 class="footer-brand">MaketOu</h4>
  1347.                             </div>
  1348.                             <p class="footer-description">
  1349.                                 MaketOu est la première marketplace haïtienne qui connecte les acheteurs et les vendeurs. 
  1350.                                                                                                                         Nous offrons une plateforme sécurisée et moderne pour le commerce en ligne en Haïti.
  1351.                             </p>
  1352.                             <div class="footer-social mt-3">
  1353.                                 <h6 class="mb-2">Suivez-nous</h6>
  1354.                                 <div class="footer-social-icons">
  1355.                                     <a href="#" class="social-icon" aria-label="Facebook" title="Facebook">
  1356.                                         <i class="fa fa-facebook"></i>
  1357.                                     </a>
  1358.                                     <a href="#" class="social-icon" aria-label="Twitter" title="Twitter">
  1359.                                         <i class="fa fa-twitter"></i>
  1360.                                     </a>
  1361.                                     <a href="#" class="social-icon" aria-label="Instagram" title="Instagram">
  1362.                                         <i class="fa fa-instagram"></i>
  1363.                                     </a>
  1364.                                     <a href="#" class="social-icon" aria-label="LinkedIn" title="LinkedIn">
  1365.                                         <i class="fa fa-linkedin"></i>
  1366.                                     </a>
  1367.                                 </div>
  1368.                             </div>
  1369.                         </div>
  1370.                     </div>
  1371.                     <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1372.                         <div class="single-footer-widget">
  1373.                             <h6 class="footer-widget-title">Liens rapides</h6>
  1374.                             <ul class="footer-list">
  1375.                                 <li>
  1376.                                     <a href="{{ path('ui_home') }}">
  1377.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Accueil</a>
  1378.                                 </li>
  1379.                                 <li>
  1380.                                     <a href="{{ path('ui_listing') }}">
  1381.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Produits</a>
  1382.                                 </li>
  1383.                                 <li>
  1384.                                     <a href="{{ path('ui_shops_list') }}">
  1385.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Boutiques</a>
  1386.                                 </li>
  1387.                                 {% if app.user %}
  1388.                                     <li>
  1389.                                         <a href="{{ path('ui_account_index') }}">
  1390.                                             <i class="fa fa-angle-right me-2"></i>&nbsp;Mon compte</a>
  1391.                                     </li>
  1392.                                 {% else %}
  1393.                                     <li>
  1394.                                         <a href="{{ path('ui_app_login') }}">
  1395.                                             <i class="fa fa-angle-right me-2"></i>&nbsp;Se connecter</a>
  1396.                                     </li>
  1397.                                     <li>
  1398.                                         <a href="{{ path('ui_app_register') }}">
  1399.                                             <i class="fa fa-angle-right me-2"></i>&nbsp;S'inscrire</a>
  1400.                                     </li>
  1401.                                 {% endif %}
  1402.                             </ul>
  1403.                         </div>
  1404.                     </div>
  1405.                     <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1406.                         <div class="single-footer-widget">
  1407.                             <h6 class="footer-widget-title">Pour les vendeurs</h6>
  1408.                             <ul class="footer-list">
  1409.                                 <li>
  1410.                                     <a href="{{ path('seller_index') }}">
  1411.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Ouvrir une boutique</a>
  1412.                                 </li>
  1413.                                 <li>
  1414.                                     <a href="{{ path('seller_help_how_to_sell') }}">
  1415.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Comment vendre</a>
  1416.                                 </li>
  1417.                                 <li>
  1418.                                     <a href="{{ path('seller_help_pricing') }}">
  1419.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Tarifs</a>
  1420.                                 </li>
  1421.                                 <li>
  1422.                                     <a href="{{ path('seller_help_support') }}">
  1423.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Support vendeur</a>
  1424.                                 </li>
  1425.                             </ul>
  1426.                         </div>
  1427.                     </div>
  1428.                     <div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
  1429.                         <div class="single-footer-widget">
  1430.                             <h6 class="footer-widget-title">Support</h6>
  1431.                             <ul class="footer-list">
  1432.                                 <li>
  1433.                                     <a href="{{ path('ui_help') }}">
  1434.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Centre d'Aide</a>
  1435.                                 </li>
  1436.                                 <li>
  1437.                                     <a href="{{ path('ui_contact') }}">
  1438.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Contactez-nous</a>
  1439.                                 </li>
  1440.                                 <li>
  1441.                                     <a href="{{ path('ui_faq') }}">
  1442.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;FAQ</a>
  1443.                                 </li>
  1444.                                 <li>
  1445.                                     <a href="{{ path('ui_privacy') }}">
  1446.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Politique de Confidentialité</a>
  1447.                                 </li>
  1448.                                 <li>
  1449.                                     <a href="{{ path('ui_terms') }}">
  1450.                                         <i class="fa fa-angle-right me-2"></i>&nbsp;Conditions d'Utilisation</a>
  1451.                                 </li>
  1452.                             </ul>
  1453.                         </div>
  1454.                     </div>
  1455.                     <div class="col-lg-3 col-md-6 col-sm-6">
  1456.                         <div class="single-footer-widget">
  1457.                             <h6 class="footer-widget-title">Newsletter</h6>
  1458.                             <p class="footer-newsletter-text">Restez informé des dernières nouveautés</p>
  1459.                             <div class="footer-newsletter-wrapper" id="mc_embed_signup">
  1460.                                 <form action="{{ path('ui_newsletter_subscribe') }}" method="post" class="footer-newsletter-form">
  1461.                                     <div class="newsletter-input-group">
  1462.                                         <i class="fa fa-envelope newsletter-icon"></i>
  1463.                                         <input class="form-control newsletter-input" name="email" placeholder="Votre email" required type="email">
  1464.                                         <button type="submit" class="newsletter-btn" aria-label="S'abonner">
  1465.                                             <span class="newsletter-btn-icon">
  1466.                                                 <i class="fa fa-paper-plane" aria-hidden="true"></i>
  1467.                                             </span>
  1468.                                             <span class="newsletter-btn-loader" style="display: none;">
  1469.                                                 <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
  1470.                                             </span>
  1471.                                         </button>
  1472.                                     </div>
  1473.                                 </form>
  1474.                             </div>
  1475.                             <div class="footer-trust-badges mt-3">
  1476.                                 <div class="trust-item">
  1477.                                     <i class="fa fa-shield"></i>
  1478.                                     <span>Paiement sécurisé</span>
  1479.                                 </div>
  1480.                                 <div class="trust-item">
  1481.                                     <i class="fa fa-truck"></i>
  1482.                                     <span>Livraison rapide</span>
  1483.                                 </div>
  1484.                             </div>
  1485.                         </div>
  1486.                     </div>
  1487.                 </div>
  1488.                 <div class="footer-divider"></div>
  1489.                 <div class="footer-bottom d-flex justify-content-center align-items-center flex-wrap">
  1490.                     <p class="footer-text m-0">
  1491.                         Copyright &copy;<script>
  1492.                             document.write(new Date().getFullYear());
  1493.                         </script>
  1494.                         Tous droits réservés | Ce site a été fait avec
  1495.                         <i class="fa fa-heart-o" aria-hidden="true" style="color: #dc3545;"></i>
  1496.                         par
  1497.                         <a href="https://tech.foulgor.com" target="_blank" style="color: #ffa200;">Foulgor Tech</a>
  1498.                     </p>
  1499.                 </div>
  1500.             </div>
  1501.         </footer>
  1502.         <style>
  1503.             .search-results-container {
  1504.                 position: absolute;
  1505.                 top: 100%;
  1506.                 left: 15px;
  1507.                 right: 15px;
  1508.                 background: #fff;
  1509.                 border: 1px solid #ddd;
  1510.                 border-top: none;
  1511.                 z-index: 9999;
  1512.                 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  1513.                 max-height: 400px;
  1514.                 overflow-y: auto;
  1515.                 display: none;
  1516.                 border-radius: 0 0 4px 4px;
  1517.             }
  1518.             .search-results-container .list-group-item {
  1519.                 border-left: none;
  1520.                 border-right: none;
  1521.                 border-radius: 0;
  1522.                 cursor: pointer;
  1523.                 padding: 12px 15px;
  1524.                 transition: background-color 0.2s ease;
  1525.             }
  1526.             .search-results-container .list-group-item:hover {
  1527.                 background-color: #f8f9fa;
  1528.             }
  1529.             .search-results-container .list-group-item i {
  1530.                 color: #777;
  1531.                 font-size: 1.2rem;
  1532.             }
  1533.             /* Ensure container is relative for absolute positioning of results */
  1534.             #search_input_box .container {
  1535.                 position: relative;
  1536.             }
  1537.             #search_input_box .form-control {
  1538.                 flex: 1;
  1539.                 padding-right: 10px;
  1540.             }
  1541.             #search_input_box #search_submit_btn {
  1542.                 background: transparent;
  1543.                 border: 0;
  1544.                 color: #fff;
  1545.                 padding: 0 15px;
  1546.                 cursor: pointer;
  1547.                 transition: opacity 0.3s ease;
  1548.             }
  1549.             #search_input_box #search_submit_btn:hover {
  1550.                 opacity: 0.8;
  1551.             }
  1552.             #search_input_box .btn-clear-search {
  1553.                 background: transparent;
  1554.                 border: 0;
  1555.                 color: #fff;
  1556.                 padding: 0 10px;
  1557.                 cursor: pointer;
  1558.                 transition: opacity 0.3s ease;
  1559.                 display: none;
  1560.             }
  1561.             #search_input_box .btn-clear-search:hover {
  1562.                 opacity: 0.8;
  1563.             }
  1564.             #search_input_box .btn-clear-search i {
  1565.                 font-size: 1.1rem;
  1566.             }
  1567.             #search_input_box form {
  1568.                 position: relative;
  1569.                 align-items: center;
  1570.             }
  1571.         </style>
  1572.         <script>
  1573.             // Debounced autocomplete for header search
  1574. (function () {
  1575. const input = document.getElementById('search_input');
  1576. const box = document.getElementById('search_results');
  1577. const searchForm = document.getElementById('search_form');
  1578. const clearSearchBtn = document.getElementById('clear_search_btn');
  1579. const closeSearchBtn = document.getElementById('close_search');
  1580. if (! input || ! box) 
  1581. return;
  1582. // Fonction pour effacer la recherche
  1583. function clearSearch() {
  1584. input.value = '';
  1585. box.style.display = 'none';
  1586. box.innerHTML = '';
  1587. if (clearSearchBtn) {
  1588. clearSearchBtn.style.display = 'none';
  1589. }
  1590. // Si on est sur la page listing, utiliser AJAX pour mettre à jour
  1591. if (typeof currentFilters !== 'undefined') {
  1592. currentFilters.q = '';
  1593. currentFilters.page = 1;
  1594. if (typeof applyFilters === 'function') {
  1595. applyFilters();
  1596. }
  1597. if (typeof updateActiveFilters === 'function') {
  1598. updateActiveFilters();
  1599. }
  1600. } else { // Sinon, rediriger vers la page listing sans recherche
  1601. window.location.href = '{{ path('ui_listing') }}';
  1602. }
  1603. }
  1604. // Bouton pour effacer la recherche
  1605. if (clearSearchBtn) {
  1606. clearSearchBtn.addEventListener('click', function (e) {
  1607. e.preventDefault();
  1608. e.stopPropagation();
  1609. clearSearch();
  1610. });
  1611. }
  1612. // Bouton de soumission de recherche
  1613. var searchSubmitBtn = document.getElementById('search_submit_btn');
  1614. if (searchSubmitBtn) {
  1615. searchSubmitBtn.addEventListener('click', function (e) {
  1616. e.preventDefault();
  1617. e.stopPropagation();
  1618. const q = input.value.trim();
  1619. if (q.length === 0) {
  1620. clearSearch();
  1621. return false;
  1622. }
  1623. // Si on est sur la page listing, utiliser AJAX
  1624. if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
  1625. currentFilters.q = q;
  1626. currentFilters.page = 1;
  1627. applyFilters();
  1628. if (typeof updateActiveFilters === 'function') {
  1629. updateActiveFilters();
  1630. }
  1631. // Fermer la boîte de recherche
  1632. if (closeSearchBtn) {
  1633. closeSearchBtn.click();
  1634. }
  1635. } else { // Sinon, rediriger vers la page listing avec la recherche
  1636. const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1637. window.location.href = listingUrl;
  1638. }
  1639. return false;
  1640. });
  1641. }
  1642. // Gestion du formulaire de recherche
  1643. if (searchForm) {
  1644. searchForm.addEventListener('submit', function (e) {
  1645. e.preventDefault();
  1646. e.stopPropagation();
  1647. const q = input.value.trim();
  1648. if (q.length === 0) {
  1649. clearSearch();
  1650. return;
  1651. }
  1652. // Si on est sur la page listing, utiliser AJAX
  1653. if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
  1654. currentFilters.q = q;
  1655. currentFilters.page = 1;
  1656. applyFilters();
  1657. if (typeof updateActiveFilters === 'function') {
  1658. updateActiveFilters();
  1659. }
  1660. // Fermer la boîte de recherche
  1661. if (closeSearchBtn) {
  1662. closeSearchBtn.click();
  1663. }
  1664. } else { // Sinon, rediriger vers la page listing avec la recherche
  1665. const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1666. window.location.href = listingUrl;
  1667. }
  1668. return false;
  1669. });
  1670. }
  1671. let t = null;
  1672. input.addEventListener('input', function () {
  1673. const q = input.value.trim();
  1674. clearTimeout(t);
  1675. if (q.length === 0) {
  1676. box.style.display = 'none';
  1677. box.innerHTML = '';
  1678. // Afficher/masquer le bouton clear
  1679. if (clearSearchBtn) {
  1680. clearSearchBtn.style.display = 'none';
  1681. }
  1682. return;
  1683. }
  1684. // Afficher le bouton clear
  1685. if (clearSearchBtn) {
  1686. clearSearchBtn.style.display = 'block';
  1687. }
  1688. t = setTimeout(() => fetchSuggestions(q), 300);
  1689. });
  1690. function fetchSuggestions(q) {
  1691. const url = '{{ path('ui_api_search_suggest') }}' + '?q=' + encodeURIComponent(q);
  1692. fetch(url).then(r => r.json()).then(data => {
  1693. if (!data.success) {
  1694. box.style.display = 'none';
  1695. return;
  1696. }
  1697. renderSuggestions(data.results || []);
  1698. }).catch(() => {
  1699. box.style.display = 'none';
  1700. });
  1701. }
  1702. function renderSuggestions(items) {
  1703. if (! items.length) {
  1704. box.style.display = 'none';
  1705. box.innerHTML = '';
  1706. return;
  1707. }
  1708. box.innerHTML = items.map(it => suggestionItem(it)).join('');
  1709. box.style.display = 'block';
  1710. }
  1711. function suggestionItem(it) {
  1712. const icon = it.type === 'product' ? 'ti-package' : (it.type === 'shop' ? 'ti-briefcase' : (it.type === 'category' ? 'ti-layers' : 'ti-tag'));
  1713. const typeLabel = it.type === 'product' ? 'Produit' : (it.type === 'shop' ? 'Boutique' : (it.type === 'category' ? 'Catégorie' : 'Tag'));
  1714. return '<a href="' + it.url + '" class="list-group-item list-group-item-action d-flex align-items-center">' + '<i class="' + icon + ' me-3"></i>' + '<span class="flex-grow-1">' + escapeHtml(it.label) + '</span>' + '<span class="badge bg-light text-dark ms-2">' + typeLabel + '</span>' + '</a>';
  1715. }
  1716. function escapeHtml(s) {
  1717. return s.replace(/[&<>"]+/g, c => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;'}[c]));
  1718. }
  1719. document.addEventListener('click', function (e) {
  1720. if (! box.contains(e.target) && e.target !== input) {
  1721. box.style.display = 'none';
  1722. }
  1723. });
  1724. window.handleSearchSubmit = function (e) {
  1725. if (e) 
  1726. e.preventDefault();
  1727. const first = box.querySelector('a');
  1728. if (first) {
  1729. window.location.href = first.getAttribute('href');
  1730. return false;
  1731. }
  1732. // fallback to listing page search by name
  1733. const q = input.value.trim();
  1734. if (q.length) { // Si on est sur la page listing, utiliser AJAX
  1735. if (typeof currentFilters !== 'undefined') {
  1736. currentFilters.q = q;
  1737. currentFilters.page = 1;
  1738. if (typeof applyFilters === 'function') {
  1739. applyFilters();
  1740. }
  1741. if (typeof updateActiveFilters === 'function') {
  1742. updateActiveFilters();
  1743. }
  1744. } else {
  1745. window.location.href = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1746. }
  1747. }
  1748. return false;
  1749. }
  1750. // Gestion de la touche Enter dans le champ de recherche
  1751. if (input) {
  1752. input.addEventListener('keydown', function (e) {
  1753. if (e.key === 'Enter' || e.keyCode === 13) {
  1754. e.preventDefault();
  1755. e.stopPropagation();
  1756. const q = input.value.trim();
  1757. if (q.length === 0) {
  1758. clearSearch();
  1759. return false;
  1760. }
  1761. // Si on est sur la page listing, utiliser AJAX
  1762. if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
  1763. currentFilters.q = q;
  1764. currentFilters.page = 1;
  1765. applyFilters();
  1766. if (typeof updateActiveFilters === 'function') {
  1767. updateActiveFilters();
  1768. }
  1769. // Fermer la boîte de recherche
  1770. if (closeSearchBtn) {
  1771. closeSearchBtn.click();
  1772. }
  1773. } else { // Sinon, rediriger vers la page listing avec la recherche
  1774. const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1775. window.location.href = listingUrl;
  1776. }
  1777. return false;
  1778. }
  1779. });
  1780. }
  1781. // Ajouter un event listener au bouton de soumission (réutiliser la variable si elle existe déjà)
  1782. if (!searchSubmitBtn) {
  1783.     searchSubmitBtn = document.getElementById('search_submit_btn');
  1784. }
  1785. if (searchSubmitBtn) {
  1786. searchSubmitBtn.addEventListener('click', function (e) {
  1787. e.preventDefault();
  1788. e.stopPropagation();
  1789. const q = input.value.trim();
  1790. if (q.length === 0) {
  1791. clearSearch();
  1792. return false;
  1793. }
  1794. // Si on est sur la page listing, utiliser AJAX
  1795. if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
  1796. currentFilters.q = q;
  1797. currentFilters.page = 1;
  1798. applyFilters();
  1799. if (typeof updateActiveFilters === 'function') {
  1800. updateActiveFilters();
  1801. }
  1802. // Fermer la boîte de recherche
  1803. if (closeSearchBtn) {
  1804. closeSearchBtn.click();
  1805. }
  1806. } else { // Sinon, rediriger vers la page listing avec la recherche
  1807. const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
  1808. window.location.href = listingUrl;
  1809. }
  1810. return false;
  1811. });
  1812. }
  1813. // Afficher le bouton clear au chargement si une recherche est active
  1814. document.addEventListener('DOMContentLoaded', function () {
  1815. const q = input.value.trim();
  1816. if (q.length > 0 && clearSearchBtn) {
  1817. clearSearchBtn.style.display = 'block';
  1818. }
  1819. });
  1820. })();
  1821. // Gestion des mega-menus avec clic (comme la barre de recherche)
  1822. document.addEventListener('DOMContentLoaded', function () {
  1823. const dropdownItems = document.querySelectorAll('.nav-item.dropdown');
  1824. dropdownItems.forEach(function (item) {
  1825. const dropdownToggle = item.querySelector('.dropdown-toggle');
  1826. const dropdownMenu = item.querySelector('.mega-menu');
  1827. if (! dropdownToggle || ! dropdownMenu) 
  1828. return;
  1829. // Fermer les autres menus quand on ouvre un nouveau
  1830. function closeAllMenus() {
  1831. dropdownItems.forEach(function (otherItem) {
  1832. const otherMenu = otherItem.querySelector('.mega-menu');
  1833. if (otherMenu && otherMenu !== dropdownMenu) {
  1834. $(otherMenu).slideUp(300);
  1835. otherMenu.classList.remove('active');
  1836. }
  1837. });
  1838. }
  1839. // Toggle au clic (comme la barre de recherche)
  1840. dropdownToggle.addEventListener('click', function (e) {
  1841. e.preventDefault();
  1842. e.stopPropagation();
  1843. closeAllMenus();
  1844. if (dropdownMenu.classList.contains('active')) { // Fermer le menu
  1845. dropdownMenu.style.opacity = '0';
  1846. dropdownMenu.style.visibility = 'hidden';
  1847. dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
  1848. setTimeout(() => {
  1849. dropdownMenu.classList.remove('active');
  1850. dropdownMenu.style.display = 'none';
  1851. }, 300);
  1852. } else { // Ouvrir le menu
  1853. dropdownMenu.style.display = 'block';
  1854. setTimeout(() => {
  1855. dropdownMenu.classList.add('active');
  1856. dropdownMenu.style.opacity = '1';
  1857. dropdownMenu.style.visibility = 'visible';
  1858. dropdownMenu.style.transform = 'translateX(-50%) translateY(0)';
  1859. }, 10);
  1860. }
  1861. });
  1862. // Fermer au clic en dehors
  1863. document.addEventListener('click', function (e) {
  1864. if (! item.contains(e.target) && dropdownMenu.classList.contains('active')) {
  1865. dropdownMenu.style.opacity = '0';
  1866. dropdownMenu.style.visibility = 'hidden';
  1867. dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
  1868. setTimeout(() => {
  1869. dropdownMenu.classList.remove('active');
  1870. dropdownMenu.style.display = 'none';
  1871. }, 300);
  1872. }
  1873. });
  1874. // Animation des éléments du menu
  1875. const menuItems = dropdownMenu.querySelectorAll('.dropdown-item a');
  1876. menuItems.forEach(function (menuItem, index) {
  1877. menuItem.style.animationDelay = (index * 0.05) + 's';
  1878. });
  1879. });
  1880. // Gestion des clics sur mobile
  1881. if (window.innerWidth <= 768) {
  1882. dropdownItems.forEach(function (item) {
  1883. const dropdownToggle = item.querySelector('.dropdown-toggle');
  1884. const dropdownMenu = item.querySelector('.mega-menu');
  1885. dropdownToggle.addEventListener('click', function (e) {
  1886. e.preventDefault();
  1887. // Fermer tous les autres menus
  1888. dropdownItems.forEach(function (otherItem) {
  1889. if (otherItem !== item) {
  1890. const otherMenu = otherItem.querySelector('.mega-menu');
  1891. otherMenu.style.opacity = '0';
  1892. otherMenu.style.visibility = 'hidden';
  1893. otherMenu.style.transform = 'translateY(-10px)';
  1894. }
  1895. });
  1896. // Toggle le menu actuel
  1897. if (dropdownMenu.style.opacity === '1') {
  1898. dropdownMenu.style.opacity = '0';
  1899. dropdownMenu.style.visibility = 'hidden';
  1900. dropdownMenu.style.transform = 'translateY(-10px)';
  1901. } else {
  1902. dropdownMenu.style.opacity = '1';
  1903. dropdownMenu.style.visibility = 'visible';
  1904. dropdownMenu.style.transform = 'translateY(0)';
  1905. }
  1906. });
  1907. });
  1908. }
  1909. // Fermer les menus quand on clique ailleurs
  1910. document.addEventListener('click', function (e) {
  1911. if (! e.target.closest('.nav-item.dropdown')) {
  1912. dropdownItems.forEach(function (item) {
  1913. const dropdownMenu = item.querySelector('.mega-menu');
  1914. dropdownMenu.style.opacity = '0';
  1915. dropdownMenu.style.visibility = 'hidden';
  1916. dropdownMenu.style.transform = 'translateY(-10px)';
  1917. });
  1918. }
  1919. });
  1920. });
  1921.         </script>
  1922.         <!-- End footer Area -->
  1923.         <script src="{{ asset('ui/js/vendor/jquery-2.2.4.min.js') }}"></script>
  1924.         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  1925.         <script>
  1926.             // Initialiser le collapse Bootstrap 5 pour le menu hamburger après chargement de Bootstrap
  1927. document.addEventListener('DOMContentLoaded', function () { // Attendre que Bootstrap soit complètement chargé
  1928. if (typeof bootstrap !== 'undefined') {
  1929. const navbarToggler = document.querySelector('.navbar-toggler');
  1930. const navbarCollapse = document.getElementById('navbarSupportedContent');
  1931. if (navbarToggler && navbarCollapse) {
  1932. // Bootstrap 5 devrait gérer automatiquement avec data-bs-toggle
  1933. // Mais on s'assure que ça fonctionne en écoutant les événements
  1934. navbarCollapse.addEventListener('show.bs.collapse', function () {
  1935. navbarToggler.setAttribute('aria-expanded', 'true');
  1936. navbarToggler.classList.add('active');
  1937. });
  1938. navbarCollapse.addEventListener('hide.bs.collapse', function () {
  1939. navbarToggler.setAttribute('aria-expanded', 'false');
  1940. navbarToggler.classList.remove('active');
  1941. });
  1942. }
  1943. }
  1944. });
  1945.         </script>
  1946.         <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
  1947.         <script src="{{ asset('ui/js/jquery.ajaxchimp.min.js') }}"></script>
  1948.         <script src="{{ asset('ui/js/jquery.nice-select.min.js') }}"></script>
  1949.         <script src="{{ asset('ui/js/jquery.sticky.js') }}"></script>
  1950.         <script src="{{ asset('ui/js/nouislider.min.js') }}"></script>
  1951.         <script src="{{ asset('ui/js/countdown.js') }}"></script>
  1952.         <script src="{{ asset('ui/js/jquery.magnific-popup.min.js') }}"></script>
  1953.         <script src="{{ asset('ui/js/owl.carousel.min.js') }}"></script>
  1954.         <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE"></script>
  1955.         <script src="{{ asset('ui/js/gmaps.min.js') }}"></script>
  1956.         <script src="{{ asset('ui/js/main.js') }}"></script>
  1957.         {{ encore_entry_script_tags('app') }}
  1958.         <!-- PWA Service Worker Registration -->
  1959.         <script>
  1960.             // Enregistrement du Service Worker
  1961. if ('serviceWorker' in navigator) {
  1962. window.addEventListener('load', () => {
  1963. navigator.serviceWorker.register('{{ asset('sw.js') }}').then((registration) => {
  1964. console.log('Service Worker enregistré avec succès:', registration.scope);
  1965. // Vérifie les mises à jour du service worker
  1966. registration.addEventListener('updatefound', () => {
  1967. const newWorker = registration.installing;
  1968. newWorker.addEventListener('statechange', () => {
  1969. if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // Nouveau service worker disponible
  1970. if (confirm('Une nouvelle version de MaketOu est disponible. Voulez-vous l\'installer maintenant ?')) {
  1971. newWorker.postMessage({type: 'SKIP_WAITING'});
  1972. window.location.reload();
  1973. }
  1974. }
  1975. });
  1976. });
  1977. }).catch((error) => {
  1978. console.log('Échec de l\'enregistrement du Service Worker:', error);
  1979. });
  1980. // Écoute les messages du service worker
  1981. navigator.serviceWorker.addEventListener('message', (event) => {
  1982. console.log('Message du Service Worker:', event.data);
  1983. });
  1984. });
  1985. }
  1986.         </script>
  1987.         <!-- PWA Install Script -->
  1988.         <script src="{{ asset('js/pwa-install.js') }}"></script>
  1989.         <script>
  1990.             // Charger les compteurs au chargement de la page
  1991. document.addEventListener('DOMContentLoaded', function () {
  1992. {% if app.user %}
  1993. loadNotificationCount();
  1994. // Délai pour s'assurer que la session est bien établie après connexion
  1995. setTimeout(function () {
  1996. loadComparisonCount();
  1997. }, 500);{% endif %}
  1998. // Gérer le bouton de recherche mobile
  1999. const searchMobileBtn = document.getElementById('searchMobileBtn');
  2000. if (searchMobileBtn) {
  2001. searchMobileBtn.addEventListener('click', function (e) {
  2002. e.preventDefault();
  2003. // Utiliser le même comportement que le bouton desktop
  2004. const searchBox = document.getElementById('search_input_box');
  2005. if (searchBox) {
  2006. searchBox.style.display = 'block';
  2007. const searchInput = document.getElementById('search_input');
  2008. if (searchInput) {
  2009. setTimeout(() => searchInput.focus(), 100);
  2010. }
  2011. }
  2012. });
  2013. }
  2014. });
  2015. function loadNotificationCount() {
  2016. {% if app.user %}
  2017. fetch('{{ path("api_notifications_count") }}').then(response => {
  2018. if (!response.ok) {
  2019. throw new Error('Erreur HTTP: ' + response.status);
  2020. }
  2021. return response.json();
  2022. }).then(data => {
  2023. const count = data.count || 0;
  2024. // Mettre à jour tous les badges de notification
  2025. const badges = [document.getElementById('notificationBadge'), document.getElementById('notificationBadgeMobile'), document.querySelector('.notification-badge')].filter(badge => badge !== null);
  2026. badges.forEach(badge => {
  2027. if (count > 0) {
  2028. badge.textContent = count;
  2029. badge.style.display = 'flex';
  2030. badge.style.color = 'white';
  2031. badge.style.backgroundColor = '#dc3545';
  2032. } else {
  2033. badge.style.display = 'none';
  2034. }
  2035. });
  2036. // Si on est sur la page des notifications, mettre à jour aussi le contenu de la page
  2037. if (typeof updateNotificationPageContent === 'function') {
  2038. updateNotificationPageContent(count);
  2039. }
  2040. }).catch(error => {
  2041. console.error('Erreur lors du chargement du compteur de notifications:', error);
  2042. });{% endif %}
  2043. }
  2044. // Exposer la fonction globalement pour qu'elle puisse être appelée depuis d'autres pages
  2045. window.loadNotificationCount = loadNotificationCount;
  2046. // Recharger le compteur toutes les 30 secondes pour garder les données à jour
  2047. {% if app.user %}
  2048. setInterval(function () {
  2049. loadNotificationCount();
  2050. }, 30000);{% endif %}function loadComparisonCount() {
  2051. {% if app.user %}
  2052. // Ne pas appeler l'API si on est sur une page d'erreur ou de redirection
  2053. if (window.location.pathname.includes('/api/') || window.location.pathname.includes('/login')) {
  2054. return;
  2055. }
  2056. // Utiliser un try-catch pour éviter toute redirection
  2057. try {
  2058. fetch('{{ path("ui_api_comparison_count") }}', {
  2059. method: 'GET',
  2060. headers: {
  2061. 'X-Requested-With': 'XMLHttpRequest',
  2062. 'Accept': 'application/json'
  2063. },
  2064. credentials: 'same-origin',
  2065. redirect: 'error' // Empêcher les redirections automatiques
  2066. }).then(response => { // Vérifier si la réponse est OK
  2067. if (!response.ok) { // Si c'est une redirection (301, 302, etc.) ou une erreur d'authentification, ignorer silencieusement
  2068. if (response.status >= 300 && response.status < 400) {
  2069. console.warn('Redirection détectée, arrêt de la requête');
  2070. return null;
  2071. }
  2072. if (response.status === 401 || response.status === 403) {
  2073. console.warn('Utilisateur non authentifié pour la comparaison');
  2074. return null;
  2075. }
  2076. // Pour les autres erreurs, throw pour être capturé par catch
  2077. throw new Error('Erreur HTTP: ' + response.status);
  2078. }
  2079. // Vérifier que c'est bien du JSON
  2080. const contentType = response.headers.get('content-type');
  2081. if (! contentType || ! contentType.includes('application/json')) { // Si ce n'est pas du JSON, c'est probablement une redirection HTML
  2082. console.warn('La réponse n\'est pas du JSON, probable redirection');
  2083. return null;
  2084. }
  2085. return response.json();
  2086. }).then(data => { // Si data est null (redirection ou erreur), ne rien faire
  2087. if (!data || typeof data.count === 'undefined') {
  2088. return;
  2089. }
  2090. // Mettre à jour les badges desktop et mobile
  2091. const badges = [document.getElementById('comparisonBadge'), document.getElementById('comparisonBadgeMobile')];
  2092. badges.forEach(badge => {
  2093. if (badge) {
  2094. if (data.count > 0) {
  2095. badge.textContent = data.count;
  2096. badge.style.display = 'flex';
  2097. } else {
  2098. badge.style.display = 'none';
  2099. }
  2100. }
  2101. });
  2102. }).catch(error => { // Ne pas afficher d'erreur si c'est juste une absence d'authentification ou une redirection
  2103. if (error.message && !error.message.includes('401') && !error.message.includes('403') && !error.message.includes('redirect')) {
  2104. console.error('Erreur lors du chargement du compteur de comparaison:', error);
  2105. }
  2106. // Empêcher toute redirection vers l'URL de l'API
  2107. return false;
  2108. });
  2109. } catch (e) { // Capturer toute erreur qui pourrait causer une redirection
  2110. console.warn('Erreur lors de l\'appel à l\'API de comparaison:', e);
  2111. }
  2112. {% endif %}
  2113. }
  2114. // Fonction globale pour gérer la comparaison
  2115. function toggleComparison(productId, element) {
  2116. if (! element) {
  2117. element = document.querySelector(`.comparison-btn[data-product-id="${productId}"]`);
  2118. }
  2119. if (! element) {
  2120. console.error('Bouton de comparaison non trouvé pour le produit', productId);
  2121. return;
  2122. }
  2123. const originalHtml = element.innerHTML;
  2124. // Désactiver le bouton/lien
  2125. if (element.tagName === 'BUTTON') {
  2126. element.disabled = true;
  2127. } else {
  2128. element.style.pointerEvents = 'none';
  2129. element.style.opacity = '0.6';
  2130. } element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
  2131. // Vérifier d'abord si le produit est déjà en comparaison
  2132. let isInComparison = false;
  2133. fetch(`/api/comparison/status/${productId}`).then(response => response.json()).then(data => {
  2134. isInComparison = data.inComparison;
  2135. const url = isInComparison ? `/api/comparison/remove/${productId}` : `/api/comparison/add/${productId}`;
  2136. return fetch(url, {
  2137. method: 'POST',
  2138. headers: {
  2139. 'Content-Type': 'application/json',
  2140. 'X-Requested-With': 'XMLHttpRequest'
  2141. }
  2142. });
  2143. }).then(response => response.json()).then(data => {
  2144. if (data.success) { // Mettre à jour le compteur
  2145. loadComparisonCount();
  2146. // Mettre à jour l'apparence du bouton
  2147. if (element) { // Inverser l'état (car on vient de le changer)
  2148. const newState = ! isInComparison;
  2149. // Restaurer le HTML original puis mettre à jour
  2150. element.innerHTML = originalHtml;
  2151. if (newState) {
  2152. element.classList.add('active');
  2153. // Mettre à jour le texte pour les deux types de boutons
  2154. const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2155. if (textEl) 
  2156. textEl.textContent = 'En comparaison';
  2157. // Mettre à jour l'icône si nécessaire (chercher dans span ou i)
  2158. const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
  2159. if (iconEl) {
  2160. iconEl.style.color = '#28a745';
  2161. iconEl.classList.add('active');
  2162. }
  2163. } else {
  2164. element.classList.remove('active');
  2165. const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2166. if (textEl) 
  2167. textEl.textContent = 'Comparer';
  2168. // Restaurer la couleur de l'icône
  2169. const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
  2170. if (iconEl) {
  2171. iconEl.style.color = '';
  2172. iconEl.classList.remove('active');
  2173. }
  2174. }
  2175. }
  2176. // Afficher une notification
  2177. showNotification(data.message, 'success');
  2178. } else {
  2179. showNotification(data.message, 'error');
  2180. if (element) {
  2181. element.innerHTML = originalHtml;
  2182. }
  2183. }
  2184. }).catch(error => {
  2185. console.error('Erreur:', error);
  2186. showNotification('Une erreur est survenue', 'error');
  2187. if (element) {
  2188. element.innerHTML = originalHtml;
  2189. }
  2190. }). finally(() => {
  2191. if (element) {
  2192. if (element.tagName === 'BUTTON') {
  2193. element.disabled = false;
  2194. } else {
  2195. element.style.pointerEvents = '';
  2196. element.style.opacity = '';
  2197. }
  2198. }
  2199. });
  2200. }
  2201. // Fonction globale pour gérer les favoris
  2202. function toggleWishlist(productId, element) {
  2203. if (! element) {
  2204. element = document.querySelector(`.wishlist-btn[data-product-id="${productId}"]`);
  2205. }
  2206. if (! element) {
  2207. console.error('Bouton de wishlist non trouvé pour le produit', productId);
  2208. return;
  2209. }
  2210. const originalHtml = element.innerHTML;
  2211. // Désactiver le bouton/lien
  2212. if (element.tagName === 'BUTTON') {
  2213. element.disabled = true;
  2214. } else {
  2215. element.style.pointerEvents = 'none';
  2216. element.style.opacity = '0.6';
  2217. } element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
  2218. // Vérifier d'abord si le produit est déjà en favoris
  2219. let isInWishlist = false;
  2220. fetch(`/api/wishlist/status/${productId}`).then(response => response.json()).then(data => {
  2221. isInWishlist = data.inWishlist;
  2222. const url = isInWishlist ? `/api/wishlist/remove/${productId}` : `/api/wishlist/add/${productId}`;
  2223. return fetch(url, {
  2224. method: 'POST',
  2225. headers: {
  2226. 'Content-Type': 'application/json',
  2227. 'X-Requested-With': 'XMLHttpRequest'
  2228. }
  2229. });
  2230. }).then(response => response.json()).then(data => {
  2231. if (data.success) { // Mettre à jour l'apparence du bouton
  2232. if (element) { // Inverser l'état (car on vient de le changer)
  2233. const newState = ! isInWishlist;
  2234. // Restaurer le HTML original puis mettre à jour
  2235. element.innerHTML = originalHtml;
  2236. // Chercher l'icône dans span ou i
  2237. const icon = element.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
  2238. const text = element.querySelector('.wishlist-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
  2239. if (newState) {
  2240. if (icon) { // Pour les icônes linr, on change la classe
  2241. if (icon.classList.contains('lnr-heart')) {
  2242. icon.classList.remove('lnr-heart');
  2243. }
  2244. if (! icon.classList.contains('lnr-heart-filled')) {
  2245. icon.classList.add('lnr-heart-filled');
  2246. }
  2247. icon.style.color = '#dc3545';
  2248. }
  2249. element.classList.add('active');
  2250. if (text) 
  2251. text.textContent = 'Dans favoris';
  2252. } else {
  2253. if (icon) { // Restaurer l'icône originale
  2254. if (icon.classList.contains('lnr-heart-filled')) {
  2255. icon.classList.remove('lnr-heart-filled');
  2256. }
  2257. if (! icon.classList.contains('lnr-heart')) {
  2258. icon.classList.add('lnr-heart');
  2259. }
  2260. icon.style.color = '';
  2261. }
  2262. element.classList.remove('active');
  2263. if (text) 
  2264. text.textContent = 'Favoris';
  2265. }
  2266. }
  2267. // Afficher une notification
  2268. showNotification(data.message, 'success');
  2269. // Si on est sur la page wishlist, recharger la page
  2270. if (window.location.pathname.includes('/account/wishlist')) {
  2271. setTimeout(() => location.reload(), 1000);
  2272. }
  2273. } else {
  2274. showNotification(data.message, 'error');
  2275. if (element) {
  2276. element.innerHTML = originalHtml;
  2277. }
  2278. }
  2279. }).catch(error => {
  2280. console.error('Erreur:', error);
  2281. showNotification('Une erreur est survenue', 'error');
  2282. if (element) {
  2283. element.innerHTML = originalHtml;
  2284. }
  2285. }). finally(() => {
  2286. if (element) {
  2287. if (element.tagName === 'BUTTON') {
  2288. element.disabled = false;
  2289. } else {
  2290. element.style.pointerEvents = '';
  2291. element.style.opacity = '';
  2292. }
  2293. }
  2294. });
  2295. }
  2296. // Fonction pour afficher des notifications
  2297. function showNotification(message, type = 'success') { // Créer l'élément de notification
  2298. const notification = document.createElement('div');
  2299. notification.className = `alert alert-${
  2300. type === 'success' ? 'success' : 'danger'
  2301. } alert-dismissible fade show position-fixed`;
  2302. notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);';
  2303. notification.innerHTML = `
  2304.             ${message}
  2305.             <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
  2306.         `;
  2307. document.body.appendChild(notification);
  2308. // Supprimer automatiquement après 3 secondes
  2309. setTimeout(() => {
  2310. notification.remove();
  2311. }, 3000);
  2312. }
  2313. // Initialiser les alertes Bootstrap 5 pour la fermeture
  2314. document.addEventListener('DOMContentLoaded', function () { // S'assurer que les alertes peuvent être fermées
  2315. const alertCloseButtons = document.querySelectorAll('.flash-message .btn-close');
  2316. alertCloseButtons.forEach(function (button) {
  2317. button.addEventListener('click', function () {
  2318. const alert = this.closest('.alert');
  2319. if (alert) {
  2320. alert.classList.remove('show');
  2321. setTimeout(function () {
  2322. alert.remove();
  2323. }, 150);
  2324. }
  2325. });
  2326. });
  2327. // Charger les statuts initiaux des produits
  2328. {% if app.user %}
  2329. // Charger les statuts de comparaison pour tous les produits visibles
  2330. document.querySelectorAll('.comparison-btn[data-product-id]').forEach(btn => {
  2331. const productId = btn.dataset.productId;
  2332. fetch(`/api/comparison/status/${productId}`).then(response => response.json()).then(data => {
  2333. if (data.inComparison) {
  2334. btn.classList.add('active');
  2335. // Mettre à jour le texte pour les deux types de boutons
  2336. const textEl = btn.querySelector('.comparison-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
  2337. if (textEl) 
  2338. textEl.textContent = 'En comparaison';
  2339. // Mettre à jour l'icône si nécessaire
  2340. const iconEl = btn.querySelector('.lnr-sync, [class*="sync"]');
  2341. if (iconEl) 
  2342. iconEl.style.color = '#28a745';
  2343. }
  2344. }).catch(() => {});
  2345. });
  2346. // Charger les statuts de wishlist pour tous les produits visibles
  2347. document.querySelectorAll('.wishlist-btn[data-product-id]').forEach(btn => {
  2348. const productId = btn.dataset.productId;
  2349. fetch(`/api/wishlist/status/${productId}`).then(response => response.json()).then(data => {
  2350. if (data.inWishlist) {
  2351. // Chercher l'icône dans span ou i
  2352. const icon = btn.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
  2353. if (icon) {
  2354. icon.classList.remove('lnr-heart');
  2355. icon.classList.add('lnr-heart');
  2356. icon.style.color = '#ffa200';
  2357. }
  2358. // Mettre à jour le texte pour les deux types de boutons
  2359. const textEl = btn.querySelector('.wishlist-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
  2360. if (textEl) 
  2361. textEl.textContent = 'favoris';
  2362. }
  2363. }).catch(() => {});
  2364. });
  2365. // Charger le compteur de comparaison
  2366. loadComparisonCount();{% endif %}
  2367. });
  2368.         </script>
  2369.         <script>
  2370.             // Loader global pour tous les boutons de formulaire
  2371. document.addEventListener('DOMContentLoaded', function () { // Intercepter tous les formulaires
  2372. const forms = document.querySelectorAll('form');
  2373. forms.forEach(function (form) {
  2374. form.addEventListener('submit', function (e) {
  2375. const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"], button:not([type])');
  2376. submitButtons.forEach(function (button) { // Vérifier si le bouton n'est pas déjà désactivé
  2377. if (! button.disabled && ! button.classList.contains('loading')) {
  2378. button.classList.add('loading');
  2379. button.disabled = true;
  2380. // Sauvegarder le texte original
  2381. const originalText = button.innerHTML;
  2382. button.setAttribute('data-original-text', originalText);
  2383. // Ajouter le loader
  2384. button.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Envoi...';
  2385. // Si le formulaire n'est pas valide, réactiver le bouton après 1 seconde
  2386. setTimeout(function () {
  2387. if (! form.checkValidity()) {
  2388. button.classList.remove('loading');
  2389. button.disabled = false;
  2390. button.innerHTML = originalText;
  2391. }
  2392. }, 1000);
  2393. }
  2394. });
  2395. });
  2396. });
  2397. // Pour les boutons qui déclenchent des actions AJAX
  2398. const ajaxButtons = document.querySelectorAll('[data-ajax], .ajax-submit, button[data-submit]');
  2399. ajaxButtons.forEach(function (button) {
  2400. button.addEventListener('click', function (e) {
  2401. if (this.disabled || this.classList.contains('loading')) {
  2402. e.preventDefault();
  2403. return false;
  2404. }
  2405. this.classList.add('loading');
  2406. this.disabled = true;
  2407. const originalText = this.innerHTML;
  2408. this.setAttribute('data-original-text', originalText);
  2409. this.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Chargement...';
  2410. });
  2411. });
  2412. // Gestionnaire spécifique pour le formulaire de newsletter
  2413. const newsletterForm = document.querySelector('.footer-newsletter-form');
  2414. if (newsletterForm) {
  2415. newsletterForm.addEventListener('submit', function (e) {
  2416. const submitBtn = this.querySelector('.newsletter-btn');
  2417. if (submitBtn && ! submitBtn.disabled) {
  2418. submitBtn.classList.add('loading');
  2419. submitBtn.disabled = true;
  2420. // Afficher le loader et masquer l'icône
  2421. const icon = submitBtn.querySelector('.newsletter-btn-icon');
  2422. const loader = submitBtn.querySelector('.newsletter-btn-loader');
  2423. if (icon) 
  2424. icon.style.display = 'none';
  2425. if (loader) 
  2426. loader.style.display = 'flex';
  2427. // Réactiver le bouton après 5 secondes au cas où la requête échoue silencieusement
  2428. setTimeout(function () {
  2429. if (submitBtn.classList.contains('loading')) {
  2430. submitBtn.classList.remove('loading');
  2431. submitBtn.disabled = false;
  2432. if (icon) 
  2433. icon.style.display = 'flex';
  2434. if (loader) 
  2435. loader.style.display = 'none';
  2436. }
  2437. }, 5000);
  2438. }
  2439. });
  2440. // Gérer la réponse du formulaire (si AJAX)
  2441. newsletterForm.addEventListener('submit', function (e) {
  2442. // Si le formulaire est soumis normalement (non-AJAX), le navigateur gère la réponse
  2443. // Sinon, on peut intercepter pour gérer la réponse AJAX ici
  2444. });
  2445. }
  2446. });
  2447. // CSS pour le spinner
  2448. const style = document.createElement('style');
  2449. style.textContent = `
  2450.     .spinner-border-sm {
  2451.         width: 1rem;
  2452.         height: 1rem;
  2453.         border-width: 0.15em;
  2454.     }
  2455.     .spinner-border {
  2456.         display: inline-block;
  2457.         width: 1rem;
  2458.         height: 1rem;
  2459.         vertical-align: text-bottom;
  2460.         border: 0.15em solid currentColor;
  2461.         border-right-color: transparent;
  2462.         border-radius: 50%;
  2463.         animation: spinner-border 0.75s linear infinite;
  2464.     }
  2465.     @keyframes spinner-border {
  2466.         to { transform: rotate(360deg); }
  2467.     }
  2468.     button.loading {
  2469.         opacity: 0.7;
  2470.         cursor: not-allowed !important;
  2471.     }
  2472. `;
  2473. document.head.appendChild(style);
  2474.         </script>
  2475.         <!-- Cart Modal -->
  2476.         <div id="ebay-cart-modal" class="ebay-cart-modal-overlay" style="display: none;" data-url="">
  2477.             <div class="ebay-cart-modal">
  2478.                 <div class="ebay-modal-header">
  2479.                     <h3>
  2480.                         <i class="lnr lnr-checkmark-circle"></i>
  2481.                         Ajouté au panier !</h3>
  2482.                     <button class="ebay-modal-close" onclick="closeEbayCartModal()">&times;</button>
  2483.                 </div>
  2484.                 <div class="ebay-modal-content">
  2485.                     <div class="ebay-product-info">
  2486.                         <div class="ebay-product-image">
  2487.                             <img id="ebay-modal-product-image" src="" alt="Produit"/>
  2488.                         </div>
  2489.                         <div class="ebay-product-details">
  2490.                             <h4 id="ebay-modal-product-name">Nom du produit</h4>
  2491.                             <div class="ebay-product-price" id="ebay-modal-product-price">Prix</div>
  2492.                             <div class="ebay-product-quantity">
  2493.                                 <span>Quantité:
  2494.                                 </span>
  2495.                                 <span id="ebay-modal-product-quantity">1</span>
  2496.                             </div>
  2497.                         </div>
  2498.                     </div>
  2499.                     <!-- Recommendations Section -->
  2500.                     <div class="ebay-recommendations" style="display: none;">
  2501.                         <h4>Les clients ont aussi acheté :</h4>
  2502.                         <div
  2503.                             class="ebay-recommendations-grid" id="ebay-modal-recommendations"><!-- Recommendations will be injected here -->
  2504.                         </div>
  2505.                     </div>
  2506.                     <div class="ebay-modal-actions">
  2507.                         <button class="ebay-btn-continue" onclick="closeEbayCartModal()">
  2508.                             Continuer mes achats
  2509.                         </button>
  2510.                         <a href="{{ path('ui_cart') }}" class="ebay-btn-view-cart">
  2511.                             Voir mon panier
  2512.                         </a>
  2513.                     </div>
  2514.                 </div>
  2515.             </div>
  2516.         </div>
  2517.         <script src="{{ asset('ui/js/cart-modal.js') }}"></script>
  2518.         <script src="{{ asset('ui/js/notification-badge.js') }}"></script>
  2519.         <script src="{{ asset('js/dark-mode.js') }}"></script>
  2520.         {% block javascripts %}{% endblock %}
  2521.     </body>
  2522. </html>