{% extends 'base_home.html.twig' %}
{% block body %}
<!-- Start Banner Area -->
<section class="banner-area organic-breadcrumb">
<div class="container">
<div class="breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end">
<div class="col-first">
<h1>Page de listage de produit</h1>
<nav class="d-flex align-items-center">
<a href="{{ path('ui_home') }}">Accueil<span class="lnr lnr-arrow-right"></span>
</a>
<a href="javascript:void(0);">Liste des produits</a>
</nav>
</div>
</div>
</div>
</section>
<!-- End Banner Area -->
<div class="container">
<div class="row">
<div class="col-xl-3 col-lg-4 col-md-5">
<!-- Bouton toggle sidebar mobile -->
<button class="btn btn-outline-primary w-100 d-md-none mb-3 listing-sidebar-toggle" type="button" data-bs-toggle="collapse" data-bs-target="#listingSidebarCollapse" aria-expanded="false" aria-controls="listingSidebarCollapse">
<i class="lnr lnr-menu me-2"></i>Filtres et catégories
<i class="lnr lnr-chevron-down ms-2 toggle-icon"></i>
</button>
<!-- Sidebar avec collapse -->
<div class="collapse d-md-block" id="listingSidebarCollapse">
<div class="sidebar-categories">
<div class="head">Catégories</div>
<ul class="main-categories">
<li class="main-nav-list">
<a href="{{ path('ui_listing') }}" class="category-link {% if not currentCategory %}active{% endif %}">
<span class="lnr lnr-tag"></span>Toutes les catégories
<span class="number">({{ totalProducts }})</span>
</a>
</li>
{% for category in categories %}
<li class="main-nav-list">
<a href="{{ path('ui_listing', {'category': category.slug}) }}" class="category-link {% if currentCategory == category.slug %}active{% endif %}" data-category="{{ category.slug }}">
<span class="lnr lnr-tag"></span>
{{ category.name }}
<span class="number">({{ category.products|length }})</span>
</a>
</li>
{% else %}
<li class="main-nav-list">
<span>Aucune catégorie</span>
</li>
{% endfor %}
</ul>
</div>
<div class="sidebar-filter mt-50" id="brandsFilter">
<div class="top-filter-head">Filtres</div>
<div class="common-filter">
<div class="head">Marques</div>
<ul class="brand-list" id="brandList">
<li class="filter-list">
<input class="pixel-radio" type="radio" id="all-brands" name="brand" checked>
<label for="all-brands">Toutes les marques</label>
</li>
{% for brand in brands %}
<li class="filter-list">
<input class="pixel-radio" type="radio" id="brand-{{ brand.id }}" name="brand" value="{{ brand.slug }}">
<label for="brand-{{ brand.id }}">{{ brand.name }}<span>({{ brand.getActiveProductsCount() }})</span>
</label>
</li>
{% endfor %}
</ul>
</div>
<!-- Filtres avancés -->
<div class="common-filter">
<div class="head">Boutiques</div>
<div id="shopsFilter">
<form action="#">
<ul
class="shop-list" id="shopList"><!-- Les boutiques seront chargées dynamiquement -->
</ul>
</form>
</div>
</div>
<div class="common-filter">
<div class="head">Condition ({{ conditions|length }})</div>
<ul class="condition-list" id="conditionList">
<li class="filter-list">
<input class="pixel-radio" type="radio" id="all-conditions" name="condition" checked>
<label for="all-conditions">Toutes les conditions</label>
</li>
{% for condition in conditions %}
<li class="filter-list">
<input class="pixel-radio" type="radio" id="condition-{{ condition.id }}" name="condition" value="{{ condition.slug }}">
<label for="condition-{{ condition.id }}">{{ condition.name }}<span>({{ condition.getActiveProductsCount() }})</span>
</label>
</li>
{% else %}
<li class="filter-list">
<span class="text-muted">Aucune condition disponible</span>
</li>
{% endfor %}
</ul>
</div>
<div class="common-filter">
<div class="head">Produits vedettes</div>
<form action="#">
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="featured-all" name="featured" checked>
<label for="featured-all">Tous les produits</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="featured-only" name="featured" value="true">
<label for="featured-only">Produits vedettes uniquement</label>
</li>
</ul>
</form>
</div>
<div class="common-filter">
<div class="head">Type de produit</div>
<form action="#">
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="digital-all" name="digital" checked>
<label for="digital-all">Tous les types</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="digital-physical" name="digital" value="false">
<label for="digital-physical">Produits physiques</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="digital-digital" name="digital" value="true">
<label for="digital-digital">Produits numériques</label>
</li>
</ul>
</form>
</div>
<div class="common-filter">
<div class="head">Disponibilité</div>
<form action="#">
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="availability-all" name="availability" checked>
<label for="availability-all">Tous</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="availability-in-stock" name="availability" value="in_stock">
<label for="availability-in-stock">En stock</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="availability-low-stock" name="availability" value="low_stock">
<label for="availability-low-stock">Stock faible</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="availability-out-of-stock" name="availability" value="out_of_stock">
<label for="availability-out-of-stock">Rupture de stock</label>
</li>
</ul>
</form>
</div>
<div class="common-filter">
<div class="head">Note minimale</div>
<form action="#">
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="rating-all" name="rating" checked>
<label for="rating-all">Toutes les notes</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="rating-4" name="rating" value="4">
<label for="rating-4">4 étoiles et plus</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="rating-3" name="rating" value="3">
<label for="rating-3">3 étoiles et plus</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="rating-2" name="rating" value="2">
<label for="rating-2">2 étoiles et plus</label>
</li>
</ul>
</form>
</div>
<div class="common-filter">
<div class="head">Poids</div>
<div class="weight-range-area">
<div class="weight-inputs d-flex">
<div class="weight-input">
<label>Min (kg):</label>
<input type="number" id="weightMin" placeholder="0" step="0.1" min="0">
</div>
<div class="weight-input">
<label>Max (kg):</label>
<input type="number" id="weightMax" placeholder="100" step="0.1" min="0">
</div>
</div>
</div>
</div>
<div class="common-filter">
<div class="head">Couleur</div>
<div id="colorsFilter">
<form action="#">
<ul
class="color-list" id="colorList"><!-- Les couleurs seront chargées dynamiquement -->
</ul>
</form>
</div>
</div>
<div class="common-filter">
<div class="head">Taille</div>
<div id="sizesFilter">
<form action="#">
<ul
class="size-list" id="sizeList"><!-- Les tailles seront chargées dynamiquement -->
</ul>
</form>
</div>
</div>
<div class="common-filter">
<div class="head">Matériau</div>
<div id="materialsFilter">
<form action="#">
<ul
class="material-list" id="materialList"><!-- Les matériaux seront chargés dynamiquement -->
</ul>
</form>
</div>
</div>
<div class="common-filter">
<div class="head">Prix</div>
<div class="price-range-area">
<div id="price-range"></div>
<div class="value-wrapper d-flex">
<div class="price">Prix:</div>
<span>$</span>
<div id="lower-value"></div>
<div class="to">to</div>
<span>$</span>
<div id="upper-value"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Fin du collapse sidebar -->
</div>
<div class="col-xl-9 col-lg-8 col-md-7">
<!-- Barre de tri et bouton de filtres -->
<div class="row mb-4">
<div
class="col-md-6 mb-3 mb-md-0">
<!-- Bouton pour ouvrir le modal de filtres -->
<button type="button" class="btn btn-lg d-flex align-items-center" data-bs-toggle="modal" data-bs-target="#filtersModal" style="background: transparent; border: none; padding: 10px 16px; font-weight: 600; transition: all 0.3s ease;">
<div class="filter-icon-circle d-flex align-items-center justify-content-center" style="width: 42px; height: 42px; background: linear-gradient(135deg, #ffa200 0%, #ff8c00 100%); border-radius: 50%; border: 2px solid #ffa200; flex-shrink: 0; margin-right: 10px; box-shadow: 0 2px 8px rgba(255, 162, 0, 0.3);">
<i class="lnr lnr-filter" style="font-size: 1.3rem; color: white; font-weight: bold;"></i>
</div>
<span style="font-size: 1.1rem; font-weight: 600; color: #333;">Filtres</span>
<span class="badge bg-warning text-dark ms-2" id="activeFiltersCount" style="display: none; font-size: 0.85rem; padding: 4px 10px; border-radius: 12px; font-weight: 600;">0</span>
</button>
</div>
<div class="col-md-6">
<select class="form-select" id="sortSelect" onchange="applySorting()">
<option value="newest" {% if currentSort == 'newest' %} selected {% endif %}>Plus récents</option>
<option value="price_asc" {% if currentSort == 'price_asc' %} selected {% endif %}>Prix croissant</option>
<option value="price_desc" {% if currentSort == 'price_desc' %} selected {% endif %}>Prix décroissant</option>
<option value="name_asc" {% if currentSort == 'name_asc' %} selected {% endif %}>Nom A-Z</option>
<option value="name_desc" {% if currentSort == 'name_desc' %} selected {% endif %}>Nom Z-A</option>
<option value="popular" {% if currentSort == 'popular' %} selected {% endif %}>Plus populaires</option>
</select>
</div>
</div>
<!-- Start Best Seller -->
<section class="lattest-product-area pb-40 category-list">
<div class="row" id="productsContainer">
{% for product in products %}
<div class="col-lg-4 col-md-6">
<div class="single-product">
<div class="product-image-container mb-2" onmouseenter="showImageNav({{ product.id }})" onmouseleave="hideImageNav({{ product.id }})">
<a href="{{ path('ui_product_show', { slug: product.slug }) }}">
{% set allImages = product.images|default([]) %}
{% if product.variants is defined %}
{% for variant in product.variants %}
{% if variant.isActive and variant.images is defined %}
{% for variantImg in variant.images %}
{% set allImages = allImages|merge([variantImg]) %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if allImages|length > 0 %}
<img class="img-fluid main-product-img" id="main-img-{{ product.id }}" src="{{ asset(allImages[0]) }}" alt="{{ product.name }}">
{% else %}
<img class="img-fluid main-product-img" id="main-img-{{ product.id }}" src="{{ asset('ui/img/product/p1.jpg') }}" alt="{{ product.name }}">
{% endif %}
</a>
<!-- Boutons de navigation (visibles au survol) -->
{% set allImages = product.images|default([]) %}
{% if product.variants is defined %}
{% for variant in product.variants %}
{% if variant.isActive and variant.images is defined %}
{% for variantImg in variant.images %}
{% set allImages = allImages|merge([variantImg]) %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if allImages|length > 1 %}
<button class="img-nav-btn img-nav-left" id="img-left-{{ product.id }}" onclick="prevImage({{ product.id }})" style="display: none;">
<span class="lnr lnr-chevron-left"></span>
</button>
<button class="img-nav-btn img-nav-right" id="img-right-{{ product.id }}" onclick="nextImage({{ product.id }})" style="display: none;">
<span class="lnr lnr-chevron-right"></span>
</button>
<!-- Indicateurs de position -->
<div class="img-indicators" id="img-indicators-{{ product.id }}" style="display: none;">
{% for img in allImages %}
<span class="indicator" id="indicator-{{ product.id }}-{{ loop.index0 }}" onclick="showImage({{ product.id }}, {{ loop.index0 }})"></span>
{% endfor %}
</div>
{% endif %}
</div>
<div class="product-details">
<a href="{{ path('ui_product_show', { slug: product.slug }) }}">
<h6>{{ product.name }}</h6>
</a>
<!-- Affichage de la boutique -->
<div class="shop-info">
<small class="text-muted">
<i class="lnr lnr-store"></i>
Vendu par :
{% if product.shop is defined and product.shop %}
<a href="{{ path('ui_shop_show', {'slug': product.shop.slug}) }}" class="shop-link">
{{ product.shop.name }}
</a>
{% else %}
<span class="text-muted">Boutique inconnue</span>
{% endif %}
</small>
</div>
<div class="price">
<h6>{{ product.price|number_format(2, '.', ' ') }}
HTG</h6>
{% if product.compareAtPrice %}
<h6 class="l-through">{{ product.compareAtPrice|number_format(2, '.', ' ') }}
HTG</h6>
{% endif %}
</div>
<div class="prd-bottom">
<a href="javascript:void(0)" class="social-info add-to-cart" data-product-id="{{ product.id }}" data-qty="1">
<span class="ti-bag"></span>
<p class="hover-text">+ panier</p>
</a>
<a href="#" class="social-info wishlist-btn" data-product-id="{{ product.id }}" {% if app.user %} onclick="toggleWishlist({{ product.id }}, this); return false;" {% else %} onclick="alert('Vous devez être connecté pour ajouter aux favoris'); return false;" {% endif %}>
<span class="lnr lnr-heart"></span>
<p class="hover-text">Favoris</p>
</a>
<a href="#" class="social-info comparison-btn" data-product-id="{{ product.id }}" {% if app.user %} onclick="toggleComparison({{ product.id }}, this); return false;" {% else %} onclick="alert('Vous devez être connecté pour comparer des produits'); return false;" {% endif %}>
<span class="lnr lnr-sync"></span>
<p class="hover-text">Comparer</p>
</a>
<a href="{{ path('ui_product_show', { slug: product.slug }) }}" class="social-info">
<span class="lnr lnr-move"></span>
<p class="hover-text">Voir plus</p>
</a>
</div>
</div>
</div>
</div>
{% else %}
<div class="col-12">
<p>Aucun produit disponible.</p>
</div>
{% endfor %}
</div>
<!-- Bouton Charger plus de produits -->
{% if totalPages > currentPage %}
<div class="text-center mt-4 mb-4" id="loadMoreContainer">
<button type="button" class="btn btn-primary btn-lg" id="loadMoreBtn" onclick="loadMoreProducts()">
<i class="lnr lnr-plus-circle me-2"></i>Charger plus de produits
</button>
</div>
{% endif %}
<!-- Message de fin -->
<div id="noMoreProducts" class="text-center py-4" style="display: none;">
<p class="text-muted">
<i class="lnr lnr-checkmark-circle"></i>
Tous les produits ont été chargés</p>
</div>
</section>
<!-- End Best Seller -->
</div>
</div>
</div>{% endblock %}{% block title %}Liste des produits | MaketOu{% endblock %}{% block stylesheets %}
<style>
/* Styles pour la navigation des images */
.product-image-container {
position: relative;
overflow: hidden;
border-radius: 8px;
}
.img-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.6);
color: white;
border: none;
width: 35px;
height: 35px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.img-nav-btn:hover {
background: #095ad3;
transform: translateY(-50%) scale(1.1);
}
.img-nav-left {
left: 10px;
}
.img-nav-right {
right: 10px;
}
.img-indicators {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
z-index: 10;
}
.indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
cursor: pointer;
margin-bottom: 10px;
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.indicator.active {
background: #ffa200;
border-color: #ffa200;
transform: scale(1.2);
}
.indicator:hover {
background: rgba(255, 255, 255, 0.8);
}
/* Styles pour l'affichage de la boutique */
.shop-info {
margin: 8px 0;
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
}
.shop-link {
color: #007bff;
text-decoration: none;
font-weight: 500;
}
.shop-link:hover {
color: #ffa200;
text-decoration: underline;
}
/* Animation pour le survol */
.product-image-container:hover .main-product-img {
transform: scale(1.05);
transition: transform 0.3s ease;
}
.main-product-img {
transition: transform 0.3s ease;
width: 100%;
height: 100%;
object-fit: cover; /* Recadre l'image pour remplir le conteneur */
object-position: center; /* Centre l'image */
display: block;
}
/* Styles pour les images des produits associés (Deals de la semaine) */
.related-product-img-container {
position: relative;
overflow: hidden;
border-radius: 8px;
width: 100%;
height: 200px; /* Hauteur fixe pour les produits associés */
background: #f8f9fa; /* Fond au cas où l'image ne charge pas */
}
.related-product-img {
width: 100%;
height: 100%;
object-fit: cover; /* Recadre l'image pour remplir le conteneur */
object-position: center; /* Centre l'image */
display: block;
transition: transform 0.3s ease;
}
.related-product-img-container:hover .related-product-img {
transform: scale(1.05);
}
/* Styles pour l'image de catégorie dans les deals */
.category-banner-container {
display: block;
position: relative;
overflow: hidden;
border-radius: 8px;
width: 100%;
height: 400px; /* Hauteur fixe pour la bannière de catégorie */
background: #f8f9fa; /* Fond au cas où l'image ne charge pas */
}
.category-banner-img {
width: 100%;
height: 100%;
object-fit: cover; /* Recadre l'image pour remplir le conteneur */
object-position: center; /* Centre l'image */
display: block;
transition: transform 0.3s ease;
}
.category-banner-container:hover .category-banner-img {
transform: scale(1.05);
}
/* Styles pour les images de la modal Quick Product View */
.quick-view-carousel .item {
width: 100%;
height: 400px; /* Hauteur fixe pour les images du carousel */
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 8px;
}
/* Contraindre le conteneur d'image pour maintenir les proportions */
.product-image-container {
position: relative;
overflow: hidden;
border-radius: 8px;
width: 200px;
aspect-ratio: 1 / 1; /* Conteneur carré */
background: #f8f9fa; /* Fond au cas où l'image ne charge pas */
}
/* Styles pour le bouton de filtre amélioré */
.filter-icon-circle {
transition: all 0.3s ease;
}
button[data-bs-target="#filtersModal"]:hover .filter-icon-circle {
background: linear-gradient(135deg, #ff8c00 0%, #ff7700 100%) !important;
transform: rotate(15deg) scale(1.1);
box-shadow: 0 4px 15px rgba(255, 162, 0, 0.5);
}
button[data-bs-target="#filtersModal"]:hover {
transform: translateY(-2px);
}
button[data-bs-target="#filtersModal"]:active {
transform: translateY(0);
}
button[data-bs-target="#filtersModal"]:hover span {
color: #ffa200 !important;
}
/* Styles pour le modal de filtres */
#filtersModal .modal-body {
padding: 1.5rem;
}
#filtersModal .modal-body::-webkit-scrollbar {
width: 8px;
}
#filtersModal .modal-body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
#filtersModal .modal-body::-webkit-scrollbar-thumb {
background: #ffa200;
border-radius: 4px;
}
#filtersModal .modal-body::-webkit-scrollbar-thumb:hover {
background: #e69100;
}
#filtersModal .modal-footer {
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
/* Styles pour les filtres avancés */
.weight-range-area {
padding: 15px 0;
}
.weight-inputs {
gap: 15px;
align-items: center;
}
.weight-input {
flex: 1;
}
.weight-input label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.weight-input input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.weight-input input:focus {
outline: none;
border-color: #ffa200;
box-shadow: 0 0 0 2px rgba(255, 162, 0, 0.2);
}
/* Styles pour les filtres dynamiques */
.shop-list,
.color-list,
.size-list,
.material-list,
.condition-list {
max-height: 200px;
overflow-y: auto;
padding-right: 5px;
}
.shop-list::-webkit-scrollbar,
.color-list::-webkit-scrollbar,
.size-list::-webkit-scrollbar,
.material-list::-webkit-scrollbar,
.condition-list::-webkit-scrollbar {
width: 6px;
}
.shop-list::-webkit-scrollbar-track,
.color-list::-webkit-scrollbar-track,
.size-list::-webkit-scrollbar-track,
.material-list::-webkit-scrollbar-track,
.condition-list::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.shop-list::-webkit-scrollbar-thumb,
.color-list::-webkit-scrollbar-thumb,
.size-list::-webkit-scrollbar-thumb,
.material-list::-webkit-scrollbar-thumb,
.condition-list::-webkit-scrollbar-thumb {
background: #ffa200;
border-radius: 3px;
}
.shop-list::-webkit-scrollbar-thumb:hover,
.color-list::-webkit-scrollbar-thumb:hover,
.size-list::-webkit-scrollbar-thumb:hover,
.material-list::-webkit-scrollbar-thumb:hover,
.condition-list::-webkit-scrollbar-thumb:hover {
background: #e69100;
}
/* Animation pour les filtres qui apparaissent */
.common-filter {
transition: all 0.3s ease;
}
.common-filter[style*="display: none"] {
opacity: 0;
transform: translateY(-10px);
}
.common-filter[style*="display: block"] {
opacity: 1;
transform: translateY(0);
}
/* Styles pour les indicateurs de chargement */
.loading-indicator {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #ffa200;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Styles pour le loader de chargement infini */
.infinite-loader {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.loader-spinner {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
border-top: 4px solid #ffa200;
border-radius: 50%;
animation: spin 1s linear infinite;
}
#infiniteScrollLoader {
min-height: 100px;
}
#noMoreProducts {
min-height: 60px;
color: #6c757d;
}
#noMoreProducts i {
font-size: 24px;
color: #28a745;
margin-right: 8px;
}
/* Responsive pour les filtres */
@media(max-width: 768px) {
.weight-inputs {
flex-direction: column;
gap: 10px;
}
.weight-input {
width: 100%;
}
.common-filter {
margin-bottom: 20px;
}
.shop-list,
.color-list,
.size-list,
.material-list,
.condition-list {
max-height: 150px;
}
}
/* Bouton toggle sidebar mobile */
.listing-sidebar-toggle {
border-radius: 0.5rem;
padding: 0.75rem;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
}
.listing-sidebar-toggle .toggle-icon {
transition: transform 0.3s ease;
display: inline-block;
}
/* État fermé (par défaut) */
.listing-sidebar-toggle[aria-expanded="false"] .toggle-icon,
.listing-sidebar-toggle.collapsed .toggle-icon {
transform: rotate(0deg);
}
/* État ouvert */
.listing-sidebar-toggle[aria-expanded="true"] .toggle-icon,
.listing-sidebar-toggle:not(.collapsed) .toggle-icon {
transform: rotate(180deg);
}
/* Sidebar mobile */
@media(max-width: 767.98px) {
#listingSidebarCollapse {
margin-bottom: 1rem;
}
}
</style>{% endblock %}{% block javascripts %}
<script>
// === Script isolé : chargement de plus de produits (comme les boutiques) ===
var currentPage = {{ currentPage }};
var totalPages = {{ totalPages }};
var isLoading = false;
function loadMoreProducts() {
if (isLoading || currentPage >= totalPages) return;
isLoading = true;
var btn = document.getElementById('loadMoreBtn');
var productsContainer = document.getElementById('productsContainer');
if (!btn || !productsContainer) {
isLoading = false;
return;
}
var originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Chargement...';
currentPage++;
var url = new URL(window.location.href);
url.searchParams.set('page', currentPage);
fetch(url.toString(), {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.success && data.products) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = data.products;
var newProducts = tempDiv.querySelectorAll('.col-lg-4, .col-lg-3, [class*="col-lg"]');
newProducts.forEach(function(product) {
productsContainer.appendChild(product);
});
currentPage = data.pagination.currentPage;
totalPages = data.pagination.totalPages;
// Initialiser les images et events pour les nouveaux produits
if (typeof initializeProductImages === 'function') initializeProductImages(tempDiv);
if (typeof initializeProductEventListeners === 'function') initializeProductEventListeners(tempDiv);
if (currentPage >= totalPages) {
document.getElementById('loadMoreContainer').style.display = 'none';
} else {
btn.disabled = false;
btn.innerHTML = originalText;
}
} else {
document.getElementById('loadMoreContainer').style.display = 'none';
}
isLoading = false;
})
.catch(function(error) {
btn.disabled = false;
btn.innerHTML = originalText;
isLoading = false;
});
}
</script>
<script>
// Variables globales pour la navigation des images
const productImages = {};
const currentImageIndex = {};
// Variables pour le chargement infini (utilise les var globales définies au-dessus)
let hasMoreProducts = totalPages > currentPage;
let currentFilters = {
category: '{{ currentCategory ?? '' }}',
brand: '{{ currentBrand ?? '' }}',
sort: '{{ currentSort ?? 'newest' }}',
priceMin: '{{ priceMin ?? '' }}',
priceMax: '{{ priceMax ?? '' }}'
};
// Modal style eBay pour remplacer les alertes - Définir AVANT son utilisation
function showEbayModal(message, type = 'info') {
const modalId = 'ebayAlertModal';
let existingModal = document.getElementById(modalId);
if (existingModal) {
existingModal.remove();
}
const modal = document.createElement('div');
modal.id = modalId;
modal.className = 'modal fade';
modal.setAttribute('tabindex', '-1');
modal.setAttribute('aria-labelledby', 'ebayAlertModalLabel');
modal.setAttribute('aria-hidden', 'true');
// Définir les couleurs et icônes selon le type
let iconClass = 'ti-info-alt';
let iconColor = '#0064D2';
let title = 'Information';
switch (type) {
case 'success': iconClass = 'ti-check-box';
iconColor = '#0A7C42';
title = 'Succès';
break;
case 'error':
case 'danger': iconClass = 'ti-alert';
iconColor = '#D32F2F';
title = 'Erreur';
break;
case 'warning': iconClass = 'ti-alert-triangle';
iconColor = '#F57C00';
title = 'Attention';
break;
default: iconClass = 'ti-info-alt';
iconColor = '#0064D2';
title = 'Information';
}
modal.innerHTML = `
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius: 8px; border: none; box-shadow: 0 4px 20px rgba(0,0,0,0.15);">
<div class="modal-header" style="border-bottom: 1px solid #e0e0e0; padding: 20px 24px;">
<h5 class="modal-title" id="ebayAlertModalLabel" style="font-weight: 600; font-size: 18px; color: #333;">
<i class="${iconClass}" style="color: ${iconColor}; margin-right: 8px;"></i>${title}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" style="margin: 0;"></button>
</div>
<div class="modal-body" style="padding: 24px; text-align: center;">
<p style="margin: 0; font-size: 16px; color: #333; line-height: 1.5;">${message}</p>
</div>
<div class="modal-footer" style="border-top: 1px solid #e0e0e0; padding: 16px 24px; justify-content: center;">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" style="min-width: 100px; background-color: ${iconColor}; border-color: ${iconColor}; border-radius: 4px; font-weight: 500;">
OK
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
const bsModal = new bootstrap.Modal(modal);
bsModal.show();
// Supprimer le modal du DOM après fermeture
modal.addEventListener('hidden.bs.modal', function () {
modal.remove();
});
}
// Rendre la fonction globale
window.showEbayModal = showEbayModal;
document.addEventListener('DOMContentLoaded', function () { // Initialisation des données d'images pour chaque produit
{% for product in products %}
{% set allImages = product.images|default([]) %}
{% if product.variants is defined %}
{% for variant in product.variants %}
{% if variant.isActive and variant.images is defined %}
{% for variantImg in variant.images %}
{% set allImages = allImages|merge([variantImg]) %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if allImages|length > 0 %}productImages[{{ product.id }}] = [{% for img in allImages %}'{{ asset(img) }}'{% if not loop.last %},{% endif %}{% endfor %}];
currentImageIndex[{{ product.id }}] = 0;
console.log('Initialized product {{ product.id }} with images:', productImages[{{ product.id }}]);{% endif %}{% endfor %}
// Gestion du panier
const cartButtons = document.querySelectorAll('.add-to-cart');
cartButtons.forEach(button => {
button.addEventListener('click', function () {
const productId = this.getAttribute('data-product-id');
const qty = this.getAttribute('data-qty');
fetch('{{ path("ui_cart_add") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'productId=' + productId + '&qty=' + qty
}).then(response => response.json()).then(data => {
if (data.ok) {
showEbayModal('Produit ajouté au panier !', 'success');
// Mettre à jour le compteur du panier si présent
const cartBadge = document.querySelector('.cart-badge');
if (cartBadge) {
cartBadge.textContent = data.totalQty;
}
} else {
showEbayModal(data.message || 'Erreur lors de l\'ajout au panier', 'error');
}
}).catch(error => {
console.error('Erreur:', error);
showEbayModal('Erreur lors de l\'ajout au panier', 'error');
});
});
});
});
// Fonctions pour la navigation des images
window.showImageNav = function(productId) {
const leftBtn = document.getElementById('img-left-' + productId);
const rightBtn = document.getElementById('img-right-' + productId);
const indicators = document.getElementById('img-indicators-' + productId);
if (leftBtn) {
leftBtn.style.display = 'flex';
}
if (rightBtn) {
rightBtn.style.display = 'flex';
}
if (indicators) {
indicators.style.display = 'flex';
}
};
window.hideImageNav = function(productId) {
const leftBtn = document.getElementById('img-left-' + productId);
const rightBtn = document.getElementById('img-right-' + productId);
const indicators = document.getElementById('img-indicators-' + productId);
if (leftBtn) {
leftBtn.style.display = 'none';
}
if (rightBtn) {
rightBtn.style.display = 'none';
}
if (indicators) {
indicators.style.display = 'none';
}
};
window.showImage = function(productId, imageIndex) {
if (! productImages[productId] || ! productImages[productId][imageIndex]) {
console.log('Image not found for product', productId, 'index', imageIndex);
return;
}
const img = document.getElementById('main-img-' + productId);
if (img) {
console.log('Changing image to:', productImages[productId][imageIndex]);
// Force le rechargement de l'image
img.style.opacity = '0.5';
setTimeout(() => {
img.src = productImages[productId][imageIndex];
img.style.opacity = '1';
currentImageIndex[productId] = imageIndex;
updateIndicators(productId);
}, 100);
} else {
console.log('Image element not found:', 'main-img-' + productId);
}
};
window.nextImage = function(productId) {
if (! productImages[productId]) {
console.log('No images for product', productId);
return;
}
const nextIndex = (currentImageIndex[productId] + 1) % productImages[productId].length;
showImage(productId, nextIndex);
};
window.prevImage = function(productId) {
if (! productImages[productId]) {
console.log('No images for product', productId);
return;
}
const prevIndex = currentImageIndex[productId] === 0 ? productImages[productId].length - 1 : currentImageIndex[productId] - 1;
showImage(productId, prevIndex);
};
function updateIndicators(productId) {
const indicators = document.querySelectorAll('#img-indicators-' + productId + ' .indicator');
indicators.forEach((indicator, index) => {
indicator.classList.toggle('active', index === currentImageIndex[productId]);
});
}
// Fonction pour tracker la vue d'un produit
function trackProductView(productId) {
fetch (`/api/track-product-view/${productId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
}).then(response => response.json()).then(data => {
if (data.success) {
console.log (`Vue enregistrée pour le produit ${productId}:`, data.viewCount);
}
}).catch(error => {
console.error('Erreur lors du tracking:', error);
});
}
// Fonction pour tracker la vue d'une boutique
function trackShopView(shopId) {
fetch (`/api/track-shop-view/${shopId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
}).then(response => response.json()).then(data => {
if (data.success) {
console.log (`Vue enregistrée pour la boutique ${shopId}:`, data.viewCount);
}
}).catch(error => {
console.error('Erreur lors du tracking:', error);
});
}
// Ajouter le tracking aux événements existants
document.addEventListener('DOMContentLoaded', function () { // Tracking au survol des produits (avec délai)
const productCards = document.querySelectorAll('.single_product_item');
productCards.forEach(card => {
const productId = card.dataset.productId;
const shopId = card.dataset.shopId;
if (productId) {
let hoverTimeout;
card.addEventListener('mouseenter', () => { // Tracker la vue du produit après 2 secondes de survol
hoverTimeout = setTimeout(() => {
trackProductView(productId);
}, 2000);
});
card.addEventListener('mouseleave', () => {
clearTimeout(hoverTimeout);
});
// Tracking au clic sur le lien du produit
const productLink = card.querySelector('.product_img a');
if (productLink) {
productLink.addEventListener('click', () => {
trackProductView(productId);
});
}
}
// Tracking au clic sur le lien de la boutique
if (shopId) {
const shopLink = card.querySelector('.shop-link');
if (shopLink) {
shopLink.addEventListener('click', () => {
trackShopView(shopId);
});
}
}
});
});
</script>
<script>
// Système de filtrage AJAX avancé
// currentFilters est déjà déclaré plus haut, on met juste à jour les valeurs
if (typeof currentFilters !== 'undefined') { // Mettre à jour les valeurs existantes
currentFilters.category = '{{ currentCategory }}';
currentFilters.brand = '{{ currentBrand }}';
currentFilters.condition = '{{ currentCondition }}';
currentFilters.sort = '{{ currentSort }}';
currentFilters.priceMin = '{{ priceMin }}';
currentFilters.priceMax = '{{ priceMax }}';
currentFilters.page = {{ currentPage }};
currentFilters.q = currentFilters.q || '{{ app.request.query.get('q', '') }}';
// Ajouter les nouveaux filtres depuis l'URL s'ils n'existent pas
if (! currentFilters.shop)
currentFilters.shop = '{{ app.request.query.get('shop', '') }}';
if (! currentFilters.featured)
currentFilters.featured = '{{ app.request.query.get('featured', '') }}';
if (! currentFilters.digital)
currentFilters.digital = '{{ app.request.query.get('digital', '') }}';
if (! currentFilters.stockStatus)
currentFilters.stockStatus = '{{ app.request.query.get('stock_status', '') }}';
if (! currentFilters.ratingMin)
currentFilters.ratingMin = '{{ app.request.query.get('rating_min', '') }}';
if (! currentFilters.rating)
currentFilters.rating = '{{ app.request.query.get('rating_min', '') }}';
if (! currentFilters.weightMin)
currentFilters.weightMin = '{{ app.request.query.get('weight_min', '') }}';
if (! currentFilters.weightMax)
currentFilters.weightMax = '{{ app.request.query.get('weight_max', '') }}';
if (! currentFilters.color)
currentFilters.color = '{{ app.request.query.get('color', '') }}';
if (! currentFilters.size)
currentFilters.size = '{{ app.request.query.get('size', '') }}';
if (! currentFilters.material)
currentFilters.material = '{{ app.request.query.get('material', '') }}';
if (! currentFilters.condition)
currentFilters.condition = '{{ app.request.query.get('condition', '') }}';
if (! currentFilters.availability)
currentFilters.availability = '{{ app.request.query.get('availability', '') }}';
} else { // Si currentFilters n'existe pas, le créer avec les valeurs de l'URL
let currentFilters = {
category: '{{ currentCategory ?? '' }}',
brand: '{{ currentBrand ?? '' }}',
condition: '{{ currentCondition ?? '' }}',
sort: '{{ currentSort ?? 'newest' }}',
priceMin: '{{ app.request.query.get('price_min', '') }}',
priceMax: '{{ app.request.query.get('price_max', '') }}',
page: {{ currentPage }},
q: '{{ app.request.query.get('q', '') }}',
// Nouveaux filtres depuis l'URL
shop: '{{ app.request.query.get('shop', '') }}',
featured: '{{ app.request.query.get('featured', '') }}',
digital: '{{ app.request.query.get('digital', '') }}',
stockStatus: '{{ app.request.query.get('stock_status', '') }}',
ratingMin: '{{ app.request.query.get('rating_min', '') }}',
rating: '{{ app.request.query.get('rating_min', '') }}',
weightMin: '{{ app.request.query.get('weight_min', '') }}',
weightMax: '{{ app.request.query.get('weight_max', '') }}',
color: '{{ app.request.query.get('color', '') }}',
size: '{{ app.request.query.get('size', '') }}',
material: '{{ app.request.query.get('material', '') }}',
condition: '{{ app.request.query.get('condition', '') }}',
availability: '{{ app.request.query.get('availability', '') }}'
};
}
// Fonction pour appliquer les filtres
function applyFilters() {
const url = new URL('{{ path('ui_api_products_filter') }}', window.location.origin);
// Ajouter les paramètres
Object.keys(currentFilters).forEach(key => {
if (currentFilters[key] && currentFilters[key] !== '') { // Mapper les clés pour l'API
let apiKey = key;
if (key === 'priceMin') {
apiKey = 'price_min';
} else if (key === 'priceMax') {
apiKey = 'price_max';
} else if (key === 'ratingMin') {
apiKey = 'rating_min';
} else if (key === 'weightMin') {
apiKey = 'weight_min';
} else if (key === 'weightMax') {
apiKey = 'weight_max';
} else if (key === 'stockStatus') {
apiKey = 'stock_status';
}
url.searchParams.set(apiKey, currentFilters[key]);
}
});
// Afficher le loader
document.getElementById('productsContainer').innerHTML = '<div class="col-12 text-center"><i class="fa fa-spinner fa-spin"></i> Chargement...</div>';
// Faire la requête AJAX
fetch(url).then(response => response.json()).then(data => {
if (data.success) { // Mettre à jour les produits
document.getElementById('productsContainer').innerHTML = data.products;
// Mettre à jour la pagination
currentPage = data.pagination.currentPage;
totalPages = data.pagination.totalPages;
hasMoreProducts = currentPage < totalPages;
// Préserver l'état des filtres statiques avant la mise à jour
const preservedFilters = {
featured: currentFilters.featured,
digital: currentFilters.digital,
availability: currentFilters.availability,
rating: currentFilters.rating || currentFilters.ratingMin
};
// Mettre à jour les marques disponibles (toujours afficher toutes)
updateAvailableBrands(data.availableBrands || []);
// Mettre à jour les conditions disponibles (toujours afficher toutes)
updateAvailableConditions(data.availableConditions || []);
// Mettre à jour les boutiques disponibles (toujours afficher toutes)
updateAvailableShops(data.availableShops || []);
// Mettre à jour les attributs disponibles (toujours afficher tous)
updateAvailableAttributes(data.availableAttributes || {});
// Restaurer l'état des filtres statiques après la mise à jour
restoreStaticFiltersState(preservedFilters);
// Réinitialiser les filtres depuis currentFilters pour s'assurer qu'ils sont tous cochés
if (typeof initializeFiltersFromURL === 'function') {
initializeFiltersFromURL();
}
// Mettre à jour l'URL
updateURL();
// Mettre à jour l'affichage du bouton
updateLoadMoreButton();
// Réinitialiser les event listeners pour les nouveaux produits
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.products;
initializeProductEventListeners(tempDiv);
initializeProductImages(tempDiv);
// Mettre à jour les filtres actifs
if (typeof updateActiveFilters === 'function') {
updateActiveFilters();
}
}
}).catch(error => {
console.error('Erreur lors du filtrage:', error);
document.getElementById('productsContainer').innerHTML = '<div class="col-12 text-center text-danger">Erreur lors du chargement des produits</div>';
});
}
// Exposer la fonction globalement
window.applyFilters = applyFilters;
// Fonction pour appliquer le tri
function applySorting() {
currentFilters.sort = document.getElementById('sortSelect').value;
currentFilters.page = 1;
applyFilters();
}
// Fonction pour appliquer le filtre de prix
function applyPriceFilter() {
currentFilters.priceMin = document.getElementById('priceMin').value;
currentFilters.priceMax = document.getElementById('priceMax').value;
currentFilters.page = 1;
applyFilters();
}
// Fonction pour appliquer le filtre de poids
function applyWeightFilter() {
currentFilters.weightMin = document.getElementById('weightMin').value;
currentFilters.weightMax = document.getElementById('weightMax').value;
currentFilters.page = 1;
applyFilters();
}
// Fonction pour changer de page
function changePage(page) {
currentFilters.page = page;
applyFilters();
}
// Fonction pour mettre à jour les marques disponibles
function updateAvailableBrands(brands) {
const brandsFilter = document.getElementById('brandsFilter');
const brandList = document.getElementById('brandList');
const currentBrandValue = currentFilters.brand || '';
// Toujours afficher le filtre des marques
brandsFilter.style.display = 'block';
// Si pas de marques disponibles, garder les marques existantes ou afficher un message
if (brands.length === 0) { // Ne pas réinitialiser si la liste existe déjà et a du contenu
if (brandList && brandList.children.length > 0) { // Juste mettre à jour les états cochés
const existingRadios = brandList.querySelectorAll('input[name="brand"]');
existingRadios.forEach(radio => {
if (currentBrandValue && radio.value === currentBrandValue) {
radio.checked = true;
} else if (! currentBrandValue && radio.id === 'all-brands') {
radio.checked = true;
} else {
radio.checked = false;
}
});
return; // Garder les marques existantes
}
// Sinon, afficher un message
brandList.innerHTML = '<li class="filter-list"><span class="text-muted">Aucune marque disponible</span></li>';
return;
}
let html = '';
const isAllSelected = ! currentBrandValue || currentBrandValue === '';
html += `<li class="filter-list"><input class="pixel-radio" type="radio" id="all-brands" name="brand" ${
isAllSelected ? 'checked' : ''
}><label for="all-brands">Toutes les marques</label></li>`;
brands.forEach(brand => {
const isSelected = currentBrandValue === brand.slug;
html += `<li class="filter-list">
<input class="pixel-radio" type="radio" id="brand-${
brand.id
}" name="brand" value="${
brand.slug
}" ${
isSelected ? 'checked' : ''
}>
<label for="brand-${
brand.id
}">${
brand.name
}<span>(${
brand.productCount
})</span></label>
</li>`;
});
brandList.innerHTML = html;
// Ajouter les event listeners
brandList.querySelectorAll('input[name="brand"]').forEach(radio => {
radio.addEventListener('change', function () {
if (this.id === 'all-brands') {
currentFilters.brand = '';
} else {
currentFilters.brand = this.value;
} currentFilters.page = 1;
applyFilters();
});
});
}
// Fonction pour mettre à jour les conditions disponibles
function updateAvailableConditions(conditions) {
const conditionList = document.getElementById('conditionList');
const currentConditionValue = currentFilters.condition || '';
// Toujours garder le contenu statique et juste mettre à jour les états cochés
if (conditionList && conditionList.children.length > 0) {
const existingRadios = conditionList.querySelectorAll('input[name="condition"]');
existingRadios.forEach(radio => {
if (currentConditionValue && radio.value === currentConditionValue) {
radio.checked = true;
} else if (! currentConditionValue && radio.id === 'all-conditions') {
radio.checked = true;
} else {
radio.checked = false;
}
});
return; // Garder les conditions existantes
}
// Si pas de contenu statique, générer dynamiquement
if (conditions.length === 0) {
conditionList.innerHTML = '<li class="filter-list"><span class="text-muted">Aucune condition disponible</span></li>';
return;
}
let html = '';
const isAllSelected = ! currentConditionValue || currentConditionValue === '';
html += `<li class="filter-list"><input class="pixel-radio" type="radio" id="all-conditions" name="condition" ${
isAllSelected ? 'checked' : ''
}><label for="all-conditions">Toutes les conditions</label></li>`;
conditions.forEach(condition => {
const isSelected = currentConditionValue === condition.slug;
html += `<li class="filter-list">
<input class="pixel-radio" type="radio" id="condition-${
condition.id
}" name="condition" value="${
condition.slug
}" ${
isSelected ? 'checked' : ''
}>
<label for="condition-${
condition.id
}">${
condition.name
}<span>(${
condition.productCount
})</span></label>
</li>`;
});
conditionList.innerHTML = html;
// Ajouter les event listeners
conditionList.querySelectorAll('input[name="condition"]').forEach(radio => {
radio.addEventListener('change', function () {
if (this.id === 'all-conditions') {
currentFilters.condition = '';
} else {
currentFilters.condition = this.value;
} currentFilters.page = 1;
applyFilters();
});
});
}
// Fonction pour mettre à jour les boutiques disponibles
function updateAvailableShops(shops) {
const shopsFilter = document.getElementById('shopsFilter');
const shopList = document.getElementById('shopList');
const currentShopValue = currentFilters.shop || '';
// Toujours afficher le filtre des boutiques
shopsFilter.style.display = 'block';
// Si pas de boutiques disponibles, afficher un message ou garder les boutiques existantes
if (shops.length === 0) { // Ne pas réinitialiser si la liste existe déjà et a du contenu
if (shopList && shopList.children.length > 0) {
return; // Garder les boutiques existantes
}
// Sinon, afficher un message
shopList.innerHTML = '<li class="filter-list"><span class="text-muted">Aucune boutique disponible</span></li>';
return;
}
let html = '';
const isAllSelected = ! currentShopValue || currentShopValue === '';
html += `<li class="filter-list"><input class="pixel-radio" type="radio" id="all-shops" name="shop" ${
isAllSelected ? 'checked' : ''
}><label for="all-shops">Toutes les boutiques</label></li>`;
shops.forEach(shop => {
const isSelected = currentShopValue === shop.slug;
html += `<li class="filter-list">
<input class="pixel-radio" type="radio" id="shop-${
shop.id
}" name="shop" value="${
shop.slug
}" ${
isSelected ? 'checked' : ''
}>
<label for="shop-${
shop.id
}">${
shop.name
}<span>(${
shop.productCount
})</span></label>
</li>`;
});
shopList.innerHTML = html;
// Mettre à jour aussi le modal
const modalShopList = document.getElementById('modalShopList');
if (modalShopList) {
modalShopList.innerHTML = html.replace(/id="all-shops"/g, 'id="modal-all-shops"').replace(/id="shop-/g, 'id="modal-shop-').replace(/name="shop"/g, 'name="modal-shop"').replace(/for="all-shops"/g, 'for="modal-all-shops"').replace(/for="shop-/g, 'for="modal-shop-');
}
// Ajouter les event listeners (seulement pour la sidebar, le modal gère ses propres listeners)
shopList.querySelectorAll('input[name="shop"]').forEach(radio => {
radio.addEventListener('change', function () {
if (this.id === 'all-shops') {
currentFilters.shop = '';
} else {
currentFilters.shop = this.value;
} currentFilters.page = 1;
applyFilters();
});
});
}
// Fonction pour mettre à jour les attributs disponibles
function updateAvailableAttributes(attributes) {
// Toujours afficher tous les filtres d'attributs, même s'ils ne sont pas pertinents
// Couleurs
updateAttributeFilter('colors', attributes.colors, 'colorList', 'colorsFilter');
// Tailles
updateAttributeFilter('sizes', attributes.sizes, 'sizeList', 'sizesFilter');
// Matériaux
updateAttributeFilter('materials', attributes.materials, 'materialList', 'materialsFilter');
// Conditions
updateAttributeFilter('conditions', attributes.conditions, 'conditionList', 'conditionsFilter');
}
// Fonction générique pour mettre à jour les filtres d'attributs
function updateAttributeFilter(attributeType, attributes, listId, filterId) {
const filter = document.getElementById(filterId);
const list = document.getElementById(listId);
const currentValue = currentFilters[attributeType] || '';
// Toujours afficher les filtres d'attributs
filter.style.display = 'block';
// Si pas d'attributs disponibles, afficher un message ou garder les attributs existants
if (! attributes || attributes.length === 0) { // Ne pas réinitialiser si la liste existe déjà et a du contenu
if (list && list.children.length > 0) {
return; // Garder les attributs existants
}
// Sinon, afficher un message
list.innerHTML = '<li class="filter-list"><span class="text-muted">Aucun attribut disponible</span></li>';
return;
}
let html = '';
const isAllSelected = ! currentValue || currentValue === '';
html += `<li class="filter-list"><input class="pixel-radio" type="radio" id="all-${attributeType}" name="${attributeType}" ${
isAllSelected ? 'checked' : ''
}><label for="all-${attributeType}">Tous</label></li>`;
attributes.forEach(attr => {
const isSelected = currentValue === attr.value;
html += `<li class="filter-list">
<input class="pixel-radio" type="radio" id="${attributeType}-${
attr.value
}" name="${attributeType}" value="${
attr.value
}" ${
isSelected ? 'checked' : ''
}>
<label for="${attributeType}-${
attr.value
}">${
attr.value
}<span>(${
attr.count
})</span></label>
</li>`;
});
list.innerHTML = html;
// Mettre à jour aussi le modal (si l'élément existe)
const modalListId = 'modal' + listId.charAt(0).toUpperCase() + listId.slice(1); // modalColorList, modalSizeList, etc.
const modalList = document.getElementById(modalListId);
if (modalList) { // Adapter le HTML pour le modal (changer les IDs et names)
let modalHtml = html.replace(new RegExp (`id="all-${attributeType}"`, 'g'), `id="modal-all-${attributeType}"`).replace(new RegExp (`id="${attributeType}-`, 'g'), `id="modal-${attributeType}-`).replace(new RegExp (`name="${attributeType}"`, 'g'), `name="modal-${attributeType}"`).replace(new RegExp (`for="all-${attributeType}"`, 'g'), `for="modal-all-${attributeType}"`).replace(new RegExp (`for="${attributeType}-`, 'g'), `for="modal-${attributeType}-`);
modalList.innerHTML = modalHtml;
}
// Ajouter les event listeners (seulement pour la sidebar, le modal gère ses propres listeners)
list.querySelectorAll (`input[name="${attributeType}"]`).forEach(radio => {
radio.addEventListener('change', function () {
if (this.id === `all-${attributeType}`) {
currentFilters[attributeType] = '';
} else {
currentFilters[attributeType] = this.value;
} currentFilters.page = 1;
applyFilters();
});
});
} else {
filter.style.display = 'none';
}
}
// Fonction pour mettre à jour l'URL
function updateURL () {
const url = new URL(window.location);
// Supprimer les anciens paramètres
const paramsToRemove = [
'category',
'brand',
'sort',
'price_min',
'price_max',
'page',
'shop',
'featured',
'digital',
'stock_status',
'rating_min',
'weight_min',
'weight_max',
'color',
'size',
'material',
'condition',
'availability'
];
paramsToRemove.forEach(param => url.searchParams.delete(param));
// Ajouter les nouveaux paramètres
Object.keys(currentFilters).forEach(key => {
if (currentFilters[key]) {
url.searchParams.set(key, currentFilters[key]);
}
});
// Mettre à jour l'URL sans recharger la page
window.history.pushState({}, '', url);
}
// Fonction pour restaurer l'état des filtres statiques
function restoreStaticFiltersState (preservedFilters) { // Restaurer featured
if (preservedFilters.featured) {
const featuredInput = document.querySelector (`input[name="featured"][value="${
preservedFilters.featured
}"]`);
if (featuredInput) {
featuredInput.checked = true;
} else {
const featuredAll = document.getElementById('featured-all');
if (featuredAll)
featuredAll.checked = true;
}
} else {
const featuredAll = document.getElementById('featured-all');
if (featuredAll)
featuredAll.checked = true;
}
// Restaurer digital
if (preservedFilters.digital) {
const digitalInput = document.querySelector (`input[name="digital"][value="${
preservedFilters.digital
}"]`);
if (digitalInput) {
digitalInput.checked = true;
} else {
const digitalAll = document.getElementById('digital-all');
if (digitalAll)
digitalAll.checked = true;
}
} else {
const digitalAll = document.getElementById('digital-all');
if (digitalAll)
digitalAll.checked = true;
}
// Restaurer availability
if (preservedFilters.availability) {
const availabilityInput = document.querySelector (`input[name="availability"][value="${
preservedFilters.availability
}"]`);
if (availabilityInput) {
availabilityInput.checked = true;
} else {
const availabilityAll = document.getElementById('availability-all');
if (availabilityAll)
availabilityAll.checked = true;
}
} else {
const availabilityAll = document.getElementById('availability-all');
if (availabilityAll)
availabilityAll.checked = true;
}
// Restaurer rating (mapping vers ratingMin)
if (preservedFilters.rating) {
const ratingInput = document.querySelector (`input[name="rating"][value="${
preservedFilters.rating
}"]`);
if (ratingInput) {
ratingInput.checked = true;
} else {
const ratingAll = document.getElementById('rating-all');
if (ratingAll)
ratingAll.checked = true;
}
} else {
const ratingAll = document.getElementById('rating-all');
if (ratingAll)
ratingAll.checked = true;
}
}
// Fonction pour initialiser tous les filtres avec les valeurs de l'URL
function initializeFiltersFromURL () { // Initialiser les filtres statiques
const staticFilters = {
featured: currentFilters.featured || '',
digital: currentFilters.digital || '',
availability: currentFilters.availability || '',
rating: currentFilters.rating || currentFilters.ratingMin || ''
};
// Cocher les filtres statiques
Object.keys(staticFilters).forEach(filterName => {
const value = staticFilters[filterName];
if (value) {
const input = document.querySelector(`input[name="${filterName}"][value="${value}"]`);
if (input) {
input.checked = true;
} else { // Cocher "all" si la valeur n'existe pas
const allInput = document.getElementById (`${filterName}-all`);
if (allInput)
allInput.checked = true;
}
} else {
const allInput = document.getElementById (`${filterName}-all`);
if (allInput)
allInput.checked = true;
}
});
// Initialiser les filtres de poids
if (currentFilters.weightMin) {
const weightMinInput = document.getElementById('weightMin');
if (weightMinInput)
weightMinInput.value = currentFilters.weightMin;
}
if (currentFilters.weightMax) {
const weightMaxInput = document.getElementById('weightMax');
if (weightMaxInput)
weightMaxInput.value = currentFilters.weightMax;
}
// Initialiser les filtres de marque (si déjà chargés)
if (currentFilters.brand) {
const brandInput = document.querySelector (`input[name="brand"][value="${
currentFilters.brand
}"]`);
if (brandInput) {
brandInput.checked = true;
} else {
const allBrands = document.getElementById('all-brands');
if (allBrands)
allBrands.checked = true;
}
}
// Initialiser les filtres de boutique (si déjà chargés)
if (currentFilters.shop) {
const shopInput = document.querySelector (`input[name="shop"][value="${
currentFilters.shop
}"]`);
if (shopInput) {
shopInput.checked = true;
} else {
const allShops = document.getElementById('all-shops');
if (allShops)
allShops.checked = true;
}
}
// Initialiser les filtres d'attributs (si déjà chargés)
['color', 'size', 'material', 'condition'].forEach(attrType => {
if (currentFilters[attrType]) {
const attrInput = document.querySelector(`input[name="${attrType}"][value="${
currentFilters[attrType]
}"]`);
if (attrInput) {
attrInput.checked = true;
} else {
const allAttr = document.getElementById (`all-${attrType}`);
if (allAttr)
allAttr.checked = true;
}
}
});
}
// Initialiser les filtres
document.addEventListener('DOMContentLoaded', function () { // Initialiser les filtres depuis l'URL
initializeFiltersFromURL();
// Event listeners pour les filtres statiques
const staticFilters = ['featured', 'digital', 'availability'];
staticFilters.forEach(filterName => {
const inputs = document.querySelectorAll (`input[name="${filterName}"]`);
inputs.forEach(input => {
input.addEventListener('change', function () {
if (this.id.includes('-all')) {
currentFilters[filterName] = '';
} else {
currentFilters[filterName] = this.value;
}
currentFilters.page = 1;
applyFilters();
});
});
});
// Event listener spécial pour rating (mapping vers ratingMin)
const ratingInputs = document.querySelectorAll('input[name="rating"]');
ratingInputs.forEach(input => {
input.addEventListener('change', function () {
if (this.id === 'rating-all') {
currentFilters.ratingMin = '';
currentFilters.rating = '';
} else {
currentFilters.ratingMin = this.value;
currentFilters.rating = this.value;
}
currentFilters.page = 1;
applyFilters();
});
});
// Event listeners pour les filtres de poids
const weightInputs = ['weightMin', 'weightMax'];
weightInputs.forEach(inputId => {
const input = document.getElementById(inputId);
if (input) {
input.addEventListener('change', applyWeightFilter);
input.addEventListener('input', debounce(applyWeightFilter, 500));
}
});
// Event listeners pour les filtres de prix
const priceInputs = ['priceMin', 'priceMax'];
priceInputs.forEach(inputId => {
const input = document.getElementById(inputId);
if (input) {
input.addEventListener('change', applyPriceFilter);
input.addEventListener('input', debounce(applyPriceFilter, 500));
}
});
// Ajouter les event listeners pour les marques existantes
const brandList = document.getElementById('brandList');
if (brandList) {
brandList.querySelectorAll('input[name="brand"]').forEach(radio => {
radio.addEventListener('change', function () {
if (this.id === 'all-brands') {
currentFilters.brand = '';
} else {
currentFilters.brand = this.value;
}
currentFilters.page = 1;
applyFilters();
});
});
}
// Appliquer les filtres initiaux si une catégorie est sélectionnée
if (currentFilters.category) {
applyFilters();
} else { // Initialiser l'affichage du bouton au chargement de la page
updateLoadMoreButton();
}
});
// Fonction de debounce pour éviter trop de requêtes
function debounce (func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Les fonctions toggleComparison, toggleWishlist, et showNotification sont définies globalement dans base_home.html.twig
// Pas besoin de les redéfinir ici
// Fonctions pour le dropshipping
function generateDropshipLink (productId) {
showEbayPromptModal('URL originale (optionnel):', '').then(originalUrl => {
if (originalUrl === null)
return;
// Utilisateur a annulé
const formData = new FormData();
formData.append('originalUrl', originalUrl || '');
fetch (`/api/dropship/generate/${productId}`, {
method: 'POST',
body: formData
}).then(response => response.json()).then(data => {
if (data.success) { // Afficher le lien généré dans une modal
showDropshipLinkModal(data.data);
} else {
showNotification(data.message || 'Erreur lors de la génération du lien', 'error');
}
}).catch(error => {
console.error('Erreur:', error);
showNotification('Erreur lors de la génération du lien', 'error');
});
});
}
function showDropshipLinkModal (linkData) {
const modal = document.createElement('div');
modal.className = 'modal fade show';
modal.style.display = 'block';
modal.innerHTML = `
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fa fa-share-alt"></i> Lien de dropshipping généré
</h5>
<button type="button" class="btn-close" onclick="closeModal(this)"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Lien de dropshipping :</label>
<div class="input-group">
<input type="text" class="form-control" value="${
linkData.dropshipUrl
}" readonly id="dropshipUrl">
<button class="btn btn-outline-secondary" onclick="copyToClipboard('${
linkData.dropshipUrl
}')">
<i class="fa fa-copy"></i>
</button>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-body text-center">
<h6 class="card-title">Taux de commission</h6>
<h4 class="text-primary">${
linkData.commissionRate
}%</h4>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-body text-center">
<h6 class="card-title">Commission par vente</h6>
<h4 class="text-success">${
linkData.commissionAmount
} HTG</h4>
</div>
</div>
</div>
</div>
<div class="alert alert-info">
<i class="fa fa-info-circle"></i>
<strong>Note :</strong> Ce lien expire le ${
new Date(linkData.expiresAt).toLocaleDateString('fr-FR')
}.
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeModal(this)">Fermer</button>
<a href="/dropship/dashboard" class="btn btn-primary">
<i class="fa fa-tachometer"></i> Voir le dashboard
</a>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
// Ajouter le backdrop
const backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop fade show';
document.body.appendChild(backdrop);
}
function closeModal (button) {
const modal = button.closest('.modal');
const backdrop = document.querySelector('.modal-backdrop');
if (modal) {
modal.remove();
}
if (backdrop) {
backdrop.remove();
}
}
function copyToClipboard (text) {
navigator.clipboard.writeText(text).then(function () {
showNotification('Lien copié dans le presse-papiers !', 'success');
}, function (err) {
console.error('Erreur lors de la copie:', err);
showNotification('Erreur lors de la copie du lien', 'error');
});
}
// Fonction pour initialiser le chargement infini
function initInfiniteScroll () {
const loader = document.getElementById('infiniteScrollLoader');
const noMoreProducts = document.getElementById('noMoreProducts');
// Afficher le loader si il y a plus de produits
if (hasMoreProducts && loader) {
loader.style.display = 'block';
} else if (!hasMoreProducts && noMoreProducts) {
noMoreProducts.style.display = 'block';
}
// Observer pour détecter quand l'utilisateur arrive en bas
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !isLoading && hasMoreProducts) {
loadMoreProducts();
}
});
}, {
root: null,
rootMargin: '200px', // Démarrer le chargement 200px avant d'arriver en bas
threshold: 0.1
});
// Observer le loader
if (loader) {
observer.observe(loader);
}
}
// Fonction pour mettre à jour l'affichage du bouton
function updateLoadMoreButton () {
const loadMoreContainer = document.getElementById('loadMoreContainer');
const noMoreProducts = document.getElementById('noMoreProducts');
if (hasMoreProducts) {
if (loadMoreContainer) {
loadMoreContainer.style.display = 'block';
}
if (noMoreProducts) {
noMoreProducts.style.display = 'none';
}
} else {
if (loadMoreContainer) {
loadMoreContainer.style.display = 'none';
}
if (noMoreProducts) {
noMoreProducts.style.display = 'block';
}
}
}
// Fonction pour initialiser les images des nouveaux produits
function initializeProductImages (container) {
const productCards = container.querySelectorAll('.single-product');
productCards.forEach(card => {
const productId = card.querySelector('.add-to-cart') ?. getAttribute('data-product-id');
if (productId) { // Récupérer les images depuis les attributs data ou depuis le DOM
const imgElement = card.querySelector('.main-product-img');
if (imgElement && imgElement.dataset.images) {
const images = JSON.parse(imgElement.dataset.images);
productImages[productId] = images;
currentImageIndex[productId] = 0;
}
}
});
}
// Fonction pour initialiser les event listeners des nouveaux produits
function initializeProductEventListeners (container) { // Boutons d'ajout au panier
const cartButtons = container.querySelectorAll('.add-to-cart');
cartButtons.forEach(button => {
button.addEventListener('click', function () {
const productId = this.getAttribute('data-product-id');
const qty = this.getAttribute('data-qty');
fetch('{{ path("ui_cart_add") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'productId=' + productId + '&qty=' + qty
}).then(response => response.json()).then(data => {
if (data.ok) {
showEbayModal('Produit ajouté au panier !', 'success');
const cartBadge = document.querySelector('.cart-badge');
if (cartBadge) {
cartBadge.textContent = data.totalQty;
}
} else {
showEbayModal(data.message || 'Erreur lors de l\'ajout au panier', 'error');
}
}).catch(error => {
console.error('Erreur:', error);
showEbayModal('Erreur lors de l\'ajout au panier', 'error');
});
});
});
}
// La fonction showEbayModal est déjà définie plus haut
// Modal style eBay pour remplacer prompt()
function showEbayPromptModal (message, defaultValue = '') {
return new Promise((resolve) => {
const modalId = 'ebayPromptModal';
let existingModal = document.getElementById(modalId);
if (existingModal) {
existingModal.remove();
}
const modal = document.createElement('div');
modal.id = modalId;
modal.className = 'modal fade';
modal.setAttribute('tabindex', '-1');
modal.setAttribute('aria-labelledby', 'ebayPromptModalLabel');
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML = `
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius: 8px; border: none; box-shadow: 0 4px 20px rgba(0,0,0,0.15);">
<div class="modal-header" style="border-bottom: 1px solid #e0e0e0; padding: 20px 24px;">
<h5 class="modal-title" id="ebayPromptModalLabel" style="font-weight: 600; font-size: 18px; color: #333;">
<i class="ti-pencil-alt" style="color: #0064D2; margin-right: 8px;"></i>Saisie
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" style="margin: 0;"></button>
</div>
<div class="modal-body" style="padding: 24px;">
<p style="margin-bottom: 16px; font-size: 16px; color: #333;">${message}</p>
<input type="text" class="form-control" id="ebayPromptInput" value="${defaultValue}" style="border-radius: 4px; border: 1px solid #ccc; padding: 10px; font-size: 14px;" autofocus>
</div>
<div class="modal-footer" style="border-top: 1px solid #e0e0e0; padding: 16px 24px; justify-content: flex-end;">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="min-width: 80px; border-radius: 4px; font-weight: 500; margin-right: 8px;">
Annuler
</button>
<button type="button" class="btn btn-primary" id="ebayPromptConfirm" style="min-width: 80px; background-color: #0064D2; border-color: #0064D2; border-radius: 4px; font-weight: 500;">
OK
</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
const bsModal = new bootstrap.Modal(modal);
const input = modal.querySelector('#ebayPromptInput');
const confirmBtn = modal.querySelector('#ebayPromptConfirm');
// Gérer la confirmation
confirmBtn.addEventListener('click', function () {
const value = input.value;
bsModal.hide();
resolve(value);
});
// Gérer l'annulation
modal.addEventListener('hidden.bs.modal', function () {
if (input.value === undefined || input.value === '') {
resolve(null);
}
modal.remove();
});
// Gérer la touche Entrée
input.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
confirmBtn.click();
}
});
bsModal.show();
input.focus();
input.select();
});
}
// Remplacer window.alert et window.prompt
window.alert = showEbayModal;
window.prompt = showEbayPromptModal;
</script>
<script>
// Gestion du toggle de la sidebar sur mobile
document.addEventListener('DOMContentLoaded', function () {
const sidebarToggle = document.querySelector('.listing-sidebar-toggle');
const sidebarCollapse = document.getElementById('listingSidebarCollapse');
if (sidebarToggle && sidebarCollapse) { // Bootstrap 5 utilise des événements natifs
sidebarCollapse.addEventListener('show.bs.collapse', function () {
sidebarToggle.classList.remove('collapsed');
sidebarToggle.setAttribute('aria-expanded', 'true');
});
sidebarCollapse.addEventListener('hide.bs.collapse', function () {
sidebarToggle.classList.add('collapsed');
sidebarToggle.setAttribute('aria-expanded', 'false');
});
sidebarCollapse.addEventListener('shown.bs.collapse', function () {
sidebarToggle.classList.remove('collapsed');
});
sidebarCollapse.addEventListener('hidden.bs.collapse', function () {
sidebarToggle.classList.add('collapsed');
});
// Vérifier l'état initial
if (sidebarCollapse.classList.contains('show')) {
sidebarToggle.classList.remove('collapsed');
sidebarToggle.setAttribute('aria-expanded', 'true');
} else {
sidebarToggle.classList.add('collapsed');
sidebarToggle.setAttribute('aria-expanded', 'false');
}
}
});
// Synchroniser la recherche avec la barre de recherche du header
const headerSearchInput = document.getElementById('search_input');
if (headerSearchInput && typeof currentFilters !== 'undefined') { // Mettre à jour la valeur de la barre de recherche du header
headerSearchInput.value = currentFilters.q || '';
// Afficher le bouton clear si nécessaire
const clearSearchBtn = document.getElementById('clear_search_btn');
if (clearSearchBtn) {
clearSearchBtn.style.display = currentFilters.q ? 'block' : 'none';
}
}
// Réinitialiser tous les filtres
const resetFiltersBtn = document.getElementById('resetFiltersBtn');
if (resetFiltersBtn) {
resetFiltersBtn.addEventListener('click', function () {
resetAllFilters();
});
}
// Fonction pour réinitialiser tous les filtres
function resetAllFilters () {
currentFilters = {
category: '',
brand: '',
sort: 'newest',
priceMin: '',
priceMax: '',
page: 1,
q: '',
shop: '',
featured: '',
digital: '',
stockStatus: '',
ratingMin: '',
rating: '',
weightMin: '',
weightMax: '',
color: '',
size: '',
material: '',
condition: '',
availability: ''
};
// Réinitialiser la barre de recherche du header
const headerSearchInput = document.getElementById('search_input');
if (headerSearchInput) {
headerSearchInput.value = '';
}
const clearSearchBtn = document.getElementById('clear_search_btn');
if (clearSearchBtn) {
clearSearchBtn.style.display = 'none';
}
// Réinitialiser les inputs
document.getElementById('sortSelect').value = 'newest';
// Réinitialiser les radios
document.querySelectorAll('input[type="radio"]').forEach(radio => {
if (radio.id.includes('all-') || radio.id.includes('featured-all') || radio.id.includes('digital-all') || radio.id.includes('availability-all') || radio.id.includes('rating-all')) {
radio.checked = true;
} else {
radio.checked = false;
}
});
// Réinitialiser les inputs de prix et poids
const weightMinInput = document.getElementById('weightMin');
const weightMaxInput = document.getElementById('weightMax');
if (weightMinInput)
weightMinInput.value = '';
if (weightMaxInput)
weightMaxInput.value = '';
applyFilters();
updateActiveFilters();
}
// Fonction pour mettre à jour l'affichage des filtres actifs
function updateActiveFilters () {
const activeFiltersDiv = document.getElementById('activeFilters');
const activeFiltersList = document.getElementById('activeFiltersList');
const filters = [];
if (currentFilters.q) {
filters.push (`<span class="badge bg-primary">Recherche: "${
currentFilters.q
}"</span>`);
}
if (currentFilters.category) {
filters.push(`<span class="badge bg-secondary">Catégorie</span>`);
}
if (currentFilters.brand) {
filters.push(`<span class="badge bg-secondary">Marque</span>`);
}
if (currentFilters.priceMin || currentFilters.priceMax) {
filters.push(`<span class="badge bg-info">Prix</span>`);
}
if (currentFilters.featured === 'true') {
filters.push(`<span class="badge bg-warning">Produits vedettes</span>`);
}
if (currentFilters.digital === 'true') {
filters.push(`<span class="badge bg-success">Numériques</span>`);
}
if (currentFilters.digital === 'false') {
filters.push(`<span class="badge bg-success">Physiques</span>`);
}
if (currentFilters.availability) {
filters.push(`<span class="badge bg-info">Disponibilité</span>`);
}
if (currentFilters.ratingMin || currentFilters.rating) {
const ratingValue = currentFilters.ratingMin || currentFilters.rating;
filters.push (`<span class="badge bg-warning">Note ≥ ${ratingValue}</span>`);
}
if (currentFilters.weightMin || currentFilters.weightMax) {
filters.push(`<span class="badge bg-secondary">Poids</span>`);
}
if (filters.length > 0) {
activeFiltersList.innerHTML = filters.join(' ');
activeFiltersDiv.style.display = 'block';
} else {
activeFiltersDiv.style.display = 'none';
}
}
// Mettre à jour les filtres actifs au chargement et après chaque filtrage
document.addEventListener('DOMContentLoaded', function () {
updateActiveFilters();
});
// Mettre à jour les filtres actifs après chaque application de filtres
const originalApplyFilters = applyFilters;
applyFilters = function () {
originalApplyFilters();
setTimeout(updateActiveFilters, 100);
// Mettre à jour le compteur de filtres actifs
updateActiveFiltersCount();
};
// S'assurer que applyFilters est accessible globalement
window.applyFilters = applyFilters;
// Fonction pour mettre à jour le compteur de filtres actifs
function updateActiveFiltersCount () {
const count = Object.keys(currentFilters).filter(key => {
const value = currentFilters[key];
return value && value !== '' && key !== 'page' && key !== 'sort';
}).length;
const badge = document.getElementById('activeFiltersCount');
if (badge) {
if (count > 0) {
badge.textContent = count;
badge.style.display = 'inline-block';
} else {
badge.style.display = 'none';
}
}
}
// Initialiser le compteur au chargement
document.addEventListener('DOMContentLoaded', function () {
updateActiveFiltersCount();
});
</script>
<!-- Modal de filtres -->
<div class="modal fade" id="filtersModal" tabindex="-1" aria-labelledby="filtersModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" style="display: flex; flex-direction: column; max-height: 90vh;">
<div class="modal-header" style="flex-shrink: 0;">
<h5 class="modal-title" id="filtersModalLabel">
<i class="lnr lnr-filter me-2"></i>Filtres de recherche
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="overflow-y: auto; flex: 1; min-height: 0;">
<div
class="row">
<!-- Colonne gauche -->
<div
class="col-md-6">
<!-- Marques -->
<div class="common-filter mb-4">
<div class="head">Marques</div>
<ul class="brand-list" id="modalBrandList">
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-all-brands" name="modal-brand" checked>
<label for="modal-all-brands">Toutes les marques</label>
</li>
{% for brand in brands %}
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-brand-{{ brand.id }}" name="modal-brand" value="{{ brand.slug }}">
<label for="modal-brand-{{ brand.id }}">{{ brand.name }}<span>({{ brand.getActiveProductsCount() }})</span>
</label>
</li>
{% endfor %}
</ul>
</div>
<!-- Conditions -->
<div class="common-filter mb-4">
<div class="head">Condition</div>
<ul class="condition-list" id="modalConditionList">
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-all-conditions" name="modal-condition" checked>
<label for="modal-all-conditions">Toutes les conditions</label>
</li>
{% for condition in conditions %}
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-condition-{{ condition.id }}" name="modal-condition" value="{{ condition.slug }}">
<label for="modal-condition-{{ condition.id }}">{{ condition.name }}<span>({{ condition.getActiveProductsCount() }})</span>
</label>
</li>
{% endfor %}
</ul>
</div>
<!-- Boutiques -->
<div class="common-filter mb-4">
<div class="head">Boutiques</div>
<div id="modalShopsFilter">
<ul
class="shop-list" id="modalShopList"><!-- Les boutiques seront chargées dynamiquement -->
</ul>
</div>
</div>
<!-- Produits vedettes -->
<div class="common-filter mb-4">
<div class="head">Produits vedettes</div>
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-featured-all" name="modal-featured" checked>
<label for="modal-featured-all">Tous les produits</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-featured-only" name="modal-featured" value="true">
<label for="modal-featured-only">Produits vedettes uniquement</label>
</li>
</ul>
</div>
<!-- Type de produit -->
<div class="common-filter mb-4">
<div class="head">Type de produit</div>
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-digital-all" name="modal-digital" checked>
<label for="modal-digital-all">Tous les types</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-digital-physical" name="modal-digital" value="false">
<label for="modal-digital-physical">Produits physiques</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-digital-digital" name="modal-digital" value="true">
<label for="modal-digital-digital">Produits numériques</label>
</li>
</ul>
</div>
</div>
<!-- Colonne droite -->
<div
class="col-md-6">
<!-- Disponibilité -->
<div class="common-filter mb-4">
<div class="head">Disponibilité</div>
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-availability-all" name="modal-availability" checked>
<label for="modal-availability-all">Tous</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-availability-in-stock" name="modal-availability" value="in_stock">
<label for="modal-availability-in-stock">En stock</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-availability-low-stock" name="modal-availability" value="low_stock">
<label for="modal-availability-low-stock">Stock faible</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-availability-out-of-stock" name="modal-availability" value="out_of_stock">
<label for="modal-availability-out-of-stock">Rupture de stock</label>
</li>
</ul>
</div>
<!-- Note minimale -->
<div class="common-filter mb-4">
<div class="head">Note minimale</div>
<ul>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-rating-all" name="modal-rating" checked>
<label for="modal-rating-all">Toutes les notes</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-rating-4" name="modal-rating" value="4">
<label for="modal-rating-4">4 étoiles et plus</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-rating-3" name="modal-rating" value="3">
<label for="modal-rating-3">3 étoiles et plus</label>
</li>
<li class="filter-list">
<input class="pixel-radio" type="radio" id="modal-rating-2" name="modal-rating" value="2">
<label for="modal-rating-2">2 étoiles et plus</label>
</li>
</ul>
</div>
<!-- Prix -->
<div class="common-filter mb-4">
<div class="head">Prix (HTG)</div>
<div class="price-range-area">
<div class="d-flex gap-3">
<div class="flex-grow-1">
<label class="form-label">Min</label>
<input type="number" class="form-control" id="modal-priceMin" placeholder="0" min="0" step="0.01">
</div>
<div class="flex-grow-1">
<label class="form-label">Max</label>
<input type="number" class="form-control" id="modal-priceMax" placeholder="999999" min="0" step="0.01">
</div>
</div>
</div>
</div>
<!-- Poids -->
<div class="common-filter mb-4">
<div class="head">Poids (kg)</div>
<div class="weight-range-area">
<div class="d-flex gap-3">
<div class="flex-grow-1">
<label class="form-label">Min</label>
<input type="number" class="form-control" id="modal-weightMin" placeholder="0" step="0.1" min="0">
</div>
<div class="flex-grow-1">
<label class="form-label">Max</label>
<input type="number" class="form-control" id="modal-weightMax" placeholder="100" step="0.1" min="0">
</div>
</div>
</div>
</div>
<!-- Couleur -->
<div class="common-filter mb-4">
<div class="head">Couleur</div>
<div id="modalColorsFilter">
<ul
class="color-list" id="modalColorList"><!-- Les couleurs seront chargées dynamiquement -->
</ul>
</div>
</div>
<!-- Taille -->
<div class="common-filter mb-4">
<div class="head">Taille</div>
<div id="modalSizesFilter">
<ul
class="size-list" id="modalSizeList"><!-- Les tailles seront chargées dynamiquement -->
</ul>
</div>
</div>
<!-- Matériau -->
<div class="common-filter mb-4">
<div class="head">Matériau</div>
<div id="modalMaterialsFilter">
<ul
class="material-list" id="modalMaterialList"><!-- Les matériaux seront chargés dynamiquement -->
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer" style="flex-shrink: 0; border-top: 1px solid #dee2e6; padding: 1rem 1.5rem; background: #f8f9fa;">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="lnr lnr-cross-circle me-1"></i>Annuler
</button>
<button type="button" class="btn btn-outline-danger" id="modalResetFiltersBtn">
<i class="lnr lnr-refresh me-1"></i>Réinitialiser
</button>
<button type="button" class="btn btn-primary" id="modalApplyFiltersBtn">
<i class="lnr lnr-checkmark-circle me-1"></i>Appliquer les filtres
</button>
</div>
</div>
</div>
</div>
<script>
// Synchroniser les filtres du modal avec currentFilters au chargement
document.addEventListener('DOMContentLoaded', function () {
console.log('[Modal] Initializing filter modal...');
// Fonction pour synchroniser les valeurs du modal avec currentFilters
function syncModalFilters() {
console.log('[Modal] Syncing filters with currentFilters:', currentFilters);
// Réinitialiser tous les radios "Tous" d'abord
document.querySelectorAll('#filtersModal input[type="radio"][id*="-all"]').forEach(radio => {
radio.checked = true;
});
// Marques
if (currentFilters.brand) {
const brandRadio = document.querySelector (`#modalBrandList input[value="${
currentFilters.brand
}"]`);
if (brandRadio) {
brandRadio.checked = true;
console.log('[Modal] Brand synced:', currentFilters.brand);
}
} else {
const allBrands = document.getElementById('modal-all-brands');
if (allBrands)
allBrands.checked = true;
}
// Conditions
if (currentFilters.condition) {
const conditionRadio = document.querySelector (`#modalConditionList input[value="${
currentFilters.condition
}"]`);
if (conditionRadio) {
conditionRadio.checked = true;
console.log('[Modal] Condition synced:', currentFilters.condition);
}
} else {
const allConditions = document.getElementById('modal-all-conditions');
if (allConditions)
allConditions.checked = true;
}
// Featured
if (currentFilters.featured === 'true') {
const featuredRadio = document.getElementById('modal-featured-only');
if (featuredRadio) {
featuredRadio.checked = true;
console.log('[Modal] Featured synced');
}
} else {
const allFeatured = document.getElementById('modal-featured-all');
if (allFeatured)
allFeatured.checked = true;
}
// Digital
if (currentFilters.digital === 'true') {
const digitalRadio = document.getElementById('modal-digital-digital');
if (digitalRadio)
digitalRadio.checked = true;
} else if (currentFilters.digital === 'false') {
const physicalRadio = document.getElementById('modal-digital-physical');
if (physicalRadio)
physicalRadio.checked = true;
} else {
const allDigital = document.getElementById('modal-digital-all');
if (allDigital)
allDigital.checked = true;
}
// Availability
if (currentFilters.availability) {
const availabilityId = `modal-availability-${
currentFilters.availability.replace('_', '-')
}`;
const availabilityRadio = document.getElementById(availabilityId);
if (availabilityRadio) {
availabilityRadio.checked = true;
console.log('[Modal] Availability synced:', currentFilters.availability);
}
} else {
const allAvailability = document.getElementById('modal-availability-all');
if (allAvailability)
allAvailability.checked = true;
}
// Rating
if (currentFilters.ratingMin || currentFilters.rating) {
const ratingValue = currentFilters.ratingMin || currentFilters.rating;
const ratingRadio = document.getElementById (`modal-rating-${ratingValue}`);
if (ratingRadio) {
ratingRadio.checked = true;
console.log('[Modal] Rating synced:', ratingValue);
}
} else {
const allRating = document.getElementById('modal-rating-all');
if (allRating)
allRating.checked = true;
}
// Prix
const priceMinInput = document.getElementById('modal-priceMin');
const priceMaxInput = document.getElementById('modal-priceMax');
if (priceMinInput)
priceMinInput.value = currentFilters.priceMin || '';
if (priceMaxInput)
priceMaxInput.value = currentFilters.priceMax || '';
// Poids
const weightMinInput = document.getElementById('modal-weightMin');
const weightMaxInput = document.getElementById('modal-weightMax');
if (weightMinInput)
weightMinInput.value = currentFilters.weightMin || '';
if (weightMaxInput)
weightMaxInput.value = currentFilters.weightMax || '';
console.log('[Modal] Filters synced successfully');
}
// Fonction pour charger les filtres dynamiques dans le modal
function loadDynamicFiltersToModal() {
console.log('[Modal] Loading dynamic filters...');
// Boutiques
const sidebarShopList = document.getElementById('shopList');
const modalShopList = document.getElementById('modalShopList');
if (modalShopList) { // Si la sidebar a déjà les boutiques, les copier
if (sidebarShopList && sidebarShopList.innerHTML.trim() && ! sidebarShopList.innerHTML.includes('Aucune boutique') && ! sidebarShopList.innerHTML.includes('seront chargées')) {
let shopHtml = sidebarShopList.innerHTML.replace(/id="all-shops"/g, 'id="modal-all-shops"').replace(/id="shop-/g, 'id="modal-shop-').replace(/name="shop"/g, 'name="modal-shop"').replace(/for="all-shops"/g, 'for="modal-all-shops"').replace(/for="shop-/g, 'for="modal-shop-');
modalShopList.innerHTML = shopHtml;
// Synchroniser la sélection
if (currentFilters.shop) {
const shopRadio = document.querySelector (`#modalShopList input[value="${
currentFilters.shop
}"]`);
if (shopRadio)
shopRadio.checked = true;
} else {
const allShops = document.getElementById('modal-all-shops');
if (allShops)
allShops.checked = true;
}
console.log('[Modal] Shops loaded from sidebar');
} else { // Si pas encore chargés, afficher un message ou laisser vide
if (! modalShopList.innerHTML.trim() || modalShopList.innerHTML.includes('seront chargées')) {
modalShopList.innerHTML = '<li class="filter-list"><span class="text-muted">Les boutiques seront chargées lors de l\'application des filtres</span></li>';
}
}
}
// Couleurs
const sidebarColorList = document.getElementById('colorList');
const modalColorList = document.getElementById('modalColorList');
if (sidebarColorList && modalColorList) {
if (sidebarColorList.innerHTML.trim() && ! sidebarColorList.innerHTML.includes('Aucun')) {
let colorHtml = sidebarColorList.innerHTML.replace(/id="all-color"/g, 'id="modal-all-color"').replace(/id="color-/g, 'id="modal-color-').replace(/name="color"/g, 'name="modal-color"').replace(/for="all-color"/g, 'for="modal-all-color"').replace(/for="color-/g, 'for="modal-color-');
modalColorList.innerHTML = colorHtml;
// Synchroniser la sélection
if (currentFilters.color) {
const colorRadio = document.querySelector (`#modalColorList input[value="${
currentFilters.color
}"]`);
if (colorRadio)
colorRadio.checked = true;
} else {
const allColor = document.getElementById('modal-all-color');
if (allColor)
allColor.checked = true;
}
console.log('[Modal] Colors loaded');
}
}
// Tailles
const sidebarSizeList = document.getElementById('sizeList');
const modalSizeList = document.getElementById('modalSizeList');
if (sidebarSizeList && modalSizeList) {
if (sidebarSizeList.innerHTML.trim() && ! sidebarSizeList.innerHTML.includes('Aucun')) {
let sizeHtml = sidebarSizeList.innerHTML.replace(/id="all-size"/g, 'id="modal-all-size"').replace(/id="size-/g, 'id="modal-size-').replace(/name="size"/g, 'name="modal-size"').replace(/for="all-size"/g, 'for="modal-all-size"').replace(/for="size-/g, 'for="modal-size-');
modalSizeList.innerHTML = sizeHtml;
// Synchroniser la sélection
if (currentFilters.size) {
const sizeRadio = document.querySelector (`#modalSizeList input[value="${
currentFilters.size
}"]`);
if (sizeRadio)
sizeRadio.checked = true;
} else {
const allSize = document.getElementById('modal-all-size');
if (allSize)
allSize.checked = true;
}
console.log('[Modal] Sizes loaded');
}
}
// Matériaux
const sidebarMaterialList = document.getElementById('materialList');
const modalMaterialList = document.getElementById('modalMaterialList');
if (sidebarMaterialList && modalMaterialList) {
if (sidebarMaterialList.innerHTML.trim() && ! sidebarMaterialList.innerHTML.includes('Aucun')) {
let materialHtml = sidebarMaterialList.innerHTML.replace(/id="all-material"/g, 'id="modal-all-material"').replace(/id="material-/g, 'id="modal-material-').replace(/name="material"/g, 'name="modal-material"').replace(/for="all-material"/g, 'for="modal-all-material"').replace(/for="material-/g, 'for="modal-material-');
modalMaterialList.innerHTML = materialHtml;
// Synchroniser la sélection
if (currentFilters.material) {
const materialRadio = document.querySelector (`#modalMaterialList input[value="${
currentFilters.material
}"]`);
if (materialRadio)
materialRadio.checked = true;
} else {
const allMaterial = document.getElementById('modal-all-material');
if (allMaterial)
allMaterial.checked = true;
}
console.log('[Modal] Materials loaded');
}
}
}
// Synchroniser à l'ouverture du modal
const filtersModal = document.getElementById('filtersModal');
if (filtersModal) {
filtersModal.addEventListener('show.bs.modal', function () {
console.log('[Modal] Modal opening, syncing filters...');
// Charger les filtres dynamiques d'abord si pas encore chargés
loadDynamicFiltersToModal();
// Puis synchroniser les valeurs
setTimeout(() => {
syncModalFilters();
}, 100);
});
}
// Charger les filtres dynamiques au chargement initial si la sidebar les a déjà
loadDynamicFiltersToModal();
// Bouton Appliquer les filtres
const applyBtn = document.getElementById('modalApplyFiltersBtn');
if (applyBtn) {
applyBtn.addEventListener('click', function (e) {
e.preventDefault();
console.log('[Modal] Apply button clicked, collecting filter values...');
// Collecter les valeurs du modal
const brandRadio = document.querySelector('#modalBrandList input[name="modal-brand"]:checked');
if (brandRadio) {
currentFilters.brand = brandRadio.id !== 'modal-all-brands' ? brandRadio.value : '';
console.log('[Modal] Brand:', currentFilters.brand);
} else {
currentFilters.brand = '';
console.log('[Modal] Brand: No selection found');
}
// Conditions
const conditionRadio = document.querySelector('#modalConditionList input[name="modal-condition"]:checked');
if (conditionRadio) {
currentFilters.condition = conditionRadio.id !== 'modal-all-conditions' ? conditionRadio.value : '';
console.log('[Modal] Condition:', currentFilters.condition);
} else {
currentFilters.condition = '';
}
// Featured
const featuredRadio = document.querySelector('#filtersModal input[name="modal-featured"]:checked');
if (featuredRadio) {
currentFilters.featured = featuredRadio.id !== 'modal-featured-all' ? featuredRadio.value : '';
console.log('[Modal] Featured:', currentFilters.featured);
} else {
currentFilters.featured = '';
}
// Digital
const digitalRadio = document.querySelector('#filtersModal input[name="modal-digital"]:checked');
if (digitalRadio) {
currentFilters.digital = digitalRadio.id !== 'modal-digital-all' ? digitalRadio.value : '';
console.log('[Modal] Digital:', currentFilters.digital);
} else {
currentFilters.digital = '';
}
// Availability
const availabilityRadio = document.querySelector('#filtersModal input[name="modal-availability"]:checked');
if (availabilityRadio) {
currentFilters.availability = availabilityRadio.id !== 'modal-availability-all' ? availabilityRadio.value : '';
console.log('[Modal] Availability:', currentFilters.availability);
} else {
currentFilters.availability = '';
}
// Rating
const ratingRadio = document.querySelector('#filtersModal input[name="modal-rating"]:checked');
if (ratingRadio && ratingRadio.id !== 'modal-rating-all') {
currentFilters.ratingMin = ratingRadio.value;
currentFilters.rating = ratingRadio.value;
console.log('[Modal] Rating:', currentFilters.ratingMin);
} else {
currentFilters.ratingMin = '';
currentFilters.rating = '';
}
// Prix
const priceMinInput = document.getElementById('modal-priceMin');
const priceMaxInput = document.getElementById('modal-priceMax');
currentFilters.priceMin = priceMinInput ? priceMinInput.value.trim() : '';
currentFilters.priceMax = priceMaxInput ? priceMaxInput.value.trim() : '';
console.log('[Modal] Price:', currentFilters.priceMin, '-', currentFilters.priceMax);
// Poids
const weightMinInput = document.getElementById('modal-weightMin');
const weightMaxInput = document.getElementById('modal-weightMax');
currentFilters.weightMin = weightMinInput ? weightMinInput.value.trim() : '';
currentFilters.weightMax = weightMaxInput ? weightMaxInput.value.trim() : '';
console.log('[Modal] Weight:', currentFilters.weightMin, '-', currentFilters.weightMax);
// Couleur
const colorRadio = document.querySelector('#modalColorList input[name="modal-color"]:checked');
if (colorRadio) {
currentFilters.color = colorRadio.id !== 'modal-all-color' ? colorRadio.value : '';
console.log('[Modal] Color:', currentFilters.color);
} else {
currentFilters.color = '';
}
// Taille
const sizeRadio = document.querySelector('#modalSizeList input[name="modal-size"]:checked');
if (sizeRadio) {
currentFilters.size = sizeRadio.id !== 'modal-all-size' ? sizeRadio.value : '';
console.log('[Modal] Size:', currentFilters.size);
} else {
currentFilters.size = '';
}
// Matériau
const materialRadio = document.querySelector('#modalMaterialList input[name="modal-material"]:checked');
if (materialRadio) {
currentFilters.material = materialRadio.id !== 'modal-all-material' ? materialRadio.value : '';
console.log('[Modal] Material:', currentFilters.material);
} else {
currentFilters.material = '';
}
// Boutiques
const shopRadio = document.querySelector('#modalShopList input[name="modal-shop"]:checked');
if (shopRadio) {
currentFilters.shop = shopRadio.id !== 'modal-all-shops' ? shopRadio.value : '';
console.log('[Modal] Shop:', currentFilters.shop);
} else {
currentFilters.shop = '';
}
currentFilters.page = 1;
console.log('[Modal] All filters collected:', currentFilters);
// Fermer le modal d'abord
const modalElement = document.getElementById('filtersModal');
const modal = bootstrap.Modal.getInstance(modalElement);
if (modal) {
modal.hide();
console.log('[Modal] Modal closed');
}
// Appliquer les filtres après un court délai pour laisser le modal se fermer
setTimeout(() => { // Vérifier que applyFilters existe
if (typeof window.applyFilters === 'function') {
console.log('[Modal] Calling applyFilters()...');
window.applyFilters();
// Mettre à jour le compteur de filtres actifs
setTimeout(() => {
if (typeof updateActiveFiltersCount === 'function') {
updateActiveFiltersCount();
}
}, 100);
} else {
console.error('[Modal] applyFilters is not available!', typeof window.applyFilters);
// Essayer de recharger la page avec les nouveaux paramètres
const url = new URL(window.location.href);
Object.keys(currentFilters).forEach(key => {
if (currentFilters[key] && currentFilters[key] !== '') {
let apiKey = key;
if (key === 'priceMin') {
apiKey = 'price_min';
} else if (key === 'priceMax') {
apiKey = 'price_max';
} else if (key === 'ratingMin') {
apiKey = 'rating_min';
} else if (key === 'weightMin') {
apiKey = 'weight_min';
} else if (key === 'weightMax') {
apiKey = 'weight_max';
} else if (key === 'stockStatus') {
apiKey = 'stock_status';
}
url.searchParams.set(apiKey, currentFilters[key]);
} else {
let apiKey = key;
if (key === 'priceMin') {
apiKey = 'price_min';
} else if (key === 'priceMax') {
apiKey = 'price_max';
} else if (key === 'ratingMin') {
apiKey = 'rating_min';
} else if (key === 'weightMin') {
apiKey = 'weight_min';
} else if (key === 'weightMax') {
apiKey = 'weight_max';
} else if (key === 'stockStatus') {
apiKey = 'stock_status';
}
url.searchParams.delete(apiKey);
}
});
window.location.href = url.toString();
}
}, 300);
});
} else {
console.error('[Modal] Apply button not found!');
}
// Bouton Réinitialiser
const resetBtn = document.getElementById('modalResetFiltersBtn');
if (resetBtn) {
resetBtn.addEventListener('click', function () {
console.log('[Modal] Reset button clicked');
// Réinitialiser tous les filtres du modal
document.querySelectorAll('#filtersModal input[type="radio"]').forEach(radio => {
if (radio.id && radio.id.includes('-all')) {
radio.checked = true;
} else {
radio.checked = false;
}
});
const priceMinInput = document.getElementById('modal-priceMin');
const priceMaxInput = document.getElementById('modal-priceMax');
const weightMinInput = document.getElementById('modal-weightMin');
const weightMaxInput = document.getElementById('modal-weightMax');
if (priceMinInput)
priceMinInput.value = '';
if (priceMaxInput)
priceMaxInput.value = '';
if (weightMinInput)
weightMinInput.value = '';
if (weightMaxInput)
weightMaxInput.value = '';
// Réinitialiser currentFilters (garder category et sort)
const category = currentFilters.category || '';
const sort = currentFilters.sort || 'newest';
currentFilters = {
category: category,
sort: sort,
page: 1,
brand: '',
condition: '',
featured: '',
digital: '',
availability: '',
ratingMin: '',
rating: '',
priceMin: '',
priceMax: '',
weightMin: '',
weightMax: '',
color: '',
size: '',
material: '',
shop: ''
};
console.log('[Modal] Filters reset, currentFilters:', currentFilters);
// Fermer le modal d'abord
const modalElement = document.getElementById('filtersModal');
const modal = bootstrap.Modal.getInstance(modalElement);
if (modal) {
modal.hide();
console.log('[Modal] Modal closed after reset');
}
// Appliquer les filtres après un court délai
setTimeout(() => {
if (typeof window.applyFilters === 'function') {
window.applyFilters();
} else {
console.error('[Modal] applyFilters not available, reloading page...');
// Recharger la page avec les filtres réinitialisés
const url = new URL(window.location.href);
url.searchParams.delete('price_min');
url.searchParams.delete('price_max');
url.searchParams.delete('rating_min');
url.searchParams.delete('weight_min');
url.searchParams.delete('weight_max');
url.searchParams.delete('brand');
url.searchParams.delete('condition');
url.searchParams.delete('featured');
url.searchParams.delete('digital');
url.searchParams.delete('availability');
url.searchParams.delete('color');
url.searchParams.delete('size');
url.searchParams.delete('material');
url.searchParams.delete('shop');
window.location.href = url.toString();
}
}, 300);
});
} else {
console.error('[Modal] Reset button not found!');
}
// S'assurer que les filtres dynamiques sont chargés au premier chargement si disponibles
setTimeout(() => {
loadDynamicFiltersToModal();
}, 500);
});
</script>{% endblock %}