{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
.image-wrapper {
width: 206px;
height: 208px;
position: relative;
overflow: hidden;
}
.product-thumb {
width: 100%;
height: 100%;
object-fit: cover; /* fill without distortion */
display: block;
}
.custom-separator {
position: relative;
margin: 30px 0;
}
/* La ligne horizontale */
.custom-separator::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: linear-gradient(
to right,
transparent,
#003B7E,
#003B7E,
#003B7E
);
}
/* Le titre */
.custom-separator h3 {
display: inline-block;
background-color: #003B7E;
color: #fff;
padding: 8px 25px;
border-radius: 0 25px 0 25px;
margin-bottom: 0;
font-size: 1.2rem;
font-weight: bold;
position: relative;
z-index: 1; /* reste au-dessus de la ligne */
}
/* État de base masqué */
.product-card-animated {
opacity: 0;
transition: all 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
will-change: transform, opacity;
}
/* Départ à gauche */
.fade-left {
transform: translateX(-80px);
}
/* Départ à droite */
.fade-right {
transform: translateX(80px);
}
/* État quand la carte est dans l'écran (FADE IN) */
.product-card-animated.is-visible {
opacity: 1;
transform: translateX(0);
}
</style>
{% endblock %}
{% block title %}Liste produit{% endblock %}
{% block body %}
<header>
{{ include('@FrontView/menus.html.twig') }} {##}
</header>
<main class="front-office-page front-office-catalog">
<div class="container"> <!-- <div class="py-3 px-5"> -->
<div class="row py-1">
<div class="col-md-2">
{{ include('@FrontView/menusgeneral.html.twig') }}
</div>
<div class="col-md-10">
{# <h1>liste de tous les produits </h1>
{% if is_granted('ROLE_ADMIN') %}
vous êtes administrateur
{% endif %} #}
{# <div class="row">
{% for product in products %}
<div class="col-xxl-2 col-xl-3 col-lg-4 col-md-4 col-sm-6 col-12 mt-2">
<div class="card bg-custom-product" >
<div class="image-content">
<div class="image-wrapper">
<img src="{{ asset('img/logo-rapeto-img.png') }}" class="logo">
<a href="{{ path('app_product_show', {'id': product.id}) }}" ><img src="{{ asset('uploads/products/'~product.picture) }}" class="imgproduct" alt="..."></a>
</div>
</div>
<div class="card-body">
<div class="row"><a href="#" class="title-product "> <a href="{{ path('app_product_show', {'id': product.id}) }}" >{{ product.title }} ({{ product.statetypeLabels|join(' / ') }}) </a>
<span class="fw-light description-product">{{ product.desctription|slice(0,50) }}...</span></div>
<div class="row mt-2">
<div class="col-md-10 col-6 col-sm-12">
{% if is_granted('ROLE_COM') %}
<span class="marge-price">{{ product.priceProducts[0].priceCustomer|number_format(2, '.', ' ') }}</span>
{% elseif is_granted('ROLE_CUSTOMER')%}
<span class="marge-price">{{ product.priceProducts[0].priceCustomer|number_format(2, '.', ' ') }} </span>
{% else %}
<span class="marge-price">{{ product.priceProducts[0].priceCustomer|number_format(2, '.', ' ') }}</span>
{% endif %}
<sup class="exposant"> {{ product.fkdevise.title }}</sup>
</div>
<div class="col-md-2 product-price-position col-6 col-sm-12">
<a href="{{ path('app_product_panier', {'product': product.id}) }}" class="btn btn-warning">
<i class="bi bi-cart-plus-fill px-1 icon-color-nav"></i>
</a>
</div>
</div>
<span class="per-tarif text-info">{{ product.fkcategorie.title }} |
{{ product.statetypeLabels|join(' / ') }}
</span>
{% if temoin is defined and product.id == temoin %}
<p>
<span class="per-tarif text-danger">{{ message }} </span>
</p>
{% endif %}
</div>
</div>
</div>
{% else %}
Aucun produit.
{% endfor %}
</div> #}
{# New design #}
{# <div class="row g-3">
{% for product in products %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% else %}
<p class="text-center">Aucun produit.</p>
{% endfor %}
</div> #}
{# ================= Featured ================= 🔥 #}
{% if featuredProducts|default([]) is not empty %}
<div class="custom-separator">
<h3>Produits phares</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in featuredProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{# ================= Interest ================= 🎯 #}
{% if interestProducts|default([]) is not empty %}
<div class="custom-separator">
<h3>Produits qui peuvent vous intéresser</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in interestProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{# ================= Cheapest ================= 💰 #}
{% if cheapestProducts|default([]) is not empty %}
<div class="custom-separator">
<h3>Produits les moins chers</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in cheapestProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{# ================= Top Ranked ================= ⭐ #}
{% if recentProducts|default([]) is not empty %}
<div class="custom-separator">
<h3>Les nouveaux produits</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in recentProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{% if topOrderedProducts|default([]) is not empty %}
<div class="custom-separator">
<h3>Les mieux notés</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in topOrderedProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{# ================= Rental ================= 🏠 #}
{% if false %}
<div class="custom-separator">
<h3>Produits à louer</h3>
</div>
<div class="row g-3 mb-5 product-card-animated">
{% for product in recentProducts %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% endif %}
{# ================= Searching products ================= #}
{% if products is defined %}
{% if products is not empty %}
<div class="row g-3 mb-5">
{% for product in products %}
{{ include('@FrontView/product/partials/_product_card.html.twig', { product: product }) }}
{% endfor %}
</div>
{% else %}
<div class="text-center my-5">
<i class="bi bi-box-seam fs-1 text-muted"></i>
<p class="mt-3 fs-5 fw-semibold">
Aucun produit ne correspond à votre recherche
</p>
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
</main>
{{ include('@FrontView/footer.html.twig') }}
{% block javascripts %}
{{ parent() }}
<script>
document.addEventListener('DOMContentLoaded', function() {
const cards = document.querySelectorAll('.product-card-animated');
// Attribution des directions (alternance)
cards.forEach((card, index) => {
if (index % 2 === 0) {
card.classList.add('fade-left');
} else {
card.classList.add('fade-right');
}
});
const observerOptions = {
threshold: 0.1, // Déclenche dès que 10% est visible
rootMargin: "-20px 0px -20px 0px" // Petit buffer pour éviter les clignotements
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Entre dans l'écran : FADE IN
entry.target.classList.add('is-visible');
} else {
// Sort de l'écran : FADE OUT (permet de rejouer au prochain passage)
entry.target.classList.remove('is-visible');
}
});
}, observerOptions);
cards.forEach(card => observer.observe(card));
});
</script>
{% endblock %}
{% endblock %}