templates/front/_product_card.html.twig line 1

Open in your IDE?
  1. {# Initialisation de la variable produit utilisée par Vue (fallback = 'prod') #}
  2. {% set var = var|default('prod') %}
  3. {# Carte produit principale avec gestion du hover (Vue events) #}
  4. <div class="card-product" @mouseenter="activateHover({{ var }})" @mouseleave="resetHover({{ var }})">
  5.     {# Zone média produit : image, zoom, tailles, couleurs #}
  6.     <div class="card-product-media"
  7.          :class="{ 'is-loading': !{{ var }}.imageLoaded }"
  8.          :style="{ '--img-aspect': {{ var }}.aspectRatio || MIXED_ASPECT_RATIO }">
  9.         {# Image principale du produit avec gestion erreur et redirection #}
  10.         <img :src="getCardImage({{ var }})"
  11.              :alt="{{ var }}.name"
  12.              :class="{ 'is-loaded': !!{{ var }}.imageLoaded }"
  13.              loading="lazy"
  14.              decoding="async"
  15.              @load="markImageLoaded({{ var }})"
  16.              @error="handleImageError({{ var }}, false)"
  17.              @click.prevent="redirectToProductDeclination({{ var }}.idProduit || {{ var }}.productId || {{ var }}.id, {{ var }}.name, {{ var }}.id)"
  18.              style="cursor:pointer;">
  19.         {# Bouton overlay pour ouverture du zoom produit #}
  20.         <button class="zoom-overlay" type="button" @click.prevent="openZoom(prod)">
  21.             <i class="fa fa-arrows-alt" aria-hidden="true"></i>
  22.             <span class="zoom-fallback" aria-hidden="true">&#8645;</span>
  23.         </button>
  24.         {# Favoris #}
  25.         <button type="button"
  26.                 class="wishlist-overlay js-wishlist-toggle"
  27.                 :class="{ 'is-active': window.__WISHLIST_IDS__ && window.__WISHLIST_IDS__.has({{ var }}.idProduit || {{ var }}.productId || {{ var }}.id) }"
  28.                 :data-product-id="{{ var }}.idProduit || {{ var }}.productId || {{ var }}.id"
  29.                 :data-product-name="{{ var }}.name"
  30.                 :data-declination-id="{{ var }}.id || ({{ var }}.activeSizes.length ? {{ var }}.activeSizes[0].id : 0)"
  31.                 aria-label="Ajouter aux favoris">
  32.             <i class="fa fa-heart" aria-hidden="true"></i>
  33.             <span class="wishlist-fallback" aria-hidden="true">♥</span>
  34.         </button>
  35.         {# Titre du produit affiché sur l’image #}
  36.         {# Overlay des tailles disponibles avec ajout rapide ou notification #}
  37.         <div class="size-overlay" v-if="{{ var }}.activeSizes.length">
  38.             <div class="size-pill-wrapper" v-for="size in {{ var }}.activeSizes" :key="size.id">
  39.                 {# Bouton taille (désactivé si stock nul) #}
  40.                 <button type="button"
  41.                         class="size-pill"
  42.                         :disabled="size.qty <= 0"
  43.                         @click.prevent="quickAddSize({{ var }}, size)"
  44.                         :title="size.qty <= 0 ? 'Prévenez-moi quand cette taille revient' : size.label">
  45.                     ${size.label}
  46.                 </button>
  47.                 {# Icône notification si taille en rupture #}
  48.                 <span class="size-alert" v-if="size.qty <= 0" title="Prévenez-moi">
  49.                     <i class="fa fa-bell"></i>
  50.                 </span>
  51.             </div>
  52.             {# Icône globale notification si produit hors stock #}
  53.             <button v-if="!{{ var }}.stock"
  54.                     class="notify-icon"
  55.                     @click.prevent="openNotifyModal({{ var }})"
  56.                     title="Préviens-moi lorsqu’il sera disponible">
  57.                 &#128276;
  58.             </button>
  59.         </div>
  60.         {# Ligne des couleurs disponibles #}
  61.         <div class="color-row">
  62.             {# Affichage des 3 premières couleurs sous forme de pastilles #}
  63.             <div class="color-swatches">
  64.                 <button v-for="(color, index) in ({{ var }}.colorSwatches || []).slice(0,3)"
  65.                         type="button"
  66.                         :key="color.id || index"
  67.                         class="color-swatch"
  68.                         :class="{'active': {{ var }}.activeColor && {{ var }}.activeColor.id === color.id}"
  69.                         :style="getColorStyle(color.code)"
  70.                         :title="color.name"
  71.                         @click.prevent="onColorClick({{ var }}, color)">
  72.                 </button>
  73.             </div>
  74.             {# Bouton indiquant le nombre de couleurs supplémentaires #}
  75.             <button v-if="({{ var }}.colorSwatches || []).length > 3"
  76.                     class="more-colors"
  77.                     type="button"
  78.                     @click="openProduct({{ var }}.idProduit || {{ var }}.productId || {{ var }}.id, {{ var }}.name)"
  79.                     v-text="'+' + (({{ var }}.colorSwatches || []).length - 3) + ' couleurs'">
  80.             </button>
  81.             <div class="product-card-rating product-card-rating--inline" v-if="Number({{ var }}.ratingCount || 0) > 0">
  82.                 <span class="product-card-rating-stars" aria-hidden="true">
  83.                     <span
  84.                         v-for="star in 5"
  85.                         :key="star"
  86.                         class="product-card-rating-star"
  87.                         :class="{
  88.                             'is-full': Number({{ var }}.ratingScore || 0) >= star,
  89.                             'is-half': Number({{ var }}.ratingScore || 0) >= (star - 0.5) && Number({{ var }}.ratingScore || 0) < star
  90.                         }"
  91.                     >&#9733;</span>
  92.                 </span>
  93.                 <span class="product-card-rating-value">${ Number({{ var }}.ratingScore || 0).toFixed(1) }</span>
  94.             </div>
  95.         </div>
  96.     </div>
  97.     {# Corps de la carte produit : prix et promotion #}
  98.     <div class="card-product-body">
  99.         <div class="card-product-head">
  100.             <h3 class="home-card-title product-name" v-text="{{ var }}.name"></h3>
  101.         </div>
  102.         {# Bloc prix principal #}
  103.         <div class="product-price">
  104.             <div class="price-current">
  105.                 {# Prix affiché (promo ou normal) #}
  106.                 <span class="price-value">
  107.                     ${ formatPrice({{ var }}.promo && {{ var }}.promo.isValid ? getDiscountedPrice({{ var }}) : {{ var }}.priceTTC) } TND
  108.                 </span>
  109.                 {# Détails promotion : ancien prix + badge remise #}
  110.                 <span class="price-meta" v-if="{{ var }}.promo && {{ var }}.promo.isValid">
  111.                     <span class="original-price">${ formatPrice({{ var }}.priceTTC) } TND</span>
  112.                     <span class="discount-flag" v-text="getPromoLabel({{ var }})"></span>
  113.                 </span>
  114.             </div>
  115.         </div>
  116.     </div>
  117. </div>