<!DOCTYPE html>
<html lang="fr" class="no-js">
<head>
<!-- Mobile Specific Meta -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MaketOu">
<meta name="theme-color" content="#ffa200">
<meta name="msapplication-TileColor" content="#ffa200">
<meta
name="msapplication-config" content="{{ asset('favicon/browserconfig.xml') }}">
<!-- Favicon-->
<link rel="shortcut icon" href="{{ asset('favicon/favicon-16x16.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset('favicon/apple-touch-icon.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ asset('favicon/favicon-32x32.png') }}">
<link
rel="icon" type="image/png" sizes="16x16" href="{{ asset('favicon/favicon-16x16.png') }}">
<!-- PWA Manifest -->
<link
rel="manifest" href="{{ asset('manifest.json') }}">
<!-- Author Meta -->
<meta
name="author" content="Foulgor Tech">
<!-- Meta Description -->
<meta
name="description" content="">
<!-- Meta Keyword -->
<meta
name="keywords" content="">
<!-- meta character set -->
<meta
charset="UTF-8">
<!-- Site Title -->
<title>
{% block title %}MaketOu
{% endblock %}
</title>
<!--
CSS
============================================= -->
<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css"/>
<link rel="stylesheet" href="{{ asset('ui/css/linearicons.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/font-awesome.min.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/themify-icons.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/bootstrap.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/owl.carousel.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/nice-select.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/nouislider.min.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.css') }}"/>
<link rel="stylesheet" href="{{ asset('ui/css/ion.rangeSlider.skinFlat.css') }}"/>
<link rel="stylesheet" href="{{ asset('ui/css/magnific-popup.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/main.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/cart-modal.css') }}">
<link rel="stylesheet" href="{{ asset('ui/css/buttons-unified.css') }}">
<meta
charset="UTF-8">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
<style>
/* Styles pour l'icône de notification */
.notification-icon {
position: relative;
display: inline-block;
color: #333;
text-decoration: none;
transition: color 0.3s ease;
}
.notification-icon:hover {
color: #007bff;
}
.notification-badge {
position: absolute;
top: -4px;
right: -2px;
background: #dc3545 !important;
color: white !important;
border-radius: 50%;
width: 18px;
height: 18px;
font-size: 8px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-weight: bold !important;
animation: pulse 2s infinite;
z-index: 10;
line-height: 1 !important;
}
/* Forcer le texte en blanc pour tous les éléments du badge - avec spécificité maximale */
.notification-badge,
.notification-badge.text-white,
.notification-badge *,
#notificationBadge,
#notificationBadge.text-white,
#notificationBadge *,
span.notification-badge,
span.notification-badge.text-white,
span#notificationBadge,
span#notificationBadge.text-white {
color: white !important;
text-color: white !important;
-webkit-text-fill-color: white !important;
}
/* S'assurer que le contenu texte est bien en blanc */
.notification-badge::before,
.notification-badge::after {
color: white !important;
}
/* Forcer la couleur pour tous les sélecteurs possibles avec spécificité maximale */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon .notification-badge.text-white,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span.notification-badge,
.notification-icon .notification-badge,
.notification-icon .notification-badge.text-white,
.notification-icon span.notification-badge,
a.notification-icon .notification-badge,
a.notification-icon span.notification-badge {
color: white !important;
text-color: white !important;
-webkit-text-fill-color: white !important;
}
/* Forcer la couleur pour tous les enfants directs */
.notification-icon > .notification-badge,
.notification-icon > span.notification-badge {
color: white !important;
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.notification-icon:hover .notification-badge {
animation: none;
}
/* Container pour les icônes - toujours alignées horizontalement */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
display: flex !important;
flex-direction: row !important;
align-items: center !important;
justify-content: center !important;
gap: 15px !important;
list-style: none !important;
margin: 0 !important;
padding: 0 !important;
width: auto !important;
}
/* Surcharger les styles existants pour les li contenant le wrapper */
.header_area .navbar .nav.navbar-nav.navbar-right li:has(.navbar-icons-wrapper) {
margin-left: 0 !important;
margin-right: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
/* Styles pour les icônes */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
display: flex !important;
align-items: center !important;
justify-content: center !important;
width: 40px !important;
height: 40px !important;
min-width: 40px !important;
min-height: 40px !important;
color: #333 !important;
text-decoration: none !important;
transition: all 0.3s ease !important;
border-radius: 50% !important;
position: relative !important;
margin: 0 !important;
padding: 0 !important;
}
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart:hover,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon:hover,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon:hover,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search:hover {
background-color: #f0f0f0 !important;
color: #ffa200 !important;
transform: scale(1.1) !important;
}
/* Surcharger le line-height de main.css */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .cart span,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .notification-icon span,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search span {
font-size: 18px !important;
line-height: 1 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
color: inherit !important;
font-weight: normal !important;
}
/* Icône de comparaison en noir comme le panier */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon {
color: #333 !important;
}
/* L'icône elle-même (lnr-sync) doit être noire, mais pas le badge */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon > span.lnr-sync,
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon span:not(.comparison-badge) {
color: #333 !important;
font-size: 18px !important;
line-height: 1 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-weight: normal !important;
}
/* Le badge doit rester blanc */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .comparison-icon .comparison-badge {
color: white !important;
}
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper .search {
background: transparent !important;
border: 0 !important;
cursor: pointer !important;
padding: 0 !important;
}
/* Alignement des icônes navbar sur mobile */
@media(max-width: 991.98px) {
/* Container navbar-right en colonne sur mobile */
.header_area .navbar .nav.navbar-nav.navbar-right {
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
width: 100% !important;
gap: 0 !important;
margin: 0 !important;
padding: 10px 0 !important;
}
/* Le container d'icônes reste horizontal */
.header_area .navbar .nav.navbar-nav.navbar-right .navbar-icons-wrapper {
width: 100% !important;
justify-content: center !important;
display: flex !important;
flex-direction: row !important;
}
/* Le bouton compte en bas - pleine largeur */
.header_area .navbar .nav.navbar-nav.navbar-right li:last-child {
width: 100% !important;
display: flex !important;
justify-content: center !important;
margin-top: 12px !important;
padding-top: 12px !important;
border-top: 1px solid #e0e0e0 !important;
}
/* Bouton compte responsive */
.header_area .navbar .nav.navbar-nav.navbar-right .btn-account {
width: 100%;
max-width: 200px;
padding: 10px 20px;
font-size: 14px;
}
}
/* Styles pour les messages flash - Design moderne */
.flash-message {
animation: slideInDownFlash 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: visible !important;
border: none !important;
padding: 16px 20px !important;
border-radius: 12px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
display: flex !important;
align-items: center;
gap: 12px;
margin-bottom: 15px;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
/* S'assurer que les icônes Font Awesome sont visibles */
.flash-message i.fa {
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
width: auto !important;
height: auto !important;
font-family: "FontAwesome" !important;
font-style: normal !important;
font-weight: normal !important;
text-rendering: auto !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale !important;
line-height: 1 !important;
flex-shrink: 0 !important;
}
/* Forcer l'affichage des pseudo-éléments Font Awesome */
.flash-message i.fa::before {
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
content: attr(data-icon) !important;
}
/* Styles spécifiques pour chaque icône */
.flash-message i.fa-check-circle::before {
content: "\f058" !important;
}
.flash-message i.fa-exclamation-circle::before {
content: "\f06a" !important;
}
.flash-message i.fa-exclamation-triangle::before {
content: "\f071" !important;
}
.flash-message i.fa-info-circle::before {
content: "\f05a" !important;
}
.flash-message:hover {
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2) !important;
transform: translateY(-2px);
}
.flash-message::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 5px;
background: linear-gradient(180deg, currentColor 0%, rgba(255,255,255,0.3) 100%);
border-radius: 12px 0 0 12px;
}
.flash-message.alert-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%) !important;
color: #155724 !important;
border-left: 5px solid #28a745 !important;
}
.flash-message.alert-success i.fa {
color: #28a745 !important;
font-size: 1.3rem !important;
display: inline-block !important;
margin-right: 8px;
vertical-align: middle;
}
.flash-message.alert-danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%) !important;
color: #721c24 !important;
border-left: 5px solid #dc3545 !important;
}
.flash-message.alert-danger i.fa {
color: #dc3545 !important;
font-size: 1.3rem !important;
display: inline-block !important;
margin-right: 8px;
vertical-align: middle;
}
.flash-message.alert-warning {
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%) !important;
color: #856404 !important;
border-left: 5px solid #ffc107 !important;
}
.flash-message.alert-warning i.fa {
color: #ffa200 !important;
font-size: 1.3rem !important;
display: inline-block !important;
margin-right: 8px;
vertical-align: middle;
}
.flash-message.alert-info {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%) !important;
color: #0c5460 !important;
border-left: 5px solid #17a2b8 !important;
}
.flash-message.alert-info i.fa {
color: #17a2b8 !important;
font-size: 1.3rem !important;
display: inline-block !important;
margin-right: 8px;
vertical-align: middle;
}
.flash-message strong {
flex: 1;
font-weight: 600;
font-size: 0.95rem;
line-height: 1.5;
}
.flash-message .btn-close {
opacity: 0.7;
padding: 8px !important;
margin: -8px -8px -8px auto !important;
transition: all 0.2s ease;
border-radius: 50% !important;
background: rgba(0, 0, 0, 0.05) !important;
width: 32px !important;
height: 32px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
border: none !important;
cursor: pointer !important;
position: relative !important;
}
/* Ajouter une icône X visible dans le bouton */
.flash-message .btn-close::before {
content: '×' !important;
font-size: 24px !important;
line-height: 1 !important;
color: currentColor !important;
font-weight: bold !important;
display: block !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
}
.flash-message .btn-close:hover {
opacity: 1 !important;
background: rgba(0, 0, 0, 0.15) !important;
transform: rotate(90deg) !important;
}
.flash-message .btn-close:hover::before {
transform: translate(-50%, -50%) rotate(-90deg) !important;
}
@keyframes slideInDownFlash {
from {
opacity: 0;
transform: translateY(-30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
#flash-messages-container {
margin-top: 20px;
margin-bottom: 0;
}
@media(max-width: 768px) {
.flash-message {
padding: 14px 16px !important;
font-size: 0.9rem;
}
.flash-message i {
font-size: 1.2rem !important;
}
}
/* Styles pour l'icône de comparaison */
.comparison-icon {
position: relative;
display: inline-block;
color: #333;
text-decoration: none;
transition: all 0.3s ease;
}
.comparison-icon:hover {
color: #ffa200;
}
.comparison-badge {
position: absolute;
top: -4px;
right: -2px;
background: #095ad3 !important;
color: white !important;
border-radius: 50%;
width: 18px;
height: 18px;
font-size: 8px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-weight: bold !important;
animation: pulse 2s infinite;
z-index: 10;
line-height: 1 !important;
}
/* Correction de l'alignement des boutons actifs dans prd-bottom */
/* Ne s'applique qu'aux boutons actifs pour éviter de casser le comportement hover */
.single-product .product-details .prd-bottom .social-info.active {
vertical-align: middle !important;
position: relative !important;
top: 0 !important;
transform: none !important;
margin: 0 !important;
padding: 0 !important;
line-height: normal !important;
width: 35px !important;
height: auto !important;
min-height: auto !important;
max-height: none !important;
}
.single-product .product-details .prd-bottom .social-info.wishlist-btn.active,
.single-product .product-details .prd-bottom .social-info.comparison-btn.active {
vertical-align: middle !important;
position: relative !important;
top: 0 !important;
transform: none !important;
margin: 0 !important;
padding: 0 !important;
line-height: normal !important;
width: 35px !important;
height: auto !important;
min-height: auto !important;
max-height: none !important;
}
.single-product .product-details .prd-bottom .social-info.active .hover-text {
margin: 0 !important;
padding: 0 !important;
line-height: normal !important;
position: absolute !important;
top: 0 !important;
background: #ffa200 !important;
left: -40px !important;
}
/* S'assurer qu'aucune marge supplémentaire n'est ajoutée quand un bouton est actif */
.single-product .product-details .prd-bottom .social-info.active span,
.single-product .product-details .prd-bottom .social-info.active p {
margin: 0 !important;
padding: 0 !important;
}
/* Empêcher le hover de changer la largeur quand le bouton est actif */
.single-product .product-details .prd-bottom .social-info.active:hover {
width: 35px !important;
}
.comparison-icon:hover .comparison-badge {
animation: none;
}
/* Styles pour les mega-menus - style comme la barre de recherche */
.mega-menu {
width: 100%;
max-width: 1200px;
left: 50%;
transform: translateX(-50%);
padding: 25px;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
border: none;
background: white;
display: none;
position: absolute;
top: 100%;
z-index: 9999 !important;
margin-top: 10px;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease, transform 0.3s ease;
}
.mega-menu.active {
display: block;
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
.nav-item.dropdown {
position: relative !important;
}
@media(min-width: 768px) {
.nav-item.dropdown {
position: relative !important;
}
.mega-menu {
position: absolute !important;
left: 50% !important;
transform: translateX(-50%) !important;
width: auto !important;
min-width: 600px !important;
}
}
/* Correction supplémentaire pour l'affichage sur la page d'accueil */
body.home .nav-item.dropdown,
.nav-item.dropdown {
position: relative !important;
}
body.home .mega-menu,
.mega-menu {
position: absolute !important;
left: 50% !important;
transform: translateX(-50%) translateY(0) !important;
width: auto !important;
min-width: 600px !important;
max-width: 90% !important;
top: 100% !important;
margin-top: 10px !important;
}
/* Assurer que les mega-menus sont toujours visibles au survol */
.nav-item.dropdown:hover .mega-menu {
opacity: 1 !important;
visibility: visible !important;
display: block !important;
transform: translateX(-50%) translateY(0) !important;
}
.mega-menu .dropdown-item {
padding: 0;
border: none;
}
.mega-menu .dropdown-header {
font-weight: 700;
color: #2c3e50;
margin-bottom: 18px;
padding-bottom: 10px;
border-bottom: 3px solid #3498db;
font-size: 16px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.mega-menu .dropdown-item a {
display: block;
padding: 10px 15px;
color: #555;
text-decoration: none;
border-radius: 6px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.mega-menu .dropdown-item a::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(52, 152, 219, 0.1), transparent);
transition: left 0.5s;
}
.mega-menu .dropdown-item a:hover::before {
left: 100%;
}
.mega-menu .dropdown-item a:hover {
background: linear-gradient(135deg, #f8f9fa, #e3f2fd);
color: #2980b9;
transform: translateX(8px);
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.2);
}
.mega-menu .dropdown-item a i {
width: 22px;
text-align: center;
margin-right: 8px;
color: #3498db;
transition: color 0.3s ease;
}
.mega-menu .dropdown-item a:hover i {
color: #2980b9;
}
/* Animation d'apparition améliorée */
@keyframes slideInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.mega-menu {
animation: slideInDown 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Effet de survol sur le lien principal */
.nav-item.dropdown .nav-link {
position: relative;
transition: all 0.3s ease;
}
.nav-item.dropdown .nav-link::after {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
width: 0;
height: 2px;
background: linear-gradient(90deg, #3498db, #2980b9);
transition: all 0.3s ease;
transform: translateX(-50%);
}
.nav-item.dropdown:hover .nav-link::after {
width: 100%;
}
.nav-item.dropdown:hover .nav-link {
color: #2980b9;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive */
@media(max-width: 768px) {
.mega-menu {
width: 100%;
left: 0;
padding: 15px;
}
.mega-menu .row {
margin: 0;
}
.mega-menu .col-md-3 {
margin-bottom: 20px;
}
}
/* ============================================
.footer-area {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
position: relative;
overflow: hidden;
padding: 50px 0 20px;
}
.footer-pattern {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
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%);
pointer-events: none;
}
.footer-area .container {
position: relative;
z-index: 1;
}
/* Widgets */
.single-footer-widget {
color: rgba(255, 255, 255, 0.9);
}
.footer-brand {
font-size: 1.6rem;
font-weight: 700;
background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 12px;
display: inline-block;
}
.footer-widget-title {
color: #fff;
font-size: 1rem;
font-weight: 600;
margin-bottom: 18px;
position: relative;
padding-bottom: 8px;
}
.footer-widget-title::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 40px;
height: 3px;
background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
border-radius: 2px;
}
.footer-description {
color: rgba(255, 255, 255, 0.7);
line-height: 1.6;
font-size: 0.9rem;
margin-bottom: 0;
}
/* Liste de liens */
.footer-list {
list-style: none;
padding: 0;
margin: 0;
}
.footer-list li {
margin-bottom: 10px;
}
.footer-list li a {
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
transition: all 0.3s ease;
font-size: 0.9rem;
display: inline-flex;
align-items: center;
}
.footer-list li a i {
font-size: 0.8rem;
opacity: 0.6;
transition: all 0.3s ease;
}
.footer-list li a:hover {
color: #ffa200;
transform: translateX(5px);
}
.footer-list li a:hover i {
opacity: 1;
color: #ffa200;
}
/* Newsletter */
.footer-newsletter-text {
color: rgba(255, 255, 255, 0.7);
font-size: 0.85rem;
margin-bottom: 15px;
line-height: 1.5;
}
.footer-newsletter-wrapper {
margin-bottom: 15px;
}
.newsletter-input-group {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.08);
border-radius: 30px;
padding: 4px 4px 4px 15px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
transition: all 0.3s ease;
gap: 8px;
}
.newsletter-input-group:focus-within {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 162, 0, 0.6);
box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.15);
}
.newsletter-icon {
color: rgba(255, 255, 255, 0.5);
font-size: 0.9rem;
flex-shrink: 0;
}
.newsletter-input-group:focus-within .newsletter-icon {
color: #ffa200;
}
.newsletter-input {
flex: 1;
background: transparent;
border: none;
color: #fff;
padding: 10px 8px;
font-size: 0.9rem;
min-width: 0;
}
.newsletter-input::placeholder {
color: rgba(255, 255, 255, 0.4);
font-size: 0.85rem;
}
.newsletter-input:focus {
outline: none;
box-shadow: none;
}
.newsletter-btn {
background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%);
border: none;
color: white;
width: 42px;
height: 42px;
min-width: 42px;
min-height: 42px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
flex-shrink: 0;
box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4), 0 0 0 0 rgba(255, 162, 0, 0.4);
font-size: 0.9rem;
position: relative;
overflow: hidden;
}
.newsletter-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.6s ease, height 0.6s ease;
}
.newsletter-btn:hover::before {
width: 100%;
height: 100%;
}
.newsletter-btn:hover {
transform: translateX(3px) scale(1.05);
box-shadow: 0 6px 20px rgba(255, 162, 0, 0.5), 0 0 0 8px rgba(255, 162, 0, 0.1);
background: linear-gradient(135deg, #e8910a 0%, #ffa200 100%);
}
.newsletter-btn:active {
transform: translateX(3px) scale(0.98);
box-shadow: 0 3px 10px rgba(255, 162, 0, 0.4);
}
.newsletter-btn:focus {
outline: none;
box-shadow: 0 4px 15px rgba(255, 162, 0, 0.4), 0 0 0 3px rgba(255, 162, 0, 0.3);
}
.newsletter-btn-icon,
.newsletter-btn-loader {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.newsletter-btn:hover .newsletter-btn-icon {
transform: translateX(2px) rotate(-10deg);
}
.newsletter-btn.loading .newsletter-btn-icon {
opacity: 0;
transform: scale(0);
}
.newsletter-btn.loading .newsletter-btn-loader {
display: flex !important;
}
.newsletter-btn.success {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
box-shadow: 0 4px 15px rgba(40, 167, 69, 0.4);
}
.newsletter-btn.success .newsletter-btn-icon i::before {
content: "\f00c";
}
/* Trust badges */
.footer-trust-badges {
display: flex;
flex-direction: row;
gap: 15px;
flex-wrap: wrap;
}
.trust-item {
display: flex;
align-items: center;
gap: 6px;
color: rgba(255, 255, 255, 0.6);
font-size: 0.8rem;
}
.trust-item i {
color: #ffa200;
font-size: 0.9rem;
}
/* Social icons */
.footer-social {
margin-top: 0 !important;
position: relative !important;
width: 100% !important;
clear: both !important;
float: none !important;
}
.footer-social h6 {
color: #fff;
font-size: 0.95rem;
font-weight: 600;
margin-bottom: 12px !important;
display: block !important;
width: 100% !important;
position: relative !important;
clear: both !important;
float: none !important;
}
.footer-social-icons {
display: flex !important;
gap: 12px !important;
flex-wrap: wrap !important;
align-items: center !important;
justify-content: flex-start !important;
width: 100% !important;
position: relative !important;
clear: both !important;
float: none !important;
margin: 0 !important;
padding: 0 !important;
}
.social-icon {
width: 38px !important;
height: 38px !important;
min-width: 38px !important;
min-height: 38px !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
background: rgba(255, 255, 255, 0.1) !important;
border-radius: 50% !important;
color: rgba(255, 255, 255, 0.8) !important;
text-decoration: none !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
font-size: 16px !important;
line-height: 1 !important;
backdrop-filter: blur(10px) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
flex-shrink: 0 !important;
position: relative !important;
float: none !important;
margin: 0 !important;
padding: 0 !important;
vertical-align: middle !important;
}
.social-icon i {
font-size: 16px !important;
line-height: 1 !important;
display: inline-block !important;
transition: transform 0.3s ease !important;
position: relative !important;
z-index: 1 !important;
margin: 0 !important;
padding: 0 !important;
}
.social-icon:hover {
background: linear-gradient(135deg, #ffa200 0%, #e8910a 100%) !important;
transform: translateY(-5px) scale(1.1) !important;
color: white !important;
border-color: transparent !important;
box-shadow: 0 8px 20px rgba(255, 162, 0, 0.4) !important;
}
.social-icon:hover i {
transform: scale(1.1) !important;
}
/* Footer divider */
.footer-divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
margin: 30px 0 20px;
}
/* Footer bottom */
.footer-bottom {
padding-top: 15px;
}
.footer-text {
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
margin: 0;
text-align: center;
}
/* Responsive */
@media(max-width: 991.98px) {
.footer-area {
padding: 40px 0 20px;
}
.single-footer-widget {
margin-bottom: 30px;
}
.footer-social {
margin-top: 15px;
}
}
@media(max-width: 767.98px) {
.footer-area {
padding: 35px 0 18px;
}
.footer-brand {
font-size: 1.4rem;
}
.footer-widget-title {
font-size: 0.95rem;
}
.newsletter-input-group {
padding: 3px 3px 3px 12px;
}
.newsletter-input {
padding: 8px 6px;
font-size: 0.85rem;
}
.newsletter-btn {
width: 38px;
height: 38px;
min-width: 38px;
min-height: 38px;
font-size: 0.85rem;
}
.footer-trust-badges {
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
gap: 12px;
}
}
@media(max-width: 575.98px) {
.footer-area {
padding: 40px 0 20px;
}
.footer-brand {
font-size: 1.3rem;
}
.social-icon {
width: 38px;
height: 38px;
font-size: 16px;
}
.social-icon i {
font-size: 16px;
}
}
</style>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
<!-- Script pour forcer l'application des styles CSS après chargement -->
<script>
document.addEventListener('DOMContentLoaded', function () { // Forcer le masquage du toggle sur grands écrans
if (window.innerWidth >= 992) {
const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
togglers.forEach(function (toggler) {
if (toggler) {
toggler.style.display = 'none';
toggler.style.visibility = 'hidden';
toggler.style.opacity = '0';
toggler.style.width = '0';
toggler.style.height = '0';
toggler.style.padding = '0';
toggler.style.margin = '0';
toggler.style.border = 'none';
toggler.style.position = 'absolute';
toggler.style.left = '-9999px';
}
});
const iconBars = document.querySelectorAll('.navbar-toggler .icon-bar, .header_area .navbar .navbar-toggler .icon-bar, .navbar-toggler-icon');
iconBars.forEach(function (bar) {
if (bar) {
bar.style.display = 'none';
bar.style.visibility = 'hidden';
}
});
}
// Alignement des icônes navbar sur mobile
const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
navbarRight.forEach(function (nav) {
if (nav && window.innerWidth<= 991.98) {
// Mobile: colonne avec icônes alignées horizontalement
nav.style.display = 'flex';
nav.style.flexDirection = 'column';
nav.style.alignItems = 'center';
nav.style.textAlign = 'center';
// Le container d'icônes reste horizontal
const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
if (iconsContainer) {
iconsContainer.style.display = 'flex';
iconsContainer.style.flexDirection = 'row';
iconsContainer.style.justifyContent = 'center';
}
// Le bouton compte en bas
const accountBtn = nav.querySelector('li:last-child');
if (accountBtn) {
accountBtn.style.width = '100%';
accountBtn.style.display = 'flex';
accountBtn.style.justifyContent = 'center';
accountBtn.style.marginTop = '12px';
accountBtn.style.paddingTop = '12px';
accountBtn.style.borderTop = '1px solid #e0e0e0';
}
} else if (nav && window.innerWidth> 991.98) { // Desktop: alignement horizontal normal
nav.style.display = 'flex';
nav.style.flexDirection = 'row';
nav.style.alignItems = 'center';
nav.style.justifyContent = 'flex-end';
}
});
});
// Réappliquer lors du redimensionnement
window.addEventListener('resize', function () {
if (window.innerWidth >= 992) {
const togglers = document.querySelectorAll('.navbar-toggler, .header_area .navbar .navbar-toggler');
togglers.forEach(function (toggler) {
if (toggler) {
toggler.style.display = 'none';
toggler.style.visibility = 'hidden';
}
});
}
// Réappliquer l'alignement des icônes navbar
const navbarRight = document.querySelectorAll('.navbar-right, .header_area .navbar .nav.navbar-nav.navbar-right');
navbarRight.forEach(function (nav) {
if (nav && window.innerWidth<= 991.98) {
// Mobile: colonne avec icônes alignées horizontalement
nav.style.display = 'flex';
nav.style.flexDirection = 'column';
nav.style.alignItems = 'center';
nav.style.textAlign = 'center';
// Le container d'icônes reste horizontal
const iconsContainer = nav.querySelector('.navbar-icons-wrapper');
if (iconsContainer) {
iconsContainer.style.display = 'flex';
iconsContainer.style.flexDirection = 'row';
iconsContainer.style.justifyContent = 'center';
}
const accountBtn = nav.querySelector('li:last-child');
if (accountBtn) {
accountBtn.style.width = '100%';
accountBtn.style.display = 'flex';
accountBtn.style.justifyContent = 'center';
accountBtn.style.marginTop = '12px';
accountBtn.style.paddingTop = '12px';
accountBtn.style.borderTop = '1px solid #e0e0e0';
}
} else if (nav && window.innerWidth> 991.98) { // Desktop: alignement horizontal normal
nav.style.display = 'flex';
nav.style.flexDirection = 'row';
nav.style.alignItems = 'center';
nav.style.justifyContent = 'flex-end';
}
});
});
</script>
</head>
<body
{% if current_menu is defined and current_menu == "listing" %} id="category" {% endif %}>
<!-- Start Header Area -->
<header class="header_area sticky-header">
<div class="main_menu">
<nav class="navbar navbar-expand-lg navbar-light main_box">
<div
class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<a class="navbar-brand logo_h" href="{{ path('ui_home') }}"><img style="width: auto;height: 60px" src="{{ asset('logo.png') }}" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse offset" id="navbarSupportedContent">
<ul class="nav navbar-nav menu_nav ml-auto">
<li class="nav-item {% if current_menu is defined and current_menu == " home" %}active{% endif %}">
<a class="nav-link" href="{{ path('ui_home') }}">Accueil</a>
</li>
<li class="nav-item {% if current_menu is defined and current_menu == " shops" %}active{% endif %}">
<a class="nav-link" href="{{ path('ui_shops_list') }}">Boutiques</a>
</li>
<li class="nav-item {% if current_menu is defined and current_menu == " listing" %}active{% endif %}">
<a class="nav-link" href="{{ path('ui_listing') }}">Produits</a>
</li>
<li class="nav-item {% if current_menu is defined and current_menu == " contact" %}active{% endif %}">
<a class="nav-link" href="{{ path('ui_contact') }}">Contact</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="nav-item">
<div class="navbar-icons-wrapper">
<a href="{{ path('ui_cart') }}" class="cart {% if current_menu is defined and current_menu == " cart" %}active{% endif %}" aria-label="Panier">
<span class="ti-bag"></span>
</a>
{% if app.user %}
<a href="{{ path('notifications_list') }}" class="notification-icon" title="Notifications" aria-label="Notifications">
<span class="ti-bell"></span>
<span class="notification-badge text-white" id="notificationBadge" style="display: none;color: white;">0</span>
</a>
<a href="{{ path('ui_product_comparison') }}" class="comparison-icon" title="Comparaison" aria-label="Comparaison">
<span class="lnr lnr-sync"></span>
<span class="comparison-badge text-white" id="comparisonBadge" style="display: none;color: white;">0</span>
</a>
{% endif %}
<button class="search" type="button" aria-label="Recherche">
<span class="lnr lnr-magnifier" id="search"></span>
</button>
</div>
</li>
<li class="nav-item">
{% if app.user %}
<a class="nav-link btn btn-primary btn-account text-white" href="{{ path('ui_account_index') }}">
<i class="ti-user"></i>
Mon compte
</a>
{% else %}
<a class="nav-link btn btn-primary btn-account text-white" href="{{ path('ui_app_login') }}">
<i class="ti-lock"></i>
Se connecter
</a>
{% endif %}
</li>
</ul>
</div>
</div>
</nav>
</div>
<div class="search_input" id="search_input_box">
<div class="container">
<form class="d-flex justify-content-between" id="search_form" action="{{ path('ui_listing') }}" method="get">
<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', '') }}">
<button type="button" class="btn" id="search_submit_btn">
<i class="lnr lnr-magnifier"></i>
</button>
{% if app.request.query.get('q') %}
<button type="button" class="btn btn-clear-search" id="clear_search_btn" title="Effacer la recherche">
<i class="lnr lnr-cross"></i>
</button>
{% endif %}
<span class="lnr lnr-cross" id="close_search" title="Fermer la recherche"></span>
</form>
<div id="search_results" class="search-results-container"></div>
</div>
</div>
</header>
<!-- End Header Area -->
<!-- Flash Messages -->
<div class="container mt-3" id="flash-messages-container" style="position: relative; z-index: 1050;">
<div class="row">
<div class="col-12">
{% for message in app.flashes('success') %}
<div class="alert alert-success alert-dismissible fade show flash-message" role="alert">
<i class="fa fa-check-circle"></i>
<strong>{{ message }}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% for message in app.flashes('error') %}
<div class="alert alert-danger alert-dismissible fade show flash-message" role="alert">
<i class="fa fa-exclamation-circle"></i>
<strong>{{ message }}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% for message in app.flashes('warning') %}
<div class="alert alert-warning alert-dismissible fade show flash-message" role="alert">
<i class="fa fa-exclamation-triangle"></i>
<strong>{{ message }}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% for message in app.flashes('info') %}
<div class="alert alert-info alert-dismissible fade show flash-message" role="alert">
<i class="fa fa-info-circle"></i>
<strong>{{ message }}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
</div>
</div>
{% block body %}{% endblock %}
<!-- start footer Area -->
<footer class="footer-area section_gap">
<div class="footer-pattern"></div>
<div class="container">
<div class="row">
<div class="col-lg-3 col-md-6 col-sm-6 mb-4 mb-lg-0">
<div class="single-footer-widget">
<div class="footer-logo mb-3">
<h4 class="footer-brand">MaketOu</h4>
</div>
<p class="footer-description">
MaketOu est la première marketplace haïtienne qui connecte les acheteurs et les vendeurs.
Nous offrons une plateforme sécurisée et moderne pour le commerce en ligne en Haïti.
</p>
<div class="footer-social mt-3">
<h6 class="mb-2">Suivez-nous</h6>
<div class="footer-social-icons">
<a href="#" class="social-icon" aria-label="Facebook" title="Facebook">
<i class="fa fa-facebook"></i>
</a>
<a href="#" class="social-icon" aria-label="Twitter" title="Twitter">
<i class="fa fa-twitter"></i>
</a>
<a href="#" class="social-icon" aria-label="Instagram" title="Instagram">
<i class="fa fa-instagram"></i>
</a>
<a href="#" class="social-icon" aria-label="LinkedIn" title="LinkedIn">
<i class="fa fa-linkedin"></i>
</a>
</div>
</div>
</div>
</div>
<div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
<div class="single-footer-widget">
<h6 class="footer-widget-title">Liens rapides</h6>
<ul class="footer-list">
<li>
<a href="{{ path('ui_home') }}">
<i class="fa fa-angle-right me-2"></i> Accueil</a>
</li>
<li>
<a href="{{ path('ui_listing') }}">
<i class="fa fa-angle-right me-2"></i> Produits</a>
</li>
<li>
<a href="{{ path('ui_shops_list') }}">
<i class="fa fa-angle-right me-2"></i> Boutiques</a>
</li>
{% if app.user %}
<li>
<a href="{{ path('ui_account_index') }}">
<i class="fa fa-angle-right me-2"></i> Mon compte</a>
</li>
{% else %}
<li>
<a href="{{ path('ui_app_login') }}">
<i class="fa fa-angle-right me-2"></i> Se connecter</a>
</li>
<li>
<a href="{{ path('ui_app_register') }}">
<i class="fa fa-angle-right me-2"></i> S'inscrire</a>
</li>
{% endif %}
</ul>
</div>
</div>
<div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
<div class="single-footer-widget">
<h6 class="footer-widget-title">Pour les vendeurs</h6>
<ul class="footer-list">
<li>
<a href="{{ path('seller_index') }}">
<i class="fa fa-angle-right me-2"></i> Ouvrir une boutique</a>
</li>
<li>
<a href="{{ path('seller_help_how_to_sell') }}">
<i class="fa fa-angle-right me-2"></i> Comment vendre</a>
</li>
<li>
<a href="{{ path('seller_help_pricing') }}">
<i class="fa fa-angle-right me-2"></i> Tarifs</a>
</li>
<li>
<a href="{{ path('seller_help_support') }}">
<i class="fa fa-angle-right me-2"></i> Support vendeur</a>
</li>
</ul>
</div>
</div>
<div class="col-lg-2 col-md-6 col-sm-6 mb-4 mb-lg-0">
<div class="single-footer-widget">
<h6 class="footer-widget-title">Support</h6>
<ul class="footer-list">
<li>
<a href="{{ path('ui_help') }}">
<i class="fa fa-angle-right me-2"></i> Centre d'Aide</a>
</li>
<li>
<a href="{{ path('ui_contact') }}">
<i class="fa fa-angle-right me-2"></i> Contactez-nous</a>
</li>
<li>
<a href="{{ path('ui_faq') }}">
<i class="fa fa-angle-right me-2"></i> FAQ</a>
</li>
<li>
<a href="{{ path('ui_privacy') }}">
<i class="fa fa-angle-right me-2"></i> Politique de Confidentialité</a>
</li>
<li>
<a href="{{ path('ui_terms') }}">
<i class="fa fa-angle-right me-2"></i> Conditions d'Utilisation</a>
</li>
</ul>
</div>
</div>
<div class="col-lg-3 col-md-6 col-sm-6">
<div class="single-footer-widget">
<h6 class="footer-widget-title">Newsletter</h6>
<p class="footer-newsletter-text">Restez informé des dernières nouveautés</p>
<div class="footer-newsletter-wrapper" id="mc_embed_signup">
<form action="{{ path('ui_newsletter_subscribe') }}" method="post" class="footer-newsletter-form">
<div class="newsletter-input-group">
<i class="fa fa-envelope newsletter-icon"></i>
<input class="form-control newsletter-input" name="email" placeholder="Votre email" required type="email">
<button type="submit" class="newsletter-btn" aria-label="S'abonner">
<span class="newsletter-btn-icon">
<i class="fa fa-paper-plane" aria-hidden="true"></i>
</span>
<span class="newsletter-btn-loader" style="display: none;">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</span>
</button>
</div>
</form>
</div>
<div class="footer-trust-badges mt-3">
<div class="trust-item">
<i class="fa fa-shield"></i>
<span>Paiement sécurisé</span>
</div>
<div class="trust-item">
<i class="fa fa-truck"></i>
<span>Livraison rapide</span>
</div>
</div>
</div>
</div>
</div>
<div class="footer-divider"></div>
<div class="footer-bottom d-flex justify-content-center align-items-center flex-wrap">
<p class="footer-text m-0">
Copyright ©<script>
document.write(new Date().getFullYear());
</script>
Tous droits réservés | Ce site a été fait avec
<i class="fa fa-heart-o" aria-hidden="true" style="color: #dc3545;"></i>
par
<a href="https://tech.foulgor.com" target="_blank" style="color: #ffa200;">Foulgor Tech</a>
</p>
</div>
</div>
</footer>
<style>
.search-results-container {
position: absolute;
top: 100%;
left: 15px;
right: 15px;
background: #fff;
border: 1px solid #ddd;
border-top: none;
z-index: 9999;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-height: 400px;
overflow-y: auto;
display: none;
border-radius: 0 0 4px 4px;
}
.search-results-container .list-group-item {
border-left: none;
border-right: none;
border-radius: 0;
cursor: pointer;
padding: 12px 15px;
transition: background-color 0.2s ease;
}
.search-results-container .list-group-item:hover {
background-color: #f8f9fa;
}
.search-results-container .list-group-item i {
color: #777;
font-size: 1.2rem;
}
/* Ensure container is relative for absolute positioning of results */
#search_input_box .container {
position: relative;
}
#search_input_box .form-control {
flex: 1;
padding-right: 10px;
}
#search_input_box #search_submit_btn {
background: transparent;
border: 0;
color: #fff;
padding: 0 15px;
cursor: pointer;
transition: opacity 0.3s ease;
}
#search_input_box #search_submit_btn:hover {
opacity: 0.8;
}
#search_input_box .btn-clear-search {
background: transparent;
border: 0;
color: #fff;
padding: 0 10px;
cursor: pointer;
transition: opacity 0.3s ease;
display: none;
}
#search_input_box .btn-clear-search:hover {
opacity: 0.8;
}
#search_input_box .btn-clear-search i {
font-size: 1.1rem;
}
#search_input_box form {
position: relative;
align-items: center;
}
</style>
<script>
// Debounced autocomplete for header search
(function () {
const input = document.getElementById('search_input');
const box = document.getElementById('search_results');
const searchForm = document.getElementById('search_form');
const clearSearchBtn = document.getElementById('clear_search_btn');
const closeSearchBtn = document.getElementById('close_search');
if (! input || ! box)
return;
// Fonction pour effacer la recherche
function clearSearch() {
input.value = '';
box.style.display = 'none';
box.innerHTML = '';
if (clearSearchBtn) {
clearSearchBtn.style.display = 'none';
}
// Si on est sur la page listing, utiliser AJAX pour mettre à jour
if (typeof currentFilters !== 'undefined') {
currentFilters.q = '';
currentFilters.page = 1;
if (typeof applyFilters === 'function') {
applyFilters();
}
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
} else { // Sinon, rediriger vers la page listing sans recherche
window.location.href = '{{ path('ui_listing') }}';
}
}
// Bouton pour effacer la recherche
if (clearSearchBtn) {
clearSearchBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
clearSearch();
});
}
// Bouton de soumission de recherche
var searchSubmitBtn = document.getElementById('search_submit_btn');
if (searchSubmitBtn) {
searchSubmitBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
const q = input.value.trim();
if (q.length === 0) {
clearSearch();
return false;
}
// Si on est sur la page listing, utiliser AJAX
if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
currentFilters.q = q;
currentFilters.page = 1;
applyFilters();
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
// Fermer la boîte de recherche
if (closeSearchBtn) {
closeSearchBtn.click();
}
} else { // Sinon, rediriger vers la page listing avec la recherche
const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
window.location.href = listingUrl;
}
return false;
});
}
// Gestion du formulaire de recherche
if (searchForm) {
searchForm.addEventListener('submit', function (e) {
e.preventDefault();
e.stopPropagation();
const q = input.value.trim();
if (q.length === 0) {
clearSearch();
return;
}
// Si on est sur la page listing, utiliser AJAX
if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
currentFilters.q = q;
currentFilters.page = 1;
applyFilters();
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
// Fermer la boîte de recherche
if (closeSearchBtn) {
closeSearchBtn.click();
}
} else { // Sinon, rediriger vers la page listing avec la recherche
const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
window.location.href = listingUrl;
}
return false;
});
}
let t = null;
input.addEventListener('input', function () {
const q = input.value.trim();
clearTimeout(t);
if (q.length === 0) {
box.style.display = 'none';
box.innerHTML = '';
// Afficher/masquer le bouton clear
if (clearSearchBtn) {
clearSearchBtn.style.display = 'none';
}
return;
}
// Afficher le bouton clear
if (clearSearchBtn) {
clearSearchBtn.style.display = 'block';
}
t = setTimeout(() => fetchSuggestions(q), 300);
});
function fetchSuggestions(q) {
const url = '{{ path('ui_api_search_suggest') }}' + '?q=' + encodeURIComponent(q);
fetch(url).then(r => r.json()).then(data => {
if (!data.success) {
box.style.display = 'none';
return;
}
renderSuggestions(data.results || []);
}).catch(() => {
box.style.display = 'none';
});
}
function renderSuggestions(items) {
if (! items.length) {
box.style.display = 'none';
box.innerHTML = '';
return;
}
box.innerHTML = items.map(it => suggestionItem(it)).join('');
box.style.display = 'block';
}
function suggestionItem(it) {
const icon = it.type === 'product' ? 'ti-package' : (it.type === 'shop' ? 'ti-briefcase' : (it.type === 'category' ? 'ti-layers' : 'ti-tag'));
const typeLabel = it.type === 'product' ? 'Produit' : (it.type === 'shop' ? 'Boutique' : (it.type === 'category' ? 'Catégorie' : 'Tag'));
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>';
}
function escapeHtml(s) {
return s.replace(/[&<>"]+/g, c => ({'&': '&', '<': '<', '>': '>', '"': '"'}[c]));
}
document.addEventListener('click', function (e) {
if (! box.contains(e.target) && e.target !== input) {
box.style.display = 'none';
}
});
window.handleSearchSubmit = function (e) {
if (e)
e.preventDefault();
const first = box.querySelector('a');
if (first) {
window.location.href = first.getAttribute('href');
return false;
}
// fallback to listing page search by name
const q = input.value.trim();
if (q.length) { // Si on est sur la page listing, utiliser AJAX
if (typeof currentFilters !== 'undefined') {
currentFilters.q = q;
currentFilters.page = 1;
if (typeof applyFilters === 'function') {
applyFilters();
}
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
} else {
window.location.href = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
}
}
return false;
}
// Gestion de la touche Enter dans le champ de recherche
if (input) {
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.keyCode === 13) {
e.preventDefault();
e.stopPropagation();
const q = input.value.trim();
if (q.length === 0) {
clearSearch();
return false;
}
// Si on est sur la page listing, utiliser AJAX
if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
currentFilters.q = q;
currentFilters.page = 1;
applyFilters();
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
// Fermer la boîte de recherche
if (closeSearchBtn) {
closeSearchBtn.click();
}
} else { // Sinon, rediriger vers la page listing avec la recherche
const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
window.location.href = listingUrl;
}
return false;
}
});
}
// Ajouter un event listener au bouton de soumission (réutiliser la variable si elle existe déjà)
if (!searchSubmitBtn) {
searchSubmitBtn = document.getElementById('search_submit_btn');
}
if (searchSubmitBtn) {
searchSubmitBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
const q = input.value.trim();
if (q.length === 0) {
clearSearch();
return false;
}
// Si on est sur la page listing, utiliser AJAX
if (typeof currentFilters !== 'undefined' && typeof applyFilters === 'function') {
currentFilters.q = q;
currentFilters.page = 1;
applyFilters();
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
// Fermer la boîte de recherche
if (closeSearchBtn) {
closeSearchBtn.click();
}
} else { // Sinon, rediriger vers la page listing avec la recherche
const listingUrl = '{{ path('ui_listing') }}' + '?q=' + encodeURIComponent(q);
window.location.href = listingUrl;
}
return false;
});
}
// Afficher le bouton clear au chargement si une recherche est active
document.addEventListener('DOMContentLoaded', function () {
const q = input.value.trim();
if (q.length > 0 && clearSearchBtn) {
clearSearchBtn.style.display = 'block';
}
});
})();
// Gestion des mega-menus avec clic (comme la barre de recherche)
document.addEventListener('DOMContentLoaded', function () {
const dropdownItems = document.querySelectorAll('.nav-item.dropdown');
dropdownItems.forEach(function (item) {
const dropdownToggle = item.querySelector('.dropdown-toggle');
const dropdownMenu = item.querySelector('.mega-menu');
if (! dropdownToggle || ! dropdownMenu)
return;
// Fermer les autres menus quand on ouvre un nouveau
function closeAllMenus() {
dropdownItems.forEach(function (otherItem) {
const otherMenu = otherItem.querySelector('.mega-menu');
if (otherMenu && otherMenu !== dropdownMenu) {
$(otherMenu).slideUp(300);
otherMenu.classList.remove('active');
}
});
}
// Toggle au clic (comme la barre de recherche)
dropdownToggle.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
closeAllMenus();
if (dropdownMenu.classList.contains('active')) { // Fermer le menu
dropdownMenu.style.opacity = '0';
dropdownMenu.style.visibility = 'hidden';
dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
setTimeout(() => {
dropdownMenu.classList.remove('active');
dropdownMenu.style.display = 'none';
}, 300);
} else { // Ouvrir le menu
dropdownMenu.style.display = 'block';
setTimeout(() => {
dropdownMenu.classList.add('active');
dropdownMenu.style.opacity = '1';
dropdownMenu.style.visibility = 'visible';
dropdownMenu.style.transform = 'translateX(-50%) translateY(0)';
}, 10);
}
});
// Fermer au clic en dehors
document.addEventListener('click', function (e) {
if (! item.contains(e.target) && dropdownMenu.classList.contains('active')) {
dropdownMenu.style.opacity = '0';
dropdownMenu.style.visibility = 'hidden';
dropdownMenu.style.transform = 'translateX(-50%) translateY(-10px)';
setTimeout(() => {
dropdownMenu.classList.remove('active');
dropdownMenu.style.display = 'none';
}, 300);
}
});
// Animation des éléments du menu
const menuItems = dropdownMenu.querySelectorAll('.dropdown-item a');
menuItems.forEach(function (menuItem, index) {
menuItem.style.animationDelay = (index * 0.05) + 's';
});
});
// Gestion des clics sur mobile
if (window.innerWidth <= 768) {
dropdownItems.forEach(function (item) {
const dropdownToggle = item.querySelector('.dropdown-toggle');
const dropdownMenu = item.querySelector('.mega-menu');
dropdownToggle.addEventListener('click', function (e) {
e.preventDefault();
// Fermer tous les autres menus
dropdownItems.forEach(function (otherItem) {
if (otherItem !== item) {
const otherMenu = otherItem.querySelector('.mega-menu');
otherMenu.style.opacity = '0';
otherMenu.style.visibility = 'hidden';
otherMenu.style.transform = 'translateY(-10px)';
}
});
// Toggle le menu actuel
if (dropdownMenu.style.opacity === '1') {
dropdownMenu.style.opacity = '0';
dropdownMenu.style.visibility = 'hidden';
dropdownMenu.style.transform = 'translateY(-10px)';
} else {
dropdownMenu.style.opacity = '1';
dropdownMenu.style.visibility = 'visible';
dropdownMenu.style.transform = 'translateY(0)';
}
});
});
}
// Fermer les menus quand on clique ailleurs
document.addEventListener('click', function (e) {
if (! e.target.closest('.nav-item.dropdown')) {
dropdownItems.forEach(function (item) {
const dropdownMenu = item.querySelector('.mega-menu');
dropdownMenu.style.opacity = '0';
dropdownMenu.style.visibility = 'hidden';
dropdownMenu.style.transform = 'translateY(-10px)';
});
}
});
});
</script>
<!-- End footer Area -->
<script src="{{ asset('ui/js/vendor/jquery-2.2.4.min.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Initialiser le collapse Bootstrap 5 pour le menu hamburger après chargement de Bootstrap
document.addEventListener('DOMContentLoaded', function () { // Attendre que Bootstrap soit complètement chargé
if (typeof bootstrap !== 'undefined') {
const navbarToggler = document.querySelector('.navbar-toggler');
const navbarCollapse = document.getElementById('navbarSupportedContent');
if (navbarToggler && navbarCollapse) {
// Bootstrap 5 devrait gérer automatiquement avec data-bs-toggle
// Mais on s'assure que ça fonctionne en écoutant les événements
navbarCollapse.addEventListener('show.bs.collapse', function () {
navbarToggler.setAttribute('aria-expanded', 'true');
navbarToggler.classList.add('active');
});
navbarCollapse.addEventListener('hide.bs.collapse', function () {
navbarToggler.setAttribute('aria-expanded', 'false');
navbarToggler.classList.remove('active');
});
}
}
});
</script>
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
<script src="{{ asset('ui/js/jquery.ajaxchimp.min.js') }}"></script>
<script src="{{ asset('ui/js/jquery.nice-select.min.js') }}"></script>
<script src="{{ asset('ui/js/jquery.sticky.js') }}"></script>
<script src="{{ asset('ui/js/nouislider.min.js') }}"></script>
<script src="{{ asset('ui/js/countdown.js') }}"></script>
<script src="{{ asset('ui/js/jquery.magnific-popup.min.js') }}"></script>
<script src="{{ asset('ui/js/owl.carousel.min.js') }}"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCjCGmQ0Uq4exrzdcL6rvxywDDOvfAu6eE"></script>
<script src="{{ asset('ui/js/gmaps.min.js') }}"></script>
<script src="{{ asset('ui/js/main.js') }}"></script>
{{ encore_entry_script_tags('app') }}
<!-- PWA Service Worker Registration -->
<script>
// Enregistrement du Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('{{ asset('sw.js') }}').then((registration) => {
console.log('Service Worker enregistré avec succès:', registration.scope);
// Vérifie les mises à jour du service worker
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // Nouveau service worker disponible
if (confirm('Une nouvelle version de MaketOu est disponible. Voulez-vous l\'installer maintenant ?')) {
newWorker.postMessage({type: 'SKIP_WAITING'});
window.location.reload();
}
}
});
});
}).catch((error) => {
console.log('Échec de l\'enregistrement du Service Worker:', error);
});
// Écoute les messages du service worker
navigator.serviceWorker.addEventListener('message', (event) => {
console.log('Message du Service Worker:', event.data);
});
});
}
</script>
<!-- PWA Install Script -->
<script src="{{ asset('js/pwa-install.js') }}"></script>
<script>
// Charger les compteurs au chargement de la page
document.addEventListener('DOMContentLoaded', function () {
{% if app.user %}
loadNotificationCount();
// Délai pour s'assurer que la session est bien établie après connexion
setTimeout(function () {
loadComparisonCount();
}, 500);{% endif %}
// Gérer le bouton de recherche mobile
const searchMobileBtn = document.getElementById('searchMobileBtn');
if (searchMobileBtn) {
searchMobileBtn.addEventListener('click', function (e) {
e.preventDefault();
// Utiliser le même comportement que le bouton desktop
const searchBox = document.getElementById('search_input_box');
if (searchBox) {
searchBox.style.display = 'block';
const searchInput = document.getElementById('search_input');
if (searchInput) {
setTimeout(() => searchInput.focus(), 100);
}
}
});
}
});
function loadNotificationCount() {
{% if app.user %}
fetch('{{ path("api_notifications_count") }}').then(response => {
if (!response.ok) {
throw new Error('Erreur HTTP: ' + response.status);
}
return response.json();
}).then(data => {
const count = data.count || 0;
// Mettre à jour tous les badges de notification
const badges = [document.getElementById('notificationBadge'), document.getElementById('notificationBadgeMobile'), document.querySelector('.notification-badge')].filter(badge => badge !== null);
badges.forEach(badge => {
if (count > 0) {
badge.textContent = count;
badge.style.display = 'flex';
badge.style.color = 'white';
badge.style.backgroundColor = '#dc3545';
} else {
badge.style.display = 'none';
}
});
// Si on est sur la page des notifications, mettre à jour aussi le contenu de la page
if (typeof updateNotificationPageContent === 'function') {
updateNotificationPageContent(count);
}
}).catch(error => {
console.error('Erreur lors du chargement du compteur de notifications:', error);
});{% endif %}
}
// Exposer la fonction globalement pour qu'elle puisse être appelée depuis d'autres pages
window.loadNotificationCount = loadNotificationCount;
// Recharger le compteur toutes les 30 secondes pour garder les données à jour
{% if app.user %}
setInterval(function () {
loadNotificationCount();
}, 30000);{% endif %}function loadComparisonCount() {
{% if app.user %}
// Ne pas appeler l'API si on est sur une page d'erreur ou de redirection
if (window.location.pathname.includes('/api/') || window.location.pathname.includes('/login')) {
return;
}
// Utiliser un try-catch pour éviter toute redirection
try {
fetch('{{ path("ui_api_comparison_count") }}', {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json'
},
credentials: 'same-origin',
redirect: 'error' // Empêcher les redirections automatiques
}).then(response => { // Vérifier si la réponse est OK
if (!response.ok) { // Si c'est une redirection (301, 302, etc.) ou une erreur d'authentification, ignorer silencieusement
if (response.status >= 300 && response.status < 400) {
console.warn('Redirection détectée, arrêt de la requête');
return null;
}
if (response.status === 401 || response.status === 403) {
console.warn('Utilisateur non authentifié pour la comparaison');
return null;
}
// Pour les autres erreurs, throw pour être capturé par catch
throw new Error('Erreur HTTP: ' + response.status);
}
// Vérifier que c'est bien du JSON
const contentType = response.headers.get('content-type');
if (! contentType || ! contentType.includes('application/json')) { // Si ce n'est pas du JSON, c'est probablement une redirection HTML
console.warn('La réponse n\'est pas du JSON, probable redirection');
return null;
}
return response.json();
}).then(data => { // Si data est null (redirection ou erreur), ne rien faire
if (!data || typeof data.count === 'undefined') {
return;
}
// Mettre à jour les badges desktop et mobile
const badges = [document.getElementById('comparisonBadge'), document.getElementById('comparisonBadgeMobile')];
badges.forEach(badge => {
if (badge) {
if (data.count > 0) {
badge.textContent = data.count;
badge.style.display = 'flex';
} else {
badge.style.display = 'none';
}
}
});
}).catch(error => { // Ne pas afficher d'erreur si c'est juste une absence d'authentification ou une redirection
if (error.message && !error.message.includes('401') && !error.message.includes('403') && !error.message.includes('redirect')) {
console.error('Erreur lors du chargement du compteur de comparaison:', error);
}
// Empêcher toute redirection vers l'URL de l'API
return false;
});
} catch (e) { // Capturer toute erreur qui pourrait causer une redirection
console.warn('Erreur lors de l\'appel à l\'API de comparaison:', e);
}
{% endif %}
}
// Fonction globale pour gérer la comparaison
function toggleComparison(productId, element) {
if (! element) {
element = document.querySelector(`.comparison-btn[data-product-id="${productId}"]`);
}
if (! element) {
console.error('Bouton de comparaison non trouvé pour le produit', productId);
return;
}
const originalHtml = element.innerHTML;
// Désactiver le bouton/lien
if (element.tagName === 'BUTTON') {
element.disabled = true;
} else {
element.style.pointerEvents = 'none';
element.style.opacity = '0.6';
} element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
// Vérifier d'abord si le produit est déjà en comparaison
let isInComparison = false;
fetch(`/api/comparison/status/${productId}`).then(response => response.json()).then(data => {
isInComparison = data.inComparison;
const url = isInComparison ? `/api/comparison/remove/${productId}` : `/api/comparison/add/${productId}`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
}).then(response => response.json()).then(data => {
if (data.success) { // Mettre à jour le compteur
loadComparisonCount();
// Mettre à jour l'apparence du bouton
if (element) { // Inverser l'état (car on vient de le changer)
const newState = ! isInComparison;
// Restaurer le HTML original puis mettre à jour
element.innerHTML = originalHtml;
if (newState) {
element.classList.add('active');
// Mettre à jour le texte pour les deux types de boutons
const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
if (textEl)
textEl.textContent = 'En comparaison';
// Mettre à jour l'icône si nécessaire (chercher dans span ou i)
const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
if (iconEl) {
iconEl.style.color = '#28a745';
iconEl.classList.add('active');
}
} else {
element.classList.remove('active');
const textEl = element.querySelector('.comparison-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
if (textEl)
textEl.textContent = 'Comparer';
// Restaurer la couleur de l'icône
const iconEl = element.querySelector('.lnr-sync, span.lnr-sync, [class*="sync"], i[class*="sync"]');
if (iconEl) {
iconEl.style.color = '';
iconEl.classList.remove('active');
}
}
}
// Afficher une notification
showNotification(data.message, 'success');
} else {
showNotification(data.message, 'error');
if (element) {
element.innerHTML = originalHtml;
}
}
}).catch(error => {
console.error('Erreur:', error);
showNotification('Une erreur est survenue', 'error');
if (element) {
element.innerHTML = originalHtml;
}
}). finally(() => {
if (element) {
if (element.tagName === 'BUTTON') {
element.disabled = false;
} else {
element.style.pointerEvents = '';
element.style.opacity = '';
}
}
});
}
// Fonction globale pour gérer les favoris
function toggleWishlist(productId, element) {
if (! element) {
element = document.querySelector(`.wishlist-btn[data-product-id="${productId}"]`);
}
if (! element) {
console.error('Bouton de wishlist non trouvé pour le produit', productId);
return;
}
const originalHtml = element.innerHTML;
// Désactiver le bouton/lien
if (element.tagName === 'BUTTON') {
element.disabled = true;
} else {
element.style.pointerEvents = 'none';
element.style.opacity = '0.6';
} element.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
// Vérifier d'abord si le produit est déjà en favoris
let isInWishlist = false;
fetch(`/api/wishlist/status/${productId}`).then(response => response.json()).then(data => {
isInWishlist = data.inWishlist;
const url = isInWishlist ? `/api/wishlist/remove/${productId}` : `/api/wishlist/add/${productId}`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
}).then(response => response.json()).then(data => {
if (data.success) { // Mettre à jour l'apparence du bouton
if (element) { // Inverser l'état (car on vient de le changer)
const newState = ! isInWishlist;
// Restaurer le HTML original puis mettre à jour
element.innerHTML = originalHtml;
// Chercher l'icône dans span ou i
const icon = element.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
const text = element.querySelector('.wishlist-text') || element.querySelector('.hover-text') || element.querySelector('p.hover-text');
if (newState) {
if (icon) { // Pour les icônes linr, on change la classe
if (icon.classList.contains('lnr-heart')) {
icon.classList.remove('lnr-heart');
}
if (! icon.classList.contains('lnr-heart-filled')) {
icon.classList.add('lnr-heart-filled');
}
icon.style.color = '#dc3545';
}
element.classList.add('active');
if (text)
text.textContent = 'Dans favoris';
} else {
if (icon) { // Restaurer l'icône originale
if (icon.classList.contains('lnr-heart-filled')) {
icon.classList.remove('lnr-heart-filled');
}
if (! icon.classList.contains('lnr-heart')) {
icon.classList.add('lnr-heart');
}
icon.style.color = '';
}
element.classList.remove('active');
if (text)
text.textContent = 'Favoris';
}
}
// Afficher une notification
showNotification(data.message, 'success');
// Si on est sur la page wishlist, recharger la page
if (window.location.pathname.includes('/account/wishlist')) {
setTimeout(() => location.reload(), 1000);
}
} else {
showNotification(data.message, 'error');
if (element) {
element.innerHTML = originalHtml;
}
}
}).catch(error => {
console.error('Erreur:', error);
showNotification('Une erreur est survenue', 'error');
if (element) {
element.innerHTML = originalHtml;
}
}). finally(() => {
if (element) {
if (element.tagName === 'BUTTON') {
element.disabled = false;
} else {
element.style.pointerEvents = '';
element.style.opacity = '';
}
}
});
}
// Fonction pour afficher des notifications
function showNotification(message, type = 'success') { // Créer l'élément de notification
const notification = document.createElement('div');
notification.className = `alert alert-${
type === 'success' ? 'success' : 'danger'
} alert-dismissible fade show position-fixed`;
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);';
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// Supprimer automatiquement après 3 secondes
setTimeout(() => {
notification.remove();
}, 3000);
}
// Initialiser les alertes Bootstrap 5 pour la fermeture
document.addEventListener('DOMContentLoaded', function () { // S'assurer que les alertes peuvent être fermées
const alertCloseButtons = document.querySelectorAll('.flash-message .btn-close');
alertCloseButtons.forEach(function (button) {
button.addEventListener('click', function () {
const alert = this.closest('.alert');
if (alert) {
alert.classList.remove('show');
setTimeout(function () {
alert.remove();
}, 150);
}
});
});
// Charger les statuts initiaux des produits
{% if app.user %}
// Charger les statuts de comparaison pour tous les produits visibles
document.querySelectorAll('.comparison-btn[data-product-id]').forEach(btn => {
const productId = btn.dataset.productId;
fetch(`/api/comparison/status/${productId}`).then(response => response.json()).then(data => {
if (data.inComparison) {
btn.classList.add('active');
// Mettre à jour le texte pour les deux types de boutons
const textEl = btn.querySelector('.comparison-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
if (textEl)
textEl.textContent = 'En comparaison';
// Mettre à jour l'icône si nécessaire
const iconEl = btn.querySelector('.lnr-sync, [class*="sync"]');
if (iconEl)
iconEl.style.color = '#28a745';
}
}).catch(() => {});
});
// Charger les statuts de wishlist pour tous les produits visibles
document.querySelectorAll('.wishlist-btn[data-product-id]').forEach(btn => {
const productId = btn.dataset.productId;
fetch(`/api/wishlist/status/${productId}`).then(response => response.json()).then(data => {
if (data.inWishlist) {
// Chercher l'icône dans span ou i
const icon = btn.querySelector('span.lnr-heart, i.lnr-heart, [class*="lnr-heart"]');
if (icon) {
icon.classList.remove('lnr-heart');
icon.classList.add('lnr-heart');
icon.style.color = '#ffa200';
}
// Mettre à jour le texte pour les deux types de boutons
const textEl = btn.querySelector('.wishlist-text') || btn.querySelector('.hover-text') || btn.querySelector('p.hover-text');
if (textEl)
textEl.textContent = 'favoris';
}
}).catch(() => {});
});
// Charger le compteur de comparaison
loadComparisonCount();{% endif %}
});
</script>
<script>
// Loader global pour tous les boutons de formulaire
document.addEventListener('DOMContentLoaded', function () { // Intercepter tous les formulaires
const forms = document.querySelectorAll('form');
forms.forEach(function (form) {
form.addEventListener('submit', function (e) {
const submitButtons = form.querySelectorAll('button[type="submit"], input[type="submit"], button:not([type])');
submitButtons.forEach(function (button) { // Vérifier si le bouton n'est pas déjà désactivé
if (! button.disabled && ! button.classList.contains('loading')) {
button.classList.add('loading');
button.disabled = true;
// Sauvegarder le texte original
const originalText = button.innerHTML;
button.setAttribute('data-original-text', originalText);
// Ajouter le loader
button.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Envoi...';
// Si le formulaire n'est pas valide, réactiver le bouton après 1 seconde
setTimeout(function () {
if (! form.checkValidity()) {
button.classList.remove('loading');
button.disabled = false;
button.innerHTML = originalText;
}
}, 1000);
}
});
});
});
// Pour les boutons qui déclenchent des actions AJAX
const ajaxButtons = document.querySelectorAll('[data-ajax], .ajax-submit, button[data-submit]');
ajaxButtons.forEach(function (button) {
button.addEventListener('click', function (e) {
if (this.disabled || this.classList.contains('loading')) {
e.preventDefault();
return false;
}
this.classList.add('loading');
this.disabled = true;
const originalText = this.innerHTML;
this.setAttribute('data-original-text', originalText);
this.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Chargement...';
});
});
// Gestionnaire spécifique pour le formulaire de newsletter
const newsletterForm = document.querySelector('.footer-newsletter-form');
if (newsletterForm) {
newsletterForm.addEventListener('submit', function (e) {
const submitBtn = this.querySelector('.newsletter-btn');
if (submitBtn && ! submitBtn.disabled) {
submitBtn.classList.add('loading');
submitBtn.disabled = true;
// Afficher le loader et masquer l'icône
const icon = submitBtn.querySelector('.newsletter-btn-icon');
const loader = submitBtn.querySelector('.newsletter-btn-loader');
if (icon)
icon.style.display = 'none';
if (loader)
loader.style.display = 'flex';
// Réactiver le bouton après 5 secondes au cas où la requête échoue silencieusement
setTimeout(function () {
if (submitBtn.classList.contains('loading')) {
submitBtn.classList.remove('loading');
submitBtn.disabled = false;
if (icon)
icon.style.display = 'flex';
if (loader)
loader.style.display = 'none';
}
}, 5000);
}
});
// Gérer la réponse du formulaire (si AJAX)
newsletterForm.addEventListener('submit', function (e) {
// Si le formulaire est soumis normalement (non-AJAX), le navigateur gère la réponse
// Sinon, on peut intercepter pour gérer la réponse AJAX ici
});
}
});
// CSS pour le spinner
const style = document.createElement('style');
style.textContent = `
.spinner-border-sm {
width: 1rem;
height: 1rem;
border-width: 0.15em;
}
.spinner-border {
display: inline-block;
width: 1rem;
height: 1rem;
vertical-align: text-bottom;
border: 0.15em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border 0.75s linear infinite;
}
@keyframes spinner-border {
to { transform: rotate(360deg); }
}
button.loading {
opacity: 0.7;
cursor: not-allowed !important;
}
`;
document.head.appendChild(style);
</script>
<!-- Cart Modal -->
<div id="ebay-cart-modal" class="ebay-cart-modal-overlay" style="display: none;" data-url="">
<div class="ebay-cart-modal">
<div class="ebay-modal-header">
<h3>
<i class="lnr lnr-checkmark-circle"></i>
Ajouté au panier !</h3>
<button class="ebay-modal-close" onclick="closeEbayCartModal()">×</button>
</div>
<div class="ebay-modal-content">
<div class="ebay-product-info">
<div class="ebay-product-image">
<img id="ebay-modal-product-image" src="" alt="Produit"/>
</div>
<div class="ebay-product-details">
<h4 id="ebay-modal-product-name">Nom du produit</h4>
<div class="ebay-product-price" id="ebay-modal-product-price">Prix</div>
<div class="ebay-product-quantity">
<span>Quantité:
</span>
<span id="ebay-modal-product-quantity">1</span>
</div>
</div>
</div>
<!-- Recommendations Section -->
<div class="ebay-recommendations" style="display: none;">
<h4>Les clients ont aussi acheté :</h4>
<div
class="ebay-recommendations-grid" id="ebay-modal-recommendations"><!-- Recommendations will be injected here -->
</div>
</div>
<div class="ebay-modal-actions">
<button class="ebay-btn-continue" onclick="closeEbayCartModal()">
Continuer mes achats
</button>
<a href="{{ path('ui_cart') }}" class="ebay-btn-view-cart">
Voir mon panier
</a>
</div>
</div>
</div>
</div>
<script src="{{ asset('ui/js/cart-modal.js') }}"></script>
<script src="{{ asset('ui/js/notification-badge.js') }}"></script>
{% block javascripts %}{% endblock %}
</body>
</html>