{% extends 'base_home.html.twig' %}
{% block title %}
{{ shop.name }}
| MaketOu
{% endblock %}
{% block stylesheets %}
<style>
/* Style pour la description formatée de la boutique */
.shop-description-content {
line-height: 1.6;
color: #333;
}
.shop-description-content h1,
.shop-description-content h2,
.shop-description-content h3 {
color: #ffa200;
margin-top: 1.5rem;
margin-bottom: 1rem;
}
.shop-description-content h1 { font-size: 1.75rem; }
.shop-description-content h2 { font-size: 1.5rem; }
.shop-description-content h3 { font-size: 1.25rem; }
.shop-description-content ul,
.shop-description-content ol {
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.shop-description-content li {
margin-bottom: 0.5rem;
}
.shop-description-content p {
margin-bottom: 1rem;
}
.shop-description-content a {
color: #ffa200;
text-decoration: underline;
}
.shop-description-content a:hover {
color: #e8910a;
}
.shop-description-content img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 1rem 0;
}
.shop-description-content blockquote {
border-left: 4px solid #ffa200;
padding-left: 1rem;
margin: 1rem 0;
font-style: italic;
color: #666;
}
.shop-description-content table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.shop-description-content table th,
.shop-description-content table td {
border: 1px solid #ddd;
padding: 0.5rem;
}
.shop-description-content table th {
background-color: #f8f9fa;
font-weight: bold;
}
/* Design eBay-like intégré avec Karma - Pleine largeur */
.shop-header {
background: #f7f7f7;
border-bottom: 1px solid #e1e1e1;
padding: 0;
margin-bottom: 0;
width: 100%;
position: relative;
}
.shop-banner {
width: 100%;
height: 350px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
/* Carrousel eBay-style amélioré */
.shop-carousel-ebay {
width: 100%;
height: 450px;
position: relative;
overflow: hidden;
background: #f8f9fa;
border-radius: 8px;
box-sizing: border-box;
margin-bottom: 0;
transition: height 0.3s ease;
}
.carousel-container {
position: relative;
width: 100%;
height: 100%;
}
.carousel-slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 0.6s ease-in-out;
}
/* Amélioration responsive pour les images */
@media (max-width: 768px) {
.carousel-slide {
background-size: cover;
background-position: center center;
}
}
.carousel-slide.active {
opacity: 1;
}
.carousel-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
}
.carousel-btn {
width: 40px;
height: 40px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
color: #333;
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.carousel-btn:hover {
background: white;
transform: scale(1.1);
}
.carousel-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.carousel-pause {
background: #dc3545;
color: white;
}
.carousel-pause:hover {
background: #c82333;
}
.carousel-indicators {
position: absolute;
bottom: 70px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 8px;
z-index: 10;
}
.carousel-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
border: none;
cursor: pointer;
transition: all 0.3s ease;
}
.carousel-indicator.active {
background: white;
transform: scale(1.2);
}
/* Contrôles eBay-style améliorés - Positionnés en bas à droite */
.carousel-controls-ebay {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
gap: 12px;
align-items: center;
z-index: 10;
pointer-events: none;
}
.carousel-btn-ebay {
width: 52px;
height: 52px;
border: none;
border-radius: 50% !important;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.9) 100%);
color: #333;
font-size: 22px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: all;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25), 0 2px 8px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(10px);
position: relative;
overflow: visible;
}
.carousel-btn-ebay::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.6s ease, height 0.6s ease;
}
.carousel-btn-ebay:hover::before {
width: 100%;
height: 100%;
}
.carousel-btn-ebay:hover {
transform: translateY(-3px) scale(1.1);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.35), 0 4px 12px rgba(0, 0, 0, 0.2);
}
/* Désactiver les effets hover sur mobile pour éviter les problèmes tactiles */
@media (hover: none) and (pointer: coarse) {
.carousel-btn-ebay:hover {
transform: none;
}
.carousel-btn-ebay:active {
transform: scale(0.95);
}
.carousel-indicator-ebay:hover {
transform: none;
}
}
.carousel-btn-ebay:active {
transform: translateY(-1px) scale(1.05);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
/* Désactiver les effets hover sur mobile pour éviter les problèmes tactiles */
@media (hover: none) and (pointer: coarse) {
.carousel-btn-ebay:hover {
transform: none;
}
.carousel-btn-ebay:active {
transform: scale(0.95);
}
.carousel-indicator-ebay:hover {
transform: none;
}
}
.carousel-btn-ebay:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
}
.carousel-btn-ebay i {
position: relative;
z-index: 10;
transition: transform 0.3s ease;
display: inline-block;
line-height: 1;
}
.carousel-btn-ebay:hover i {
transform: scale(1.15);
}
.carousel-btn-ebay.pause-btn i,
.carousel-btn-ebay.pause-btn #pauseIcon {
z-index: 10 !important;
color: white !important;
display: inline-block !important;
visibility: visible !important;
opacity: 1 !important;
}
.carousel-btn-ebay.prev-btn,
.carousel-btn-ebay.next-btn {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98) 0%, rgba(255, 255, 255, 0.92) 100%);
color: #333;
}
.carousel-btn-ebay.prev-btn:hover,
.carousel-btn-ebay.next-btn:hover {
background: linear-gradient(135deg, #ffa200 0%, #ff6b00 100%);
color: white;
}
.carousel-btn-ebay.pause-btn {
background: linear-gradient(135deg, rgba(220, 53, 69, 0.95) 0%, rgba(200, 35, 51, 0.95) 100%);
color: white !important;
}
.carousel-btn-ebay.pause-btn:hover {
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
color: white !important;
}
.carousel-btn-ebay.pause-btn.playing {
background: linear-gradient(135deg, rgba(40, 167, 69, 0.95) 0%, rgba(33, 136, 56, 0.95) 100%);
color: white !important;
}
.carousel-btn-ebay.pause-btn.playing:hover {
background: linear-gradient(135deg, #28a745 0%, #218838 100%);
color: white !important;
}
/* Style spécifique pour l'icône du bouton pause - Utilisation directe d'Unicode */
.carousel-btn-ebay.pause-btn {
text-align: center;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.carousel-btn-ebay.pause-btn #pauseIcon,
.carousel-btn-ebay.pause-btn .pause-icon-content {
display: block !important;
color: white !important;
font-size: 24px !important;
line-height: 1 !important;
width: auto !important;
height: auto !important;
position: static !important;
text-align: center !important;
z-index: 10 !important;
visibility: visible !important;
opacity: 1 !important;
pointer-events: none !important;
font-family: Arial, "Segoe UI", sans-serif !important;
font-weight: normal !important;
margin: 0 !important;
padding: 0 !important;
}
/* Indicateurs eBay-style améliorés - Positionnés en bas à gauche */
.carousel-indicators-ebay {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
background: rgba(0, 0, 0, 0.4);
padding: 8px 14px;
border-radius: 25px;
backdrop-filter: blur(8px);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}
.carousel-indicator-ebay {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
border: 2px solid rgba(255, 255, 255, 0.8);
cursor: pointer;
transition: all 0.3s ease;
}
.carousel-indicator-ebay.active {
background: white !important;
border-color: #ffa200 !important;
transform: scale(1.3) !important;
box-shadow: 0 0 8px rgba(255, 162, 0, 0.6) !important;
}
.carousel-indicator-ebay:hover {
background: rgba(255, 255, 255, 0.8);
transform: scale(1.1);
}
/* Styles eBay-style pour les détails de boutique */
.shop-avatar-container-ebay {
position: relative;
margin-right: 20px;
}
.shop-avatar-ebay {
width: 100px;
height: 100px;
border-radius: 12px;
object-fit: cover;
border: 3px solid white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease;
}
.shop-avatar-ebay:hover {
transform: scale(1.05);
}
.shop-avatar-placeholder {
background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
color: #999;
}
.verified-badge-ebay {
position: absolute;
bottom: -5px;
right: -5px;
background: #28a745;
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
border: 2px solid white;
}
.shop-info-ebay {
padding: 0;
}
.shop-name-section {
margin-bottom: 12px;
}
.shop-name-ebay {
font-size: 2.2rem;
font-weight: 800;
background: linear-gradient(135deg, #333 0%, #555 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin: 0;
line-height: 1.2;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
letter-spacing: -0.5px;
}
.verified-text {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-size: 0.9rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 4px;
word-wrap: break-word;
overflow-wrap: break-word;
line-height: 1.4;
white-space: normal;
flex-wrap: wrap;
max-width: 100%;
}
.verified-text i {
-webkit-text-fill-color: #28a745;
color: #28a745;
flex-shrink: 0;
}
.shop-category-ebay {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
color: #666;
padding: 6px 14px;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 500;
display: inline-block;
border: 1px solid #dee2e6;
word-wrap: break-word;
overflow-wrap: break-word;
max-width: 100%;
line-height: 1.4;
white-space: normal;
}
.shop-stats-ebay {
margin-top: 16px;
}
.stat-row {
display: flex;
gap: 32px;
margin-bottom: 12px;
flex-wrap: wrap;
}
.stat-item-ebay {
display: flex;
flex-direction: column;
align-items: flex-start;
min-width: 100px;
}
.stat-percentage {
font-size: 1.4rem;
font-weight: 800;
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.1;
margin-bottom: 4px;
word-wrap: break-word;
overflow-wrap: break-word;
}
.stat-number-ebay {
font-size: 1.4rem;
font-weight: 800;
background: linear-gradient(135deg, #333 0%, #555 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.1;
margin-bottom: 4px;
word-wrap: break-word;
overflow-wrap: break-word;
}
.stat-label-ebay {
font-size: 0.75rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.8px;
font-weight: 600;
margin-top: 0;
line-height: 1.3;
word-wrap: break-word;
overflow-wrap: break-word;
text-align: left;
hyphens: auto;
max-width: 100%;
}
.shop-additional-info {
display: flex;
flex-wrap: wrap;
gap: 12px 20px;
font-size: 0.875rem;
color: #495057;
margin-top: 12px;
padding-top: 16px;
border-top: 1px solid #e9ecef;
line-height: 1.5;
word-wrap: break-word;
overflow-wrap: break-word;
}
.member-since {
font-weight: 500;
display: inline-flex;
align-items: center;
white-space: normal;
padding: 6px 12px;
background: #f8f9fa;
border-radius: 8px;
transition: all 0.3s ease;
border: 1px solid transparent;
word-wrap: break-word;
overflow-wrap: break-word;
line-height: 1.4;
max-width: 100%;
flex-wrap: wrap;
text-align: center;
}
.member-since:hover {
background: #e9ecef;
border-color: #dee2e6;
transform: translateY(-1px);
}
.member-since i {
color: #ffa200;
margin-right: 6px;
font-size: 0.95rem;
transition: transform 0.3s ease;
flex-shrink: 0;
}
.member-since:hover i {
transform: scale(1.2);
}
.location {
display: flex;
align-items: center;
}
.shop-actions-ebay {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 160px;
}
.btn-ebay {
padding: 10px 18px;
font-size: 0.9rem;
font-weight: 500;
border-radius: 6px;
text-align: center;
transition: all 0.3s ease;
border-width: 1px;
white-space: normal;
word-wrap: break-word;
overflow-wrap: break-word;
line-height: 1.4;
}
.btn-ebay:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.btn-ebay i {
vertical-align: middle;
}
.btn-ebay.following {
background: #28a745;
border-color: #28a745;
color: white;
}
/* Styles pour les avis */
.review-stats {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.stat-card {
padding: 15px;
}
.stat-number {
font-size: 2.5rem;
font-weight: 700;
color: #333;
margin: 0;
}
.stat-label {
color: #666;
font-size: 0.9rem;
margin: 5px 0 0;
}
.rating-stars {
color: #ffc107;
font-size: 1.2rem;
margin: 10px 0;
}
.reviews-preview {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.review-item {
border-bottom: 1px solid #f0f0f0;
padding: 15px 0;
}
.review-item:last-child {
border-bottom: none;
}
.review-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.reviewer-info {
display: flex;
align-items: center;
gap: 10px;
}
.reviewer-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
.reviewer-name {
margin: 0;
font-size: 1rem;
font-weight: 600;
}
.review-rating {
color: #ffc107;
font-size: 0.9rem;
}
.review-meta {
text-align: right;
}
.review-date {
color: #666;
font-size: 0.8rem;
}
.verified-badge {
display: inline-block;
background: #28a745;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.7rem;
margin-left: 10px;
}
.review-comment {
margin: 10px 0 0;
line-height: 1.5;
color: #333;
}
.review-actions-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
.shop-banner-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 1;
}
.shop-header-content {
background: white;
border: 1px solid #e1e1e1;
border-radius: 4px;
padding: 20px;
margin: 0 20px 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: relative;
z-index: 2;
}
.shop-avatar {
width: 80px;
height: 80px;
border-radius: 4px;
border: 1px solid #e1e1e1;
object-fit: cover;
position: relative;
z-index: 3;
}
.shop-title {
font-size: 1.5rem;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.shop-subtitle {
color: #666;
font-size: 0.9rem;
margin-bottom: 15px;
}
.shop-stats-row {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 15px;
}
.shop-stat-item {
display: flex;
align-items: center;
gap: 5px;
font-size: 0.9rem;
}
.positive-feedback {
color: #007bff;
font-weight: 600;
}
.items-sold {
color: #666;
}
.followers-count {
color: #666;
}
.shop-badges {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.shop-badge {
background: #f0f0f0;
color: #333;
padding: 4px 8px;
border-radius: 3px;
font-size: 0.8rem;
font-weight: 500;
}
.shop-badge.verified {
background: #d4edda;
color: #155724;
}
.shop-badge.premium {
background: #fff3cd;
color: #856404;
}
.shop-actions-row {
display: flex;
align-items: center;
gap: 15px;
flex-wrap: wrap;
}
.shop-action-btn {
background: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 3px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
}
.shop-action-btn:hover {
background: #0056b3;
color: white;
}
.shop-action-btn.following {
background: #28a745;
}
.shop-action-btn.following:hover {
background: #1e7e34;
}
.shop-action-btn.secondary {
background: #6c757d;
}
.shop-action-btn.secondary:hover {
background: #545b62;
}
/* Onglets eBay-style avec classes Karma */
.shop-tabs {
background: white;
border: 1px solid #e1e1e1;
border-top: none;
border-radius: 0 0 4px 4px;
margin-top: 0;
}
.shop-tabs-nav {
display: flex;
border-bottom: 1px solid #e1e1e1;
margin: 0;
padding: 0;
list-style: none;
background: #f8f9fa;
}
.shop-tab {
margin: 0;
}
.shop-tab-link {
display: block;
padding: 12px 20px;
color: #000000;
text-decoration: none;
border-bottom: 2px solid transparent;
transition: all 0.2s;
font-weight: 500;
font-size: 1rem;
}
.shop-tab-link:hover,
.shop-tab-link.active {
color: #095ad3;
border-bottom-color: #095ad3;
background: #ffa200;
}
.shop-tab-content {
padding: 30px;
min-height: 400px;
}
.shop-tab-content:not(.active) {
display: none;
}
.shop-tab-content.active {
display: block;
}
/* Cards d'information avec style Karma */
.shop-info-card {
background: #f8f9fa;
border: 1px solid #e1e1e1;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
.shop-info-card h6 {
color: #333;
font-weight: 600;
margin-bottom: 15px;
font-size: 1.1rem;
}
.shop-info-card p {
color: #666;
font-size: 0.9rem;
margin: 0;
line-height: 1.5;
}
.shop-contact-info {
display: flex;
flex-direction: column;
gap: 10px;
}
.shop-contact-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.9rem;
color: #666;
padding: 5px 0;
}
.shop-contact-item i {
width: 16px;
color: #007bff;
text-align: center;
}
.shop-social-links {
display: flex;
gap: 10px;
margin-top: 15px;
flex-wrap: wrap;
}
.shop-social-link {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: #f0f0f0;
color: #666;
border-radius: 4px;
text-decoration: none;
transition: all 0.2s;
font-size: 1.1rem;
}
.shop-social-link:hover {
background: #007bff;
color: white;
transform: translateY(-2px);
}
/* Produits avec style Karma */
.product-card {
background: white;
border: 1px solid #e1e1e1;
border-radius: 4px;
overflow: hidden;
transition: all 0.3s ease;
margin-bottom: 20px;
}
.product-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Animation de tri des produits */
.product-item {
transition: all 0.3s ease;
}
.product-item.sorting {
opacity: 0;
transform: translateY(20px);
}
.product-item.sorted {
opacity: 1;
transform: translateY(0);
}
.product-image {
height: 200px;
background-size: cover;
background-position: center;
position: relative;
overflow: hidden;
}
.product-info {
padding: 15px;
}
.product-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 8px;
color: #333;
line-height: 1.3;
}
.product-title a {
color: #333;
text-decoration: none;
}
.product-title a:hover {
color: #007bff;
}
.product-price {
font-size: 1.2rem;
font-weight: bold;
color: #ff6b35;
margin-bottom: 10px;
}
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.85rem;
color: #666;
}
.rating {
color: #ffc107;
}
/* Responsive avec classes Karma */
/* Responsive amélioré pour la page shop */
/* Tablettes et écrans moyens */
@media(max-width: 1200px) {
.shop-carousel-ebay {
height: 400px;
}
.carousel-controls-ebay {
bottom: 18px;
right: 18px;
gap: 11px;
}
.carousel-btn-ebay {
width: 48px;
height: 48px;
font-size: 21px;
}
.carousel-indicators-ebay {
bottom: 18px;
padding: 7px 13px;
}
.carousel-indicator-ebay {
width: 9px;
height: 9px;
}
}
@media(max-width: 991.98px) {
.shop-carousel-ebay {
margin: 0 10px;
height: 380px;
}
.carousel-controls-ebay {
bottom: 16px;
right: 16px;
gap: 10px;
}
.carousel-btn-ebay {
width: 46px;
height: 46px;
font-size: 20px;
}
.carousel-indicators-ebay {
bottom: 16px;
left: 50%;
transform: translateX(-50%);
padding: 6px 12px;
}
.carousel-indicator-ebay {
width: 9px;
height: 9px;
}
.shop-header-content {
padding: 20px;
margin: 0 10px 20px;
}
.shop-header-content .row {
flex-wrap: wrap;
}
.shop-info-ebay {
margin-top: 0;
text-align: center;
}
.shop-name-ebay {
font-size: 1.75rem;
line-height: 1.3;
}
.verified-text {
font-size: 0.85rem;
}
.shop-category-ebay {
font-size: 0.8rem;
padding: 5px 12px;
}
.stat-row {
justify-content: center;
gap: 20px;
}
.stat-item-ebay {
align-items: center;
min-width: 90px;
}
.stat-percentage,
.stat-number-ebay {
font-size: 1.15rem;
}
.stat-label-ebay {
font-size: 0.75rem;
text-align: center;
}
.shop-additional-info {
justify-content: center;
gap: 12px 18px;
font-size: 0.8rem;
}
.member-since {
font-size: 0.8rem;
}
.shop-actions-ebay {
min-width: auto;
}
}
@media(max-width: 768px) {
.shop-banner,
.shop-carousel-ebay {
height: 300px;
margin: 0;
border-radius: 0;
}
.shop-carousel-ebay .carousel-container {
border-radius: 0;
}
.carousel-controls-ebay {
bottom: 12px;
right: 12px;
gap: 8px;
}
.carousel-btn-ebay {
width: 40px;
height: 40px;
font-size: 16px;
}
.carousel-btn-ebay #pauseIcon,
.carousel-btn-ebay .pause-icon-content {
font-size: 18px !important;
}
.carousel-indicators-ebay {
bottom: 12px;
left: 50%;
transform: translateX(-50%);
gap: 6px;
padding: 5px 10px;
}
.carousel-indicator-ebay {
width: 8px;
height: 8px;
}
.shop-header-content {
padding: 20px 16px;
margin: 0;
border-radius: 0;
}
.shop-header-content .row {
flex-direction: column;
text-align: center;
}
.shop-avatar-container-ebay {
margin: 0 auto 16px;
}
.shop-avatar-ebay {
width: 90px;
height: 90px;
}
.shop-name-ebay {
font-size: 1.5rem;
margin-bottom: 8px;
line-height: 1.3;
word-break: break-word;
hyphens: auto;
max-width: 100%;
}
.shop-name-section {
margin-bottom: 10px;
}
.verified-text {
font-size: 0.8rem;
white-space: normal;
justify-content: center;
flex-wrap: wrap;
line-height: 1.4;
}
.verified-text i {
font-size: 0.85rem;
}
.shop-category-ebay {
font-size: 0.75rem;
padding: 5px 10px;
max-width: 100%;
line-height: 1.3;
word-break: break-word;
}
.shop-info-ebay {
text-align: center;
}
.stat-row {
justify-content: center;
gap: 10px;
flex-wrap: wrap;
}
.stat-item-ebay {
align-items: center;
min-width: 75px;
flex: 0 1 auto;
padding: 10px 8px;
}
.stat-percentage,
.stat-number-ebay {
font-size: 1rem;
line-height: 1.1;
word-break: break-word;
}
.stat-label-ebay {
font-size: 0.65rem;
text-align: center;
line-height: 1.2;
letter-spacing: 0.3px;
word-break: break-word;
hyphens: auto;
}
.shop-additional-info {
justify-content: center;
flex-wrap: wrap;
gap: 8px 10px;
font-size: 0.75rem;
line-height: 1.4;
}
.member-since {
flex-direction: row;
text-align: center;
gap: 4px;
white-space: normal;
font-size: 0.75rem;
line-height: 1.4;
padding: 5px 8px;
word-break: break-word;
}
.member-since i {
margin-right: 4px;
font-size: 0.85rem;
flex-shrink: 0;
}
.shop-actions-ebay {
flex-direction: column;
justify-content: center;
min-width: auto;
margin-top: 20px;
width: 100%;
}
.btn-ebay {
width: 100%;
min-width: auto;
}
.shop-actions-ebay .d-flex {
width: 100%;
}
.btn-shop,
.shop-action-btn {
width: 100%;
min-width: auto;
height: 45px;
font-size: 0.9rem;
padding: 12px 20px;
}
.shop-tabs-nav {
flex-wrap: wrap;
justify-content: center;
}
.shop-tab-link {
font-size: 0.85rem;
padding: 0.5rem 0.75rem;
flex: 1;
min-width: auto;
}
/* Produits responsive */
#products-container .product-item {
margin-bottom: 20px;
}
.product-card {
padding: 15px;
}
.product-title {
font-size: 0.95rem;
}
.product-price {
font-size: 1.1rem;
}
/* Détails de la boutique responsive */
.shop-info-ebay {
text-align: center;
}
}
@media(max-width: 575.98px) {
.shop-banner,
.shop-carousel-ebay {
height: 280px;
margin: 0;
}
.carousel-controls-ebay {
bottom: 10px;
right: 10px;
gap: 6px;
}
.carousel-btn-ebay {
width: 36px;
height: 36px;
font-size: 14px;
}
.carousel-btn-ebay #pauseIcon,
.carousel-btn-ebay .pause-icon-content {
font-size: 16px !important;
}
.carousel-indicators-ebay {
bottom: 10px;
left: 50%;
transform: translateX(-50%);
padding: 4px 8px;
gap: 5px;
}
.carousel-indicator-ebay {
width: 7px;
height: 7px;
}
.shop-header-content {
margin: 0;
padding: 16px 12px;
}
.shop-avatar-ebay {
width: 80px;
height: 80px;
}
.shop-name-ebay {
font-size: 1.3rem;
line-height: 1.3;
word-break: break-word;
letter-spacing: -0.1px;
hyphens: auto;
max-width: 100%;
}
.shop-name-section {
margin-bottom: 8px;
}
.verified-text {
font-size: 0.7rem;
white-space: normal;
line-height: 1.4;
flex-wrap: wrap;
justify-content: center;
word-break: break-word;
}
.verified-text i {
font-size: 0.75rem;
flex-shrink: 0;
}
.shop-category-ebay {
font-size: 0.65rem;
padding: 4px 8px;
max-width: 100%;
line-height: 1.3;
word-break: break-word;
}
.stat-row {
gap: 6px;
flex-direction: row;
justify-content: space-around;
}
.stat-item-ebay {
flex: 1;
min-width: 0;
align-items: center;
padding: 8px 4px;
}
.stat-percentage,
.stat-number-ebay {
font-size: 0.95rem;
line-height: 1.1;
word-break: break-word;
}
.stat-label-ebay {
font-size: 0.6rem;
text-align: center;
line-height: 1.2;
letter-spacing: 0.2px;
padding: 0 2px;
word-break: break-word;
hyphens: auto;
}
.shop-additional-info {
flex-direction: column;
gap: 8px;
font-size: 0.7rem;
align-items: center;
line-height: 1.5;
}
.member-since {
font-size: 0.7rem;
padding: 4px 8px;
line-height: 1.4;
word-break: break-word;
text-align: center;
width: 100%;
max-width: 100%;
justify-content: center;
white-space: normal;
}
.member-since i {
font-size: 0.8rem;
margin-right: 3px;
flex-shrink: 0;
}
.btn-ebay {
font-size: 0.8rem;
padding: 10px 14px;
line-height: 1.3;
}
.member-since i {
margin-right: 4px;
font-size: 0.8rem;
}
.btn-ebay {
font-size: 0.85rem;
padding: 10px 16px;
}
.member-since {
font-size: 0.75rem;
}
.shop-tab-content {
padding: 15px 10px;
}
.shop-stats-row {
flex-direction: column;
gap: 10px;
align-items: flex-start;
}
}
.follow-button {
background: #ffa200;
border: none;
color: white;
padding: 12px 30px;
border-radius: 25px;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
}
.follow-button:hover {
background: #e8910a;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 162, 0, 0.3);
}
.follow-button.following {
background: #28a745;
}
.follow-button.following:hover {
background: #218838;
}
.follow-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.follow-stats {
display: flex;
gap: 30px;
margin-top: 20px;
flex-wrap: wrap;
}
.follow-stat {
text-align: center;
padding: 15px;
background: #f8f9fa;
border-radius: 10px;
min-width: 120px;
}
.follow-stat-number {
font-size: 1.8rem;
font-weight: bold;
color: #ffa200;
display: block;
}
.follow-stat-label {
color: #666;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.shop-actions {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
margin-top: 20px;
}
.action-button {
background: white;
border: 2px solid #ffa200;
color: #ffa200;
padding: 10px 20px;
border-radius: 20px;
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.action-button:hover {
background: #ffa200;
color: white;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 162, 0, 0.3);
}
.positive-feedback {
color: #28a745;
font-weight: bold;
}
.items-sold {
color: #666;
font-size: 0.9rem;
}
.followers-count {
color: #666;
font-size: 0.9rem;
}
.shop-avatar {
width: 120px;
height: 120px;
border-radius: 50%;
border: 4px solid white;
object-fit: cover;
margin-bottom: 20px;
}
.shop-stats {
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
margin-top: -50px;
position: relative;
z-index: 10;
}
.stat-item {
text-align: center;
padding: 20px;
}
.stat-number {
font-size: 2.5rem;
font-weight: bold;
color: #ffa200;
display: block;
}
.stat-label {
color: #666;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.shop-description {
background: #f8f9fa;
padding: 30px;
border-radius: 10px;
margin-bottom: 40px;
}
.shop-contact {
background: white;
border: 1px solid #e9ecef;
border-radius: 10px;
padding: 25px;
margin-bottom: 40px;
}
.contact-item {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.contact-item i {
color: #ffa200;
margin-right: 15px;
width: 20px;
}
.product-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
margin-bottom: 30px;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.product-image {
height: 200px;
background-size: cover;
background-position: center;
position: relative;
}
.product-info {
padding: 20px;
}
.product-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
color: #333;
}
.product-price {
font-size: 1.3rem;
font-weight: bold;
color: #ffa200;
margin-bottom: 15px;
}
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
color: #666;
}
.rating {
color: #ffa200;
}
/* Boutons d'action uniformes */
.btn-shop,
.shop-action-btn {
background: #ffa200;
border: 1px solid #ffa200;
color: white;
padding: 12px 24px;
border-radius: 6px;
font-weight: 500;
font-size: 0.9rem;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
min-width: 120px;
height: 44px;
white-space: nowrap;
}
.btn-shop:hover,
.shop-action-btn:hover {
background: #e8910a;
border-color: #e8910a;
color: white;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(255, 162, 0, 0.3);
}
/* Boutons secondaires */
.shop-action-btn.secondary {
background: transparent;
border: 1px solid #6c757d;
color: #6c757d;
}
.shop-action-btn.secondary:hover {
background: #6c757d;
border-color: #6c757d;
color: white;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3);
}
/* Responsive */
@media(max-width: 768px) {
.shop-header {
padding: 100px 0 40px;
}
.shop-avatar {
width: 80px;
height: 80px;
}
.shop-stats {
margin-top: -30px;
padding: 20px;
}
.stat-number {
font-size: 2rem;
}
}
/* Très petits écrans */
@media(max-width: 480px) {
.btn-shop,
.shop-action-btn {
min-width: 90px;
height: 38px;
font-size: 0.8rem;
padding: 8px 16px;
}
.shop-actions-row {
gap: 8px;
}
}
/* Styles pour la section des avis */
.review-stats-card {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.stats-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.overall-rating {
text-align: center;
margin-right: 20px;
}
.rating-number {
font-size: 36px;
font-weight: bold;
color: #333;
display: block;
}
.stars {
color: #ffc107;
font-size: 18px;
margin-top: 5px;
}
.rating-summary p {
margin: 0;
color: #666;
}
.rating-breakdown {
margin-top: 15px;
}
.rating-bar {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.rating-label {
width: 40px;
font-size: 14px;
color: #666;
}
.rating-progress {
flex: 1;
height: 6px;
background: #e9ecef;
border-radius: 3px;
margin: 0 10px;
overflow: hidden;
}
.rating-fill {
height: 100%;
background: linear-gradient(45deg, #ffc107, #ff8c00);
transition: width 0.3s ease;
}
.rating-count {
width: 30px;
text-align: right;
font-size: 14px;
color: #666;
}
.reviews-preview {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.review-card-preview {
border-bottom: 1px solid #f0f0f0;
padding-bottom: 15px;
margin-bottom: 15px;
}
.review-card-preview:last-child {
border-bottom: none;
margin-bottom: 0;
}
.review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.review-user {
display: flex;
align-items: center;
}
.user-avatar {
width: 35px;
height: 35px;
border-radius: 50%;
background: linear-gradient(45deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
margin-right: 10px;
}
.user-info h6 {
margin: 0;
font-size: 14px;
font-weight: 600;
color: #333;
}
.user-info small {
color: #666;
font-size: 12px;
}
.review-rating {
display: flex;
align-items: center;
gap: 5px;
}
.verified-badge {
background: #28a745;
color: white;
padding: 2px 4px;
border-radius: 3px;
font-size: 10px;
font-weight: bold;
}
.review-content {
color: #666;
font-size: 14px;
line-height: 1.5;
}
.empty-reviews {
text-align: center;
padding: 40px 20px;
color: #666;
}
.empty-reviews i {
font-size: 48px;
color: #ddd;
margin-bottom: 15px;
}
.empty-reviews h4 {
color: #333;
margin-bottom: 10px;
}
.section-title h2 {
color: #333;
margin-bottom: 10px;
}
.section-title p {
color: #666;
margin-bottom: 30px;
}
@media(max-width: 768px) {
.stats-header {
flex-direction: column;
text-align: center;
}
.overall-rating {
margin-right: 0;
margin-bottom: 15px;
}
.review-header {
flex-direction: column;
align-items: flex-start;
}
.review-rating {
margin-top: 5px;
}
}
</style>
{% endblock %}
{% block body %}
<!-- Shop Header eBay-style avec Karma - Pleine largeur -->
<section
class="shop-header">
<!-- Carrousel eBay-style -->
<div class="shop-carousel-ebay">
<div class="carousel-container">
{% set allBannerImages = shop.getAllBannerImages() %}
{% if allBannerImages|length > 0 %}
{% for image in allBannerImages %}
<div class="carousel-slide {% if loop.first %}active{% endif %}" style="background-image: url('{{ asset(image) }}')">
<div class="shop-banner-overlay"></div>
</div>
{% endfor %}
{% else %}
<!-- Slide par défaut avec dégradé si aucune image de bannière -->
<div class="carousel-slide active" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);">
<div class="shop-banner-overlay"></div>
</div>
{% endif %}
</div>
<!-- Contrôles eBay-style améliorés - toujours affichés si plusieurs images -->
{% set totalBannerImages = allBannerImages|length %}
{% if totalBannerImages > 1 %}
<div class="carousel-controls-ebay">
<button class="carousel-btn-ebay prev-btn" onclick="previousSlide()" id="prevBtn" title="Image précédente" aria-label="Image précédente">
<i class="lnr lnr-chevron-left"></i>
</button>
<button class="carousel-btn-ebay pause-btn" onclick="toggleCarousel()" id="pauseBtn" title="Pause/Reprendre" aria-label="Pause/Reprendre">
<span id="pauseIcon" class="pause-icon-content">⏸</span>
</button>
<button class="carousel-btn-ebay next-btn" onclick="nextSlide()" id="nextBtn" title="Image suivante" aria-label="Image suivante">
<i class="lnr lnr-chevron-right"></i>
</button>
</div>
<!-- Indicateurs eBay-style améliorés -->
<div class="carousel-indicators-ebay">
{% for image in allBannerImages %}
<button class="carousel-indicator-ebay {% if loop.first %}active{% endif %}" onclick="goToSlide({{ loop.index0 }})" title="Image {{ loop.index }}" aria-label="Aller à l'image {{ loop.index }}"></button>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Contenu de la boutique eBay-style -->
<div class="shop-header-content">
<div class="row g-3 g-md-4 align-items-start align-items-md-center">
<!-- Logo de la boutique -->
<div class="col-12 col-md-auto text-center text-md-start">
<div class="shop-avatar-container-ebay">
{% if shop.logo %}
<img src="{{ asset(shop.logo) }}" alt="{{ shop.name }}" class="shop-avatar-ebay">
{% else %}
<div class="shop-avatar-ebay shop-avatar-placeholder">
<i class="lnr lnr-store"></i>
</div>
{% endif %}
{% if shop.isVerified %}
<div class="verified-badge-ebay">
<i class="lnr lnr-checkmark-circle"></i>
</div>
{% endif %}
</div>
</div>
<!-- Informations principales -->
<div class="col-12 col-md">
<div class="shop-info-ebay">
<!-- Nom de la boutique -->
<div class="shop-name-section mb-2">
<h1 class="shop-name-ebay mb-1">{{ shop.name }}</h1>
<div class="d-flex flex-wrap align-items-center gap-2">
{% if shop.isVerified %}
<span class="verified-text">
<i class="lnr lnr-checkmark-circle me-1"></i>Vendeur vérifié
</span>
{% endif %}
{% if shop.shopCategory %}
<span class="shop-category-ebay">{{ shop.shopCategory.name }}</span>
{% endif %}
</div>
</div>
<!-- Statistiques principales -->
<div class="shop-stats-ebay">
<div class="stat-row mb-2">
{% if shop.averageRating > 0 %}
<div class="stat-item-ebay">
<span class="stat-percentage">{{ shop.averageRating|number_format(1) }}%</span>
<span class="stat-label-ebay">Évaluations positives</span>
</div>
{% endif %}
{% if stats.totalProductSales > 0 %}
<div class="stat-item-ebay">
<span class="stat-number-ebay">{{ stats.totalProductSales|number_format }}</span>
<span class="stat-label-ebay">Articles vendus</span>
</div>
{% endif %}
{% if followStats.active_follows > 0 %}
<div class="stat-item-ebay">
<span class="stat-number-ebay">{{ followStats.active_follows|number_format }}</span>
<span class="stat-label-ebay">Suiveurs</span>
</div>
{% endif %}
</div>
<!-- Informations supplémentaires -->
<div class="shop-additional-info">
<div class="d-flex flex-wrap gap-3 gap-md-4">
{% if shop.averageRating > 0 %}
<span class="member-since">
<i class="lnr lnr-thumbs-up me-1"></i>
{{ shop.averageRating|number_format(1) }}% positive feedback
</span>
{% endif %}
{% if stats.totalProductSales > 0 %}
<span class="member-since">
<i class="lnr lnr-cart me-1"></i>
{{ stats.totalProductSales|number_format }} produit{{ stats.totalProductSales > 1 ? 's' : '' }} vendu{{ stats.totalProductSales > 1 ? 's' : '' }}
</span>
{% endif %}
<span class="member-since">
<i class="lnr lnr-history me-1"></i>
Créé depuis {{ shop.createdAt|date('M Y') }}
</span>
</div>
</div>
</div>
</div>
</div>
<!-- Actions eBay-style -->
<div class="col-12 col-md-auto">
<div class="shop-actions-ebay">
{% if app.user %}
<button id="follow-button" class="btn btn-primary btn-ebay w-100 {% if isFollowing %}following{% endif %}" data-shop-id="{{ shop.id }}" data-following="{{ isFollowing ? 'true' : 'false' }}">
<span id="follow-text">
{% if isFollowing %}
<i class="lnr lnr-checkmark-circle me-1"></i> Suivi
{% else %}
<i class="lnr lnr-plus me-1"></i> Suivre
{% endif %}
</span>
</button>
{% else %}
<a href="{{ path('ui_app_login') }}" class="btn btn-primary btn-ebay w-100">
<i class="lnr lnr-user me-1"></i> Se connecter pour suivre
</a>
{% endif %}
<div class="d-flex gap-2 mt-2">
<button class="btn btn-outline-secondary btn-ebay flex-fill" onclick="shareShop()" title="Partager">
<i class="lnr lnr-bubble"></i>
<span class="d-none d-md-inline ms-1">Partager</span>
</button>
<button class="btn btn-outline-danger btn-ebay flex-fill" onclick="reportShop()" title="Signaler">
<i class="lnr lnr-flag"></i>
<span class="d-none d-md-inline ms-1">Signaler</span>
</button>
</div>
{% if canEdit %}
<a href="{{ path('seller_shop_edit', {slug: shop.slug}) }}" class="btn btn-outline-primary btn-ebay w-100 mt-2">
<i class="lnr lnr-pencil me-1"></i> Modifier la boutique
</a>
{% endif %}
</div>
</div>
</div>
</div>
</section>
<!-- Shop Tabs eBay-style avec Karma -->
<section class="container">
<div class="shop-tabs">
<ul class="nav nav-tabs shop-tabs-nav" role="tablist">
<li class="nav-item shop-tab">
<a href="#shop-items" class="nav-link shop-tab-link active" data-tab="items" role="tab">
Boutique
</a>
</li>
<li class="nav-item shop-tab">
<a href="#shop-sale" class="nav-link shop-tab-link" data-tab="sale" role="tab">
Promotions
</a>
</li>
<li class="nav-item shop-tab">
<a href="#shop-about" class="nav-link shop-tab-link" data-tab="about" role="tab">
À propos
</a>
</li>
<li class="nav-item shop-tab">
<a href="#shop-feedback" class="nav-link shop-tab-link" data-tab="feedback" role="tab">
Avis ({{ shop.getVisibleReviewsCount() }})
</a>
</li>
</ul>
<!-- Tab Content: Shop Items avec Karma -->
<div id="shop-items" class="tab-content shop-tab-content active">
<div class="row">
<div class="col-lg-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="mb-0">Produits de
{{ shop.name }}</h4>
<div class="d-flex align-items-center gap-3">
<span style="font-size: 2rem; font-weight: 600; min-width: 100px; display: inline-block; text-align: center;">
<span class="product-count">{{ products|length|number_format }}</span>
<span style="font-size: 1rem; font-weight: 400;">produits</span>
</span>
<select class="form-select" id="product-sort" style="width: auto;" onchange="if(window.sortProducts) window.sortProducts(this.value);">
<option value="">Trier par</option>
<option value="price_asc">Prix : Croissant</option>
<option value="price_desc">Prix : Décroissant</option>
<option value="newest">Plus récents</option>
<option value="popular">Plus populaires</option>
<option value="name_asc">Nom : A-Z</option>
<option value="name_desc">Nom : Z-A</option>
</select>
</div>
</div>
{% if products|length > 0 %}
<div class="row" id="products-container">
{% for product in products %}
<div class="col-lg-3 col-md-4 col-sm-6 mb-4 product-item" data-price="{{ product.price }}" data-name="{{ product.name|lower }}" data-views="{{ product.viewCount ?? 0 }}" data-created="{{ product.publishedAt|date('Y-m-d') }}">
<div class="product-card">
<a href="{{ path('ui_product_show', { slug: product.slug }) }}">
<div class="product-image" style="background-image: url('{% if product.images is defined and product.images|length > 0 %}{{ asset(product.images[0]) }}{% else %}{{ asset('ui/img/product/p1.jpg') }}{% endif %}')"></div>
</a>
<div class="product-info">
<h6 class="product-title">
<a href="{{ path('ui_product_show', { slug: product.slug }) }}" class="text-decoration-none">
{{ product.name }}
</a>
</h6>
<div class="product-price">{{ product.price|number_format(2, '.', ' ') }}
HTG</div>
<div class="product-meta">
<span>
{% if product.averageRating %}
<div class="rating">
{% for i in 1..5 %}
<i class="fa fa-star{% if i <= product.averageRating %}{% else %}-o{% endif %}"></i>
{% endfor %}
</div>
{% endif %}
</span>
<span class="text-muted">{{ product.viewCount ?? 0 }}
vues</span>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="lnr lnr-package" style="font-size: 4rem; color: #ccc;"></i>
<h4 class="mt-3">Aucun produit disponible</h4>
<p class="text-muted">Cette boutique n'a pas encore de produits en vente.</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Tab Content: Sale avec Karma -->
<div id="shop-sale" class="tab-content shop-tab-content">
<div class="text-center py-5">
<i class="lnr lnr-tag" style="font-size: 4rem; color: #ccc;"></i>
<h4 class="mt-3">Aucune promotion actuellement</h4>
<p class="text-muted">Cette boutique n'a pas de promotions actives pour le moment.</p>
</div>
</div>
<!-- Tab Content: About avec Karma -->
<div id="shop-about" class="tab-content shop-tab-content">
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="lnr lnr-user me-2"></i>À propos de
{{ shop.name }}
</h5>
</div>
<div class="card-body">
{% if shop.description %}
<div class="card-text shop-description-content">{{ shop.description|raw }}</div>
{% else %}
<p class="text-muted">Aucune description disponible.</p>
{% endif %}
{% if shop.businessHours %}
<h6 class="mt-4">
<i class="lnr lnr-clock me-2"></i>Heures d'ouverture
</h6>
<p>{{ shop.businessHours }}</p>
{% endif %}
{% if shop.returnPolicy %}
<h6 class="mt-4">
<i class="lnr lnr-undo me-2"></i>Politique de retour
</h6>
<p>{{ shop.returnPolicy }}</p>
{% endif %}
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card shop-info-card">
<div class="card-header">
<h6 class="mb-0">
<i class="lnr lnr-chart me-2"></i>Statistiques de la boutique
</h6>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-6">
<div class="shop-stat-item">
<span class="stat-number h4 text-primary">{{ stats.activeProducts }}</span>
<span class="stat-label d-block">Produits actifs</span>
</div>
</div>
<div class="col-6">
<div class="shop-stat-item">
<span class="stat-number h4 text-info">{{ stats.shopViews }}</span>
<span class="stat-label d-block">Vues boutique</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Tab Content: Feedback avec Karma -->
<div id="shop-feedback" class="tab-content shop-tab-content">
<div class="row">
<div
class="col-lg-8">
<!-- Statistiques des avis -->
<div class="review-stats mb-4">
<div class="row">
<div class="col-md-4">
<div class="stat-card text-center">
<h3 class="stat-number">{{ shop.getAverageRating() }}</h3>
<div class="rating-stars">
{% for i in 1..5 %}
<i class="lnr lnr-star{% if i <= shop.getAverageRating() %}{% else %}-empty{% endif %}"></i>
{% endfor %}
</div>
<p class="stat-label">Note moyenne</p>
</div>
</div>
<div class="col-md-4">
<div class="stat-card text-center">
<h3 class="stat-number">{{ shop.getVisibleReviewsCount() }}</h3>
<p class="stat-label">Avis clients</p>
</div>
</div>
<div class="col-md-4">
<div class="stat-card text-center">
<h3 class="stat-number">{{ ((shop.getVisibleReviewsCount() / 100) * 100)|round }}%</h3>
<p class="stat-label">Satisfaction</p>
</div>
</div>
</div>
</div>
<!-- Liste des avis récents -->
<div class="reviews-preview">
<h5 class="mb-3">Avis récents</h5>
{% if shop.reviews|length > 0 %}
<div class="reviews-list">
{% for review in shop.reviews|slice(0, 5) %}
{% if review.isVisible %}
<div class="review-item">
<div class="review-header">
<div class="reviewer-info">
<div class="reviewer-avatar">
<i class="lnr lnr-user"></i>
</div>
<div class="reviewer-details">
<h6 class="reviewer-name">{{ review.user.userIdentifier }}</h6>
<div class="review-rating">
{% for i in 1..5 %}
<i class="lnr lnr-star{% if i <= review.rating %}{% else %}-empty{% endif %}"></i>
{% endfor %}
</div>
</div>
</div>
<div class="review-meta">
<span class="review-date">{{ review.createdAt|date('d/m/Y') }}</span>
{% if review.isVerified %}
<span class="verified-badge">
<i class="lnr lnr-checkmark-circle"></i>
Vérifié
</span>
{% endif %}
</div>
</div>
{% if review.comment %}
<div class="review-comment">
<p>{{ review.comment|length > 150 ? review.comment|slice(0, 150) ~ '...' : review.comment }}</p>
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
<div class="text-center mt-4">
<a href="{{ path('shop_reviews_index', {'slug': shop.slug}) }}" class="btn btn-primary">
<i class="lnr lnr-star me-1"></i>Voir tous les avis
</a>
</div>
{% else %}
<div class="text-center py-4">
<i class="lnr lnr-star" style="font-size: 3rem; color: #ccc;"></i>
<h5 class="mt-3">Aucun avis pour le moment</h5>
<p class="text-muted">Cette boutique n'a pas encore reçu d'avis clients.</p>
</div>
{% endif %}
</div>
</div>
<div
class="col-lg-4">
<!-- Lien vers la page complète des avis -->
<div class="review-actions-card">
<h5>Évaluer cette boutique</h5>
<p class="text-muted">Partagez votre expérience avec cette boutique.</p>
<a href="{{ path('shop_reviews_new', {'slug': shop.slug}) }}" class="btn btn-primary w-100">
<i class="lnr lnr-star me-1"></i>Laisser un avis
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Modal de signalement -->
<div class="modal fade" id="reportModal" tabindex="-1" aria-labelledby="reportModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="reportModalLabel">
<i class="lnr lnr-flag me-2"></i> Signaler cette boutique
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="reportForm">
<div class="mb-3">
<label for="reportReason" class="form-label">Motif du signalement *</label>
<select class="form-select" id="reportReason" required>
<option value="">Sélectionnez un motif</option>
<option value="fake_products">Produits contrefaits</option>
<option value="inappropriate_content">Contenu inapproprié</option>
<option value="spam">Spam ou publicité abusive</option>
<option value="fraud">Fraude ou escroquerie</option>
<option value="harassment">Harcèlement</option>
<option value="other">Autre</option>
</select>
</div>
<div class="mb-3">
<label for="reportDescription" class="form-label">Description détaillée *</label>
<textarea class="form-control" id="reportDescription" rows="4" placeholder="Décrivez le problème rencontré..." required></textarea>
</div>
<div class="mb-3">
<label for="reportEmail" class="form-label">Votre email (optionnel)</label>
<input type="email" class="form-control" id="reportEmail" placeholder="votre@email.com">
<div class="form-text">Nous pourrons vous contacter pour plus d'informations</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-danger" onclick="submitReport()">
<i class="lnr lnr-flag me-2"></i>Envoyer le signalement
</button>
</div>
</div>
</div>
</div>
<!-- Section des avis -->
<div class="container my-5">
<div class="row">
<div class="col-12">
<div class="section-title text-center">
<h2>Avis clients</h2>
<p>Découvrez ce que pensent nos clients de cette boutique</p>
</div>
</div>
</div>
<div
class="row">
<!-- Statistiques des avis -->
<div class="col-md-4">
<div class="review-stats-card">
<div class="stats-header">
<div class="overall-rating">
<span class="rating-number">{{ shop.reviews|length > 0 ? (shop.reviews|map(r => r.rating)|reduce((a, b) => a + b) / shop.reviews|length)|number_format(1) : '0.0' }}</span>
<div class="stars">
{% set avgRating = shop.reviews|length > 0 ? (shop.reviews|map(r => r.rating)|reduce((a, b) => a + b) / shop.reviews|length) : 0 %}
{% for i in 1..5 %}
{% if i <= avgRating %}
★
{% elseif i - avgRating < 1 %}
☆
{% else %}
☆
{% endif %}
{% endfor %}
</div>
</div>
<div class="rating-summary">
<p class="mb-1">{{ shop.reviews|length }}
avis</p>
<p class="text-muted small">Basé sur
{{ shop.reviews|filter(r => r.isVerified)|length }}
avis vérifiés</p>
</div>
</div>
<div class="rating-breakdown">
{% for rating in 5..1 %}
{% set count = shop.reviews|filter(r => r.rating == rating)|length %}
{% set percentage = shop.reviews|length > 0 ? (count / shop.reviews|length * 100) : 0 %}
<div class="rating-bar">
<span class="rating-label">{{ rating }}★</span>
<div class="rating-progress">
<div class="rating-fill" style="width: {{ percentage }}%"></div>
</div>
<span class="rating-count">{{ count }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Aperçu des avis -->
<div class="col-md-8">
<div class="reviews-preview">
{% if shop.reviews|length > 0 %}
{% for review in shop.reviews|slice(0, 3) %}
<div class="review-card-preview">
<div class="review-header">
<div class="review-user">
<div class="user-avatar">
{{ review.user.firstName|first|upper|default(review.user.email|first|upper) }}
</div>
<div class="user-info">
<h6>{{ review.user.firstName|default('Utilisateur') }}</h6>
<small>{{ review.getTimeAgo() }}</small>
</div>
</div>
<div class="review-rating">
<div class="stars">{{ review.getRatingStars() }}</div>
{% if review.isVerified %}
<span class="verified-badge">✓</span>
{% endif %}
</div>
</div>
<div class="review-content">
{{ review.comment|slice(0, 150) }}
{% if review.comment|length > 150 %}...
{% endif %}
</div>
</div>
{% endfor %}
<div class="text-center mt-3">
<a href="{{ path('shop_reviews_index', {'slug': shop.slug}) }}" class="btn btn-outline-primary">
Voir tous les avis ({{ shop.reviews|length }})
</a>
</div>
{% else %}
<div class="empty-reviews">
<i class="lnr lnr-star"></i>
<h4>Aucun avis pour le moment</h4>
<p>Soyez le premier à laisser un avis sur cette boutique !</p>
{% if app.user %}
<a href="{{ path('shop_reviews_new', {'slug': shop.slug}) }}" class="btn btn-primary">
<i class="lnr lnraccount_recently_viewed-star"></i>
Laisser un avis
</a>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script>
document.addEventListener('DOMContentLoaded', function () {
// Initialisation du carrousel
console.log('DOM Content Loaded - Initializing carousel...');
setTimeout(function() {
initCarousel();
}, 100);
// Gestion des onglets eBay-style avec Bootstrap
const tabLinks = document.querySelectorAll('.shop-tab-link');
const tabContents = document.querySelectorAll('.shop-tab-content');
tabLinks.forEach(link => {
link.addEventListener('click', function (e) {
e.preventDefault();
// Retirer la classe active de tous les liens et contenus
tabLinks.forEach(l => {
l.classList.remove('active');
l.setAttribute('aria-selected', 'false');
});
tabContents.forEach(c => c.classList.remove('active'));
// Ajouter la classe active au lien cliqué
this.classList.add('active');
this.setAttribute('aria-selected', 'true');
// Afficher le contenu correspondant
const targetTab = this.dataset.tab;
const targetContent = document.getElementById (`shop-${targetTab}`);
if (targetContent) {
targetContent.classList.add('active');
}
});
});
// Gestion du système de follow
const followButton = document.getElementById('follow-button');
if (followButton) {
followButton.addEventListener('click', function () {
const shopId = this.dataset.shopId;
const isFollowing = this.dataset.following === 'true';
// Désactiver le bouton pendant la requête
this.disabled = true;
const originalText = this.innerHTML;
this.innerHTML = '<i class="lnr lnr-spinner"></i> Loading...';
// Envoyer la requête
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'
}
}).then(response => response.json()).then(data => {
if (data.success) { // Mettre à jour l'interface
this.dataset.following = data.isFollowing;
if (data.isFollowing) {
this.classList.add('following');
this.innerHTML = '<i class="lnr lnr-checkmark"></i> Suivi';
} else {
this.classList.remove('following');
this.innerHTML = '<i class="lnr lnr-plus"></i> Suivre';
}
// Mettre à jour le compteur de followers
if (followersCount && data.followersCount !== undefined) {
followersCount.textContent = data.followersCount.toLocaleString();
}
// Afficher un message de confirmation
showNotification(data.message, 'success');
} else {
showNotification(data.message, 'error');
}
}).catch(error => {
console.error('Erreur:', error);
showNotification('Une erreur est survenue', 'error');
}). finally(() => {
this.disabled = false;
});
});
}
// Fonction pour afficher les notifications
function showNotification(message, type = 'info') { // Créer l'élément de notification
const notification = document.createElement('div');
notification.className = `alert alert-${
type === 'success' ? 'success' : type === 'error' ? 'danger' : 'info'
} alert-dismissible fade show position-fixed`;
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// Supprimer automatiquement après 5 secondes
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 5000);
}
// Fonctions pour les actions de boutique
window.shareShop = function () { // Vérifier si l'API Web Share est disponible et si on est sur mobile
if (navigator.share && /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
navigator.share({title: '{{ shop.name }}- Boutique sur MaketOu', text: 'Découvrez cette boutique et ses produits sur MaketOu', url: window.location.href}).then(() => {
console.log('Partage réussi');
showNotification('Lien partagé avec succès !', 'success');
}).catch((error) => {
console.log('Erreur lors du partage:', error);
// Fallback vers la copie
copyToClipboard();
});
} else { // Sur desktop ou si Web Share n'est pas disponible, copier directement
copyToClipboard();
}
};
// Fonction pour copier le lien dans le presse-papiers
function copyToClipboard() {
const shopUrl = window.location.href;
const shopName = '{{ shop.name }}';
const shareText = `Découvrez la boutique "${shopName}" sur MaketOu : ${shopUrl}`;
// Utiliser directement le fallback pour éviter les conflits
fallbackCopyToClipboard(shareText);
}
// Fallback pour copier le texte
function fallbackCopyToClipboard(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showNotification('Lien copié dans le presse-papiers !', 'success');
} catch (err) {
console.error('Impossible de copier:', err);
showNotification('Impossible de copier le lien', 'error');
}
document.body.removeChild(textArea);
}
window.contactShop = function () { // Ouvrir un modal de signalement
openReportModal();
};
window.saveShop = function () { // Sauvegarder la boutique dans les favoris
showNotification('Boutique sauvegardée dans vos favoris', 'success');
};
// Fonction pour ouvrir le modal de signalement
window.openReportModal = function () {
const modal = new bootstrap.Modal(document.getElementById('reportModal'));
modal.show();
};
// Fonction pour soumettre le signalement
window.submitReport = function () {
const reason = document.getElementById('reportReason').value;
const description = document.getElementById('reportDescription').value;
const email = document.getElementById('reportEmail').value;
if (! reason || ! description) {
showNotification('Veuillez remplir tous les champs obligatoires', 'error');
return;
}
// Simuler l'envoi du signalement
const reportData = {
shopId: {{ shop.id }},
reason: reason,
description: description,
email: email,
reportedAt: new Date().toISOString()
};
console.log('Signalement envoyé:', reportData);
// Fermer le modal
const modal = bootstrap.Modal.getInstance(document.getElementById('reportModal'));
modal.hide();
// Réinitialiser le formulaire
document.getElementById('reportForm').reset();
// Afficher la confirmation
showNotification('Signalement envoyé avec succès. Nous examinerons votre demande.', 'success');
};
// Gestion du carrousel eBay-style - Variables globales
let currentSlide = 0;
let carouselInterval = null;
let isPaused = false;
let slides = [];
let indicators = [];
let carouselInitialized = false;
let autoSlideDelay = 5000; // Délai par défaut : 5 secondes
let userInteractionTimeout = null; // Timeout pour reprendre après interaction utilisateur
let isUserInteracting = false; // Flag pour savoir si l'utilisateur interagit
let pageVisibilityHidden = false; // Flag pour savoir si la page est cachée
// Définir les fonctions globales immédiatement pour qu'elles soient disponibles pour onclick
window.nextSlide = function() {
if (slides.length === 0) {
console.warn('nextSlide: No slides available, initializing...');
initCarousel();
return;
}
// Marquer comme interaction utilisateur
handleUserInteraction();
currentSlide = (currentSlide + 1) % slides.length;
console.log('nextSlide: Moving to slide', currentSlide);
updateCarousel();
};
window.previousSlide = function() {
if (slides.length === 0) {
console.warn('previousSlide: No slides available, initializing...');
initCarousel();
return;
}
// Marquer comme interaction utilisateur
handleUserInteraction();
currentSlide = (currentSlide - 1 + slides.length) % slides.length;
console.log('previousSlide: Moving to slide', currentSlide);
updateCarousel();
};
window.goToSlide = function(index) {
if (slides.length === 0) {
console.warn('goToSlide: No slides available, initializing...');
initCarousel();
// Attendre que l'initialisation soit terminée
setTimeout(() => {
if (index >= 0 && index < slides.length) {
currentSlide = index;
updateCarousel();
// Réinitialiser le carrousel automatique
handleUserInteraction();
}
}, 150);
return;
}
if (index < 0 || index >= slides.length) {
console.warn('goToSlide: Invalid index', index, '. Valid range: 0 to', slides.length - 1);
return;
}
// Marquer comme interaction utilisateur
handleUserInteraction();
currentSlide = index;
console.log('goToSlide: Moving to slide', currentSlide, 'of', slides.length);
updateCarousel();
};
window.toggleCarousel = function() {
if (slides.length === 0) {
console.warn('toggleCarousel: No slides available, initializing...');
initCarousel();
return;
}
if (isPaused) {
resumeCarousel();
} else {
pauseCarousel();
}
};
function initCarousel() {
if (carouselInitialized) {
console.log('Carousel already initialized');
return;
}
// Réinitialiser les variables
currentSlide = 0;
isPaused = false;
// Récupérer les slides et indicateurs à nouveau
const carouselContainer = document.querySelector('.shop-carousel-ebay');
if (!carouselContainer) {
console.log('Carousel container not found');
return;
}
const allSlides = carouselContainer.querySelectorAll('.carousel-slide');
const allIndicators = carouselContainer.querySelectorAll('.carousel-indicator-ebay');
if (allSlides.length === 0) {
// Masquer les contrôles si aucune image
const controls = carouselContainer.querySelector('.carousel-controls-ebay');
const indicatorsContainer = carouselContainer.querySelector('.carousel-indicators-ebay');
if (controls) controls.style.display = 'none';
if (indicatorsContainer) indicatorsContainer.style.display = 'none';
return;
}
// Si une seule image, masquer les contrôles
if (allSlides.length <= 1) {
const controls = carouselContainer.querySelector('.carousel-controls-ebay');
const indicatorsContainer = carouselContainer.querySelector('.carousel-indicators-ebay');
if (controls) controls.style.display = 'none';
if (indicatorsContainer) indicatorsContainer.style.display = 'none';
// S'assurer que la slide unique est active
allSlides[0].classList.add('active');
return;
}
// Afficher les contrôles si plusieurs images
const controls = carouselContainer.querySelector('.carousel-controls-ebay');
const indicatorsContainer = carouselContainer.querySelector('.carousel-indicators-ebay');
if (controls) controls.style.display = 'flex';
if (indicatorsContainer) indicatorsContainer.style.display = 'flex';
// Mettre à jour les références globales - convertir NodeList en Array
slides = Array.from(allSlides);
indicators = Array.from(allIndicators);
console.log('initCarousel: Found', slides.length, 'slides and', indicators.length, 'indicators');
// S'assurer que la première slide est active immédiatement
currentSlide = 0;
slides.forEach((slide, index) => {
if (index === 0) {
slide.classList.add('active');
slide.style.opacity = '1';
slide.style.zIndex = '2';
} else {
slide.classList.remove('active');
slide.style.opacity = '0';
slide.style.zIndex = '1';
}
});
if (indicators.length > 0) {
indicators.forEach((indicator, index) => {
if (index === 0) {
indicator.classList.add('active');
} else {
indicator.classList.remove('active');
}
});
}
// Démarrer le carrousel automatique immédiatement
console.log('Initializing carousel - starting automatic rotation...');
isPaused = false; // S'assurer que le carrousel n'est pas en pause
startCarousel();
// Gérer les événements de survol (une seule fois)
carouselContainer.addEventListener('mouseenter', function() {
if (!isPaused) {
pauseCarousel();
}
});
carouselContainer.addEventListener('mouseleave', function() {
// Ne reprendre que si l'utilisateur n'a pas mis en pause manuellement
if (!isPaused && !isUserInteracting && !pageVisibilityHidden) {
startCarousel();
}
});
// Gérer la visibilité de la page (pause quand la page est cachée)
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
pageVisibilityHidden = true;
if (!isPaused) {
pauseCarousel();
}
} else {
pageVisibilityHidden = false;
// Reprendre seulement si l'utilisateur n'a pas mis en pause manuellement
if (!isPaused && !isUserInteracting) {
startCarousel();
}
}
});
carouselInitialized = true;
console.log('Carousel initialized with', slides.length, 'slides');
}
function startCarousel() {
// Nettoyer l'intervalle existant
clearInterval(carouselInterval);
// Ne pas démarrer si la page est cachée ou si l'utilisateur interagit
if (pageVisibilityHidden || isUserInteracting) {
return;
}
if (slides.length <= 1) {
console.log('Not enough slides to start carousel');
return;
}
// S'assurer que isPaused est false pour démarrer
isPaused = false;
const pauseBtn = document.getElementById('pauseBtn');
const pauseIcon = document.getElementById('pauseIcon');
if (pauseBtn) {
pauseBtn.classList.remove('playing');
if (pauseIcon) {
pauseIcon.textContent = '⏸';
pauseIcon.className = 'pause-icon-content';
pauseIcon.style.display = 'inline-block';
pauseIcon.style.color = 'white';
pauseIcon.style.fontSize = '22px';
pauseIcon.style.visibility = 'visible';
pauseIcon.style.opacity = '1';
}
}
console.log('Starting carousel automatically...');
carouselInterval = setInterval(() => {
// Vérifier les conditions avant de changer de slide
if (!isPaused && !isUserInteracting && !pageVisibilityHidden && slides.length > 0) {
// Utiliser la fonction interne nextSlide pour éviter de déclencher handleUserInteraction
currentSlide = (currentSlide + 1) % slides.length;
console.log('Auto slide:', currentSlide);
updateCarousel();
}
}, autoSlideDelay);
}
// Fonction pour gérer les interactions utilisateur
function handleUserInteraction() {
isUserInteracting = true;
// Nettoyer le timeout précédent s'il existe
if (userInteractionTimeout) {
clearTimeout(userInteractionTimeout);
}
// Reprendre le carrousel automatique après 3 secondes d'inactivité
userInteractionTimeout = setTimeout(() => {
isUserInteracting = false;
// Reprendre seulement si l'utilisateur n'a pas mis en pause manuellement
if (!isPaused && !pageVisibilityHidden) {
startCarousel();
}
}, 3000);
}
function pauseCarousel() {
isPaused = true;
clearInterval(carouselInterval);
// Nettoyer le timeout d'interaction utilisateur
if (userInteractionTimeout) {
clearTimeout(userInteractionTimeout);
userInteractionTimeout = null;
}
const pauseBtn = document.getElementById('pauseBtn');
const pauseIcon = document.getElementById('pauseIcon');
if (pauseBtn) {
pauseBtn.classList.add('playing');
if (pauseIcon) {
pauseIcon.textContent = '▶';
pauseIcon.className = 'pause-icon-content';
pauseIcon.style.display = 'inline-block';
pauseIcon.style.color = 'white';
pauseIcon.style.fontSize = '22px';
pauseIcon.style.visibility = 'visible';
pauseIcon.style.opacity = '1';
}
}
}
function resumeCarousel() {
isPaused = false;
isUserInteracting = false; // Réinitialiser le flag d'interaction
// Nettoyer le timeout d'interaction utilisateur
if (userInteractionTimeout) {
clearTimeout(userInteractionTimeout);
userInteractionTimeout = null;
}
const pauseBtn = document.getElementById('pauseBtn');
const pauseIcon = document.getElementById('pauseIcon');
if (pauseBtn) {
pauseBtn.classList.remove('playing');
if (pauseIcon) {
pauseIcon.textContent = '⏸';
pauseIcon.className = 'pause-icon-content';
pauseIcon.style.display = 'inline-block';
pauseIcon.style.color = 'white';
pauseIcon.style.fontSize = '22px';
pauseIcon.style.visibility = 'visible';
pauseIcon.style.opacity = '1';
}
}
// Ne reprendre que si la page est visible
if (!pageVisibilityHidden) {
startCarousel();
}
}
function toggleCarousel() {
const pauseBtn = document.getElementById('pauseBtn');
const pauseIcon = document.getElementById('pauseIcon');
if (isPaused) {
resumeCarousel();
} else {
pauseCarousel();
}
}
function nextSlide() {
if (slides.length === 0) {
console.warn('No slides available');
return;
}
currentSlide = (currentSlide + 1) % slides.length;
console.log('Next slide:', currentSlide);
updateCarousel();
}
function previousSlide() {
if (slides.length === 0) {
console.warn('No slides available');
return;
}
currentSlide = (currentSlide - 1 + slides.length) % slides.length;
console.log('Previous slide:', currentSlide);
updateCarousel();
}
// Fonction interne goToSlide (utilisée par window.goToSlide)
function goToSlide(index) {
if (slides.length === 0) {
console.warn('goToSlide: No slides available');
return;
}
if (index < 0 || index >= slides.length) {
console.warn('goToSlide: Invalid index', index, '. Valid range: 0 to', slides.length - 1);
return;
}
// Marquer comme interaction utilisateur
handleUserInteraction();
currentSlide = index;
console.log('goToSlide: Moving to slide', currentSlide, 'of', slides.length);
updateCarousel();
}
function updateCarousel() {
if (slides.length === 0) {
console.warn('No slides to update');
return;
}
console.log('updateCarousel: Updating to slide', currentSlide, 'of', slides.length, 'slides');
console.log('updateCarousel: Total indicators:', indicators.length);
// Masquer toutes les slides et réinitialiser leurs styles
slides.forEach((slide, index) => {
slide.classList.remove('active');
if (index === currentSlide) {
slide.classList.add('active');
slide.style.opacity = '1';
slide.style.zIndex = '2';
} else {
slide.style.opacity = '0';
slide.style.zIndex = '1';
}
});
// Mettre à jour tous les indicateurs - FORCER la mise à jour en deux passes distinctes
if (indicators.length > 0) {
console.log('updateCarousel: Updating', indicators.length, 'indicators, currentSlide:', currentSlide);
// Première passe : retirer active de TOUS les indicateurs
indicators.forEach((indicator) => {
indicator.classList.remove('active');
});
// Deuxième passe : ajouter active à l'indicateur correspondant à currentSlide
if (currentSlide >= 0 && currentSlide < indicators.length && indicators[currentSlide]) {
const activeIndicator = indicators[currentSlide];
activeIndicator.classList.add('active');
console.log('updateCarousel: Activated indicator', currentSlide);
// Vérification et forcer si nécessaire
if (!activeIndicator.classList.contains('active')) {
console.warn('updateCarousel: Class not added, forcing with setAttribute');
activeIndicator.setAttribute('class', activeIndicator.className + ' active');
}
} else {
console.error('updateCarousel: Invalid currentSlide', currentSlide, 'or indicator not found. Total indicators:', indicators.length);
}
} else {
console.warn('updateCarousel: No indicators available');
}
// Afficher la slide active
if (slides[currentSlide]) {
slides[currentSlide].classList.add('active');
slides[currentSlide].style.opacity = '1';
slides[currentSlide].style.zIndex = '2';
console.log('updateCarousel: Showing slide', currentSlide);
} else {
console.error('updateCarousel: Slide', currentSlide, 'not found. Total slides:', slides.length);
}
}
// Les fonctions globales sont définies au début du script
// Gestion du tri des produits via API Symfony
console.log('=== INITIALIZING PRODUCT SORTING ===');
console.log('Shop ID:', {{ shop.id }});
// Fonction de tri via AJAX - RENDUE GLOBALE
window.sortProducts = function (sortValue) {
console.log('=== SORT PRODUCTS CALLED ===');
console.log('Sort value:', sortValue);
const productsContainer = document.getElementById('products-container');
console.log('Products container:', productsContainer);
if (! productsContainer) {
console.error('Products container not found!');
return;
}
if (! sortValue) {
console.log('Resetting to original order');
// Recharger la page pour revenir à l'ordre original
window.location.reload();
return;
}
console.log('Showing loading spinner...');
// Afficher un indicateur de chargement
productsContainer.innerHTML = '<div class="col-12 text-center py-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Chargement...</span></div><p class="mt-3">Tri des produits en cours...</p></div>';
// Récupérer l'ID de la boutique
const shopId = {{ shop.id }};
console.log('Shop ID for request:', shopId);
const url = "{{ path('ui_api_shop_products_sort', {id: '__SHOP_ID__'}) }}".replace('__SHOP_ID__', shopId);
console.log('Request URL:', url);
const requestData = {
sortBy: sortValue
};
console.log('Request data:', requestData);
// Envoyer la requête AJAX
console.log('Sending AJAX request...');
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(requestData)
}).then(response => {
console.log('Response received:', response);
console.log('Response status:', response.status);
return response.json();
}).then(data => {
console.log('Sort response data:', data);
if (data.success) {
console.log('Sort successful, updating content...');
// Mettre à jour le contenu avec le HTML retourné
productsContainer.innerHTML = data.html;
// Mettre à jour le compteur de produits
const productCount = document.querySelector('.product-count');
if (productCount) {
productCount.textContent = data.count;
}
console.log('Products sorted successfully!');
} else {
console.error('Sort failed:', data.message);
showNotification('Erreur lors du tri: ' + data.message, 'error');
// Recharger la page en cas d'erreur
window.location.reload();
}
}).catch(error => {
console.error('Sort request failed:', error);
showNotification('Erreur de connexion lors du tri', 'error');
// Recharger la page en cas d'erreur
window.location.reload();
});
};
// Attendre que le DOM soit chargé
console.log('Setting up event listeners...');
setTimeout(() => {
const sortSelect = document.getElementById('product-sort');
console.log('Sort select element:', sortSelect);
if (sortSelect) {
console.log('Adding change event listener...');
sortSelect.addEventListener('change', function () {
console.log('=== SELECT CHANGE EVENT TRIGGERED ===');
console.log('Selected value:', this.value);
console.log('Calling sortProducts...');
window.sortProducts(this.value);
});
console.log('Event listener added successfully');
} else {
console.error('Sort select element not found!');
console.log('Available elements with ID containing "sort":', document.querySelectorAll('[id*="sort"]'));
console.log('Available elements with class containing "form-select":', document.querySelectorAll('.form-select'));
}
}, 100);
// Gestion du bouton "Suivre"
const followBtn = document.querySelector('.btn-shop');
if (followBtn) {
followBtn.addEventListener('click', function () {
if (this.innerHTML.includes('Suivre')) {
this.innerHTML = '<i class="lnr lnr-heart me-1"></i> Suivi';
this.classList.remove('btn-shop');
this.classList.add('btn-success');
} else {
this.innerHTML = '<i class="lnr lnr-heart me-1"></i> Suivre';
this.classList.remove('btn-success');
this.classList.add('btn-shop');
}
});
}
});
</script>
{% endblock %}