<div class="avis-widget" {% if widget.bgcolor is defined and widget.bgcolor != null %} style="background-color: {{ widget.bgcolor }};" {% endif %}>
<style>
.avis-widget {
margin: auto;
}
.avis-cards-container {
display: flex;
gap: 30px;
justify-content: center;
}
.avis-author {
font-size: 18px;
font-weight: 700;
color: #4a6c82;
}
.avis-ville,
.avis-activite,
.avis-date {
font-size: 14px;
margin-top: 4px;
}
.avis-rating {
margin: 10px 0;
}
.star {
color: #bbdefb;
font-size: 18px;
}
.star.filled {
color: #4a6c82;
}
.avis-comment {
font-size: 15px;
margin-top: 14px;
line-height: 1.6;
font-style: italic;
}
.avis-reponse {
margin-top: 16px;
padding: 14px;
background: #e3f2fd;
border-left: 4px solid #4a6c82;
border-radius: 8px;
}
.avis-reponse-titre {
font-weight: 600;
font-size: 14px;
margin-bottom: 6px;
color: #4a6c82;
}
.carousel-item.active .avis-card {
animation: bounceUpDown 0.7s ease forwards;
}
.carousel-item.active .avis-card:nth-child(2) {
animation-delay: 0.2s;
}
.carousel-item.active .avis-card:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounceUpDown {
0% {
opacity: 0;
transform: translateY(30px);
}
50% {
opacity: 1;
transform: translateY(-8px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@media(max-width: 992px) {
.avis-cards-container {
flex-direction: column;
align-items: center;
}
.avis-card {
max-width: 100%;
}
}
.carousel-control-prev,
.carousel-control-next {
position: absolute;
right: 10px; /* les déplace complètement à droite */
top: auto; /* désactive la position verticale par défaut */
bottom: auto;
transform: none; /* annule le centrage vertical par défaut */
width: 40px;
height: 40px;
margin: 5px 0; /* espace entre les flèches */
}
.carousel-control-prev {
top: 50px; /* flèche du haut */
}
.carousel-control-next {
top: 110px; /* flèche du bas */
}
.carousel-control-prev-icon,
.carousel-control-next-icon {
border-radius: 100%
}
button {
background: transparent !important;
}
.carousel-item {
transition: none; /* on gère la transition via JS */
display: flex;
justify-content: start;
}
.avis-card {
opacity: 0;
transform: translateY(30px); /* carte initialement en bas */
transition: transform 0.50s ease, opacity 0.50s ease;
}
.carousel-control-prev-icon {
background-image: url("data:image/svg+xml;charset=utf-8, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3E%3Cpath d='M1 8a.5.5 0 0 1 .5-.5h10.793L9.146 4.354a.5.5 0 1 1 .708-.708l4 4a.5.5 0 0 1 0 0.708l-4 4a.5.5 0 1 1-.708-.708L12.293 8.5H1.5A.5.5 0 0 1 1 8z'/%3E%3C/svg%3E") !important;
transform: rotate(-90deg);
}
.carousel-control-next-icon {
background-image: url("data:image/svg+xml;charset=utf-8, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3E%3Cpath d='M1 8a.5.5 0 0 1 .5-.5h10.793L9.146 4.354a.5.5 0 1 1 .708-.708l4 4a.5.5 0 0 1 0 0.708l-4 4a.5.5 0 1 1-.708-.708L12.293 8.5H1.5A.5.5 0 0 1 1 8z'/%3E%3C/svg%3E") !important;
transform: rotate(90deg);
}
.content-avis {
min-height: 100%;
justify-content: space-evenly;
display: flex;
flex-direction: column;
}
</style>
<div class="container-fluid">
<div class="row">
{% if widget.bgimage %}
<div class="col-lg-5 order-lg-1 order-2 d-flex justify-content-center align-items-center image-avis">
<div class="avis-image">
<img src="{{ asset(widget.bgimage) }}" alt="Image avis" loading="lazy">
</div>
{% endif %}
</div>
<div class="col-lg-7 order-lg-2 order-1 content-avis">
<div class="avis-header">
<h2>
<span>{{ 'label.item_avis'|trans([],'textes') }}</span/>{{ 'label.avis_client'|trans([],'textes') }}</h3>
</div>
{% set avis_chunks = avis|batch(1) %}
<div>
<div id="avis-carousel-{{ widget.id|default('1') }}" class="carousel slide avis-carousel" data-bs-ride="carousel">
<div class="carousel-inner">
{% for chunk in avis_chunks %}
<div class="carousel-item {% if loop.first %}active{% endif %}">
<div class="avis-cards-container">
{% for response in chunk %}
<div class="avis-card">
<strong class="avis-author">
{{ response.data.nom|default('') }}
{{ response.data.prenom|default('') }}
</strong>
{% if response.data.ville is defined and response.data.ville %}
<div class="avis-ville">
De :
{{ response.data.ville }}
</div>
{% endif %}
{% if response.data.activite is defined and response.data.activite %}
<div class="avis-activite">
{{ response.data.activite }}
</div>
{% endif %}
{% if response.data.note is defined %}
<div class="avis-rating">
{% for i in 1..5 %}
<span class="star {{ i <= response.data.note ? 'filled' : '' }}">★</span>
{% endfor %}
</div>
{% endif %}
{% if response.data.commentaire is defined and response.data.commentaire %}
<p class="avis-comment">
“{{ response.data.commentaire|nl2br }}”
</p>
{% endif %}
{% if response.reponse is defined and response.reponse %}
<div class="avis-reponse">
<p class="avis-reponse-titre">
Réponse de l'équipe :
</p>
<p>{{ response.reponse|nl2br }}</p>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#avis-carousel-{{ widget.id|default('1') }}" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#avis-carousel-{{ widget.id|default('1') }}" data-bs-slide="next">
<span class="carousel-control-next-icon"></span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function () {
const carousel = document.getElementById('avis-carousel- {{ widget.id|default('1') }}');
if (! carousel)
return;
carousel.addEventListener('slide.bs.carousel', function () {
carousel.querySelectorAll('.avis-card').forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(25px)';
});
});
})();
</script>