templates/base_home.html.twig line 1236

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