{% extends 'base_home.html.twig' %}
{% block title %}Boutiques | MaketOu{% endblock %}
{% block body %}
<!-- Start Banner Area -->
{% if selectedCategory and selectedCategory.bannerImage %}
<section class="banner-area shop-category-banner organic-breadcrumb" style="background-image: url('{{ asset(selectedCategory.bannerImage) }}'); background-size: cover; background-position: center; min-height: 300px; position: relative;object-fit: cover;height:400px;">
<div class="banner-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.3);"></div>
<div class="container" style="position: relative; z-index: 1;">
<div class="breadcrumb-banner d-flex flex-wrap align-items-center justify-content-end">
<div class="col-first text-white">
<h1 style="color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5);">{{ selectedCategory.name }}</h1>
<nav class="d-flex align-items-center">
<a href="{{ path('ui_home') }}" style="color: white;">Accueil<span class="lnr lnr-arrow-right"></span></a>
<a href="{{ path('ui_shops_list') }}" style="color: white;">Boutiques<span class="lnr lnr-arrow-right"></span></a>
<a href="javascript:void(0);" style="color: white;">{{ selectedCategory.name }}</a>
</nav>
</div>
</div>
</div>
</section>
{% else %}
<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>{% if selectedCategory %}{{ selectedCategory.name }}{% else %}Boutiques{% endif %}</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);">Boutiques</a>
</nav>
</div>
</div>
</div>
</section>
{% endif %}
<!-- End Banner Area -->
<div class="container mt-4 mb-4">
<div class="row">
<div class="col-xl-3 col-lg-4 col-md-5">
<!-- Filtres des boutiques -->
<div class="sidebar-categories">
<div class="head">Catégories de boutiques</div>
<ul class="main-categories">
{% for category in shopCategories %}
<li class="main-nav-list">
<a href="{{ path('ui_shops_list', {'category': category.slug}) }}"
class="category-link {% if app.request.query.get('category') == category.slug %}active{% endif %}">
<i class="lnr lnr-store"></i>
{{ category.name }}
<span class="number">({{ category.shops|length }})</span>
</a>
</li>
{% endfor %}
</ul>
</div>
<!-- Statistiques modernes inline -->
<div class="sidebar-filter mt-4">
<div class="top-filter-head">Statistiques</div>
<div class="modern-stats">
<div class="stat-item-inline mt-3">
<i class="lnr lnr-store"></i>
<div>
<div class="stat-value">{{ total_shops|default(0) }}</div>
<div class="stat-label">Total</div>
</div>
</div>
<div class="stat-item-inline">
<i class="lnr lnr-star"></i>
<div>
<div class="stat-value">{{ shops|filter(shop => shop.isActive)|length }}</div>
<div class="stat-label">Actives</div>
</div>
</div>
<div class="stat-item-inline">
<i class="lnr lnr-checkmark-circle"></i>
<div>
<div class="stat-value">{{ shops|filter(shop => shop.isVerified)|length }}</div>
<div class="stat-label">Vérifiées</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-9 col-lg-8 col-md-7">
<!-- Liste des boutiques -->
<div class="row" id="shopsContainer">
{% for shop in shops|slice(0, 9) %}
<div class="mt-4 col-lg-4 col-md-6 shop-item" data-shop-id="{{ shop.id }}">
<div class="single-shop h-100">
<div class="shop-image">
<a href="{{ path('ui_shop_show', {'slug': shop.slug}) }}">
{% if shop.logo %}
<img class="img-fluid" src="{{ asset(shop.logo) }}" alt="{{ shop.name }}">
{% else %}
<img class="img-fluid" src="{{ asset('ui/img/shop/default-shop.jpg') }}" alt="{{ shop.name }}">
{% endif %}
</a>
{% if shop.isVerified %}
<div class="verified-badge">
<i class="lnr lnr-checkmark-circle"></i>
</div>
{% endif %}
</div>
<div class="shop-details">
<div class="shop-header">
<h6 class="shop-name">
<a href="{{ path('ui_shop_show', {'slug': shop.slug}) }}">{{ shop.name }}</a>
</h6>
{% if shop.shopCategory %}
<span class="shop-category">{{ shop.shopCategory.name }}</span>
{% endif %}
</div>
<div class="shop-stats">
<div class="stat-item">
<span class="stat-value">{{ shop.getActiveProductsCount() }}</span>
<span class="stat-label">Produits</span>
</div>
<div class="stat-item">
<span class="stat-value followers-count" data-shop-id="{{ shop.id }}">{{ shop.getActiveFollowersCount() }}</span>
<span class="stat-label">Suiveurs</span>
</div>
<div class="stat-item">
<span class="stat-value">{{ shop.viewCount }}</span>
<span class="stat-label">Vues</span>
</div>
</div>
<div class="shop-description">
<p>{{ shop.description|striptags|length > 100 ? shop.description|striptags|slice(0, 100) ~ '...' : shop.description|striptags }}</p>
</div>
<div class="shop-actions">
<a href="{{ path('ui_shop_show', {'slug': shop.slug}) }}" class="btn btn-primary btn-sm">
<i class="lnr lnr-eye me-1"></i> Voir
</a>
{% if app.user %}
{% set followed_shop_ids = followed_shop_ids|default([]) %}
{% set is_following = shop.id in followed_shop_ids %}
<button class="btn btn-sm follow-btn {{ is_following ? 'btn-outline-danger' : 'btn-outline-secondary' }}"
data-shop-id="{{ shop.id }}"
data-following="{{ is_following ? 'true' : 'false' }}"
onclick="toggleFollow({{ shop.id }}, this)">
<i class="lnr {{ is_following ? 'lnr-cross' : 'lnr-plus' }} me-1"></i>
<span class="follow-text">
{{ is_following ? 'Ne plus suivre' : 'Suivre' }}
</span>
</button>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Bouton Voir plus -->
{% if shops|length > 9 or current_page < total_pages %}
<div class="text-center mt-4 mb-4" id="loadMoreContainer">
<button class="btn btn-primary btn-lg" id="loadMoreBtn" onclick="loadMoreShops()">
<i class="lnr lnr-plus-circle me-2"></i>Voir plus de boutiques
</button>
</div>
{% endif %}
<!-- Message si aucune boutique -->
{% if shops|length == 0 %}
<div class="text-center py-5">
<i class="lnr lnr-store" style="font-size: 4rem; color: #ccc;"></i>
<h4 class="mt-3">Aucune boutique trouvée</h4>
<p class="text-muted">Aucune boutique ne correspond à vos critères de recherche.</p>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block stylesheets %}
<style>
.single-shop {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; /* Empêcher le débordement */
}
.single-shop:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.shop-image {
position: relative;
margin-bottom: 15px;
}
.shop-image img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
}
.verified-badge {
position: absolute;
top: 10px;
right: 10px;
background: #28a745;
color: white;
padding: 5px 8px;
border-radius: 12px;
font-size: 0.8rem;
}
.shop-header {
margin-bottom: 15px;
}
.shop-name {
margin: 0 0 5px 0;
font-size: 1.1rem;
font-weight: 600;
}
.shop-name a {
color: #333;
text-decoration: none;
}
.shop-name a:hover {
color: #007bff;
}
.shop-category {
background: #f8f9fa;
color: #666;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.8rem;
}
.shop-stats {
display: flex;
justify-content: space-around;
margin: 15px 0;
padding: 15px 0;
border-top: 1px solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
}
.stat-item {
text-align: center;
}
.stat-value {
display: block;
font-size: 1.2rem;
font-weight: 600;
color: #333;
}
.stat-label {
font-size: 0.8rem;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.shop-description {
margin: 15px 0;
color: #666;
font-size: 0.9rem;
line-height: 1.4;
}
.shop-actions {
display: flex;
gap: 8px;
margin-top: auto;
padding-top: 15px;
flex-wrap: wrap;
}
.shop-actions .btn {
flex: 1 1 auto;
min-width: 0;
font-size: 0.85rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 8px 12px;
}
.shop-actions .btn i {
margin-right: 4px;
flex-shrink: 0;
}
/* Assurer que shop-details utilise flexbox pour garder les boutons en bas */
.shop-details {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0; /* Important pour le flexbox */
}
/* Statistiques modernes inline */
.modern-stats {
display: flex;
flex-direction: column;
gap: 15px;
}
.stat-item-inline {
display: flex;
align-items: center;
gap: 15px;
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
transition: all 0.3s ease;
}
.stat-item-inline:hover {
background: #e9ecef;
transform: translateX(5px);
}
.stat-item-inline i {
font-size: 1.5rem;
color: #007bff;
width: 40px;
text-align: center;
}
.stat-item-inline .stat-value {
font-size: 1.5rem;
font-weight: 700;
color: #333;
line-height: 1;
}
.stat-item-inline .stat-label {
font-size: 0.85rem;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 5px;
}
.stat-number {
font-size: 1.5rem;
font-weight: 700;
color: #007bff;
margin-top: 5px;
}
@media (max-width: 768px) {
.single-shop {
padding: 15px;
}
.shop-stats {
flex-direction: column;
gap: 10px;
}
.shop-actions {
flex-direction: column;
}
}
</style>
{% endblock %}
{% block javascripts %}
<script>
let currentPage = {{ current_page }};
let totalPages = {{ total_pages }};
let isLoading = false;
function sortShops(sortValue) {
console.log('Tri des boutiques:', sortValue);
// TODO: Implémenter le tri AJAX
}
function toggleFollow(shopId, button) {
if (!button) {
button = document.querySelector(`.follow-btn[data-shop-id="${shopId}"]`);
if (!button) return;
}
const isFollowing = button.getAttribute('data-following') === 'true';
const originalHtml = button.innerHTML;
button.disabled = true;
button.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
fetch(`{{ path('ui_api_shop_toggle_follow', {'id': 'SHOP_ID'}) }}`.replace('SHOP_ID', shopId), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({ action: isFollowing ? 'unfollow' : 'follow' })
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Mettre à jour l'état du bouton
const isFollowing = data.isFollowing === true;
button.setAttribute('data-following', isFollowing ? 'true' : 'false');
// Mettre à jour les classes CSS
button.classList.remove('btn-outline-secondary', 'btn-outline-danger');
if (isFollowing) {
button.classList.add('btn-outline-danger');
} else {
button.classList.add('btn-outline-secondary');
}
// Reconstruire le HTML complet du bouton
const iconClass = isFollowing ? 'lnr-cross' : 'lnr-plus';
const textContent = isFollowing ? 'Ne plus suivre' : 'Suivre';
button.innerHTML = `<i class="lnr ${iconClass} me-1"></i><span class="follow-text">${textContent}</span>`;
// Mettre à jour le compteur
const followersCounter = document.querySelector(`.followers-count[data-shop-id="${shopId}"]`);
if (followersCounter && typeof data.followersCount !== 'undefined') {
followersCounter.textContent = data.followersCount;
}
if (data.message) {
console.log(data.message);
}
button.disabled = false;
} else {
alert(data.message || 'Impossible de mettre à jour le suivi.');
button.innerHTML = originalHtml;
button.disabled = false;
}
})
.catch(error => {
console.error('Erreur suivi boutique:', error);
alert('Erreur lors de la mise à jour du suivi.');
button.innerHTML = originalHtml;
button.disabled = false;
});
}
function loadMoreShops() {
if (isLoading || currentPage >= totalPages) return;
isLoading = true;
const btn = document.getElementById('loadMoreBtn');
const originalText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Chargement...';
currentPage++;
const url = new URL(window.location.href);
url.searchParams.set('page', currentPage);
fetch(url.toString(), {
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success && data.shops.length > 0) {
const container = document.getElementById('shopsContainer');
data.shops.forEach(shop => {
const shopHtml = createShopCard(shop);
container.insertAdjacentHTML('beforeend', shopHtml);
});
// Mettre à jour le bouton
if (!data.hasMore) {
document.getElementById('loadMoreContainer').style.display = 'none';
} else {
btn.disabled = false;
btn.innerHTML = originalText;
}
} else {
document.getElementById('loadMoreContainer').style.display = 'none';
}
isLoading = false;
})
.catch(error => {
console.error('Erreur:', error);
btn.disabled = false;
btn.innerHTML = originalText;
isLoading = false;
});
}
function createShopCard(shop) {
const logoUrl = shop.logo ? '/' + shop.logo : '{{ asset("ui/img/shop/default-shop.jpg") }}';
const verifiedBadge = shop.isVerified ?
'<div class="verified-badge"><i class="lnr lnr-checkmark-circle"></i></div>' : '';
const category = shop.category ?
`<span class="shop-category">${shop.category}</span>` : '';
return `
<div class="mt-4 col-lg-4 col-md-6 shop-item" data-shop-id="${shop.id}">
<div class="single-shop h-100">
<div class="shop-image">
<a href="/shop/${shop.slug}">
<img class="img-fluid" src="${logoUrl}" alt="${shop.name}">
</a>
${verifiedBadge}
</div>
<div class="shop-details">
<div class="shop-header">
<h6 class="shop-name">
<a href="/shop/${shop.slug}">${shop.name}</a>
</h6>
${category}
</div>
<div class="shop-stats">
<div class="stat-item">
<span class="stat-value">${shop.productsCount || 0}</span>
<span class="stat-label">Produits</span>
</div>
<div class="stat-item">
<span class="stat-value">${shop.followersCount || 0}</span>
<span class="stat-label">Suiveurs</span>
</div>
<div class="stat-item">
<span class="stat-value">${shop.viewCount || 0}</span>
<span class="stat-label">Vues</span>
</div>
</div>
<div class="shop-description">
<p>${(shop.description || '').substring(0, 100)}${(shop.description || '').length > 100 ? '...' : ''}</p>
</div>
<div class="shop-actions">
<a href="/shop/${shop.slug}" class="btn btn-primary btn-sm" style="flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<i class="lnr lnr-eye me-1"></i> Voir
</a>
{% if app.user %}
<button class="btn btn-sm follow-btn ${shop.following ? 'btn-outline-danger' : 'btn-outline-secondary'}"
data-shop-id="${shop.id}"
data-following="${shop.following ? 'true' : 'false'}"
onclick="toggleFollow(${shop.id}, this)"
style="flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<i class="lnr ${shop.following ? 'lnr-cross' : 'lnr-plus'} me-1"></i>
<span class="follow-text">${shop.following ? 'Ne plus suivre' : 'Suivre'}</span>
</button>
{% endif %}
</div>
</div>
</div>
</div>
`;
}
</script>
{% endblock %}