templates/Parent/DetailsSejour.html.twig line 1

Open in your IDE?
  1. {% extends "Parent/LayoutParent.html.twig" %} {% block LinksCss %}
  2. {{ parent() }}
  3. <script src="https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js" defer></script>
  4. <link rel="stylesheet" href="{{ '/css/Parent/css/premiercnx.css' }}" />
  5. <link href="{{ asset('css/Parent/css/detailsejour.css') }}" type="text/css" rel="stylesheet" />
  6. <link rel="stylesheet" href="{{ '/css/Accompagnateur/imgzoom.css' }}" />
  7. <link rel="stylesheet" href="{{ asset('Plugins/css/dropzone.css') }}" />
  8. <link rel="stylesheet" href="{{ asset('css/splide.min.css') }}" />
  9. <link rel="stylesheet" href="{{ asset('css/favorites-sidebar.css') }}" />
  10. {% set destination = "detailsejour" %}
  11. {# ==================== Configuration B2C Conversion ==================== #}
  12. <script>
  13.   window.__FEATURE_PARENT_CONVERSION__ = true;
  14.   // Configuration runtime pour le système B2C
  15.   window.PARENT_CONVERSION_CONFIG = {
  16.     user: {
  17.       prenom: "{{ app.user.prenom|default('Parent')|e('js') }}"
  18.     },
  19.     sejour: {
  20.       id: {{ sejour.id }},
  21.       dateDebut: "{{ sejour.dateDebutSejour|date('Y-m-d') }}",
  22.       dateFin: "{{ sejour.dateFinSejour|date('Y-m-d') }}",
  23.       dateFinCode: "{{ sejour.dateFinCode|date('Y-m-d') }}",
  24.       isTermine: {{ (sejour.dateFinSejour < date()) ? 'true' : 'false' }}
  25.     },
  26.     dates: {
  27.       aujourdhui: "{{ 'now'|date('Y-m-d') }}",
  28.       isFirstDay: {{ (sejour.dateDebutSejour|date('Y-m-d') == 'now'|date('Y-m-d')) ? 'true' : 'false' }},
  29.       isWithin: {{ (sejour.dateDebutSejour <= date() and sejour.dateFinSejour >= date()) ? 'true' : 'false' }},
  30.       daysSinceStart: {{ date().diff(sejour.dateDebutSejour).days }},
  31.       daysUntilEnd: {{ sejour.dateFinSejour.diff(date()).days }},
  32.       daysAfterEnd: {{ date().diff(sejour.dateFinSejour).days }},
  33.       daysUntilClose: {{ sejour.dateFinCode.diff(date()).days }}
  34.     },
  35.     stats: {
  36.       totalDays: {{ sejour.dateFinSejour.diff(sejour.dateDebutSejour).days }},
  37.       totalPhotos: {{ attachementsCount|default(0) }},
  38.       todayPhotos: {{ photosDuJour|default(0) }}
  39.     },
  40.     parent: {
  41.       favorites: {{ nblikes|default(0) }},
  42.       cartCount: 0,
  43.       lastVisitAt: "{{ 'now'|date('Y-m-d H:i:s') }}"
  44.     }
  45.   };
  46.   // Catalogue produits (routes réelles 5sur5)
  47.   window.PARENT_PRODUCT_CATALOG = {
  48.     digi10: { title:"Pack numérique 10 photos", badge:"Petit budget", url:"{{ path('boutique5sur5') }}" },
  49.     digi25: { title:"Pack numérique 25 photos", badge:"Immédiat", url:"{{ path('boutique5sur5') }}" },
  50.     retro12: { title:"Pochette rétro 12 tirages", badge:"Cadeau", url:"{{ path('boutique5sur5') }}" },
  51.     prints20: { title:"Pochette 20 tirages", badge:"Best seller", url:"{{ path('boutique5sur5') }}" },
  52.     album24: { title:"Mini-album 24 pages", badge:"Parfait début", url:"{{ path('Album_du_Sejour') }}" },
  53.     album48: { title:"Album Premium 48 pages", badge:"Meilleure vente", url:"{{ path('Album_du_Sejour') }}" },
  54.     mix10Poster: { title:"Pack mixte 10 tirages + poster", badge:"Combo", url:"{{ path('boutique5sur5') }}" },
  55.     bundleAlbumPrints: { title:"Album + tirages (-15%)", badge:"Éco", url:"{{ path('boutique5sur5') }}" }
  56.   };
  57. </script>
  58. <style>
  59. .btn-close {
  60.   font-size: 1.2rem;
  61.   border: none;
  62.   background: transparent;
  63. }
  64. .slide img {
  65.   max-width: 100%;
  66.   max-height: 100%;
  67.   object-fit: contain;
  68.   border-radius: 8px;
  69.   box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
  70.   transition: transform 0.3s ease;
  71.   cursor: pointer;
  72. }
  73. .slide img.zoomed {
  74.   transform: scale(1.5);
  75.   cursor: zoom-in;
  76. }
  77. /* === Badges discrets des cartes jours (style accompagnateur avec Bootstrap) === */
  78. /* Les badges utilisent directement les classes Bootstrap: badge, bg-success, bg-info, position-absolute */
  79. /* === Affichage des dates (style accompagnateur exact) === */
  80. </style>
  81. {% endblock %} {% set pageMenu = app.session.get('pageMenu') %} {% block Content
  82. %}
  83. <div class="main-content">
  84.   <div class="row no-margin">
  85.     <!-- Bouton cadeau attractif e-commerce -->
  86.     <div
  87.       class="gift-button"
  88.       id="gift-button-trigger"
  89.       style="cursor: pointer;"
  90.     >
  91.       <i id="gift-icon-payment" class="bi bi-gift gift-pulse"></i>
  92.       <label
  93.         id="giftCount"
  94.         class="labelGiftCount gift-count-bounce"
  95.         style="background-color: #f56040"
  96.       >
  97.         {{ likes | length }}
  98.       </label>
  99.       <!-- Message flottant attractif -->
  100.       <div class="gift-tooltip" id="giftTooltip">
  101.         <div class="gift-tooltip-content">
  102.           <div class="gift-tooltip-header">
  103.             <i class="bi bi-clock-history"></i>
  104.             <strong>Offre limitée !</strong>
  105.           </div>
  106.           <p>Commandez vite ! Les photos seront disponibles seulement pendant <strong>6 semaines</strong> après la fin du séjour.</p>
  107.           <div class="gift-tooltip-cta">
  108.             <i class="bi bi-lightning-charge"></i>
  109.             Commander maintenant
  110.           </div>
  111.         </div>
  112.       </div>
  113.     </div>
  114.     <!-- Sidebar E-commerce -->
  115.     <div id="ecommerce-sidebar">
  116.       <div class="ecommerce-header">
  117.         <h2 class="ecommerce-title" id="ecommerce-title">
  118.           🎉 Vos souvenirs favoris sont prêts !
  119.         </h2>
  120.         <p class="ecommerce-subtitle" id="ecommerce-subtitle">
  121.           {{ likes | length }} photos sélectionnées avec amour ❤️
  122.         </p>
  123.         <button class="ecommerce-close" onclick="closeEcommerceSidebar()">
  124.           <i class="bi bi-x"></i>
  125.         </button>
  126.       </div>
  127.       <div class="ecommerce-content">
  128.         <!-- Bannière d'urgence -->
  129.         <div class="urgency-banner" id="urgency-banner">
  130.           <div>
  131.             <i class="bi bi-clock-history"></i>
  132.             <strong>⏳ Commandez vite !</strong>
  133.           </div>
  134.           <div> Les photos seront disponibles seulement <strong>6 semaines</strong> après la fin du séjour</div>
  135.           <div class="countdown" id="countdown-timer" style="display: none;">
  136.             Plus que <span id="days-left">0</span> jours !
  137.           </div>
  138.         </div>
  139.         <!-- Produits recommandés -->
  140.         <div id="products-container">
  141.           <!-- Album Photo -->
  142.           <div class="product-card popular">
  143.             <div class="product-header">
  144.               <div class="product-icon">
  145.                 <img src="/images/produit/Album5sur5-3.jpg" alt="Album Photo Premium" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  146.               </div>
  147.               <div class="product-info">
  148.                 <h4>Album Photo Premium</h4>
  149.                 <div class="product-pricing">
  150.                   <span class="current-price">24,90€</span>
  151.                   <span class="original-price">28,90€</span>
  152.                   <span class="savings">-14%</span>
  153.                 </div>
  154.               </div>
  155.               <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  156.                 <i class="bi bi-heart-fill" style="color: #f56040"></i>
  157.                 <span id="album-fav-count">{{ likes | length }}</span>
  158.               </div>
  159.             </div>
  160.             <p class="product-description">
  161.               Album photo personnalisé avec vos <strong id="album-count">{{ likes | length }}</strong> photos favorites. Papier de qualité supérieure, reliure rigide.
  162.             </p>
  163.             <button class="product-cta" onclick="orderProduct('album')" 
  164.                     title="Commander l'album photo avec vos photos favorites">
  165.               <i class="bi bi-cart-plus"></i>
  166.               Commander l'album
  167.             </button>
  168.           </div>
  169.           <!-- Pack Numérique -->
  170.           <div class="product-card">
  171.             <div class="product-header">
  172.               <div class="product-icon">
  173.                 <img src="/images/produit/photoNumerique.jpg" alt="Pack Numérique HD" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px;">
  174.               </div>
  175.               <div class="product-info">
  176.                 <h4>Pack Numérique HD</h4>
  177.                 <p class="price">À partir de 3,90€</p>
  178.               </div>
  179.                 <div class="product-favorites-indicator" title="Photos favorites sélectionnées">
  180.                   <i class="bi bi-heart-fill" style="color: #f56040"></i>
  181.                   <span id="digital-fav-count">{{ likes | length }}</span>
  182.                 </div>
  183.             </div>
  184.             <p class="product-description">
  185.               Téléchargement immédiat de vos <strong id="digital-count">{{ likes | length }}</strong> photos en haute définition. Qualité professionnelle garantie.
  186.             </p>
  187.             <button class="product-cta" onclick="orderProduct('digital')" 
  188.                     title="Télécharger vos photos en haute définition">
  189.               <i class="bi bi-download"></i>
  190.               Télécharger HD
  191.             </button>
  192.           </div>
  193.           <!-- Pochette Tirages -->
  194.           <div class="product-card" id="prints-card" style="display: none;">
  195.             <div class="product-header">
  196.               <div class="product-icon">
  197.                 <i class="bi bi-images"></i>
  198.               </div>
  199.               <div class="product-info">
  200.                 <h4>Pochette Tirages</h4>
  201.                 <p class="price">À partir de 9,90€</p>
  202.               </div>
  203.             </div>
  204.             <p class="product-description">
  205.               <strong id="prints-count">12</strong> tirages photo 10x15cm de vos favoris. Papier brillant professionnel, envoi sous 48h.
  206.             </p>
  207.             <button class="product-cta" onclick="orderProduct('prints')" 
  208.                     title="Commander des tirages photo de vos favoris">
  209.               <i class="bi bi-printer"></i>
  210.               Commander les tirages
  211.             </button>
  212.           </div>
  213.         </div>
  214.         <!-- Réassurance -->
  215.         <div class="reassurance">
  216.           <i class="bi bi-shield-check" style="color: #10b981; margin-right: 8px;"></i>
  217.           <strong>Imprimé en France – satisfaction garantie ✅</strong>
  218.         </div>
  219.         
  220.       </div>
  221.     </div>
  222.     <div class="selection-popover" id="selectionPopover">
  223.       <h4>Votre sélection</h4>
  224.       <p>Tirages : {{ likes | length }} / 12</p>
  225.       <p>Numériques : {{ likes | length }} / 15</p>
  226.       <p>Album : {{ likes | length }} / 20</p>
  227.       <button class="finalize-button" onclick="openEcommerceSidebar()" 
  228.               title="Voir les produits disponibles avec vos favoris">
  229.         <i class="bi bi-gift"></i>
  230.         Finaliser ma commande
  231.       </button>
  232.     </div>
  233.     
  234.     <style>
  235.       /* Amélioration des boutons de produits */
  236.       .product-cta {
  237.         background: #3a8d95;
  238.         border: none;
  239.         border-radius: 25px;
  240.         color: white;
  241.         padding: 12px 24px;
  242.         font-weight: 600;
  243.         cursor: pointer;
  244.         transition: all 0.3s ease;
  245.         display: flex;
  246.         align-items: center;
  247.         gap: 8px;
  248.         width: 100%;
  249.         justify-content: center;
  250.         margin-top: 10px;
  251.       }
  252.       
  253.       .product-cta:hover {
  254.         transform: translateY(-2px);
  255.         box-shadow: 0 5px 15px rgba(58, 141, 149, 0.4);
  256.         background: #2f7a82;
  257.       }
  258.       
  259.       .product-cta:active {
  260.         transform: translateY(0);
  261.       }
  262.       
  263.       .product-cta:disabled {
  264.         background: #ccc;
  265.         cursor: not-allowed;
  266.         transform: none;
  267.         box-shadow: none;
  268.       }
  269.       
  270.       .finalize-button {
  271.         background: linear-gradient(135deg, #e91e63 0%, #ad1457 100%);
  272.         border: none;
  273.         border-radius: 25px;
  274.         color: white;
  275.         padding: 12px 24px;
  276.         font-weight: 600;
  277.         cursor: pointer;
  278.         transition: all 0.3s ease;
  279.         display: flex;
  280.         align-items: center;
  281.         gap: 8px;
  282.         width: 100%;
  283.         justify-content: center;
  284.         margin-top: 15px;
  285.       }
  286.       
  287.       .finalize-button:hover {
  288.         transform: translateY(-2px);
  289.         box-shadow: 0 5px 15px rgba(233, 30, 99, 0.4);
  290.         background: linear-gradient(135deg, #d81b60 0%, #9c1450 100%);
  291.       }
  292.       
  293.       /* Amélioration des cartes de produits */
  294.       .product-card {
  295.         border-radius: 12px;
  296.         box-shadow: 0 4px 15px rgba(0,0,0,0.1);
  297.         transition: all 0.3s ease;
  298.         margin-bottom: 20px;
  299.         overflow: hidden;
  300.       }
  301.       
  302.       .product-card:hover {
  303.         transform: translateY(-5px);
  304.         box-shadow: 0 8px 25px rgba(0,0,0,0.15);
  305.       }
  306.       
  307.       .product-header {
  308.         display: flex;
  309.         align-items: center;
  310.         gap: 15px;
  311.         padding: 20px;
  312.         background: #f8f9fa;
  313.       }
  314.       
  315.       .product-icon {
  316.         width: 60px;
  317.         height: 60px;
  318.         border-radius: 8px;
  319.         overflow: hidden;
  320.         flex-shrink: 0;
  321.       }
  322.       
  323.       .product-info h4 {
  324.         margin: 0 0 5px 0;
  325.         color: #333;
  326.         font-weight: 600;
  327.       }
  328.       
  329.       .product-pricing {
  330.         display: flex;
  331.         align-items: center;
  332.         gap: 10px;
  333.         margin: 5px 0;
  334.       }
  335.       
  336.       .current-price {
  337.         font-size: 1.2rem;
  338.         font-weight: 700;
  339.         color: #e91e63;
  340.       }
  341.       
  342.       .original-price {
  343.         text-decoration: line-through;
  344.         color: #666;
  345.         font-size: 0.9rem;
  346.       }
  347.       
  348.       .savings {
  349.         background: #4caf50;
  350.         color: white;
  351.         padding: 2px 8px;
  352.         border-radius: 12px;
  353.         font-size: 0.8rem;
  354.         font-weight: 600;
  355.       }
  356.       
  357.       .product-description {
  358.         padding: 0 20px 20px 20px;
  359.         color: #666;
  360.         line-height: 1.5;
  361.       }
  362.       
  363.       .product-favorites-indicator {
  364.         display: flex;
  365.         align-items: center;
  366.         gap: 5px;
  367.         background: rgba(245, 96, 64, 0.1);
  368.         padding: 5px 10px;
  369.         border-radius: 15px;
  370.         font-size: 0.9rem;
  371.         color: #f56040;
  372.         font-weight: 600;
  373.       }
  374.     </style>
  375.   </div>
  376.   {# Slider héros (contrôlé par B2C) #}
  377.   <section id="heroPromo">
  378.     <div class="divSliderModern">
  379.       <div
  380.         class="splide no-padding no-margin"
  381.         id="imageSlider"
  382.         style="max-height: 200px"
  383.       >
  384.       <div class="splide__track">
  385.         <ul class="splide__list">
  386.           <!-- Slide 1 -->
  387.           <li class="splide__slide">
  388.             <div class="slider-content" style="background: white">
  389.               <div class="namePRD" style="display: block">
  390.                 <h4
  391.                   class="titleProdbienvenu titleProdbienvenu1"
  392.                   style="color: #41a2aa"
  393.                 >
  394.                   Ajoutez vos favoris dès maintenant
  395.                 </h4>
  396.                 <h4
  397.                   class="titleProdbienvenu titleProdbienvenu2"
  398.                   style="color: #f09e7a"
  399.                 >
  400.                   et profitez de nos produits souvenirs personnalisés !
  401.                 </h4>
  402.               </div>
  403.               <img
  404.                 src="{{ asset('/images/imgSliderEmpty2.png') }}"
  405.                 class="imgslider"
  406.                 alt="Image 1"
  407.               />
  408.             </div>
  409.           </li>
  410.           <!-- Slide 2 -->
  411.           <li class="splide__slide">
  412.             <div class="slider-content" style="background: white">
  413.               <div class="namePRD" style="display: block">
  414.                 <h4
  415.                   class="titleProdbienvenu titleProdbienvenu1"
  416.                   style="color: #f09e7a"
  417.                 >
  418.                   Pensez à commander le livre du séjour
  419.                 </h4>
  420.                 <h4
  421.                   class="titleProdbienvenu titleProdbienvenu2"
  422.                   style="color: #41a2aa"
  423.                 >
  424.                   et offrez lui le plus beau des cadeaux !
  425.                 </h4>
  426.               </div>
  427.               <img
  428.                 src="{{ asset('/images/imgSliderEmpty1.png') }}"
  429.                 class="imgslider"
  430.                 alt="Image 2"
  431.               />
  432.             </div>
  433.           </li>
  434.         </ul>
  435.       </div>
  436.     </div>
  437.   </div>
  438.   </section>
  439.   {# Fin heroPromo #}
  440.   
  441.   <!-- Section de contenu à atteindre après le scroll -->
  442.   <div
  443.     class="no-margin"
  444.     id="scrollTarget"
  445.     style="width: 100%; background: #f9f9f9; padding-top: 30px"
  446.   >
  447.     <div class="no-margin" id="scrollTarget" style="width: 100%">
  448.       <!-- Header compact 2 bandes -->
  449.       <div class="header-restructured">
  450.         <!-- Ligne 1: Icône + Titre + Stats (alignés) -->
  451.         <div class="header-line-1">
  452.           <div class="header-content">
  453.             <img src="/Accueil/imagesAccueil/sejour.png" alt="Icône séjour" width="40" height="40">
  454.             <h1 class="titreDetailSej">Séjour {{ sejour.themSejour }}</h1>
  455.       
  456.             <nav class="header-pills" aria-label="Statistiques du séjour">
  457.              
  458.                
  459.               <button id="filtre_photos_voir" class="pill stat-pill" data-filter="photos" aria-label="Voir {{ attachementsCount }} photos et vidéos partagées">
  460.                 <i class="bi bi-images"></i> {{ attachementsCount }}
  461.               </button>
  462.               <button class="pill stat-pill" data-filter="audios" aria-label="Voir {{ nbmessages }} messages audio enregistrés">
  463.                 <i class="bi bi-mic-fill"></i> {{ nbmessages }}
  464.               </button>
  465.               <button class="pill stat-pill" data-filter="favoris" aria-label="Voir {{ nblikes }} contenus marqués en favoris">
  466.                 <i class="bi bi-heart-fill"></i> {{ nblikes }}
  467.               </button>
  468.             </nav>
  469.           </div>
  470.         </div>
  471.         <!-- Ligne 2: Dates (gris clair) -->
  472.         <div class="header-line-2">
  473.           du {{ sejour.dateDebutSejour|date("d M Y")|replace({"Jan": "janv.", "Feb": "févr.", "Mar": "mars", "Apr": "avr.", "May": "mai", "Jun": "juin", "Jul": "juil.", "Aug": "août", "Sep": "sept.", "Oct.": "oct.", "Nov.": "nov.", "Dec.": "déc."}) }}
  474.           au {{ sejour.dateFinSejour|date("d M Y")|replace({"Jan": "janv.", "Feb": "févr.", "Mar": "mars", "Apr": "avr.", "May": "mai", "Jun": "juin", "Jul": "juil.", "Aug": "août", "Sep": "sept.", "Oct.": "oct.", "Nov.": "nov.", "Dec.": "déc."}) }}
  475.           · Code&nbsp;: {{sejour.codeSejour}}
  476.         </div>
  477.         <!-- 🎯 MEDIA TOOLBAR CAPSULE (Senior UX) -->
  478.         <nav class="media-toolbar" aria-label="Filtres médias">
  479.           <div class="mtb-inner" role="group">
  480.             <!-- Tout -->
  481.             <button class="mtb-btn active" data-filter="all" title="Tout">
  482.               <i class="bi bi-grid-3x3-gap-fill" aria-hidden="true"></i>
  483.               <span class="mtb-count">Tout {{ attachementsCount }}</span>
  484.             </button>
  485.         
  486.             <!-- Audios -->
  487.             <button class="mtb-btn" data-filter="audio" title="Audios">
  488.              
  489.        
  490. <span class="icon-telephone-voicemail" >
  491.   <i class="bi bi-telephone-fill tel text-secandary" style="color:#ffd700"></i>
  492.   <i class="bi bi-voicemail vm text-secandary" style="color:#ffd700"></i>
  493. </span>
  494.               <span class="mtb-count" data-bind="audio">0</span>
  495.             </button>
  496.           
  497.    
  498.             <!-- Favoris -->
  499.             <button class="mtb-btn" data-filter="favoris" title="Favoris">
  500.               <i class="bi bi-heart-fill" style="color:#f56040" aria-hidden="true"></i>
  501.               <span class="mtb-count" data-bind="fav" id="mesFavCount">{{ nblikes }}</span>
  502.             </button>
  503.                <!-- Photos -->
  504.             <button class="mtb-btn" data-filter="photos" aria-pressed="false" title="Voir toutes les photos">
  505.               <i class="bi bi-eye-fill" aria-hidden="true"></i>
  506.           
  507.             </button>
  508.           </div>
  509.         </nav>
  510.         <!-- Navigation par jours -->
  511.         <div class="section-days">
  512.           <div class="date-navigation">
  513.           
  514.      
  515.             <div class="date-container">
  516.               {% for x, groupAttach in listeattach %}
  517.                 {% set xDate = date(x) %}
  518.                 {% set finDate = date(sejour.dateFinSejour) %}
  519.                 {% if xDate <= finDate %}
  520.                   <div
  521.                     class="date-card modern-card {% if loop.last and xDate <= finDate %} active {% endif %}"
  522.                     data-aos="fade-up"
  523.                     data-bs-toggle="collapse"
  524.                     data-day-id="{{ xDate|date('Y-m-d') }}"
  525.                     data-bs-target="#demP{{ loop.index }}"
  526.                     id="iconedemoP{{ loop.index }}"
  527.                     role="button"
  528.                     aria-label="Contenu du {{ xDate|date("d F Y")|replace({
  529.                       "Jan": "janvier", "Feb": "février", "Mar": "mars", "Apr": "avril",
  530.                       "May": "mai", "Jun": "juin", "Jul": "juillet", "Aug": "août",
  531.                       "Sep": "septembre", "Oct": "octobre", "Nov": "novembre", "Dec": "décembre"
  532.                     }) }} : {{ groupAttach.countPhotos }} photos, {{ groupAttach.countAudio }} audios, {{ groupAttach.countVideos }} vidéos"
  533.                     tabindex="0"
  534.                     style="position: relative;"
  535.                   >
  536.                     {# Badges discrets style accompagnateur #}
  537.                     {% set isToday = xDate|date('Y-m-d') == 'now'|date('Y-m-d') %}
  538.                     {% if groupAttach.isFirstDay == "yes" %}
  539.                       <span class="badge bg-success text-white position-absolute" style="top: 5px; left: 5px; font-size: 0.65rem; padding: 0.25rem 0.5rem; z-index: 10;">
  540.                         <i class="bi bi-play-fill me-1"></i>Premier jour
  541.                       </span>
  542.                     {% elseif groupAttach.isLastDay == "yes" %}
  543.                       <span class="badge bg-info text-white position-absolute" style="top: 5px; right: 5px; font-size: 0.65rem; padding: 0.25rem 0.5rem; z-index: 10;">
  544.                         Dernier jour<i class="bi bi-flag-fill ms-1"></i>
  545.                       </span>
  546.                     {% endif %}
  547.                     
  548.                     {% if isToday %}
  549.                       <span class="badge text-white position-absolute" style="top: 1px; {% if groupAttach.isFirstDay == "yes" %}left: 105px{% else %}left: 5px{% endif %}; font-size: 0.65rem; padding: 0.25rem 0.5rem; z-index: 10; background: #F88A0A;">
  550.                         <i class="bi bi-calendar-day me-1"></i>Aujourd'hui
  551.                       </span>
  552.                     {% endif %}
  553.                     
  554.                     <div class="card-content text-center">
  555.                       <span class="full-date">
  556.                        
  557.                        {{ xDate|date("D")|replace({
  558.                           "Mon": "lun.", "Tue": "mar.", "Wed": "mer.",
  559.                           "Thu": "jeu.", "Fri": "ven.", "Sat": "sam.",
  560.                           "Sun": "dim."
  561.                         }) }}  
  562.                         {{ xDate|date("d M Y")|replace({
  563.                           "Jan": "janv.", "Feb": "fév.", "Mar": "mars",
  564.                           "Apr": "avr.",  "May": "mai",   "Jun": "juin",
  565.                           "Jul": "juil.", "Aug": "août",  "Sep": "sept.",
  566.                           "Oct": "oct.",  "Nov": "nov.",  "Dec": "déc."
  567.                         }) }}
  568.                       </span>
  569.                       <span class="title-line" style="display:none;">
  570.                       
  571.                      
  572.                           <span class="badge-new d-none" aria-label="Nouveau contenu"></span>
  573.                     
  574.                       </span>
  575.                       <ul class="media-list-horizontal">
  576.                         {% if groupAttach.countPhotos > 0 %}
  577.                         <li><i class="bi bi-images"></i>{{ groupAttach.countPhotos }}</li>
  578.                         {% endif %}
  579.                         {% if groupAttach.countAudio > 0 %}
  580.                         <li><span class="icon-telephone-voicemail">
  581.   <i class="bi bi-telephone-fill tel text-secandary" style="color:#ffd700!important"></i>
  582.   <i class="bi bi-voicemail vm text-secandary" style="color:#ffd700!important;  transform: translate(-8%, -46%);font-size:0.4rem!important"></i>
  583. </span>{{ groupAttach.countAudio }}</li>
  584.                         {% endif %}
  585.                         {% if groupAttach.countVideos > 0 %}
  586.                         <li><i class="bi bi-camera-video-fill"></i>{{ groupAttach.countVideos }}</li>
  587.                         {% endif %}
  588.                       </ul>
  589.                     </div>
  590.                   </div>
  591.                 {% endif %}
  592.               {% endfor %}
  593.       </div>
  594.     </div>
  595.             </div>
  596.           </div>
  597.         </div>
  598.     <!-- Carte produit flottante -->
  599.     <div id="dynamic-card" class="dynamic-card" style="display:none">
  600.       <div id="dynamic-card-content" class="dynamic-card-content" >
  601.         <!-- Le contenu dynamique (album, pochette, montage vidéo) sera injecté ici -->
  602.       </div>
  603.     </div>
  604.     <!-- Descriptions and Attachments avec sidebar B2C -->
  605.     <div class="container-xxl my-3">
  606.       <div class="row g-4 col-md-12">
  607.         <div class="col-lg-12 col-md-12">
  608.           {# Galerie principale #}
  609.           <div class="container--gallery modern" data-anchor="gallery">
  610.             {% set lastValidIndex = 0 %}
  611.             {% set hasAttachments = false %}
  612.       {% for x, groupAttach in listeattach %}
  613.       {% set xDate = date(x) %}
  614.       {% set finDate = date(sejour.dateFinSejour) %}
  615.       {% if xDate <= finDate %}
  616.       {% set lastValidIndex = loop.index %}
  617.       {% set hasAttachments = true %}
  618.       <div
  619.         id="demP{{ loop.index }}"
  620.         class="collapse {% if loop.last and xDate <= finDate %}show{% endif %}"
  621.         style="padding: 2%; padding-top: 0%"
  622.       >
  623.         <div class="journal-entry">
  624.         <div class="entry-header" style="
  625.     display: none;
  626.     align-items: center;
  627.     justify-content: space-between;
  628.     background: #ffffff;
  629.     padding: 8px 15px;
  630.     border-radius: 10px;
  631.     margin-bottom: 20px;
  632.     box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  633.     border-bottom: 1px solid #eee;
  634.     flex-wrap: wrap;
  635. ">
  636.  
  637.  
  638.   <!-- Centre : Date + Médias -->
  639.   <div style="
  640.       flex-grow: 1;
  641.       display: flex;
  642.       align-items: center;
  643.       justify-content: center;
  644.       flex-wrap: wrap;
  645.       gap: 12px;
  646.   ">
  647.     <!-- Date -->
  648.     <div style="font-weight: bold; font-size: 16px; color: #333;">
  649.       {{ x|date("l d F Y")|replace({
  650.           "Monday": "Lundi", "Tuesday": "Mardi", "Wednesday": "Mercredi",
  651.           "Thursday": "Jeudi", "Friday": "Vendredi", "Saturday": "Samedi",
  652.           "Sunday": "Dimanche",
  653.           "January": "Janvier", "February": "Février", "March": "Mars",
  654.           "April": "Avril", "May": "Mai", "June": "Juin",
  655.           "July": "Juillet", "August": "Août", "September": "Septembre",
  656.           "October": "Octobre", "November": "Novembre", "December": "Décembre"
  657.       }) }}
  658.     </div>
  659.   <!-- Filtres Médias -->
  660. <div class="filter-icons" style="display: flex; gap: 8px; flex-wrap: wrap;">
  661.     <span class="filter-badge active" data-filter="all" title="Afficher tout">
  662.         <i class="bi bi-grid-3x3-gap-fill" style="margin-right: 5px; font-size: 14px;"></i> Tout
  663.     </span>
  664.     {% if groupAttach.countPhotos > 0 %}
  665.     <span class="filter-badge" data-filter="photo" title="Filtrer les photos">
  666.         <i class="bi bi-image" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countPhotos }}
  667.     </span>
  668.     {% endif %}
  669.     {% if groupAttach.countVideos > 0 %}
  670.     <span class="filter-badge" data-filter="video" title="Filtrer les vidéos">
  671.         <i class="bi bi-camera-video-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countVideos }}
  672.     </span>
  673.     {% endif %}
  674.     {% if groupAttach.countAudio > 0 %}
  675.     <span class="filter-badge" data-filter="audio" title="Filtrer les messages audio">
  676.         <i class="bi bi-mic-fill" style="margin-right: 5px; font-size: 14px;"></i> {{ groupAttach.countAudio }}
  677.     </span>
  678.     {% endif %}
  679. </div>
  680.   </div>
  681.    
  682.    
  683.         </div>
  684.   <!-- Bouton Jour Suivant -->
  685.         <!-- Contenu -->
  686.         <div class="entry-content" id="TourContent">
  687.           <!-- Dropdown de filtrage par jour -->
  688.           <div class="day-filter-dropdown" data-day-index="{{ loop.index }}">
  689.             <button class="filter-toggle" type="button" aria-label="Options de filtrage">
  690.               <i class="bi bi-three-dots"></i>
  691.             </button>
  692.             <div class="filter-dropdown-menu">
  693.               <div class="filter-option active" data-filter="all">
  694.                 <i class="bi bi-grid-3x3-gap-fill"></i>
  695.                 <span>Tout</span>
  696.                 <span class="count" data-count-all>0</span>
  697.               </div>
  698.               <div class="filter-option" data-filter="photo">
  699.                 <i class="bi bi-image"></i>
  700.                 <span>Photos</span>
  701.                 <span class="count" data-count-photo>{{ groupAttach.countPhotos|default(0) }}</span>
  702.               </div>
  703.               <div class="filter-option" data-filter="video">
  704.                 <i class="bi bi-camera-video-fill"></i>
  705.                 <span>Vidéos</span>
  706.                 <span class="count" data-count-video>{{ groupAttach.countVideos|default(0) }}</span>
  707.               </div>
  708.               <div class="filter-option" data-filter="audio">
  709.                 <i class="bi bi-mic-fill"></i>
  710.                 <span>Messages</span>
  711.                 <span class="count" data-count-audio>{{ groupAttach.countAudio|default(0) }}</span>
  712.               </div>
  713.             </div>
  714.           </div>
  715.                {% for description in sejour.jourdescripdate %}
  716.       {%if   description.datejourphoto|date("m/d/Y") !=""%}
  717.           <p class="description" style="margin-left:2%;width:95%;margin-top:1%;margin-bottom:1%;text-align:left">
  718.         {% if
  719.             description.datejourphoto|date("m/d/Y") == x|date("m/d/Y") %}
  720.             {{ description.description | nl2br }}
  721.             {% endif %} 
  722.           </p>
  723.        {%endif%}{% endfor %}
  724.  
  725.           <!-- Conteneur des photos et vidéos -->
  726.           <div
  727.             class="rowimag no-margin"
  728.             style="
  729.               width: 100%;
  730.               display: flex;
  731.               flex-wrap: wrap;
  732.               margin: 0;
  733.               box-sizing: border-box;
  734.             "
  735.           >
  736.             <!-- Afficher les Photos et Vidéos -->
  737.             {% for attach in groupAttach.attachments %}
  738.               {% if attach.libiller == 'photo' %}
  739.            
  740.             <div class="column" data-type="{{ attach.libiller }}">
  741.             
  742.               <div class="photo-zoom photo-item">
  743.                 <!-- Image sans lien -->
  744.                 <img src="{{ attach.path }}" alt="{{ attach.descreption }}" loading="lazy" decoding="async" />
  745.                 <!-- Icône "voir" pour ouvrir le slider -->
  746.                 <div class="view-icon" onclick="viewImage('{{ attach.id_attchment }}', this)" title="Voir en plein écran">
  747.                   <i class="bi bi-eye-fill"></i>
  748.                 </div>
  749.                 <!-- Icône du cœur avec logique existante -->
  750.                 <div
  751.                   class="heart-icon"
  752.                   id="coeur{{ attach.id_attchment }}"
  753.                   data-id="{{ attach.id_attchment }}"
  754.                   data-sejour-id="{{ sejour.id }}"
  755.                   data-path="{{ attach.path }}"
  756.                   data-description="{{ attach.descreption }}"
  757.                 >
  758.                   {% if app.user %} {% if attach.is_favorite %}
  759.                   <i
  760.                     class="bi bi-heart-fill"
  761.                     title="Sélectionnée"
  762.                     style="color: #f56040"
  763.                   ></i>
  764.                   {% else %}
  765.                   <i class="bi bi-heart" title="Ajouter à ma sélection"></i>
  766.                   {% endif %} {% endif %}
  767.                 </div>
  768.                 <div class="photo-actions" style="display: none">
  769.                   <button class="menu-btn">⋮</button>
  770.                   <div class="menu-options">
  771.                     <button onclick="addToPack('tirage')">
  772.                       🖨️ Ajouter au tirage
  773.                     </button>
  774.                     <button onclick="addToPack('numerique')">
  775.                       💾 Ajouter au numérique
  776.                     </button>
  777.                   </div>
  778.                 </div>
  779.               
  780.               </div>
  781.               {% if attach.descreption != "" %}
  782.               <h4 class="description">{{ attach.descreption }}</h4>
  783.               {% endif %}
  784.             </div>
  785.             {% endif %} 
  786.              {% if attach.libiller == 'video' %}
  787.            
  788.             <div class="column" data-type="{{ attach.libiller }}">
  789.                  
  790.             
  791.                  <div class="video-container" style="position: relative; display: inline-block; width: 100%; border-radius: 8px; overflow: hidden;">
  792.                         <video class="photo-zoom" controls controlslist="nodownload noplaybackrate" style="width: 100%;">
  793.                           <source src="{{ attach.path }}" type="video/mp4" />
  794.                           Votre navigateur ne supporte pas la lecture vidéo.
  795.                         </video>
  796.                       
  797.                       </div>
  798.               {% if attach.descreption != "" %}
  799.               <h4 class="description">{{ attach.descreption }}</h4>
  800.               {% endif %}
  801.             </div>
  802.             {% endif %} 
  803.             
  804.             {% endfor %}
  805.           </div>
  806.           <!-- Section séparée pour les messages audio -->
  807.           {% if groupAttach.countAudio > 0 %}
  808.           <div class="audio-messages-section" style="margin-top:15px; border-top: 1px solid #eee; padding: 30px;">
  809.             <h4 style="margin-bottom: 15px; color: #555">
  810.             
  811.               <i class="bi bi-mic-fill" style="margin-right: 8px; color: #ffa500"></i>
  812.               Messages vocaux ({{ groupAttach.countAudio }})
  813.             </h4>
  814.             
  815.             {% if sejour.codeSejour|slice(0, 2) == 'PF' %}
  816.               {# Les séjours commençant par PF ont toujours accès #}
  817.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  818.                 {% for attach in groupAttach.attachments %}
  819.                   {% if attach.libiller == 'message' %}
  820.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  821.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  822.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  823.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  824.                           <source src="{{ attach.path }}" type="audio/mp3" />
  825.                           Votre navigateur ne supporte pas la lecture audio.
  826.                         </audio>
  827.                       </div>
  828.                       {% if attach.descreption != "" %}
  829.                         <div class="audio-description" style="padding-left: 30px">
  830.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  831.                             {{ attach.descreption }}
  832.                           </p>
  833.                         </div>
  834.                       {% endif %}
  835.                     </div>
  836.                   {% endif %}
  837.                 {% endfor %}
  838.               </div>
  839.             
  840.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment  == 1 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment  == 1 %}
  841.               {# PP ou EF avec paiement effectué #}
  842.               <div class="audio-messages-container" data-type="audio" style="display: flex; flex-wrap: wrap; gap: 15px">
  843.                 {% for attach in groupAttach.attachments %}
  844.                   {% if attach.libiller == 'message' %}
  845.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  846.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  847.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  848.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1">
  849.                           <source src="{{ attach.path }}" type="audio/mp3" />
  850.                           Votre navigateur ne supporte pas la lecture audio.
  851.                         </audio>
  852.                       </div>
  853.                       {% if attach.descreption != "" %}
  854.                         <div class="audio-description" style="padding-left: 30px">
  855.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  856.                             {{ attach.descreption }}
  857.                           </p>
  858.                         </div>
  859.                       {% endif %}
  860.                     </div>
  861.                   {% endif %}
  862.                 {% endfor %}
  863.               </div>
  864.             
  865.             {% elseif sejour.codeSejour|slice(0, 2) == 'PP' and parentsejour.payment == 0 or sejour.codeSejour|slice(0, 2) == 'EF' and parentsejour.payment == 0 %}
  866.               {# PP ou EF sans paiement - message d'accès limité et audio désactivés #}
  867.               <div class="audio-messages-container" style="display: flex; flex-wrap: wrap; gap: 15px; opacity: 0.5; pointer-events: none; filter: grayscale(100%);">
  868.                 <div class="audio-messages-restricted" data-type="audio" style="padding: 20px; background: #f0f0f0; border-radius: 8px; text-align: center; margin-bottom: 15px; width: 100%;">
  869.                   <i class="bi bi-lock-fill" style="font-size: 24px; color: #808080; margin-bottom: 10px;"></i>
  870.                   <p style="margin: 0; color: #555;">📢 Les messages vocaux sont disponibles via la boîte vocale premium. Un paiement est requis pour l’activer et y accéder.</p>
  871.                 </div>
  872.                 {% for attach in groupAttach.attachments %}
  873.                   {% if attach.libiller == 'message' %}
  874.                     <div class="audio-message-item" data-type="audio" style="background: #f9f9f9; border-radius: 8px; padding: 12px; display: flex; flex-direction: column; width: calc(33.33% - 10px); min-width: 250px; flex-grow: 1;">
  875.                       <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 10px;">
  876.                         <i class="bi bi-mic-fill" style="font-size: 20px; margin-right: 10px; color: #ffa500"></i>
  877.                         <audio controls controlslist="nodownload noplaybackrate" style="flex: 1" disabled>
  878.                           <source src="{{ attach.path }}" type="audio/mp3" />
  879.                           Votre navigateur ne supporte pas la lecture audio.
  880.                         </audio>
  881.                       </div>
  882.                       {% if attach.descreption != "" %}
  883.                         <div class="audio-description" style="padding-left: 30px">
  884.                           <p style="margin: 0; font-size: 14px; color: #555; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;">
  885.                             {{ attach.descreption }}
  886.                           </p>
  887.                         </div>
  888.                       {% endif %}
  889.                     </div>
  890.                   {% endif %}
  891.                 {% endfor %}
  892.               </div>
  893.             {% endif %}
  894.           </div>
  895.         {% endif %}
  896.         </div>
  897.       </div>
  898.     </div>
  899.     {% endif %}
  900.     {% endfor %}
  901.     {% if not hasAttachments %}
  902.     <div class="welcome-message" style="padding: 50px 20px; text-align: center; background: #f9f9f9; border-radius: 15px; margin: 30px auto; max-width: 800px; box-shadow: 0 5px 15px rgba(0,0,0,0.05);">
  903.       <img src="/images/welcome-icon.svg" alt="Bienvenue" style="width: 80px; margin-bottom: 20px;" loading="lazy" decoding="async" fetchpriority="low" onerror="this.src='/images/Accompagnateur/Picto5sur5.svg'; this.style.width='120px';">
  904.       
  905.       <h2 style="color: #41a2aa; margin-bottom: 20px; font-size: 24px;">Bienvenue sur votre espace séjour !</h2>
  906.       
  907.       <p style="font-size: 18px; color: #555; margin-bottom: 15px;">
  908.         Aucun contenu n'a encore été partagé pour ce séjour.
  909.       </p>
  910.       
  911.       <p style="font-size: 16px; color: #666; margin-bottom: 25px;">
  912.         L'accompagnateur partagera bientôt des photos, vidéos et messages vocaux.
  913.         <br>Revenez consulter cette page régulièrement pour suivre les aventures du séjour !
  914.       </p>
  915.       
  916.       <div style="display: flex; justify-content: center; gap: 15px; flex-wrap: wrap;">
  917.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  918.           <i class="bi bi-images" style="font-size: 24px; color: #3a8d95; margin-right: 10px;"></i>
  919.           <span>Photos du séjour</span>
  920.         </div>
  921.         
  922.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  923.           <i class="bi bi-camera-video-fill" style="font-size: 24px; color: #41a2aa; margin-right: 10px;"></i>
  924.           <span>Vidéos des activités</span>
  925.         </div>
  926.         
  927.         <div style="display: flex; align-items: center; background: #fff; padding: 15px; border-radius: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
  928.           <i class="bi bi-mic-fill" style="font-size: 24px; color: #ffa500; margin-right: 10px;"></i>
  929.           <span>Messages vocaux</span>
  930.         </div>
  931.       </div>
  932.     </div>
  933.     
  934.     <!-- Espace supplémentaire pour déplacer le footer vers le bas -->
  935.     <div style="height: 300px;"></div>
  936.     {% endif %}
  937.     
  938.     <!-- JavaScript pour les pills et la gestion du nouveau contenu -->
  939.     <script>
  940.       document.addEventListener('DOMContentLoaded', function() {
  941.         // Valider les liens de produits au chargement
  942.         setTimeout(() => {
  943.           if (typeof window.validateProductLinks === 'function') {
  944.             window.validateProductLinks();
  945.           }
  946.         }, 1000);
  947.         
  948.         // ⚡ OPTIMISATION: Event delegation avec throttling pour éviter les clics multiples
  949.         let clickThrottleTimeout = null;
  950.         document.addEventListener('click', function(e) {
  951.           // Throttling pour éviter les clics multiples rapides
  952.           if (clickThrottleTimeout) return;
  953.           
  954.           const pill = e.target.closest('.stat-pill');
  955.           if (pill) {
  956.             clickThrottleTimeout = setTimeout(() => {
  957.               clickThrottleTimeout = null;
  958.             }, 100);
  959.             
  960.             // ⚡ OPTIMISÉ: Une seule boucle pour gérer les états actifs
  961.             const statPills = document.querySelectorAll('.stat-pill');
  962.             for (let i = 0; i < statPills.length; i++) {
  963.               const p = statPills[i];
  964.               if (p && p.classList) {
  965.                 if (p === pill) {
  966.                   p.classList.add('active');
  967.                 } else {
  968.                   p.classList.remove('active');
  969.                 }
  970.               }
  971.             }
  972.             
  973.             // Déclencher le filtrage du contenu
  974.             const filter = pill.getAttribute('data-filter');
  975.             if (typeof window.filterContent === 'function') {
  976.               window.filterContent(filter);
  977.             }
  978.           }
  979.         });
  980.         // Fonction de filtrage du contenu - globale
  981.         window.filterContent = function(filter) {
  982.           // ⚡ OPTIMISÉ: Suppression des console.log en production
  983.           if (window.location.hostname === 'localhost' || window.location.hostname.includes('dev')) {
  984.             console.log('Filtrage par:', filter);
  985.           }
  986.           
  987.           const dateNavigation = document.querySelector('.date-navigation');
  988.           const sectionDays = document.querySelector('.section-days');
  989.           
  990.           if (filter === 'favoris') {
  991.             // Afficher la vue des favoris
  992.             showFavoritesView();
  993.           } else if (filter === 'audio') {
  994.             // Afficher la vue des messages audio
  995.             showAudiosView();
  996.           } else if (filter === 'photos') {
  997.             // Ouvrir le carrousel du jour actif ou du dernier jour
  998.             openCurrentDayCarousel();
  999.           } else {
  1000.             // Restaurer la vue normale des jours
  1001.             showDaysView();
  1002.           }
  1003.         }
  1004.         // Fonction pour afficher la vue des favoris - globale
  1005.         window.showFavoritesView = function() {
  1006.           const sectionDays = document.querySelector('.section-days');
  1007.           const containerGallery = document.querySelector('.container--gallery');
  1008.           
  1009.           // Masquer le container gallery
  1010.           if (containerGallery) {
  1011.             containerGallery.style.display = 'none';
  1012.           }
  1013.           
  1014.           // Mettre à jour l'état des boutons
  1015.           updatePhotosButtonState('favorites');
  1016.           updateAudioButtonState('favorites');
  1017.           updateFavoritesButtonState('favorites');
  1018.           
  1019.           const favoriteCount = getCurrentFavoriteCount();
  1020.           
  1021.           // sla vue des favoris avec e-commerce
  1022.           const favoritesHTML = `
  1023.             <div class="favorites-ecommerce-view">
  1024.               <!-- Header avec progression -->
  1025.               <div class="favorites-header">
  1026.                 <div class="favorites-title">
  1027.                   <h3>Vos souvenirs sélectionnés</h3>
  1028.                   <span class="favorites-count" id="favorites-count">${favoriteCount} photos</span>
  1029.                 </div>
  1030.                 <button class="btn-back-to-days" onclick="window.showDaysView()">
  1031.                   <i class="bi bi-arrow-left"></i>
  1032.                   Retour aux jours
  1033.                 </button>
  1034.               </div>
  1035.             
  1036.               <!-- Grille des favoris -->
  1037.               <div class="favorites-content">
  1038.                 <div class="favorites-grid" id="favoritesGrid">
  1039.                   ${generateFavoritesGrid()}
  1040.                 </div>
  1041.                 
  1042.                 <!-- Propositions de produits -->
  1043.                 <div class="product-suggestions">
  1044.                   ${generateProductSuggestions(favoriteCount)}
  1045.                 </div>
  1046.               </div>
  1047.             </div>
  1048.           `;
  1049.           
  1050.           sectionDays.innerHTML = favoritesHTML;
  1051.         }
  1052.         // Fonction pour afficher la vue des messages audio - globale
  1053.         window.showAudiosView = function() {
  1054.           const sectionDays = document.querySelector('.section-days');
  1055.           const containerGallery = document.querySelector('.container--gallery');
  1056.           
  1057.           // Masquer le container gallery
  1058.           if (containerGallery) {
  1059.             containerGallery.style.display = 'none';
  1060.           }
  1061.           
  1062.           // Mettre à jour l'état des boutons
  1063.           updatePhotosButtonState('audios');
  1064.           updateAudioButtonState('audios');
  1065.           updateFavoritesButtonState('audios');
  1066.           
  1067.           // Collecter tous les messages audio de tous les jours
  1068.           const allAudioMessages = [];
  1069.           const audioItems = document.querySelectorAll('.audio-message-item[data-type="audio"]');
  1070.           
  1071.           audioItems.forEach(item => {
  1072.             const audio = item.querySelector('audio source');
  1073.             const description = item.querySelector('.audio-description p');
  1074.             const dayContainer = item.closest('[id^="demP"]');
  1075.             let dayTitle = 'Jour inconnu';
  1076.             
  1077.             if (dayContainer) {
  1078.               // Trouver le titre du jour
  1079.               const dayIndex = dayContainer.id.replace('demP', '');
  1080.               const dayCard = document.querySelector(`[data-target="#demP${dayIndex}"]`);
  1081.               if (dayCard) {
  1082.                 dayTitle = dayCard.textContent.trim();
  1083.               } else {
  1084.                 // Essayer de trouver dans les cartes de date
  1085.                 const dateCard = document.querySelector(`.date-card[data-bs-target="#demP${dayIndex}"], .date-card[data-target="#demP${dayIndex}"]`);
  1086.                 if (dateCard) {
  1087.                   const dateText = dateCard.querySelector('.date-text, .card-title, h5, .title-line');
  1088.                   if (dateText) {
  1089.                     dayTitle = dateText.textContent.trim();
  1090.                   }
  1091.                 }
  1092.               }
  1093.             }
  1094.             
  1095.             if (audio) {
  1096.               allAudioMessages.push({
  1097.                 src: audio.src,
  1098.                 description: description ? description.textContent.trim() : '',
  1099.                 day: dayTitle,
  1100.                 isRestricted: item.closest('.audio-messages-container[style*="opacity: 0.5"]') !== null
  1101.               });
  1102.             }
  1103.           });
  1104.           
  1105.           // Créer la vue des messages audio
  1106.           const audiosHTML = `
  1107.             <div class="audios-view">
  1108.               <!-- Header -->
  1109.               <div class="audios-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding: 20px; background: #f8f9fa; border-radius: 10px;">
  1110.                 <div class="audios-title">
  1111.                   <h3><span class="icon-telephone-voicemail">
  1112.   <i class="bi bi-telephone-fill tel"  style="color: #ffd700;font-size:1.5em"></i>
  1113.   <i class="bi bi-voicemail vm" style="color: #ffd700;font-size: 0.9rem;
  1114.     transform: translate(-40%, -46%);"></i>
  1115. </span>
  1116.                   <span class="audios-count">${allAudioMessages.length} messages</span>
  1117.                 </div>
  1118.                 <button class="btn-back-to-days" onclick="window.showDaysView()" style="background: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer;">
  1119.                   <i class="bi bi-arrow-left"></i>
  1120.                   Retour aux jours
  1121.                 </button>
  1122.               </div>
  1123.               <!-- Grille des messages audio -->
  1124.               <div class="audios-content" style="display: flex; flex-wrap: wrap; gap: 20px;">
  1125.                 ${allAudioMessages.map(msg => `
  1126.                   <div class="audio-message-card" style="background: #f9f9f9; border-radius: 12px; padding: 20px; width: calc(50% - 10px); min-width: 300px; ${msg.isRestricted ? 'opacity: 0.5; filter: grayscale(100%);' : ''}">
  1127.                     ${msg.day && msg.day !== 'Jour inconnu' ? `
  1128.                       <div class="audio-day-tag" style="background: #e9ecef; color: #495057; padding: 4px 8px; border-radius: 15px; font-size: 12px; margin-bottom: 12px; display: inline-block;">
  1129.                         ${msg.day}
  1130.                       </div>
  1131.                     ` : ''}
  1132.                     <div class="audio-player-container" style="display: flex; align-items: center; margin-bottom: 12px;">
  1133.                       <i class="bi bi-mic-fill" style="font-size: 24px; margin-right: 15px; color: #ffa500;"></i>
  1134.                       <audio controls controlslist="nodownload noplaybackrate" style="flex: 1; border-radius: 5px;" ${msg.isRestricted ? 'disabled' : ''}>
  1135.                         <source src="${msg.src}" type="audio/mp3" />
  1136.                         Votre navigateur ne supporte pas la lecture audio.
  1137.                       </audio>
  1138.                     </div>
  1139.                     ${msg.description ? `
  1140.                       <div class="audio-description" style="padding-left: 39px;">
  1141.                         <p style="margin: 0; font-size: 14px; color: #666; line-height: 1.4;">${msg.description}</p>
  1142.                       </div>
  1143.                     ` : ''}
  1144.                     ${msg.isRestricted ? `
  1145.                       <div class="audio-restricted-notice" style="text-align: center; margin-top: 10px; padding: 8px; background: #fff3cd; border-radius: 5px; border: 1px solid #ffeaa7;">
  1146.                         <i class="bi bi-lock-fill" style="color: #856404; margin-right: 5px;"></i>
  1147.                         <small style="color: #856404;">Accès premium requis</small>
  1148.                       </div>
  1149.                     ` : ''}
  1150.                   </div>
  1151.                 `).join('')}
  1152.               </div>
  1153.               
  1154.               ${allAudioMessages.length === 0 ? `
  1155.                 <div class="no-audios" style="text-align: center; padding: 60px 20px; color: #6c757d;">
  1156.                   <i class="bi bi-mic" style="font-size: 4rem; margin-bottom: 20px; opacity: 0.3;"></i>
  1157.                   <h4>Aucun message vocal</h4>
  1158.                   <p>Il n'y a pas encore de messages vocaux dans ce séjour.</p>
  1159.                 </div>
  1160.               ` : ''}
  1161.             </div>
  1162.           `;
  1163.           
  1164.           sectionDays.innerHTML = audiosHTML;
  1165.         }
  1166.         
  1167.         // Variables pour sauvegarder le contenu original
  1168.         let originalDaysContent = null;
  1169.         
  1170.         // Sauvegarder le contenu original au chargement
  1171.         document.addEventListener('DOMContentLoaded', function() {
  1172.           setTimeout(() => {
  1173.             const containerGallery = document.querySelector('.container--gallery');
  1174.             if (containerGallery) {
  1175.               originalDaysContent = containerGallery.outerHTML;
  1176.             }
  1177.           }, 1000);
  1178.         });
  1179.         
  1180.       
  1181.         // Fonction pour ouvrir le carrousel contextuel - globale
  1182.         window.openCurrentDayCarousel = function() {
  1183.           // Détecter la vue active
  1184.           const currentView = detectCurrentView();
  1185.           
  1186.           switch (currentView) {
  1187.             case 'favorites':
  1188.               openFavoritesCarousel();
  1189.               break;
  1190.             case 'audios':
  1191.               // Dans la vue audios, pas de carrousel photos pertinent
  1192.               console.log('Carrousel photos non disponible dans la vue audios');
  1193.               return;
  1194.             case 'days':
  1195.             default:
  1196.               openActiveDayCarousel();
  1197.               break;
  1198.           }
  1199.         }
  1200.         
  1201.         // Fonction pour mettre à jour l'état du bouton photos selon le contexte
  1202.         function updatePhotosButtonState(currentView) {
  1203.           const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  1204.           if (!photosBtn) return;
  1205.           
  1206.           const btnIcon = photosBtn.querySelector('i');
  1207.           const btnCount = photosBtn.querySelector('.mtb-count');
  1208.           
  1209.           switch (currentView) {
  1210.             case 'favorites':
  1211.               // Dans les favoris : bouton actif, ouvre le carrousel des favoris
  1212.               photosBtn.style.opacity = '1';
  1213.               photosBtn.style.pointerEvents = 'auto';
  1214.               photosBtn.title = 'Voir le carrousel des favoris';
  1215.               if (btnIcon) btnIcon.style.color = '#007bff';
  1216.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur
  1217.               break;
  1218.               
  1219.             case 'audios':
  1220.               // Dans les audios : bouton désactivé
  1221.               photosBtn.style.opacity = '0.4';
  1222.               photosBtn.style.pointerEvents = 'none';
  1223.               photosBtn.title = 'Carrousel non disponible dans la vue audios';
  1224.               if (btnIcon) btnIcon.style.color = '#6c757d';
  1225.               if (btnCount) btnCount.style.display = 'none';
  1226.               break;
  1227.               
  1228.             case 'days':
  1229.             default:
  1230.               // Vue normale : utiliser la logique existante du carrousel
  1231.               photosBtn.style.opacity = '1';
  1232.               photosBtn.style.pointerEvents = 'auto';
  1233.               photosBtn.title = 'Voir les photos du jour actif';
  1234.               if (btnIcon) btnIcon.style.color = '';
  1235.               if (btnCount) btnCount.style.display = 'none'; // Cacher le compteur pour éviter la confusion
  1236.               break;
  1237.           }
  1238.         }
  1239.         
  1240.         // Fonction pour mettre à jour l'état du bouton audio selon le contexte
  1241.         window.updateAudioButtonState = function updateAudioButtonState(currentView) {
  1242.           const audioBtn = document.querySelector('.mtb-btn[data-filter="audio"]');
  1243.           if (!audioBtn) return;
  1244.           
  1245.           const btnCount = audioBtn.querySelector('.mtb-count');
  1246.           
  1247.           switch (currentView) {
  1248.             case 'favorites':
  1249.               // Dans les favoris : pas d'action sur le compteur audio
  1250.               break;
  1251.               
  1252.             case 'audios':
  1253.               // Dans les audios : afficher le nombre total calculé par le frontend
  1254.               const totalAudioCount = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  1255.               if (btnCount) {
  1256.                 btnCount.textContent = totalAudioCount;
  1257.               }
  1258.               break;
  1259.               
  1260.             case 'days':
  1261.             default:
  1262.               // Vue normale : compter les audios du jour actif
  1263.               const activeDayAudioCount = countActiveDayAudios();
  1264.               if (btnCount) {
  1265.                 btnCount.textContent = activeDayAudioCount;
  1266.               }
  1267.               break;
  1268.           }
  1269.         }
  1270.         
  1271.         // Fonction pour mettre à jour l'état du bouton favoris selon le contexte
  1272.         window.updateFavoritesButtonState = function updateFavoritesButtonState(currentView) {
  1273.           const favoritesBtn = document.querySelector('.mtb-btn[data-filter="favoris"]');
  1274.           if (!favoritesBtn) return;
  1275.           
  1276.           const btnIcon = favoritesBtn.querySelector('i');
  1277.           const btnCount = favoritesBtn.querySelector('.mtb-count');
  1278.           
  1279.           // ✅ IMPORTANT : Le bouton favoris reste TOUJOURS accessible, peu importe la vue
  1280.           switch (currentView) {
  1281.             case 'favorites':
  1282.               // Dans les favoris : bouton actif marqué visuellement
  1283.               favoritesBtn.style.opacity = '1';
  1284.               favoritesBtn.style.pointerEvents = 'auto';
  1285.               favoritesBtn.title = 'Vous êtes dans la vue favoris';
  1286.               if (btnIcon) btnIcon.style.color = '#f56040';
  1287.               if (btnCount) btnCount.style.display = 'inline';
  1288.               favoritesBtn.classList.add('active');
  1289.               break;
  1290.               
  1291.             case 'audios':
  1292.               // Dans les audios : bouton reste actif et accessible ✅
  1293.               favoritesBtn.style.opacity = '1';
  1294.               favoritesBtn.style.pointerEvents = 'auto';
  1295.               favoritesBtn.title = 'Voir les favoris';
  1296.               if (btnIcon) btnIcon.style.color = '#f56040';
  1297.               if (btnCount) btnCount.style.display = 'inline';
  1298.               favoritesBtn.classList.remove('active');
  1299.               break;
  1300.               
  1301.             case 'days':
  1302.             default:
  1303.               // Vue normale : bouton normal et accessible
  1304.               favoritesBtn.style.opacity = '1';
  1305.               favoritesBtn.style.pointerEvents = 'auto';
  1306.               favoritesBtn.title = 'Voir les favoris';
  1307.               if (btnIcon) btnIcon.style.color = '#f56040';
  1308.               if (btnCount) btnCount.style.display = 'inline';
  1309.               favoritesBtn.classList.remove('active');
  1310.               break;
  1311.           }
  1312.         }
  1313.         
  1314.         // Fonction pour compter les audios du jour actif (réutilise la même logique)
  1315.         // ⚡ OPTIMISÉ: Cache des éléments DOM pour éviter les requêtes répétitives
  1316.         let cachedActiveDay = null;
  1317.         let cachedAudioCount = 0;
  1318.         let lastCacheTime = 0;
  1319.         const CACHE_DURATION = 1000; // 1 seconde
  1320.         
  1321.         function countActiveDayAudios() {
  1322.           const now = Date.now();
  1323.           
  1324.           // Utiliser le cache si récent
  1325.           if (cachedActiveDay && (now - lastCacheTime) < CACHE_DURATION) {
  1326.             return cachedAudioCount;
  1327.           }
  1328.           
  1329.           // Trouver le jour actuellement ouvert
  1330.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  1331.           
  1332.           // Si aucun jour ouvert, prendre le dernier jour
  1333.           if (!activeDay) {
  1334.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1335.             activeDay = allDays[allDays.length - 1];
  1336.           }
  1337.           
  1338.           if (!activeDay) {
  1339.             cachedActiveDay = null;
  1340.             cachedAudioCount = 0;
  1341.             lastCacheTime = now;
  1342.             return 0;
  1343.           }
  1344.           
  1345.           // Compter les messages audio dans ce jour
  1346.           const audiosInDay = activeDay.querySelectorAll('.audio-message-item[data-type="audio"]');
  1347.           const count = audiosInDay.length;
  1348.           
  1349.           // Mettre à jour le cache
  1350.           cachedActiveDay = activeDay;
  1351.           cachedAudioCount = count;
  1352.           lastCacheTime = now;
  1353.           
  1354.           return count;
  1355.         }
  1356.         
  1357.         // Fonction pour détecter la vue active
  1358.         // ⚡ OPTIMISÉ: Cache de la vue active pour éviter les requêtes DOM répétitives
  1359.         let cachedCurrentView = null;
  1360.         let lastViewCheck = 0;
  1361.         const VIEW_CACHE_DURATION = 500; // 500ms
  1362.         
  1363.         function detectCurrentView() {
  1364.           const now = Date.now();
  1365.           
  1366.           // Utiliser le cache si récent
  1367.           if (cachedCurrentView && (now - lastViewCheck) < VIEW_CACHE_DURATION) {
  1368.             return cachedCurrentView;
  1369.           }
  1370.           
  1371.           let view = 'days';
  1372.           if (document.querySelector('.favorites-ecommerce-view')) {
  1373.             view = 'favorites';
  1374.           } else if (document.querySelector('.audios-view')) {
  1375.             view = 'audios';
  1376.           }
  1377.           
  1378.           // Mettre à jour le cache
  1379.           cachedCurrentView = view;
  1380.           lastViewCheck = now;
  1381.           
  1382.           return view;
  1383.         }
  1384.         
  1385.         // ⚡ OPTIMISÉ: Cache global des éléments DOM fréquemment utilisés
  1386.         const domCache = {
  1387.           favoritesGrid: null,
  1388.           lastFavoritesUpdate: 0,
  1389.           CACHE_DURATION: 2000 // 2 secondes
  1390.         };
  1391.         
  1392.         function getCachedFavorites() {
  1393.           const now = Date.now();
  1394.           if (!domCache.favoritesGrid || (now - domCache.lastFavoritesUpdate) > domCache.CACHE_DURATION) {
  1395.             domCache.favoritesGrid = document.querySelector('.favorites-grid');
  1396.             domCache.lastFavoritesUpdate = now;
  1397.           }
  1398.           return domCache.favoritesGrid;
  1399.         }
  1400.         
  1401.         // Fonction pour ouvrir le carrousel des favoris
  1402.         function openFavoritesCarousel() {
  1403.           const favoritesGrid = getCachedFavorites();
  1404.           const favoriteItems = favoritesGrid ? favoritesGrid.querySelectorAll('.favorite-item') : [];
  1405.           if (favoriteItems.length > 0) {
  1406.             // Trouver le premier favori avec une action de vue
  1407.             const firstFavorite = favoriteItems[0];
  1408.             const viewBtn = firstFavorite.querySelector('.btn-view-favorite');
  1409.             if (viewBtn && viewBtn.onclick) {
  1410.               viewBtn.click();
  1411.             } else {
  1412.               // Fallback : extraire l'ID du data-id et utiliser viewImage
  1413.               const favoriteId = firstFavorite.dataset.id;
  1414.               if (favoriteId && typeof window.viewImage === 'function') {
  1415.                 window.viewImage(favoriteId, firstFavorite);
  1416.               }
  1417.             }
  1418.           } else {
  1419.             console.log('Aucun favori trouvé pour ouvrir le carrousel');
  1420.           }
  1421.         }
  1422.         
  1423.         // Fonction pour ouvrir le carrousel du jour actif
  1424.         function openActiveDayCarousel() {
  1425.           // Trouver le jour actuellement ouvert (avec classe 'show')
  1426.           let activeDay = document.querySelector('.collapse.show[id^="demP"]');
  1427.           
  1428.           // Si aucun jour n'est ouvert, prendre le dernier jour
  1429.           if (!activeDay) {
  1430.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1431.             activeDay = allDays[allDays.length - 1]; // Dernier jour
  1432.           }
  1433.           
  1434.           if (!activeDay) {
  1435.             console.warn('Aucun jour trouvé pour ouvrir le carrousel');
  1436.             return;
  1437.           }
  1438.           
  1439.           // Trouver la première photo de ce jour
  1440.           const viewIcon = activeDay.querySelector('.view-icon[onclick*="viewImage"]');
  1441.           if (viewIcon) {
  1442.             // Extraire l'ID de la photo du onclick
  1443.             const onclickAttr = viewIcon.getAttribute('onclick');
  1444.             const match = onclickAttr.match(/viewImage\('(\d+)'/);
  1445.             if (match) {
  1446.               const imageId = match[1];
  1447.               // Ouvrir le slider avec cette image
  1448.               if (typeof window.viewImage === 'function') {
  1449.                 window.viewImage(imageId, viewIcon);
  1450.               }
  1451.             }
  1452.           } else {
  1453.             // Fallback : chercher dans tous les jours
  1454.             const allDays = document.querySelectorAll('.collapse[id^="demP"]');
  1455.             for (let day of allDays) {
  1456.               const photoInDay = day.querySelector('.view-icon[onclick*="viewImage"]');
  1457.               if (photoInDay) {
  1458.                 photoInDay.click();
  1459.                 break;
  1460.               }
  1461.             }
  1462.           }
  1463.         }
  1464.         // Fonction pour afficher la vue normale des jours - globale
  1465.         window.showDaysView = function() {
  1466.           const sectionDays = document.querySelector('.section-days');
  1467.           const containerGallery = document.querySelector('.container--gallery');
  1468.           
  1469.           // Si la section days contient des vues spéciales (favoris/audios), les supprimer
  1470.           if (sectionDays && (sectionDays.innerHTML.includes('favorites-ecommerce-view') || sectionDays.innerHTML.includes('audios-view'))) {
  1471.             // Restaurer le contenu original si disponible
  1472.             if (originalDaysContent) {
  1473.               sectionDays.innerHTML = originalDaysContent;
  1474.             } else {
  1475.               // Fallback : recharger la page si pas de sauvegarde
  1476.               location.reload();
  1477.               return;
  1478.             }
  1479.           }
  1480.           
  1481.           // Restaurer la visibilité du container gallery
  1482.           if (containerGallery) {
  1483.             containerGallery.style.display = 'block';
  1484.           }
  1485.           
  1486.           // Appeler le filtre "all" pour restaurer l'affichage complet
  1487.           if (typeof window.filterContent === 'function') {
  1488.             window.filterContent('toutVoir');
  1489.           }
  1490.           
  1491.           // Mettre à jour l'état des boutons
  1492.           updatePhotosButtonState('days');
  1493.           updateAudioButtonState('days');
  1494.           updateFavoritesButtonState('days');
  1495.         }
  1496.         // Fonction pour générer les cartes de jours (alternative simple)
  1497.         function generateDaysCards() {
  1498.           // Cette fonction pourrait régénérer les cartes, mais pour simplifier
  1499.           // on recharge la page dans showDaysView()
  1500.           return '';
  1501.         }
  1502.         // Fonction pour revenir à la vue des jours
  1503.      
  1504.         // Fonction pour générer la grille des favoris
  1505.         function generateFavoritesGrid() {
  1506.           const favorites = getFavoriteItems();
  1507.           
  1508.           if (favorites.length === 0) {
  1509.             return `
  1510.               <div class="no-favorites">
  1511.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  1512.                 <h4>Aucun favori sélectionné</h4>
  1513.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  1514.               </div>
  1515.             `;
  1516.           }
  1517.           
  1518.           return favorites.map(item => `
  1519.             <div class="favorite-item" data-id="${item.id}">
  1520.               ${item.type === 'image' ? 
  1521.                 `<img src="${item.url}" alt="Photo favorite" loading="lazy">` :
  1522.                 item.type === 'video' ? 
  1523.                 `<video src="${item.url}" poster="${item.thumbnail}"></video>
  1524.                  <div class="video-overlay"><i class="bi bi-play-circle"></i></div>` :
  1525.                 `<div class="audio-item">
  1526.                    <i class="bi bi-mic-fill"></i>
  1527.                    <span>Audio ${item.duration || '00:00'}</span>
  1528.                  </div>`
  1529.               }
  1530.               <!-- Actions overlay -->
  1531.               <div class="favorite-actions">
  1532.                 <button class="btn-view-favorite" onclick="viewFavorite('${item.id}')" title="Voir en grand">
  1533.                   <i class="bi bi-eye"></i>
  1534.                   <span>Voir</span>
  1535.                 </button>
  1536.                 <button class="btn-remove-favorite" onclick="removeFavorite('${item.id}')" title="Retirer des favoris">
  1537.                   <i class="bi bi-heart-fill"></i>
  1538.                   <span>Retirer</span>
  1539.                 </button>
  1540.               </div>
  1541.             </div>
  1542.           `).join('');
  1543.         }
  1544.         // Fonction pour réorganiser la grille des favoris
  1545.         function reorganizeFavoritesGrid() {
  1546.           const favoritesGrid = document.getElementById('favoritesGrid');
  1547.           if (!favoritesGrid) return;
  1548.           
  1549.           const remainingItems = favoritesGrid.querySelectorAll('.favorite-item');
  1550.           if (remainingItems.length === 0) {
  1551.             // Afficher le message "Aucun favori"
  1552.             favoritesGrid.innerHTML = `
  1553.               <div class="no-favorites">
  1554.                 <i class="bi bi-heart" style="font-size: 3rem; color: #ccc; margin-bottom: 1rem;"></i>
  1555.                 <h4>Aucun favori sélectionné</h4>
  1556.                 <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos photos préférées pour les ajouter ici.</p>
  1557.               </div>
  1558.             `;
  1559.           } else {
  1560.             // Réorganiser les éléments restants avec une animation douce
  1561.             remainingItems.forEach((item, index) => {
  1562.               item.style.transition = 'all 0.3s ease';
  1563.               item.style.order = index;
  1564.             });
  1565.           }
  1566.         }
  1567.         // Fonction pour récupérer les éléments favoris
  1568.         function getFavoriteItems() {
  1569.           const favorites = [];
  1570.           
  1571.           // ⚡ OPTIMISÉ: Cache et parcours plus efficace des favoris
  1572.           const heartIcons = document.querySelectorAll('.heart-icon');
  1573.           for (let i = 0; i < heartIcons.length; i++) {
  1574.             const heartIcon = heartIcons[i];
  1575.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  1576.             if (heartFill) {
  1577.               const id = heartIcon.getAttribute('data-id');
  1578.               const path = heartIcon.getAttribute('data-path');
  1579.               const description = heartIcon.getAttribute('data-description');
  1580.               
  1581.               if (id && path) {
  1582.                 // Déterminer le type de média
  1583.                 let type = 'image';
  1584.                 let url = path;
  1585.                 let thumbnail = path;
  1586.                 
  1587.                 if (path.includes('.mp4') || path.includes('.mov') || path.includes('.avi')) {
  1588.                   type = 'video';
  1589.                   thumbnail = path.replace(/\.(mp4|mov|avi)$/i, '_thumb.jpg');
  1590.                 } else if (path.includes('.mp3') || path.includes('.wav') || path.includes('.m4a')) {
  1591.                   type = 'audio';
  1592.                   url = path;
  1593.                 }
  1594.                 
  1595.                 // Récupérer la date depuis le conteneur parent
  1596.                 const dateCard = heartIcon.closest('[id^="demP"]');
  1597.                 let date = 'Date inconnue';
  1598.                 if (dateCard) {
  1599.                   const dateElement = dateCard.querySelector('.full-date, .day-title');
  1600.                   if (dateElement) {
  1601.                     date = dateElement.textContent.trim();
  1602.                   }
  1603.                 }
  1604.                 
  1605.                 favorites.push({
  1606.                   id: id,
  1607.                   url: url,
  1608.                   thumbnail: thumbnail,
  1609.                   type: type,
  1610.                   date: date,
  1611.                   description: description || ''
  1612.                 });
  1613.               }
  1614.             }
  1615.           }
  1616.           
  1617.           // ⚡ OPTIMISÉ: Log conditionnel pour réduire l'impact performance
  1618.           if (favorites.length > 0) {
  1619.             console.log('Favoris trouvés:', favorites.length);
  1620.           }
  1621.           return favorites;
  1622.         }
  1623.         // Rendre les fonctions accessibles globalement
  1624.         window.viewFavorite = viewFavorite;
  1625.         window.removeFavorite = removeFavorite;
  1626.         window.getFavoriteItems = getFavoriteItems;
  1627.         // Fonction pour supprimer un favori
  1628.         function removeFavorite(id) {
  1629.          
  1630.           const heartIcon = document.querySelector(`[data-id="${id}"]`);
  1631.          
  1632.           if (heartIcon) {
  1633.             const heartFill = heartIcon.querySelector('.bi-heart-fill');
  1634.             if (heartFill && heartFill.classList) {
  1635.               heartFill.classList.remove('bi-heart-fill');
  1636.               heartFill.classList.add('bi-heart');
  1637.               heartFill.style.color = '';
  1638.             }
  1639.           }
  1640.           const favoriteItem = document.querySelector(`.favorite-item[data-id="${id}"]`);
  1641.         
  1642.           if (favoriteItem) {
  1643.             // Animation de disparition puis suppression complète
  1644.             favoriteItem.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
  1645.             favoriteItem.style.opacity = '0';
  1646.             favoriteItem.style.transform = 'scale(0.8)';
  1647.             
  1648.             setTimeout(() => {
  1649.               favoriteItem.remove();
  1650.               
  1651.               // Appeler la fonction métier si présente
  1652.               const heartIcon = document.querySelector(`#coeur${id}`);
  1653.               const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  1654.               supprimerFavoris(id, sejourId);
  1655.               
  1656.               // Mettre à jour les compteurs
  1657.               updateAllFavoriteCounters();
  1658.               
  1659.               // Réorganiser la grille si nécessaire
  1660.               reorganizeFavoritesGrid();
  1661.             }, 300);
  1662.       
  1663.      
  1664.    
  1665.   }
  1666.           
  1667.         }
  1668.         // Slider plein écran pour les favoris
  1669.         class FavoritesSlider {
  1670.           constructor() {
  1671.             this.currentIndex = 0;
  1672.             this.favorites = [];
  1673.             this.isOpen = false;
  1674.             this.touchStartX = 0;
  1675.             this.touchEndX = 0;
  1676.           }
  1677.           open(favoriteId) {
  1678.             console.log('Ouverture du slider pour:', favoriteId);
  1679.             
  1680.             // Récupérer tous les favoris images
  1681.             this.favorites = getFavoriteItems().filter(item => item.type === 'image');
  1682.             
  1683.             if (this.favorites.length === 0) {
  1684.               console.error('Aucun favori image trouvé');
  1685.               return;
  1686.             }
  1687.             // Trouver l'index de l'image courante
  1688.             this.currentIndex = this.favorites.findIndex(fav => fav.id === favoriteId);
  1689.             if (this.currentIndex === -1) this.currentIndex = 0;
  1690.             console.log('Index courant:', this.currentIndex, 'Total:', this.favorites.length);
  1691.             this.createSlider();
  1692.             this.showSlide(this.currentIndex);
  1693.             this.attachEvents();
  1694.             this.isOpen = true;
  1695.             // Animation d'ouverture immédiate pour favoris avec diagnostic
  1696.             setTimeout(() => {
  1697.               if (window.diagnoseSliderDisplay) {
  1698.                 window.diagnoseSliderDisplay('#favoritesSlider');
  1699.               }
  1700.             }, 50);
  1701.           }
  1702.           createSlider() {
  1703.             // ⚡ OPTIMISATION: Créer le slider seulement quand nécessaire
  1704.             if (document.querySelector('#favoritesSlider')) {
  1705.               return; // Déjà créé, ne pas recréer
  1706.             }
  1707.             
  1708.             // Structure HTML minimale et optimisée
  1709.             const sliderHTML = `
  1710.               <div class="favorites-slider" id="favoritesSlider" style="
  1711.                 position: fixed !important; 
  1712.                 top: 0 !important; 
  1713.                 left: 0 !important; 
  1714.                 width: 100% !important; 
  1715.                 height: 100% !important; 
  1716.                 background: rgba(0,0,0,0.95) !important; 
  1717.                 z-index: 9999 !important; 
  1718.                 display: flex !important; 
  1719.                 align-items: center !important; 
  1720.                 justify-content: center !important; 
  1721.                 opacity: 1 !important; 
  1722.                 visibility: visible !important;
  1723.               ">
  1724.                 <div class="slider-overlay"></div>
  1725.                 
  1726.                 <!-- Header avec contrôles -->
  1727.                 <div class="slider-header">
  1728.                   <div class="slider-controls">
  1729.                     <button class="slider-btn favorite-btn" title="Retirer des favoris">
  1730.                       <i class="bi bi-heart-fill"></i>
  1731.                     </button>
  1732.                     <button class="slider-btn zoom-btn" title="Zoom">
  1733.                       <i class="bi bi-zoom-in"></i>
  1734.                     </button>
  1735.                     <button class="slider-btn close-btn" title="Fermer">
  1736.                       <i class="bi bi-x-lg"></i>
  1737.                     </button>
  1738.                   </div>
  1739.                 </div>
  1740.                 <!-- Navigation -->
  1741.                 <button class="slider-nav prev-btn" title="Précédent">
  1742.                   <i class="bi bi-arrow-left-circle-fill"></i>
  1743.                 </button>
  1744.                 <button class="slider-nav next-btn" title="Suivant">
  1745.                   <i class="bi bi-arrow-right-circle-fill"></i>
  1746.                 </button>
  1747.                 <!-- Container des slides -->
  1748.                 <div class="slider-container">
  1749.                   <div class="slider-track" id="sliderTrack">
  1750.                     ${this.favorites.map((fav, index) => `
  1751.                       <div class="slide" data-index="${index}">
  1752.                         <div class="slide-content">
  1753.                           <img src="${fav.url}" alt="${fav.description || 'Photo favorite'}" 
  1754.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  1755.                                draggable="false">
  1756.                         </div>
  1757.                         <div class="slide-info">
  1758.                           <h4>${fav.description || 'Photo favorite'}</h4>
  1759.                           <p>${fav.date || 'Date inconnue'}</p>
  1760.                         </div>
  1761.                       </div>
  1762.                     `).join('')}
  1763.                   </div>
  1764.                 </div>
  1765.                 <!-- Thumbnails -->
  1766.                 <div class="slider-thumbnails">
  1767.                   ${this.favorites.map((fav, index) => `
  1768.                     <div class="thumbnail ${index === this.currentIndex ? 'active' : ''}" 
  1769.                          data-index="${index}">
  1770.                       <img src="${fav.url}" alt="Thumbnail ${index + 1}">
  1771.                     </div>
  1772.                   `).join('')}
  1773.                 </div>
  1774.               </div>
  1775.             `;
  1776.             // Injecter dans le DOM
  1777.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  1778.           }
  1779.           showSlide(index) {
  1780.             if (index < 0 || index >= this.favorites.length) return;
  1781.             this.currentIndex = index;
  1782.             const track = document.getElementById('sliderTrack');
  1783.             const translateX = -index * 100;
  1784.             
  1785.             track.style.transform = `translateX(${translateX}%)`;
  1786.             // ⚡ OPTIMISÉ: Éviter forEach sur querySelectorAll (mémoire)
  1787.             const thumbnails = document.querySelectorAll('.thumbnail');
  1788.             for (let i = 0; i < thumbnails.length; i++) {
  1789.               const thumb = thumbnails[i];
  1790.               if (thumb?.classList) {
  1791.                 thumb.classList.toggle('active', i === index);
  1792.               }
  1793.             }
  1794.             // Mettre à jour le bouton favori
  1795.             const currentFav = this.favorites[index];
  1796.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  1797.             if (favoriteBtn && currentFav) {
  1798.               favoriteBtn.setAttribute('data-id', currentFav.id);
  1799.             }
  1800.           }
  1801.           nextSlide() {
  1802.             const nextIndex = (this.currentIndex + 1) % this.favorites.length;
  1803.             this.showSlide(nextIndex);
  1804.           }
  1805.           prevSlide() {
  1806.             const prevIndex = (this.currentIndex - 1 + this.favorites.length) % this.favorites.length;
  1807.             this.showSlide(prevIndex);
  1808.           }
  1809.           close() {
  1810.             if (!this.isOpen) return;
  1811.             const slider = document.querySelector('.favorites-slider');
  1812.             if (slider && slider.classList) {
  1813.               slider.classList.remove('active');
  1814.               
  1815.               setTimeout(() => {
  1816.                 slider.remove();
  1817.                 this.isOpen = false;
  1818.               }, 300);
  1819.             }
  1820.           }
  1821.           attachEvents() {
  1822.             const slider = document.getElementById('favoritesSlider');
  1823.             
  1824.             // Bouton fermer
  1825.             slider.querySelector('.close-btn').addEventListener('click', () => this.close());
  1826.             
  1827.             // Navigation
  1828.             slider.querySelector('.prev-btn').addEventListener('click', () => this.prevSlide());
  1829.             slider.querySelector('.next-btn').addEventListener('click', () => this.nextSlide());
  1830.             
  1831.             // ⚡ OPTIMISÉ: Event delegation au lieu de listeners multiples
  1832.             const thumbContainer = slider.querySelector('.thumbnails-container') || slider;
  1833.             if (thumbContainer && !thumbContainer.hasAttribute('data-thumb-delegated')) {
  1834.               thumbContainer.addEventListener('click', (e) => {
  1835.                 const thumb = e.target.closest('.thumbnail');
  1836.                 if (thumb) {
  1837.                   const thumbnails = slider.querySelectorAll('.thumbnail');
  1838.                   const index = Array.from(thumbnails).indexOf(thumb);
  1839.                   if (index !== -1) this.showSlide(index);
  1840.                 }
  1841.               });
  1842.               thumbContainer.setAttribute('data-thumb-delegated', 'true');
  1843.             }
  1844.             // Bouton favori
  1845.             slider.querySelector('.favorite-btn').addEventListener('click', (e) => {
  1846.               const favoriteId = e.currentTarget.getAttribute('data-id');
  1847.               if (favoriteId) {
  1848.                 removeFavorite(favoriteId);
  1849.                 // Recharger le slider avec les favoris mis à jour
  1850.                 setTimeout(() => {
  1851.                   this.close();
  1852.                   if (getFavoriteItems().filter(item => item.type === 'image').length > 0) {
  1853.                     this.open(this.favorites[0]?.id);
  1854.                   }
  1855.                 }, 100);
  1856.               }
  1857.             });
  1858.             // Clavier
  1859.             document.addEventListener('keydown', this.handleKeyboard.bind(this));
  1860.             
  1861.             // Clic sur overlay pour fermer
  1862.             slider.querySelector('.slider-overlay').addEventListener('click', () => this.close());
  1863.             // Touch/swipe sur mobile
  1864.             const track = slider.querySelector('.slider-track');
  1865.             track.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true });
  1866.             track.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true });
  1867.             // Zoom sur clic simple (5 niveaux) + double-clic (legacy)
  1868.             slider.querySelectorAll('.slide img').forEach(img => {
  1869.               let zoomLevel = 1;
  1870.               let isDragging = false;
  1871.               let startX, startY, translateX = 0, translateY = 0;
  1872.               
  1873.               // Zoom sur clic simple (nouveau système 5 niveaux)
  1874.               img.addEventListener('click', (e) => {
  1875.                 e.preventDefault();
  1876.                 e.stopPropagation();
  1877.                 
  1878.                 // Cycle à travers 5 niveaux de zoom
  1879.                 switch(zoomLevel) {
  1880.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  1881.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  1882.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  1883.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  1884.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  1885.                   case 5: 
  1886.                     zoomLevel = 1;                 // 5x → 1x (reset)
  1887.                     translateX = 0;
  1888.                     translateY = 0;
  1889.                     break;
  1890.                 }
  1891.                 console.log('FavoritesSlider - Zoom niveau:', zoomLevel);
  1892.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1893.                 
  1894.                 // Curseur selon le niveau
  1895.                 if (zoomLevel === 1) {
  1896.                   img.style.cursor = 'zoom-in';
  1897.                   img.removeAttribute('data-zoomed');
  1898.                 } else if (zoomLevel === 5) {
  1899.                   img.style.cursor = 'zoom-out';
  1900.                   img.setAttribute('data-zoomed', zoomLevel);
  1901.                 } else {
  1902.                   img.style.cursor = 'zoom-in';
  1903.                   img.setAttribute('data-zoomed', zoomLevel);
  1904.                 }
  1905.               });
  1906.               
  1907.               // Drag pour déplacer l'image zoomée
  1908.               img.addEventListener('mousedown', (e) => {
  1909.                 if (zoomLevel > 1) {
  1910.                   isDragging = true;
  1911.                   startX = e.clientX - translateX;
  1912.                   startY = e.clientY - translateY;
  1913.                   img.style.cursor = 'grabbing';
  1914.                   e.preventDefault();
  1915.                   e.stopPropagation();
  1916.                 }
  1917.               });
  1918.               const handleMouseMove = (e) => {
  1919.                 if (isDragging && zoomLevel > 1) {
  1920.                   translateX = e.clientX - startX;
  1921.                   translateY = e.clientY - startY;
  1922.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1923.                 }
  1924.               };
  1925.               const handleMouseUp = () => {
  1926.                 if (isDragging) {
  1927.                   isDragging = false;
  1928.                   if (zoomLevel === 1) {
  1929.                     img.style.cursor = 'zoom-in';
  1930.                   } else if (zoomLevel === 5) {
  1931.                     img.style.cursor = 'zoom-out';
  1932.                   } else {
  1933.                     img.style.cursor = 'zoom-in';
  1934.                   }
  1935.                 }
  1936.               };
  1937.               document.addEventListener('mousemove', handleMouseMove);
  1938.               document.addEventListener('mouseup', handleMouseUp);
  1939.               // Support tactile pour mobile
  1940.               img.addEventListener('touchstart', (e) => {
  1941.                 if (zoomLevel > 1 && e.touches.length === 1) {
  1942.                   isDragging = true;
  1943.                   const touch = e.touches[0];
  1944.                   startX = touch.clientX - translateX;
  1945.                   startY = touch.clientY - translateY;
  1946.                   e.preventDefault();
  1947.                 }
  1948.               });
  1949.               img.addEventListener('touchmove', (e) => {
  1950.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  1951.                   const touch = e.touches[0];
  1952.                   translateX = touch.clientX - startX;
  1953.                   translateY = touch.clientY - startY;
  1954.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  1955.                   e.preventDefault();
  1956.                 }
  1957.               });
  1958.               img.addEventListener('touchend', () => {
  1959.                 isDragging = false;
  1960.               });
  1961.               // Initialiser le curseur
  1962.               img.style.cursor = 'zoom-in';
  1963.               
  1964.               // Garder aussi le double-clic pour compatibilité (legacy)
  1965.               img.addEventListener('dblclick', this.handleZoom.bind(this));
  1966.             });
  1967.           }
  1968.           handleKeyboard(e) {
  1969.             if (!this.isOpen) return;
  1970.             
  1971.             switch(e.key) {
  1972.               case 'Escape':
  1973.                 this.close();
  1974.                 break;
  1975.               case 'ArrowLeft':
  1976.                 e.preventDefault();
  1977.                 this.prevSlide();
  1978.                 break;
  1979.               case 'ArrowRight':
  1980.                 e.preventDefault();
  1981.                 this.nextSlide();
  1982.                 break;
  1983.             }
  1984.           }
  1985.           handleTouchStart(e) {
  1986.             this.touchStartX = e.changedTouches[0].screenX;
  1987.           }
  1988.           handleTouchEnd(e) {
  1989.             this.touchEndX = e.changedTouches[0].screenX;
  1990.             this.handleSwipe();
  1991.           }
  1992.           handleSwipe() {
  1993.             const swipeThreshold = 50;
  1994.             const diff = this.touchStartX - this.touchEndX;
  1995.             if (Math.abs(diff) > swipeThreshold) {
  1996.               if (diff > 0) {
  1997.                 this.nextSlide(); // Swipe left -> next
  1998.               } else {
  1999.                 this.prevSlide(); // Swipe right -> prev
  2000.               }
  2001.             }
  2002.           }
  2003.           handleZoom(e) {
  2004.             const img = e.target;
  2005.             
  2006.             // Vérifier que l'élément et ses propriétés existent
  2007.             if (!img || !img.classList) {
  2008.               console.warn('Élément image invalide pour le zoom');
  2009.               return;
  2010.             }
  2011.             
  2012.             // Gérer les différents niveaux de zoom
  2013.             if (img.classList.contains('zoom-4x')) {
  2014.               // Retour à la taille normale
  2015.               img.classList.remove('zoom-4x', 'zoom-3x', 'zoom-2x', 'zoomed');
  2016.               img.style.cursor = 'pointer';
  2017.               img.style.transform = 'scale(1)'; // Réinitialiser la position
  2018.             } else if (img.classList.contains('zoom-3x')) {
  2019.               // Passer au zoom 4x
  2020.               img.classList.remove('zoom-3x');
  2021.               img.classList.add('zoom-4x');
  2022.               img.style.cursor = 'zoom-out';
  2023.               img.style.transform = 'scale(4)'; // Réinitialiser la position
  2024.             } else if (img.classList.contains('zoom-2x')) {
  2025.               // Passer au zoom 3x
  2026.               img.classList.remove('zoom-2x');
  2027.               img.classList.add('zoom-3x');
  2028.               img.style.cursor = 'zoom-in';
  2029.               img.style.transform = 'scale(3)'; // Réinitialiser la position
  2030.             } else if (img.classList.contains('zoomed')) {
  2031.               // Passer au zoom 2x
  2032.               img.classList.remove('zoomed');
  2033.               img.classList.add('zoom-2x');
  2034.               img.style.cursor = 'zoom-in';
  2035.               img.style.transform = 'scale(2)'; // Réinitialiser la position
  2036.             } else {
  2037.               // Premier zoom (1.5x)
  2038.               img.classList.add('zoomed');
  2039.               img.style.cursor = 'zoom-in';
  2040.               img.style.transform = 'scale(1.5)'; // Réinitialiser la position
  2041.             }
  2042.           }
  2043.         }
  2044.         // Slider universel pour toutes les images (favoris + galerie normale)
  2045.         class UniversalImageSlider {
  2046.           constructor() {
  2047.             this.currentIndex = 0;
  2048.             this.images = [];
  2049.             this.isOpen = false;
  2050.             this.touchStartX = 0;
  2051.             this.touchEndX = 0;
  2052.           }
  2053.           // Ouvrir avec une image spécifique depuis les favoris
  2054.           openFromFavorites(favoriteId) {
  2055.             console.log('Ouverture du slider depuis favoris:', favoriteId);
  2056.             
  2057.             // Récupérer tous les favoris images
  2058.             this.images = getFavoriteItems().filter(item => item.type === 'image');
  2059.             
  2060.             if (this.images.length === 0) {
  2061.               console.error('Aucun favori image trouvé');
  2062.               return;
  2063.             }
  2064.             // Trouver l'index de l'image courante
  2065.             this.currentIndex = this.images.findIndex(img => img.id === favoriteId);
  2066.             if (this.currentIndex === -1) this.currentIndex = 0;
  2067.             this.openSlider();
  2068.           }
  2069.           // Ouvrir avec une image spécifique depuis la galerie normale
  2070.           openFromGallery(imageId, dayContainer) {
  2071.             console.log('Ouverture du slider depuis galerie:', imageId, dayContainer);
  2072.             
  2073.             // Récupérer toutes les images du jour courant
  2074.             const dayImages = [];
  2075.             
  2076.             // Chercher dans le container du jour (peut être .dynamic-card ou autre)
  2077.             let photoItems;
  2078.             if (dayContainer) {
  2079.               photoItems = dayContainer.querySelectorAll('.photo-item');
  2080.             } else {
  2081.               // Fallback: chercher dans tout le document
  2082.               photoItems = document.querySelectorAll('.photo-item');
  2083.             }
  2084.             
  2085.             console.log('Photo items trouvés:', photoItems.length);
  2086.             
  2087.             photoItems.forEach(item => {
  2088.               const img = item.querySelector('img');
  2089.               const heartIcon = item.querySelector('.heart-icon');
  2090.               
  2091.               console.log('Processing item:', {
  2092.                 img: !!img,
  2093.                 heartIcon: !!heartIcon,
  2094.                 imgSrc: img ? img.src : null,
  2095.                 heartIconId: heartIcon ? heartIcon.getAttribute('data-id') : null
  2096.               });
  2097.               
  2098.               if (img && heartIcon) {
  2099.                 dayImages.push({
  2100.                   id: heartIcon.getAttribute('data-id'),
  2101.                   url: heartIcon.getAttribute('data-path') || img.src,
  2102.                   description: heartIcon.getAttribute('data-description') || img.alt || 'Photo',
  2103.                   date: '', // Pas de date spécifique pour les images de galerie
  2104.                   type: 'image'
  2105.                 });
  2106.               }
  2107.             });
  2108.             console.log('Images trouvées:', dayImages);
  2109.             this.images = dayImages;
  2110.             
  2111.             if (this.images.length === 0) {
  2112.               console.error('Aucune image trouvée dans ce jour');
  2113.               // Essayer de créer au moins l'image courante
  2114.               const currentHeartIcon = document.querySelector(`[data-id="${imageId}"]`);
  2115.               if (currentHeartIcon) {
  2116.                 const currentImg = currentHeartIcon.closest('.photo-item').querySelector('img');
  2117.                 if (currentImg) {
  2118.                   this.images = [{
  2119.                     id: imageId,
  2120.                     url: currentHeartIcon.getAttribute('data-path') || currentImg.src,
  2121.                     description: currentHeartIcon.getAttribute('data-description') || currentImg.alt || 'Photo',
  2122.                     date: '',
  2123.                     type: 'image'
  2124.                   }];
  2125.                   console.log('Image de fallback créée:', this.images);
  2126.                 }
  2127.               }
  2128.               
  2129.               if (this.images.length === 0) {
  2130.                 return;
  2131.               }
  2132.             }
  2133.             // Trouver l'index de l'image courante
  2134.             this.currentIndex = this.images.findIndex(img => img.id === imageId);
  2135.             if (this.currentIndex === -1) this.currentIndex = 0;
  2136.             console.log('Index trouvé:', this.currentIndex);
  2137.             this.openSlider();
  2138.           }
  2139.           openSlider() {
  2140.             console.log('=== openSlider appelée ===');
  2141.             console.log('Index courant:', this.currentIndex, 'Total:', this.images.length);
  2142.             console.log('Images à afficher:', this.images);
  2143.             // Supprimer tout slider existant
  2144.             const existingSlider = document.querySelector('.universal-slider');
  2145.             if (existingSlider) {
  2146.               console.log('Suppression du slider existant');
  2147.               existingSlider.remove();
  2148.             }
  2149.             this.createSlider();
  2150.             this.showSlide(this.currentIndex);
  2151.             this.attachEvents();
  2152.             this.isOpen = true;
  2153.               // Animation d'ouverture immédiate et garantie avec diagnostic
  2154.               setTimeout(() => {
  2155.                 if (window.diagnoseSliderDisplay) {
  2156.                   window.diagnoseSliderDisplay('#universalSlider');
  2157.                 }
  2158.               }, 50);
  2159.           }
  2160.           createSlider() {
  2161.             // ⚡ OPTIMISATION: Créer seulement si nécessaire
  2162.             if (document.querySelector('#universalSlider')) {
  2163.               return; // Déjà créé
  2164.             }
  2165.             
  2166.             console.log('Création slider pour', this.images.length, 'images');
  2167.             
  2168.             // HTML optimisé et minimal
  2169.             const sliderHTML = `
  2170.               <div class="universal-slider" id="universalSlider" style="
  2171.                 position: fixed !important; 
  2172.                 top: 0 !important; 
  2173.                 left: 0 !important; 
  2174.                 width: 100% !important; 
  2175.                 height: 100% !important; 
  2176.                 background: rgba(0,0,0,0.95) !important; 
  2177.                 z-index: 9999 !important; 
  2178.                 display: flex !important; 
  2179.                 align-items: center !important; 
  2180.                 justify-content: center !important; 
  2181.                 opacity: 1 !important; 
  2182.                 visibility: visible !important;
  2183.               ">
  2184.                 <div class="slider-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer;"></div>
  2185.                 
  2186.                 <!-- Header avec contrôles -->
  2187.                 <div class="slider-header" style="
  2188.                   position: absolute; 
  2189.                   top: 20px; 
  2190.                   right: 20px; 
  2191.                   display: flex; 
  2192.                   align-items: center; 
  2193.                   gap: 15px; 
  2194.                   z-index: 10001;
  2195.                 ">
  2196.                   <button class="slider-btn close-btn" title="Fermer" style="
  2197.                     background: rgba(255,255,255,0.2); 
  2198.                     border: none; 
  2199.                     color: white; 
  2200.                     width: 40px; 
  2201.                     height: 40px; 
  2202.                     border-radius: 50%; 
  2203.                     cursor: pointer; 
  2204.                     display: flex; 
  2205.                     align-items: center; 
  2206.                     justify-content: center;
  2207.                     transition: background 0.2s ease;
  2208.                   ">
  2209.                     <i class="bi bi-x-lg"></i>
  2210.                   </button>
  2211.                 </div>
  2212.                 <!-- Navigation -->
  2213.                 <button class="slider-nav prev-btn" title="Précédent" style="
  2214.                   position: absolute; 
  2215.                   left: 20px; 
  2216.                   top: 50%; 
  2217.                   transform: translateY(-50%); 
  2218.                   background: rgba(255,255,255,0.2); 
  2219.                   border: none; 
  2220.                   color: white; 
  2221.                   width: 50px; 
  2222.                   height: 50px; 
  2223.                   border-radius: 50%; 
  2224.                   cursor: pointer; 
  2225.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  2226.                   align-items: center; 
  2227.                   justify-content: center; 
  2228.                   z-index: 10001;
  2229.                   transition: background 0.2s ease;
  2230.                 ">
  2231.                   <i class="bi bi-arrow-left-circle-fill" style="font-size: 20px;"></i>
  2232.                 </button>
  2233.                 <button class="slider-nav next-btn" title="Suivant" style="
  2234.                   position: absolute; 
  2235.                   right: 20px; 
  2236.                   top: 50%; 
  2237.                   transform: translateY(-50%); 
  2238.                   background: rgba(255,255,255,0.2); 
  2239.                   border: none; 
  2240.                   color: white; 
  2241.                   width: 50px; 
  2242.                   height: 50px; 
  2243.                   border-radius: 50%; 
  2244.                   cursor: pointer; 
  2245.                   display: ${this.images.length > 1 ? 'flex' : 'none'}; 
  2246.                   align-items: center; 
  2247.                   justify-content: center; 
  2248.                   z-index: 10001;
  2249.                   transition: background 0.2s ease;
  2250.                 ">
  2251.                   <i class="bi bi-arrow-right-circle-fill" style="font-size: 20px;"></i>
  2252.                 </button>
  2253.                 <!-- Container des slides -->
  2254.                 <div class="slider-container" style="
  2255.                   position: relative; 
  2256.                   width: 90%; 
  2257.                   height: 90%; 
  2258.                   display: flex; 
  2259.                   align-items: center; 
  2260.                   justify-content: center; 
  2261.                   z-index: 10000;
  2262.                 ">
  2263.                   <div class="slider-track" id="universalSliderTrack" style="
  2264.                     position: relative; 
  2265.                     width: 100%; 
  2266.                     height: 100%; 
  2267.                     display: flex; 
  2268.                     align-items: center; 
  2269.                     justify-content: center;
  2270.                     overflow: hidden;
  2271.                   ">
  2272.                     ${this.images.map((img, index) => `
  2273.                       <div class="slide" data-index="${index}" style="
  2274.                         position: absolute; 
  2275.                         width: 100%; 
  2276.                         height: 100%; 
  2277.                         display: ${index === 0 ? 'flex' : 'none'}; 
  2278.                         align-items: center; 
  2279.                         justify-content: center; 
  2280.                         flex-direction: column;
  2281.                       ">
  2282.                         <div class="slide-content" style="
  2283.                           position: relative; 
  2284.                           max-width: 100%; 
  2285.                           max-height: 90%; 
  2286.                           display: flex; 
  2287.                           align-items: center; 
  2288.                           justify-content: center;
  2289.                         ">
  2290.                           <img src="${img.url}" alt="${img.description || 'Photo'}" 
  2291.                                loading="${index <= 2 ? 'eager' : 'lazy'}"
  2292.                                draggable="false"
  2293.                                style="
  2294.                                  max-width: 100%; 
  2295.                                  max-height: 100%; 
  2296.                                  object-fit: contain; 
  2297.                                  cursor: zoom-in;
  2298.                                  transition: transform 0.3s ease;
  2299.                                  user-select: none;
  2300.                                ">
  2301.                         </div>
  2302.                         ${img.description ? `
  2303.                         <div class="slide-info" style="
  2304.                           position: absolute; 
  2305.                           bottom: 60px; 
  2306.                           left: 50%; 
  2307.                           transform: translateX(-50%); 
  2308.                           text-align: center; 
  2309.                           color: white; 
  2310.                           background: rgba(0,0,0,0.6); 
  2311.                           padding: 8px 16px; 
  2312.                           border-radius: 20px; 
  2313.                           max-width: 80%;
  2314.                           font-size: 14px;
  2315.                         ">
  2316.                           ${img.description}
  2317.                         </div>
  2318.                         ` : ''}
  2319.                       </div>
  2320.                     `).join('')}
  2321.                   </div>
  2322.                 </div>
  2323.                 <!-- Galerie de thumbnails révolutionnaire -->
  2324.                 <div class="cinema-gallery" style="
  2325.                   position: absolute; 
  2326.                   bottom: 0; 
  2327.                   left: 0; 
  2328.                   right: 0;
  2329.                   height: 180px;
  2330.                   background: linear-gradient(0deg, rgba(0,0,0,0.95) 0%, rgba(0,0,0,0.7) 50%, transparent 100%);
  2331.                   z-index: 10001;
  2332.                   display: flex;
  2333.                   flex-direction: column;
  2334.                   justify-content: flex-end;
  2335.                   padding: 0 20px 20px;
  2336.                 ">
  2337.                   
  2338.                   <!-- Info header avec compteur élégant -->
  2339.                   <div class="gallery-header" style="
  2340.                     display: flex;
  2341.                     justify-content: space-between;
  2342.                     align-items: center;
  2343.                     margin-bottom: 15px;
  2344.                   ">
  2345.                     <div class="photo-counter" style="
  2346.                       color: white;
  2347.                       font-size: 16px;
  2348.                       font-weight: 600;
  2349.                       display: flex;
  2350.                       align-items: center;
  2351.                       gap: 8px;
  2352.                     ">
  2353.                       <div style="
  2354.                         width: 8px;
  2355.                         height: 8px;
  2356.                         background: #10b981;
  2357.                         border-radius: 50%;
  2358.                         animation: pulse 2s infinite;
  2359.                       "></div>
  2360.                       <span>${(this.currentIndex || 0) + 1}</span>
  2361.                       <span style="opacity: 0.6;">sur</span>
  2362.                       <span>${this.images.length}</span>
  2363.                     </div>
  2364.                     <div class="gallery-controls" style="
  2365.                       display: flex;
  2366.                       gap: 12px;
  2367.                       align-items: center;
  2368.                     ">
  2369.                       <div style="color: rgba(255,255,255,0.7); font-size: 12px;">
  2370.                         Clic = Zoom 5x • Drag = Déplacer
  2371.                       </div>
  2372.                     </div>
  2373.                   </div>
  2374.                   
  2375.                   <!-- Carrousel de thumbnails cinématographique -->
  2376.                   <div class="cinema-carousel" style="
  2377.                     position: relative;
  2378.                     height: 80px;
  2379.                     overflow: hidden;
  2380.                   ">
  2381.                     <!-- Container scrollable -->
  2382.                     <div class="carousel-track" id="carouselTrack" style="
  2383.                       display: flex;
  2384.                       gap: 12px;
  2385.                       height: 100%;
  2386.                       overflow-x: auto;
  2387.                       overflow-y: hidden;
  2388.                       scroll-behavior: smooth;
  2389.                       scrollbar-width: none;
  2390.                       -ms-overflow-style: none;
  2391.                       padding: 0 50px;
  2392.                     ">
  2393.                       ${this.images.map((img, index) => `
  2394.                         <div class="cinema-thumb ${index === 0 ? 'active' : ''}" 
  2395.                              data-index="${index}" 
  2396.                              style="
  2397.                                flex: 0 0 auto;
  2398.                                width: ${index === 0 ? '100px' : '70px'};
  2399.                                height: 70px;
  2400.                                border-radius: 8px;
  2401.                                overflow: hidden;
  2402.                                cursor: pointer;
  2403.                                position: relative;
  2404.                                transition: transform 0.2s ease, opacity 0.2s ease;
  2405.                                transform: ${index === 0 ? 'translateY(-4px)' : 'translateY(0)'};
  2406.                                opacity: ${index === 0 ? '1' : '0.7'};
  2407.                                border: ${index === 0 ? '2px solid #10b981' : '1px solid rgba(255,255,255,0.3)'};
  2408.                              "
  2409.                              onmouseenter="
  2410.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  2411.                                  this.style.opacity = '0.9';
  2412.                                }
  2413.                              "
  2414.                              onmouseleave="
  2415.                                if (${index} !== (window.universalSlider?.currentIndex || 0)) {
  2416.                                  this.style.opacity = '0.7';
  2417.                                }
  2418.                              ">
  2419.                           
  2420.                           <!-- Image principale -->
  2421.                           <img src="${img.url}" 
  2422.                                alt="${img.description || 'Photo'}" 
  2423.                                loading="lazy" 
  2424.                                decoding="async"
  2425.                                style="
  2426.                                  width: 100%; 
  2427.                                  height: 100%; 
  2428.                                  object-fit: cover;
  2429.                                ">
  2430.                           
  2431.                           <!-- Numéro simplifié -->
  2432.                           <div class="thumb-number" style="
  2433.                             position: absolute;
  2434.                             top: 4px;
  2435.                             right: 4px;
  2436.                             background: ${index === 0 ? '#10b981' : 'rgba(0,0,0,0.8)'};
  2437.                             color: white;
  2438.                             font-size: 10px;
  2439.                             font-weight: 500;
  2440.                             padding: 2px 5px;
  2441.                             border-radius: 4px;
  2442.                             min-width: 16px;
  2443.                             text-align: center;
  2444.                           ">
  2445.                             ${index + 1}
  2446.                           </div>
  2447.                           
  2448.                           <!-- Indicateur actif simplifié -->
  2449.                           ${index === 0 ? `
  2450.                           <div class="active-indicator" style="
  2451.                             position: absolute;
  2452.                             bottom: -1px;
  2453.                             left: 50%;
  2454.                             transform: translateX(-50%);
  2455.                             width: 16px;
  2456.                             height: 2px;
  2457.                             background: #10b981;
  2458.                             border-radius: 1px;
  2459.                           "></div>
  2460.                           ` : ''}
  2461.                           
  2462.                           <!-- Preview au hover pour les non-actifs -->
  2463.                           ${index !== 0 ? `
  2464.                           <div class="hover-preview" style="
  2465.                             position: absolute;
  2466.                             bottom: -40px;
  2467.                             left: 50%;
  2468.                             transform: translateX(-50%);
  2469.                             background: rgba(0,0,0,0.9);
  2470.                             color: white;
  2471.                             font-size: 11px;
  2472.                             padding: 4px 8px;
  2473.                             border-radius: 6px;
  2474.                             opacity: 0;
  2475.                             pointer-events: none;
  2476.                             transition: all 0.3s ease;
  2477.                             white-space: nowrap;
  2478.                             z-index: 1000;
  2479.                           ">
  2480.                             Photo ${index + 1}
  2481.                           </div>
  2482.                           ` : ''}
  2483.                         </div>
  2484.                       `).join('')}
  2485.                     </div>
  2486.                     
  2487.                     <!-- Gradients de fade sur les côtés -->
  2488.                     <div style="
  2489.                       position: absolute;
  2490.                       left: 0;
  2491.                       top: 0;
  2492.                       bottom: 0;
  2493.                       width: 50px;
  2494.                       background: linear-gradient(90deg, rgba(0,0,0,0.8), transparent);
  2495.                       pointer-events: none;
  2496.                       z-index: 1;
  2497.                     "></div>
  2498.                     <div style="
  2499.                       position: absolute;
  2500.                       right: 0;
  2501.                       top: 0;
  2502.                       bottom: 0;
  2503.                       width: 50px;
  2504.                       background: linear-gradient(-90deg, rgba(0,0,0,0.8), transparent);
  2505.                       pointer-events: none;
  2506.                       z-index: 1;
  2507.                     "></div>
  2508.                   </div>
  2509.                 </div>
  2510.                 <!-- Animations CSS Optimisées -->
  2511.                 <style>
  2512.                   /* Optimisation: animations réduites et ciblées */
  2513.                   @keyframes pulse {
  2514.                     0%, 100% { opacity: 1; }
  2515.                     50% { opacity: 0.7; }
  2516.                   }
  2517.                   
  2518.                   /* Suppression du glow coûteux - remplacé par border simple */
  2519.                   .cinema-thumb.active .active-indicator {
  2520.                     background: #10b981;
  2521.                     animation: none; /* Suppression de l'animation glow */
  2522.                   }
  2523.                   
  2524.                   /* Hover optimisé - moins d'effets */
  2525.                   .cinema-thumb:hover .hover-preview {
  2526.                     opacity: 1 !important;
  2527.                     bottom: -35px !important;
  2528.                   }
  2529.                   
  2530.                   /* Masquer scrollbar */
  2531.                   .carousel-track::-webkit-scrollbar {
  2532.                     display: none;
  2533.                   }
  2534.                   
  2535.                   /* Optimisation: will-change pour les éléments animés */
  2536.                   .cinema-thumb {
  2537.                     will-change: transform, filter;
  2538.                   }
  2539.                   
  2540.                   .progress-bar {
  2541.                     will-change: width;
  2542.                   }
  2543.                 </style>
  2544.               </div>
  2545.             `;
  2546.             // Injecter dans le DOM
  2547.             console.log('Ajout du slider au DOM');
  2548.             document.body.insertAdjacentHTML('beforeend', sliderHTML);
  2549.             
  2550.             // Vérifier que le slider a été ajouté
  2551.             const addedSlider = document.querySelector('#universalSlider');
  2552.             console.log('Slider ajouté:', !!addedSlider);
  2553.             if (addedSlider) {
  2554.               console.log('Slider trouvé dans le DOM');
  2555.               console.log('Styles du slider:', {
  2556.                 display: addedSlider.style.display,
  2557.                 opacity: addedSlider.style.opacity,
  2558.                 visibility: addedSlider.style.visibility,
  2559.                 zIndex: addedSlider.style.zIndex,
  2560.                 position: addedSlider.style.position
  2561.               });
  2562.               console.log('Slider dans viewport:', addedSlider.getBoundingClientRect());
  2563.             } else {
  2564.               console.error('Erreur: slider non trouvé après ajout');
  2565.             }
  2566.           }
  2567.           showSlide(index) {
  2568.             console.log('=== showSlide appelée ===', 'index:', index, 'total:', this.images.length);
  2569.             if (index < 0 || index >= this.images.length) {
  2570.               console.log('Index invalide, arrêt');
  2571.               return;
  2572.             }
  2573.             this.currentIndex = index;
  2574.             // Masquer toutes les slides et afficher seulement la courante
  2575.             const slides = document.querySelectorAll('#universalSlider .slide');
  2576.             console.log('Slides trouvées:', slides.length);
  2577.             slides.forEach((slide, i) => {
  2578.               slide.style.display = i === index ? 'flex' : 'none';
  2579.               console.log(`Slide ${i}:`, i === index ? 'visible' : 'cachée');
  2580.             });
  2581.             // Mettre à jour le compteur
  2582.             const currentSlideSpan = document.querySelector('#universalSlider .current-slide');
  2583.             if (currentSlideSpan) {
  2584.               currentSlideSpan.textContent = index + 1;
  2585.               console.log('Compteur mis à jour:', index + 1);
  2586.             }
  2587.             // Mettre à jour les boutons de navigation
  2588.             const prevBtn = document.querySelector('#universalSlider .prev-btn');
  2589.             const nextBtn = document.querySelector('#universalSlider .next-btn');
  2590.             
  2591.             if (prevBtn) {
  2592.               prevBtn.style.opacity = index > 0 ? '1' : '0.5';
  2593.             }
  2594.             if (nextBtn) {
  2595.               nextBtn.style.opacity = index < this.images.length - 1 ? '1' : '0.5';
  2596.             }
  2597.             // Mettre à jour la galerie cinématographique
  2598.             this.updateCinemaGallery(index);
  2599.             // Mettre à jour le bouton favori
  2600.             this.updateFavoriteButton();
  2601.           }
  2602.           updateCinemaGallery(activeIndex) {
  2603.             // Mettre à jour la progress bar (optimisé)
  2604.             const progressBar = document.querySelector('.progress-bar');
  2605.             if (progressBar) {
  2606.               progressBar.style.width = `${(activeIndex + 1) / this.images.length * 100}%`;
  2607.             }
  2608.             // Mettre à jour le compteur (optimisé)
  2609.             const counter = document.querySelector('.photo-counter span');
  2610.             if (counter) {
  2611.               counter.textContent = activeIndex + 1;
  2612.             }
  2613.             // Mettre à jour les thumbnails - optimisé avec moins de calculs
  2614.             const cinemaThumbs = document.querySelectorAll('.cinema-thumb');
  2615.             cinemaThumbs.forEach((thumb, i) => {
  2616.               const isActive = i === activeIndex;
  2617.               
  2618.               // Optimisation: changements minimaux
  2619.               if (isActive) {
  2620.                 thumb.style.width = '100px';
  2621.                 thumb.style.transform = 'translateY(-4px)';
  2622.                 thumb.style.opacity = '1';
  2623.                 thumb.style.borderColor = '#10b981';
  2624.                 thumb.style.borderWidth = '2px';
  2625.                 
  2626.                 // Numéro actif - simplifié
  2627.                 const number = thumb.querySelector('.thumb-number');
  2628.                 if (number) {
  2629.                   number.style.background = '#10b981';
  2630.                 }
  2631.               } else {
  2632.                 thumb.style.width = '70px';
  2633.                 thumb.style.transform = 'translateY(0)';
  2634.                 thumb.style.opacity = '0.7';
  2635.                 thumb.style.borderColor = 'rgba(255,255,255,0.3)';
  2636.                 thumb.style.borderWidth = '1px';
  2637.                 
  2638.                 // Numéro inactif - simplifié
  2639.                 const number = thumb.querySelector('.thumb-number');
  2640.                 if (number) {
  2641.                   number.style.background = 'rgba(0,0,0,0.8)';
  2642.                 }
  2643.               }
  2644.             });
  2645.             // Auto-scroll optimisé avec throttling
  2646.             if (!this.scrollTimeout) {
  2647.               this.scrollTimeout = setTimeout(() => {
  2648.                 const track = document.getElementById('carouselTrack');
  2649.                 const activeThumb = document.querySelector(`.cinema-thumb[data-index="${activeIndex}"]`);
  2650.                 if (track && activeThumb) {
  2651.                   const scrollLeft = activeThumb.offsetLeft - (track.clientWidth / 2) + (activeThumb.clientWidth / 2);
  2652.                   track.scrollTo({
  2653.                     left: scrollLeft,
  2654.                     behavior: 'smooth'
  2655.                   });
  2656.                 }
  2657.                 this.scrollTimeout = null;
  2658.               }, 50); // Throttle à 50ms
  2659.             }
  2660.           }
  2661.           updateFavoriteButton() {
  2662.             const currentImage = this.images[this.currentIndex];
  2663.             const favoriteBtn = document.querySelector('.slider-controls .favorite-btn');
  2664.             
  2665.             if (favoriteBtn && currentImage) {
  2666.               favoriteBtn.setAttribute('data-id', currentImage.id);
  2667.               
  2668.               // Vérifier si l'image est déjà en favoris
  2669.               const heartIcon = document.querySelector(`[data-id="${currentImage.id}"]`);
  2670.               const isFavorite = heartIcon && heartIcon.querySelector('i.bi-heart-fill');
  2671.               
  2672.               const icon = favoriteBtn.querySelector('i');
  2673.               if (isFavorite) {
  2674.                 icon.className = 'bi bi-heart-fill';
  2675.                 favoriteBtn.style.color = '#e91e63';
  2676.               } else {
  2677.                 icon.className = 'bi bi-heart';
  2678.                 favoriteBtn.style.color = 'white';
  2679.               }
  2680.             }
  2681.           }
  2682.           nextSlide() {
  2683.             const nextIndex = (this.currentIndex + 1) % this.images.length;
  2684.             this.showSlide(nextIndex);
  2685.           }
  2686.           prevSlide() {
  2687.             const prevIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
  2688.             this.showSlide(prevIndex);
  2689.           }
  2690.           close() {
  2691.             if (!this.isOpen) return;
  2692.             const slider = document.querySelector('.universal-slider');
  2693.             if (slider) {
  2694.               slider.classList.remove('active');
  2695.               
  2696.               setTimeout(() => {
  2697.                 slider.remove();
  2698.                 this.isOpen = false;
  2699.               }, 300);
  2700.             }
  2701.           }
  2702.           attachEvents() {
  2703.             console.log('=== attachEvents appelée ===');
  2704.             const slider = document.getElementById('universalSlider');
  2705.             if (!slider) {
  2706.               console.error('Slider non trouvé pour attachEvents');
  2707.               return;
  2708.             }
  2709.             console.log('Slider trouvé pour attachEvents');
  2710.             
  2711.             // Bouton fermer
  2712.             const closeBtn = slider.querySelector('.close-btn');
  2713.             if (closeBtn) {
  2714.               console.log('Bouton fermer trouvé');
  2715.               closeBtn.addEventListener('click', () => {
  2716.                 console.log('Clic fermer');
  2717.                 this.close();
  2718.               });
  2719.             }
  2720.             
  2721.             // Navigation
  2722.             const prevBtn = slider.querySelector('.prev-btn');
  2723.             const nextBtn = slider.querySelector('.next-btn');
  2724.             if (prevBtn) {
  2725.               console.log('Bouton précédent trouvé');
  2726.               prevBtn.addEventListener('click', () => {
  2727.                 console.log('Clic précédent');
  2728.                 this.prevSlide();
  2729.               });
  2730.             }
  2731.             if (nextBtn) {
  2732.               console.log('Bouton suivant trouvé');
  2733.               nextBtn.addEventListener('click', () => {
  2734.                 console.log('Clic suivant');
  2735.                 this.nextSlide();
  2736.               });
  2737.             }
  2738.             // Navigation cinématographique des thumbnails
  2739.             const cinemaThumbs = slider.querySelectorAll('.cinema-thumb');
  2740.             cinemaThumbs.forEach((thumb, index) => {
  2741.               thumb.addEventListener('click', () => {
  2742.                 console.log('Clic cinema thumbnail:', index);
  2743.                 this.showSlide(index);
  2744.                 this.updateCinemaGallery(index);
  2745.               });
  2746.             });
  2747.             console.log('Cinema thumbnails cliquables:', cinemaThumbs.length);
  2748.             // Clic sur overlay pour fermer
  2749.             const overlay = slider.querySelector('.slider-overlay');
  2750.             if (overlay) {
  2751.               overlay.addEventListener('click', () => {
  2752.                 console.log('Clic overlay');
  2753.                 this.close();
  2754.               });
  2755.             }
  2756.             // Clavier
  2757.             this.keyboardHandler = this.handleKeyboard.bind(this);
  2758.             document.addEventListener('keydown', this.keyboardHandler);
  2759.             console.log('Événements clavier attachés');
  2760.             // Zoom sur les images
  2761.             this.attachZoomEvents(slider);
  2762.             console.log('Événements zoom attachés');
  2763.             console.log('Événements attachés avec succès');
  2764.           }
  2765.           close() {
  2766.             console.log('=== close appelée ===');
  2767.             const slider = document.querySelector('.universal-slider');
  2768.             if (slider) {
  2769.               console.log('Fermeture du slider');
  2770.               // Animation de fermeture
  2771.               slider.style.opacity = '0';
  2772.               
  2773.               setTimeout(() => {
  2774.                 slider.remove();
  2775.                 console.log('Slider supprimé du DOM');
  2776.                 
  2777.                 // Nettoyer les événements
  2778.                 if (this.keyboardHandler) {
  2779.                   document.removeEventListener('keydown', this.keyboardHandler);
  2780.                   this.keyboardHandler = null;
  2781.                 }
  2782.                 
  2783.                 this.isOpen = false;
  2784.               }, 300);
  2785.             }
  2786.           }
  2787.           nextSlide() {
  2788.             if (this.currentIndex < this.images.length - 1) {
  2789.               this.showSlide(this.currentIndex + 1);
  2790.             }
  2791.           }
  2792.           prevSlide() {
  2793.             if (this.currentIndex > 0) {
  2794.               this.showSlide(this.currentIndex - 1);
  2795.             }
  2796.           }
  2797.           handleKeyboard(e) {
  2798.             if (!this.isOpen) return;
  2799.             
  2800.             switch (e.key) {
  2801.               case 'Escape':
  2802.                 this.close();
  2803.                 break;
  2804.               case 'ArrowLeft':
  2805.                 this.prevSlide();
  2806.                 break;
  2807.               case 'ArrowRight':
  2808.                 this.nextSlide();
  2809.                 break;
  2810.             }
  2811.           }
  2812.           attachZoomEvents(slider) {
  2813.             // Fonctionnalité de zoom à 5 niveaux pour les images
  2814.             const images = slider.querySelectorAll('.slide img');
  2815.             images.forEach(img => {
  2816.               let zoomLevel = 1; // 1x, 1.5x, 2x, 3x, 4x, 5x
  2817.               let isDragging = false;
  2818.               let startX = 0;
  2819.               let startY = 0;
  2820.               let translateX = 0;
  2821.               let translateY = 0;
  2822.               // Clic simple pour zoom progressif (5 niveaux)
  2823.               img.addEventListener('click', (e) => {
  2824.                 e.preventDefault();
  2825.                 e.stopPropagation();
  2826.                 
  2827.                 // Cycle à travers 5 niveaux de zoom
  2828.                 switch(zoomLevel) {
  2829.                   case 1: zoomLevel = 1.5; break;  // 1x → 1.5x
  2830.                   case 1.5: zoomLevel = 2; break;  // 1.5x → 2x
  2831.                   case 2: zoomLevel = 3; break;    // 2x → 3x
  2832.                   case 3: zoomLevel = 4; break;    // 3x → 4x
  2833.                   case 4: zoomLevel = 5; break;    // 4x → 5x
  2834.                   case 5: 
  2835.                     zoomLevel = 1;                 // 5x → 1x (reset)
  2836.                     translateX = 0;
  2837.                     translateY = 0;
  2838.                     break;
  2839.                 }
  2840.                 console.log('Zoom niveau:', zoomLevel);
  2841.                 img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2842.                 
  2843.                 // Curseur selon le niveau
  2844.                 if (zoomLevel === 1) {
  2845.                   img.style.cursor = 'zoom-in';
  2846.                   img.removeAttribute('data-zoomed');
  2847.                 } else if (zoomLevel === 5) {
  2848.                   img.style.cursor = 'zoom-out';
  2849.                   img.setAttribute('data-zoomed', zoomLevel);
  2850.                 } else {
  2851.                   img.style.cursor = 'zoom-in';
  2852.                   img.setAttribute('data-zoomed', zoomLevel);
  2853.                 }
  2854.               });
  2855.               // Drag pour déplacer l'image zoomée
  2856.               img.addEventListener('mousedown', (e) => {
  2857.                 if (zoomLevel > 1) {
  2858.                   isDragging = true;
  2859.                   startX = e.clientX - translateX;
  2860.                   startY = e.clientY - translateY;
  2861.                   img.style.cursor = 'grabbing';
  2862.                   e.preventDefault();
  2863.                   e.stopPropagation();
  2864.                 }
  2865.               });
  2866.               // Mousemove global pour le drag
  2867.               const handleMouseMove = (e) => {
  2868.                 if (isDragging && zoomLevel > 1) {
  2869.                   translateX = e.clientX - startX;
  2870.                   translateY = e.clientY - startY;
  2871.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2872.                 }
  2873.               };
  2874.               // Mouseup global pour arrêter le drag
  2875.               const handleMouseUp = () => {
  2876.                 if (isDragging) {
  2877.                   isDragging = false;
  2878.                   if (zoomLevel === 1) {
  2879.                     img.style.cursor = 'zoom-in';
  2880.                   } else if (zoomLevel === 5) {
  2881.                     img.style.cursor = 'zoom-out';
  2882.                   } else {
  2883.                     img.style.cursor = 'zoom-in';
  2884.                   }
  2885.                 }
  2886.               };
  2887.               document.addEventListener('mousemove', handleMouseMove);
  2888.               document.addEventListener('mouseup', handleMouseUp);
  2889.               // Touch support pour mobile
  2890.               img.addEventListener('touchstart', (e) => {
  2891.                 if (zoomLevel > 1 && e.touches.length === 1) {
  2892.                   isDragging = true;
  2893.                   const touch = e.touches[0];
  2894.                   startX = touch.clientX - translateX;
  2895.                   startY = touch.clientY - translateY;
  2896.                   e.preventDefault();
  2897.                 }
  2898.               });
  2899.               img.addEventListener('touchmove', (e) => {
  2900.                 if (isDragging && zoomLevel > 1 && e.touches.length === 1) {
  2901.                   const touch = e.touches[0];
  2902.                   translateX = touch.clientX - startX;
  2903.                   translateY = touch.clientY - startY;
  2904.                   img.style.transform = `scale(${zoomLevel}) translate(${translateX}px, ${translateY}px)`;
  2905.                   e.preventDefault();
  2906.                 }
  2907.               });
  2908.               img.addEventListener('touchend', () => {
  2909.                 isDragging = false;
  2910.               });
  2911.               // Initialiser le curseur
  2912.               img.style.cursor = 'zoom-in';
  2913.             });
  2914.           }
  2915.         }
  2916.         // ⚡ OPTIMISATION MAJEURE: Sliders lazy (créés seulement quand utilisés)
  2917.         let favoritesSlider = null;
  2918.         let universalSlider = null;
  2919.         
  2920.         // Fonctions pour créer les sliders à la demande
  2921.         function getFavoritesSlider() {
  2922.           if (!favoritesSlider) {
  2923.             favoritesSlider = new FavoritesSlider();
  2924.           }
  2925.           return favoritesSlider;
  2926.         }
  2927.         
  2928.         function getUniversalSlider() {
  2929.           if (!universalSlider) {
  2930.             universalSlider = new UniversalImageSlider();
  2931.           }
  2932.           return universalSlider;
  2933.         }
  2934.         // Fonction pour voir un favori avec le slider plein écran
  2935.         function viewFavorite(id) {
  2936.           console.log('Ouverture du slider pour favori:', id);
  2937.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  2938.           const slider = getUniversalSlider();
  2939.           if (slider && slider.openFromFavorites) {
  2940.             slider.openFromFavorites(id);
  2941.           } else {
  2942.             console.error('Erreur slider favoris');
  2943.           }
  2944.         }
  2945.         // Fonction pour voir une image depuis la galerie normale
  2946.         function viewImage(imageId, clickedElement) {
  2947.           console.log('=== viewImage appelée ===');
  2948.           console.log('imageId:', imageId);
  2949.           console.log('clickedElement:', clickedElement);
  2950.           
  2951.           // Trouver le container du jour - peut être plusieurs niveaux au-dessus
  2952.           let dayContainer = clickedElement;
  2953.           
  2954.           // Remonter jusqu'à trouver un container approprié
  2955.           while (dayContainer && dayContainer !== document.body) {
  2956.             console.log('Checking element:', dayContainer.tagName, dayContainer.className, dayContainer.id);
  2957.             if (dayContainer && dayContainer.classList && (
  2958.                 dayContainer.classList.contains('dynamic-card') || 
  2959.                 dayContainer.classList.contains('collapse') ||
  2960.                 dayContainer.id && dayContainer.id.startsWith('demP')
  2961.             )) {
  2962.               break;
  2963.             }
  2964.             dayContainer = dayContainer.parentElement;
  2965.           }
  2966.           
  2967.           // ⚡ OPTIMISÉ: Logs réduits pour performance
  2968.           
  2969.           // ⚡ OPTIMISÉ: Utiliser le slider lazy
  2970.           const slider = getUniversalSlider();
  2971.           if (slider && slider.openFromGallery) {
  2972.             slider.openFromGallery(imageId, dayContainer);
  2973.           } else {
  2974.             console.error('Erreur slider galerie');
  2975.           }
  2976.         }
  2977.         // ⚡ OPTIMISÉ: Fonctions globales avec sliders lazy
  2978.         window.viewImage = viewImage;
  2979.         window.viewFavorite = viewFavorite;
  2980.         window.getUniversalSlider = getUniversalSlider; // Fonction lazy au lieu de l'instance
  2981.         // Variables globales pour éviter les ReferenceError
  2982.         if (typeof favoriteCount === 'undefined') {
  2983.           window.favoriteCount = 0;
  2984.         }
  2985.         if (typeof favButton === 'undefined') {
  2986.           window.favButton = null;
  2987.         }
  2988.         if (typeof favoriteButton === 'undefined') {
  2989.           window.favoriteButton = null;
  2990.         }
  2991.         if (typeof purchaseAlertTimeout === 'undefined') {
  2992.           window.purchaseAlertTimeout = null;
  2993.         }
  2994.         
  2995.         // Définir favoriteCount globalement pour éviter les ReferenceError
  2996.         let favoriteCount = window.favoriteCount || 0;
  2997.         // Fonction de sécurité pour vérifier les éléments DOM
  2998.         function safeAddEventListener(selector, event, callback) {
  2999.           const element = document.querySelector(selector);
  3000.           if (element) {
  3001.             element.addEventListener(event, callback);
  3002.           } else {
  3003.             console.warn(`Élément non trouvé: ${selector}`);
  3004.           }
  3005.         }
  3006.         // Fonction utilitaire pour manipuler classList de manière sécurisée
  3007.         function safeClassList(element, action, ...classes) {
  3008.           if (!element || !element.classList) {
  3009.             console.warn('Élément ou classList invalide:', element);
  3010.             return false;
  3011.           }
  3012.           
  3013.           try {
  3014.             switch(action) {
  3015.               case 'add':
  3016.                 element.classList.add(...classes);
  3017.                 break;
  3018.               case 'remove':
  3019.                 element.classList.remove(...classes);
  3020.                 break;
  3021.               case 'toggle':
  3022.                 return element.classList.toggle(classes[0], classes[1]);
  3023.               case 'contains':
  3024.                 return element.classList.contains(classes[0]);
  3025.               default:
  3026.                 console.warn('Action classList inconnue:', action);
  3027.                 return false;
  3028.             }
  3029.             return true;
  3030.           } catch (error) {
  3031.             console.warn('Erreur lors de la manipulation de classList:', error);
  3032.             return false;
  3033.           }
  3034.         }
  3035.         // Rendre la fonction utilitaire globale
  3036.         window.safeClassList = safeClassList;
  3037.         // Fonction pour sécuriser automatiquement tous les accès à classList
  3038.         function secureClassListAccess() {
  3039.           // Intercepter les erreurs classList globalement
  3040.           window.addEventListener('error', function(e) {
  3041.             if (e.message && (e.message.includes('classList') || e.message.includes('Cannot read properties of null'))) {
  3042.               console.warn('Erreur DOM interceptée et corrigée:', e.message, 'Ligne:', e.lineno, 'Fichier:', e.filename);
  3043.               e.preventDefault(); // Empêcher l'erreur de se propager
  3044.               return true;
  3045.             }
  3046.           });
  3047.           
  3048.           // Intercepter les erreurs non catchées
  3049.           window.addEventListener('unhandledrejection', function(e) {
  3050.             if (e.reason && e.reason.message && e.reason.message.includes('classList')) {
  3051.               console.warn('Promise rejection classList interceptée:', e.reason.message);
  3052.               e.preventDefault();
  3053.               return true;
  3054.             }
  3055.           });
  3056.           
  3057.           console.log('Système de sécurisation DOM/classList activé');
  3058.         }
  3059.         // Activer la sécurisation
  3060.         secureClassListAccess();
  3061.         // Fonction de diagnostic pour vérifier l'affichage du slider
  3062.         function diagnoseSliderDisplay(sliderId) {
  3063.           const slider = document.querySelector(sliderId);
  3064.           if (!slider) {
  3065.             console.error('Diagnostic: Slider non trouvé -', sliderId);
  3066.             return false;
  3067.           }
  3068.           
  3069.           const styles = window.getComputedStyle(slider);
  3070.           const rect = slider.getBoundingClientRect();
  3071.           
  3072.           console.log('=== DIAGNOSTIC SLIDER ===', sliderId);
  3073.           console.log('Élément présent:', !!slider);
  3074.           console.log('Styles calculés:', {
  3075.             display: styles.display,
  3076.             opacity: styles.opacity,
  3077.             visibility: styles.visibility,
  3078.             zIndex: styles.zIndex,
  3079.             position: styles.position
  3080.           });
  3081.           console.log('Position/Taille:', rect);
  3082.           console.log('Dans le viewport:', rect.width > 0 && rect.height > 0);
  3083.           console.log('Parent:', slider.parentElement?.tagName);
  3084.           
  3085.           // Vérifier les règles CSS qui pourraient masquer le slider
  3086.           const hiddenReasons = [];
  3087.           if (styles.display === 'none') hiddenReasons.push('display: none');
  3088.           if (styles.opacity === '0') hiddenReasons.push('opacity: 0');
  3089.           if (styles.visibility === 'hidden') hiddenReasons.push('visibility: hidden');
  3090.           if (parseInt(styles.zIndex) < 0) hiddenReasons.push('z-index négatif');
  3091.           
  3092.           if (hiddenReasons.length > 0) {
  3093.             console.warn('Raisons du masquage:', hiddenReasons);
  3094.             
  3095.             // Forcer l'affichage
  3096.             console.log('Forçage de l\'affichage...');
  3097.             slider.style.display = 'flex';
  3098.             slider.style.opacity = '1';
  3099.             slider.style.visibility = 'visible';
  3100.             slider.style.zIndex = '9999';
  3101.             slider.style.position = 'fixed';
  3102.             slider.style.top = '0';
  3103.             slider.style.left = '0';
  3104.             slider.style.width = '100%';
  3105.             slider.style.height = '100%';
  3106.             
  3107.             console.log('Slider forcé visible');
  3108.           } else {
  3109.             console.log('✅ Slider devrait être visible');
  3110.           }
  3111.           
  3112.           return true;
  3113.         }
  3114.         // Rendre la fonction globale
  3115.         window.diagnoseSliderDisplay = diagnoseSliderDisplay;
  3116.         // Fonction de sécurité pour checkFavorites
  3117.         function checkFavorites() {
  3118.           try {
  3119.             let count = 0;
  3120.             if (typeof getFavoriteItems === 'function') {
  3121.               count = getFavoriteItems().length;
  3122.             } else {
  3123.               // Fallback : compter depuis le DOM
  3124.               const giftCount = document.getElementById('giftCount');
  3125.               if (giftCount && giftCount.textContent) {
  3126.                 count = parseInt(giftCount.textContent.trim()) || 0;
  3127.               }
  3128.             }
  3129.             
  3130.             // Mettre à jour les variables globales
  3131.             window.favoriteCount = count;
  3132.             favoriteCount = count;
  3133.             
  3134.             console.log('Favoris vérifiés:', count);
  3135.             return count;
  3136.           } catch (error) {
  3137.             console.warn('Erreur lors de la vérification des favoris:', error);
  3138.             return 0;
  3139.           }
  3140.         }
  3141.         // Rendre checkFavorites globale si elle n'existe pas
  3142.         if (typeof window.checkFavorites === 'undefined') {
  3143.           window.checkFavorites = checkFavorites;
  3144.         }
  3145.         // Initialisation sécurisée après chargement du DOM
  3146.         document.addEventListener('DOMContentLoaded', function() {
  3147.           console.log('=== DOM chargé, initialisation des sliders ===');
  3148.           console.log('viewImage disponible:', typeof window.viewImage);
  3149.           console.log('viewFavorite disponible:', typeof window.viewFavorite);
  3150.           console.log('universalSlider disponible:', typeof window.universalSlider);
  3151.           
  3152.           // Vérifier que les éléments critiques existent
  3153.           const criticalElements = [
  3154.             'filtre_photos_voir',
  3155.             // Ajoutez d'autres IDs critiques ici si nécessaire
  3156.           ];
  3157.           
  3158.           criticalElements.forEach(id => {
  3159.             const element = document.getElementById(id);
  3160.             if (element) {
  3161.               console.log(`✓ Élément trouvé: ${id}`);
  3162.             } else {
  3163.               console.warn(`⚠ Élément manquant: ${id}`);
  3164.             }
  3165.           });
  3166.           
  3167.           // Initialiser checkFavorites si la fonction existe
  3168.           if (typeof window.checkFavorites === 'function') {
  3169.             window.checkFavorites();
  3170.           }
  3171.         });
  3172.       
  3173.         // Fonction pour obtenir l'image d'un produit selon son type
  3174.         function getProductImage(productId) {
  3175.           const productImages = {
  3176.             'album': '/images/produit/Album5sur5-3.jpg',
  3177.             'digital': '/images/produit/photoNumerique.jpg', 
  3178.             'prints': '/images/produit/PochettePhoto5sur5-2.jpg',
  3179.             'calendrier': '/images/produit/Calendrier5sur5-1.jpg',
  3180.             'livre': '/images/produit/LivreSouvenir5sur5-1.jpg',
  3181.             'coffret': '/images/produit/CoffretCadeau5sur5-2.jpg',
  3182.             'retro': '/images/produit/PochetteRetro5sur5-1.jpg'
  3183.           };
  3184.           
  3185.           return productImages[productId] || '/images/produit/albumm.PNG';
  3186.         }
  3187.         
  3188.         // Fonction pour générer les suggestions de produits (synchronisées avec ecommerce-sidebar)
  3189.         function generateProductSuggestions(favoriteCount) {
  3190.           if (favoriteCount === 0) {
  3191.             return `
  3192.               <div class="no-favorites-cta">
  3193.                 <div class="cta-content">
  3194.                   <i class="bi bi-heart" style="font-size: 4rem; color: #e91e63; margin-bottom: 1rem;"></i>
  3195.                   <h3>Commencez par sélectionner vos photos préférées</h3>
  3196.                   <p>Cliquez sur <i class="bi bi-heart" style="color: #e91e63;"></i> sur vos plus beaux souvenirs pour débloquer nos produits personnalisés.</p>
  3197.                   <button class="btn-start-selection" onclick="window.showDaysView()">
  3198.                     <i class="bi bi-images"></i>
  3199.                     Parcourir les photos
  3200.                   </button>
  3201.                 </div>
  3202.               </div>
  3203.             `;
  3204.           }
  3205.           let suggestionsHTML = '<div class="suggestions-header"><h4><i class="bi bi-gift"></i> Produits recommandés pour vous</h4></div>';
  3206.           
  3207.           // Utiliser les mêmes produits que dans ecommerce-sidebar
  3208.           const products = [];
  3209.           
  3210.           // Album Photo Premium (toujours disponible)
  3211.           products.push({
  3212.             id: 'album',
  3213.             name: 'Album Photo Premium',
  3214.             description: `Album photo personnalisé avec vos ${favoriteCount} photos favorites. Papier de qualité supérieure, reliure rigide.`,
  3215.             currentPrice: '24,90€',
  3216.             originalPrice: '28,90€',
  3217.             savings: '-14%',
  3218.             badge: 'Populaire',
  3219.             available: favoriteCount > 0,
  3220.             disabled: favoriteCount === 0
  3221.           });
  3222.           // Pack Numérique HD (toujours disponible)
  3223.           products.push({
  3224.             id: 'digital',
  3225.             name: 'Pack Numérique HD',
  3226.             description: `Téléchargement vos photos en haute définition. Qualité professionnelle garantie.`,
  3227.             currentPrice: '3,90€',
  3228.             originalPrice: '',
  3229.             savings: '',
  3230.             badge: 'Nouveau',
  3231.             available: favoriteCount > 0,
  3232.             disabled: favoriteCount === 0
  3233.           });
  3234.           // Tirages Premium (toujours visible, mais état change selon favoris)
  3235.           if (favoriteCount >= 12) {
  3236.             products.push({
  3237.               id: 'prints',
  3238.               name: 'Tirages Premium',
  3239.               description: `12 tirages photo 10x15cm de vos favoris. Papier brillant professionnel, livraison gratuite.`,
  3240.               currentPrice: '9,90€',
  3241.               originalPrice: '',
  3242.               savings: '',
  3243.               badge: '✨ Débloqué !',
  3244.               available: true,
  3245.               disabled: false
  3246.             });
  3247.           } else {
  3248.             products.push({
  3249.               id: 'prints',
  3250.               name: 'Tirages Premium',
  3251.               description: `Sélectionnez ${12 - favoriteCount} photo${(12 - favoriteCount) > 1 ? 's' : ''} de plus pour débloquer les tirages premium.`,
  3252.               currentPrice: '9,90€',
  3253.               originalPrice: '',
  3254.               savings: '',
  3255.               badge: `🔒 ${favoriteCount}/12`,
  3256.               available: false,
  3257.               disabled: true
  3258.             });
  3259.           }
  3260.           
  3261.           suggestionsHTML += '<div class="products-grid">';
  3262.           
  3263.           products.forEach(product => {
  3264.             suggestionsHTML += `
  3265.               <div class="product-card ${product.disabled ? 'disabled' : ''}" data-product="${product.id}">
  3266.                 <div class="product-badge ${product.disabled ? 'disabled' : ''}">${product.badge}</div>
  3267.                 <div class="product-image">
  3268.                   <img src="${getProductImage(product.id)}" alt="${product.name}" onerror="this.src='/images/produit/albumm.PNG'">
  3269.                 </div>
  3270.                 <div class="product-info">
  3271.                   <h5>${product.name}</h5>
  3272.                   <p>${product.description}</p>
  3273.                   <div class="product-pricing">
  3274.                     <span class="current-price">${product.currentPrice}</span>
  3275.                     ${product.originalPrice ? `<span class="original-price">${product.originalPrice}</span>` : ''}
  3276.                     ${product.savings ? `<span class="savings">${product.savings}</span>` : ''}
  3277.                   </div>
  3278.                   <button class="btn-add-to-cart ${product.disabled ? 'disabled' : ''}" 
  3279.                           onclick="orderProduct('${product.id}')" 
  3280.                           ${product.disabled ? 'disabled title="Ajoutez des favoris pour commander"' : ''}>
  3281.                     <i class="bi bi-cart-plus"></i>
  3282.                     ${product.disabled ? 'Ajoutez des favoris' : 'Commander'}
  3283.                   </button>
  3284.                 </div>
  3285.               </div>
  3286.             `;
  3287.           });
  3288.           
  3289.           suggestionsHTML += '</div>';
  3290.           // Message d'encouragement avec progression visuelle
  3291.           if (favoriteCount < 12) {
  3292.             const remaining = 12 - favoriteCount;
  3293.             const progress = Math.round((favoriteCount / 12) * 100);
  3294.             suggestionsHTML += `
  3295.               <div class="encouragement-card">
  3296.                 <div class="encouragement-content">
  3297.                   <div class="encouragement-icon">🎯</div>
  3298.                   <div class="encouragement-text">
  3299.                     <h5>Encore ${remaining} photo${remaining > 1 ? 's' : ''} pour débloquer les Tirages Premium !</h5>
  3300.                     <p>Sélectionnez ${remaining} photo${remaining > 1 ? 's' : ''} supplémentaire${remaining > 1 ? 's' : ''} pour accéder à tous nos produits premium</p>
  3301.                     <div class="progress-bar-container" style="margin-top: 12px;">
  3302.                       <div class="progress-bar-fill" style="width: ${progress}%; background: linear-gradient(90deg, #f56040, #ff8c5a); height: 8px; border-radius: 4px; transition: width 0.5s ease;"></div>
  3303.                     </div>
  3304.                     <div class="progress-text" style="margin-top: 6px; font-size: 13px; color: #6c757d; font-weight: 600;">
  3305.                       ${favoriteCount}/12 photos sélectionnées (${progress}%)
  3306.                     </div>
  3307.                   </div>
  3308.                 </div>
  3309.               </div>
  3310.             `;
  3311.           } else {
  3312.             // Message de félicitations si 12+ favoris
  3313.             suggestionsHTML += `
  3314.               <div class="encouragement-card" style="background: linear-gradient(135deg, #e8f5e9, #c8e6c9); border-color: #4caf50;">
  3315.                 <div class="encouragement-content">
  3316.                   <div class="encouragement-icon">🎉</div>
  3317.                   <div class="encouragement-text">
  3318.                     <h5>Bravo ! Tous les produits sont débloqués !</h5>
  3319.                     <p>Vous avez sélectionné ${favoriteCount} photos — parfait pour créer de magnifiques souvenirs personnalisés 💚</p>
  3320.                   </div>
  3321.                 </div>
  3322.               </div>
  3323.             `;
  3324.           }
  3325.           return suggestionsHTML;
  3326.         }
  3327.         // Fonction pour ajouter au panier
  3328.         function addToCart(productId) {
  3329.           // Animation du bouton cadeau
  3330.           const giftButton = document.querySelector('.gift-button');
  3331.           if (giftButton) {
  3332.             if (giftButton.classList) {
  3333.               giftButton.classList.add('active');
  3334.               setTimeout(() => {
  3335.                 if (giftButton.classList) {
  3336.                   giftButton.classList.remove('active');
  3337.                 }
  3338.               }, 600);
  3339.             }
  3340.           }
  3341.           // Afficher une notification
  3342.           showNotification(`Produit ajouté au panier !`, 'success');
  3343.           
  3344.           // Ici vous pourriez ajouter la logique d'ajout au panier réel
  3345.           console.log('Ajout au panier:', productId);
  3346.         }
  3347.         // Fonction pour afficher une notification
  3348.         function showNotification(message, type = 'info') {
  3349.           const notification = document.createElement('div');
  3350.           notification.className = `notification ${type}`;
  3351.           notification.innerHTML = `
  3352.             <i class="bi bi-check-circle"></i>
  3353.             <span>${message}</span>
  3354.           `;
  3355.           
  3356.           document.body.appendChild(notification);
  3357.           
  3358.           setTimeout(() => {
  3359.             if (notification && notification.classList) {
  3360.               notification.classList.add('show');
  3361.             }
  3362.           }, 100);
  3363.           
  3364.           setTimeout(() => {
  3365.             if (notification && notification.classList) {
  3366.               notification.classList.remove('show');
  3367.               setTimeout(() => {
  3368.                 if (document.body.contains(notification)) {
  3369.                   document.body.removeChild(notification);
  3370.                 }
  3371.               }, 300);
  3372.             }
  3373.           }, 3000);
  3374.         }
  3375.         // Fonction pour obtenir le nombre actuel de favoris
  3376.         function getCurrentFavoriteCount() {
  3377.           return getFavoriteItems().length;
  3378.         }
  3379.         // 🎯 SUPPRIMÉ: Fonction dupliquée - la vraie est dans parent-toasts.js
  3380.         // Cette fonction était en conflit avec updateProductSuggestions() de parent-toasts.js
  3381.         // qui gère déjà les mises à jour dynamiques avec dates et contexte
  3382.         
  3383.         // Fonction legacy pour compatibilité (redirige vers parent-toasts.js)
  3384.         window.updateProductSuggestionsLive = function(favoriteCount) {
  3385.           console.log('[Legacy] updateProductSuggestionsLive appelé, redirection vers parent-toasts.js');
  3386.           // La vraie logique est dans parent-toasts.js > updateProductSuggestions()
  3387.           // qui est appelée automatiquement par le MutationObserver
  3388.         }
  3389.         // Gestion du localStorage pour l'indicateur "nouveau contenu"
  3390.         const lastVisitKey = 'lastVisitISO';
  3391.         const currentVisit = new Date().toISOString();
  3392.         
  3393.         // Sauvegarder la visite actuelle
  3394.         localStorage.setItem(lastVisitKey, currentVisit);
  3395.         
  3396.         // Vérifier et marquer les jours avec nouveau contenu
  3397.         const dateCards = document.querySelectorAll('.date-card.modern-card');
  3398.         dateCards.forEach(card => {
  3399.           // Ici vous devriez comparer avec updatedAt du backend
  3400.           // Pour l'instant, on simule avec un attribut data-updated
  3401.           const updatedAt = card.getAttribute('data-updated');
  3402.           if (updatedAt) {
  3403.             const lastVisit = localStorage.getItem(lastVisitKey);
  3404.             if (new Date(updatedAt) > new Date(lastVisit)) {
  3405.               if (card && card.classList) {
  3406.                 card.classList.add('has-new-content');
  3407.               }
  3408.               // Ajouter le badge "nouveau" si pas déjà présent
  3409.               if (!card.querySelector('.badge-new')) {
  3410.                 const badge = document.createElement('span');
  3411.                 badge.className = 'badge-new';
  3412.                 badge.setAttribute('aria-label', 'Nouveau contenu');
  3413.                 card.querySelector('.title-line').appendChild(badge);
  3414.               }
  3415.             }
  3416.           }
  3417.         });
  3418.         // Gestion des aria-label dynamiques pour les cartes de jours
  3419.         dateCards.forEach(card => {
  3420.           const dayText = card.querySelector('.day').textContent.trim();
  3421.           const dateText = card.querySelector('.full-date').textContent.trim();
  3422.           const photos = card.querySelector('.media-list-horizontal li:nth-child(1)')?.textContent.trim() || '0';
  3423.           const audios = card.querySelector('.media-list-horizontal li:nth-child(2)')?.textContent.trim() || '0';
  3424.           const videos = card.querySelector('.media-list-horizontal li:nth-child(3)')?.textContent.trim() || '0';
  3425.           
  3426.           const ariaLabel = `Contenu du ${dateText} : ${photos} photos, ${audios} audios, ${videos} vidéos`;
  3427.           card.setAttribute('aria-label', ariaLabel);
  3428.         });
  3429.       });
  3430.     </script>
  3431.     
  3432.     <!-- Make sure we're showing the right content by default if no valid content is marked 'show' -->
  3433.     {% if lastValidIndex > 0 %}
  3434.     <script>
  3435.       document.addEventListener('DOMContentLoaded', function() {
  3436.         // Check if no content is showing
  3437.         if (document.querySelectorAll('.container--gallery .collapse.show').length === 0) {
  3438.           // Show the content for the last valid date
  3439.           var lastValidContent = document.getElementById('demP{{ lastValidIndex }}');
  3440.           if (lastValidContent) {
  3441.             if (lastValidContent.classList) {
  3442.               lastValidContent.classList.add('show');
  3443.             }
  3444.             
  3445.             // Also mark the corresponding date card as active
  3446.             var dateCards = document.querySelectorAll('.date-card');
  3447.             if (dateCards.length >= {{ lastValidIndex }}) {
  3448.               dateCards.forEach(card => {
  3449.                 if (card && card.classList) {
  3450.                   card.classList.remove('active');
  3451.                 }
  3452.               });
  3453.               const targetCard = dateCards[{{ lastValidIndex - 1 }}];
  3454.               if (targetCard && targetCard.classList) {
  3455.                 targetCard.classList.add('active');
  3456.               }
  3457.             }
  3458.           }
  3459.         }
  3460.       });
  3461.     </script>
  3462.     {% endif %}
  3463.   </div>
  3464. </div>
  3465.   {% endblock %} {% block Javascript %}
  3466.   {{ parent() }}
  3467.   <script>// Gestion de la sidebar des favoris
  3468. document.addEventListener('DOMContentLoaded', function() {
  3469.     const sidebar = document.getElementById('favorites-sidebar');
  3470.     const openBtn = document.getElementById('openFavoritesSidebar');
  3471.     const closeBtn = document.querySelector('.favorites-close');
  3472.     const giftButton = document.querySelector('.gift-button');
  3473.     function openSidebar() {
  3474.         if (sidebar && sidebar.classList) {
  3475.           sidebar.classList.add('active');
  3476.           updateFavoritesSidebar();
  3477.         }
  3478.     }
  3479.     function closeSidebar() {
  3480.         if (sidebar && sidebar.classList) {
  3481.           sidebar.classList.remove('active');
  3482.         }
  3483.     }
  3484.     function updateFavoritesSidebar() {
  3485.         const grid = document.getElementById('favorites-grid');
  3486.         const counter = document.getElementById('favorites-counter');
  3487.         const emptyState = document.getElementById('favorites-empty-state');
  3488.         const progress = document.getElementById('favorites-progress');
  3489.         
  3490.         const mesFavCount = document.getElementById('mesFavCount');
  3491.         const count = parseInt(mesFavCount ? mesFavCount.textContent : '0');
  3492.         counter.textContent = count;
  3493.         
  3494.         if (count === 0) {
  3495.             emptyState.style.display = 'flex';
  3496.             grid.style.display = 'none';
  3497.         } else {
  3498.             emptyState.style.display = 'none';
  3499.             grid.style.display = 'grid';
  3500.             const percentage = (count / 20) * 100;
  3501.             progress.style.width = `${percentage}%`;
  3502.         }
  3503.     }
  3504.     if (openBtn) {
  3505.         openBtn.addEventListener('click', openSidebar);
  3506.     }
  3507.     if (favButton) {
  3508.         favButton.addEventListener('click', openSidebar);
  3509.     }
  3510.     if (closeBtn) {
  3511.         closeBtn.addEventListener('click', closeSidebar);
  3512.     }
  3513.     document.addEventListener('click', (e) => {
  3514.         if (sidebar && sidebar.classList && sidebar.classList.contains('active') && 
  3515.             !sidebar.contains(e.target) && 
  3516.             (!favButton || !favButton.contains(e.target)) && 
  3517.             (!openBtn || !openBtn.contains(e.target))) {
  3518.             closeSidebar();
  3519.         }
  3520.     });
  3521. });
  3522. // Modification des fonctions existantes
  3523. const originalAddFavoris = window.AddFavoris;
  3524. window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  3525.     if (originalAddFavoris) {
  3526.         originalAddFavoris($id, $idSejour, $urlimg, $description);
  3527.     }
  3528.     
  3529.   
  3530.     
  3531.     // Vérifier si la sidebar est ouverte - noter que nous vérifions la valeur CSS right: 0
  3532.     if ($("#favorites-sidebar").css("right") === "0px") {
  3533.         // Recharger les favoris
  3534.         loadFavorites();
  3535.     }
  3536. };
  3537. const originalSupprimerFavoris = window.supprimerFavoris;
  3538. window.supprimerFavoris = function($id, $idSejour) {
  3539.     if (originalSupprimerFavoris) {
  3540.         originalSupprimerFavoris($id, $idSejour);
  3541.     }
  3542.     
  3543.     // Mettre à jour tous les compteurs de favoris
  3544.     updateAllFavoriteCounters();
  3545.     
  3546.     // Vérifier si la sidebar est ouverte
  3547.     if ($("#favorites-sidebar").css("right") === "0px") {
  3548.         // Recharger les favoris
  3549.         loadFavorites();
  3550.     }
  3551. };
  3552. // Variables globales
  3553. let selectedFavorites = [];
  3554. let allFavorites = [];
  3555. // Fonction pour mettre à jour la sidebar
  3556. function loadFavorites() {
  3557.     $("#favorites-grid").html("<div style='text-align:center;padding:20px;'>Chargement...</div>");
  3558.     
  3559.     $.ajax({
  3560.         url: "/Parent/mes-favoris",
  3561.         type: "GET",
  3562.         dataType: "json",
  3563.         success: function(data) {
  3564.             $("#favorites-grid").empty();
  3565.             allFavorites = data.data || [];
  3566.             
  3567.             if (allFavorites.length > 0) {
  3568.                 $("#favorites-empty-state").hide();
  3569.                 
  3570.                 $.each(allFavorites, function(i, fav) {
  3571.                     var isSelected = selectedFavorites.includes(fav.id);
  3572.                     
  3573.                     var item = $("<div class='favorite-item' style='position:relative;border-radius:8px;overflow:hidden;aspect-ratio:1;cursor:pointer;'></div>");
  3574.                     var img = $("<img style='width:100%;height:100%;object-fit:cover;transition:transform 0.3s ease;'>").attr("src", fav.path).attr("alt", fav.descreption || "Photo favorite");
  3575.                     
  3576.                     // Overlay de sélection
  3577.                     var selectionOverlay = $("<div class='selection-overlay' style='position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.3);display:flex;align-items:center;justify-content:center;'></div>");
  3578.                     
  3579.                     // Icône de sélection
  3580.                     var checkIcon = $("<div style='width:25px;height:25px;border-radius:50%;border:2px solid white;display:flex;align-items:center;justify-content:center;background:" + (isSelected ? "#F56040" : "transparent") + ";transition:background 0.2s;'></div>");
  3581.                     if (isSelected) {
  3582.                         checkIcon.append("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3583.                     }
  3584.                     
  3585.                     selectionOverlay.append(checkIcon);
  3586.                     
  3587.                     // Overlay d'action (bouton supprimer)
  3588.                     var actionOverlay = $("<div class='action-overlay' style='position:absolute;top:5px;right:5px;opacity:0;transition:opacity 0.2s;'></div>");
  3589.                   
  3590.                     actionOverlay.append(deleteBtn);
  3591.                     
  3592.                     // Ajouter les événements
  3593.                     item.hover(
  3594.                         function() { $(this).find(".action-overlay").css("opacity", "1"); },
  3595.                         function() { $(this).find(".action-overlay").css("opacity", "0"); }
  3596.                     );
  3597.                     
  3598.                     item.click(function() {
  3599.                         toggleSelection(fav.id, $(this).find(".selection-overlay > div"));
  3600.                     });
  3601.                     
  3602.                     item.append(img).append(selectionOverlay).append(actionOverlay);
  3603.                     $("#favorites-grid").append(item);
  3604.                 });
  3605.                 
  3606.                 $("#favorites-counter").text(allFavorites.length);
  3607.                 
  3608.                 // Mettre à jour le compteur sur le bouton également
  3609.                 $("#openFavoritesSidebar span").text(allFavorites.length);
  3610.                 
  3611.                 // Mettre à jour l'interface produits
  3612.                 updateProductsView();
  3613.                 
  3614.             } else {
  3615.                 $("#favorites-empty-state").show();
  3616.                 $("#favorites-counter").text("0");
  3617.                 $("#openFavoritesSidebar span").text("0");
  3618.                 $("#selection-count").text("0");
  3619.                 updateProductsView();
  3620.             }
  3621.         },
  3622.         error: function() {
  3623.             $("#favorites-grid").html("<div style='color:red;text-align:center;padding:20px;'>Erreur de chargement</div>");
  3624.         }
  3625.     });
  3626. }
  3627. // Fonction pour supprimer un favori
  3628. function removeFavorite(id) {
  3629.     $.ajax({
  3630.         url: "/Parent/remove-favorite/" + id,
  3631.         type: "POST",
  3632.         success: function() {
  3633.             // Retirer de la sélection si présent
  3634.             selectedFavorites = selectedFavorites.filter(favId => favId != id);
  3635.             
  3636.             // Mettre à jour le compteur de sélection
  3637.             $("#selection-count").text(selectedFavorites.length);
  3638.             
  3639.             // Recharger les favoris
  3640.             loadFavorites();
  3641.             
  3642.             // Mettre à jour tous les compteurs de favoris
  3643.             updateAllFavoriteCounters();
  3644.             
  3645.             // Mettre à jour l'interface produits
  3646.             updateProductsView();
  3647.         },
  3648.         error: function() {
  3649.             alert("Erreur lors de la suppression du favori");
  3650.         }
  3651.     });
  3652. }
  3653. // Fonction pour basculer la sélection d'une photo
  3654. function toggleSelection(id, checkElement) {
  3655.     const index = selectedFavorites.indexOf(id);
  3656.     
  3657.     if (index === -1) {
  3658.         // Ajouter à la sélection
  3659.         selectedFavorites.push(id);
  3660.         checkElement.css("background", "#F56040");
  3661.         checkElement.html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3662.     } else {
  3663.         // Retirer de la sélection
  3664.         selectedFavorites.splice(index, 1);
  3665.         checkElement.css("background", "transparent");
  3666.         checkElement.html("");
  3667.     }
  3668.     
  3669.     // Mettre à jour le compteur
  3670.     $("#selection-count").text(selectedFavorites.length);
  3671.     
  3672.     // Mettre à jour l'interface produits
  3673.     updateProductsView();
  3674. }
  3675. // Fonction pour mettre à jour la vue des produits
  3676. function updateProductsView() {
  3677.     const current = selectedFavorites.length;
  3678.     $("#product-photo-count").text(current);
  3679.     
  3680.     let remainingForAlbum = Math.max(0, 20 - current);
  3681.     let remainingForPochette = Math.max(0, 12 - current);
  3682.     let remainingForPack = Math.max(0, 12 - current);
  3683.     const progressBar = (count, total, color) => `
  3684.         <div style="margin: 5px 0;">
  3685.             <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  3686.                 <div style="width: ${(count / total) * 100}%; background-color: ${color}; height: 100%;"></div>
  3687.             </div>
  3688.             <small style="font-size: 12px;">${count}/${total} photos</small>
  3689.         </div>
  3690.     `;
  3691.     // Liste des produits
  3692.     const products = [
  3693.         {
  3694.             name: "Pack numérique (20 photos)",
  3695.             required: 20,
  3696.             remaining: Math.max(0, 20 - current),
  3697.             image: "/images/produit/photoNumerique.jpg",
  3698.             color: "#4caf50",
  3699.             link: "{{ path('PackPhotosNumerique_Favoris', {'nbr':20}) }}",
  3700.         },
  3701.        +
  3702.         {
  3703.             name: "Pochette photo (12 photos)",
  3704.             required: 12,
  3705.             remaining: Math.max(0, 12 - current),
  3706.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  3707.             color: "#2196f3",
  3708.             link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  3709.         },
  3710.     ].sort((a, b) => a.remaining - b.remaining);
  3711.     const productList = products
  3712.         .map((product) => {
  3713.             const count = current;
  3714.             const total = product.required;
  3715.             const remaining = product.remaining;
  3716.             return `
  3717.                 <li style="margin-bottom: 20px;">
  3718.                     <div style="display: flex; align-items: center; gap: 10px;">
  3719.                         <img src="${product.image}" alt="${product.name}" style="width: 70px; height: 70px; border-radius: 5px; object-fit: cover;" />
  3720.                         <div style="flex: 1;">
  3721.                             <strong style="font-size: 14px;">${product.name}</strong>
  3722.                             ${progressBar(count, total, product.color)}
  3723.                             ${
  3724.                                 `<small style="color: ${product.color}; font-size: 12px;">
  3725.                                     Encore ${remaining} photos pour compléter ${product.name.toLowerCase()}
  3726.                                 </small>
  3727.                                 <button
  3728.                                     style="
  3729.                                         margin-top: 5px;
  3730.                                         padding: 6px 12px;
  3731.                                         background-color: ${product.color};
  3732.                                         color: white;
  3733.                                         border: none;
  3734.                                         border-radius: 5px;
  3735.                                         font-size: 13px;
  3736.                                         cursor: pointer;
  3737.                                     "
  3738.                                     onclick="window.location.href='${product.link}'"
  3739.                                 >
  3740.                                     Commander
  3741.                                 </button>`
  3742.                             }
  3743.                         </div>
  3744.                     </div>
  3745.                 </li>
  3746.             `;
  3747.         })
  3748.         .join("");
  3749.     const boutiqueButton = `
  3750.         <li style="margin-top: 25px; text-align: center;">
  3751.             <button
  3752.                 style="
  3753.                     padding: 8px 15px;
  3754.                     background-color: #F56040;
  3755.                     color: white;
  3756.                     border: none;
  3757.                     border-radius: 5px;
  3758.                     font-size: 14px;
  3759.                     width: 170px;
  3760.                     height: 40px;
  3761.                     cursor: pointer;
  3762.                 "
  3763.                 onclick="window.location.href='{{ path('boutique5sur5') }}'"
  3764.             >
  3765.                 Voir toute la boutique
  3766.             </button>
  3767.         </li>
  3768.     `;
  3769.     $("#product-list").html(productList + boutiqueButton);
  3770. }
  3771. // Modifications au script existant
  3772. $(document).ready(function() {
  3773.     // Fonctions pour ouvrir/fermer la sidebar
  3774.     $("#openFavoritesSidebar").click(function() {
  3775.         $("#favorites-sidebar").css("right", "0");
  3776.         loadFavorites();
  3777.     });
  3778.     
  3779.     $("#close-favorites-btn").click(function() {
  3780.         closeFavoritesSidebar();
  3781.     });
  3782.     
  3783.     // Fonction pour fermer la sidebar
  3784.     window.closeFavoritesSidebar = function() {
  3785.         $("#favorites-sidebar").css("right", "-500px");
  3786.     };
  3787.     
  3788.     // Tout sélectionner / Tout désélectionner
  3789.     $("#select-all-btn").click(function() {
  3790.         if (selectedFavorites.length === allFavorites.length) {
  3791.             // Tout désélectionner
  3792.             selectedFavorites = [];
  3793.             $(".selection-overlay > div").css("background", "transparent").html("");
  3794.             $(this).text("Tout sélectionner");
  3795.         } else {
  3796.             // Tout sélectionner
  3797.             selectedFavorites = allFavorites.map(fav => fav.id);
  3798.             $(".selection-overlay > div").css("background", "#F56040").html("<i class='bi bi-check' style='color:white;font-size:16px;'></i>");
  3799.             $(this).text("Tout désélectionner");
  3800.         }
  3801.         
  3802.         // Mettre à jour le compteur
  3803.         $("#selection-count").text(selectedFavorites.length);
  3804.         
  3805.         // Mettre à jour l'interface produits
  3806.         updateProductsView();
  3807.     });
  3808.     
  3809.     // Gestion des onglets
  3810.     $(".sidebar-tab").click(function() {
  3811.         $(".sidebar-tab").removeClass("active").css({
  3812.             "color": "#666",
  3813.             "border-bottom": "none"
  3814.         });
  3815.         $(this).addClass("active").css({
  3816.             "color": "#F56040",
  3817.             "border-bottom": "2px solid #F56040"
  3818.         });
  3819.         
  3820.         const tabId = $(this).attr("id");
  3821.         $(".tab-content").hide();
  3822.         
  3823.         if (tabId === "tab-photos") {
  3824.             $("#photos-content").show();
  3825.         } else if (tabId === "tab-products") {
  3826.             $("#products-content").show();
  3827.         }
  3828.     });
  3829.     
  3830.     // Modifier les fonctions existantes pour intercepter l'ajout/suppression de favoris
  3831.     const originalAddFavoris = window.AddFavoris;
  3832.     window.AddFavoris = function($id, $idSejour, $urlimg, $description) {
  3833.         if (originalAddFavoris) {
  3834.             originalAddFavoris($id, $idSejour, $urlimg, $description);
  3835.         }
  3836.         
  3837.         // Mettre à jour tous les compteurs de favoris
  3838.         updateAllFavoriteCounters();
  3839.         
  3840.         // Recharger les favoris si la sidebar est ouverte
  3841.         if ($("#favorites-sidebar").css("right") === "0px") {
  3842.             loadFavorites();
  3843.         }
  3844.     };
  3845.     const originalSupprimerFavoris = window.supprimerFavoris;
  3846.     window.supprimerFavoris = function($id, $idSejour) {
  3847.         if (originalSupprimerFavoris) {
  3848.             originalSupprimerFavoris($id, $idSejour);
  3849.         }
  3850.         
  3851.         // Mettre à jour tous les compteurs de favoris
  3852.         updateAllFavoriteCounters();
  3853.         
  3854.         // Recharger les favoris si la sidebar est ouverte
  3855.         if ($("#favorites-sidebar").css("right") === "0px") {
  3856.             // Retirer de la sélection si présent
  3857.             selectedFavorites = selectedFavorites.filter(favId => favId != $id);
  3858.             loadFavorites();
  3859.         }
  3860.     };
  3861.     
  3862.     // Fermer en cliquant en dehors
  3863.     $(document).click(function(event) {
  3864.         if (!$(event.target).closest("#favorites-sidebar").length && 
  3865.             !$(event.target).closest("#openFavoritesSidebar").length && 
  3866.             $("#favorites-sidebar").css("right") === "0px") {
  3867.             closeFavoritesSidebar();
  3868.         }
  3869.     });
  3870. });
  3871. // Initialisation de la sidebar
  3872. $(document).ready(function() {
  3873.     // Fonctions pour ouvrir/fermer la sidebar
  3874.     $("#openFavoritesSidebar").click(function() {
  3875.         $("#favorites-sidebar").css("right", "0");
  3876.         loadFavorites();
  3877.     });
  3878.     
  3879.     $("#close-favorites-btn").click(function() {
  3880.         $("#favorites-sidebar").css("right", "-500px");
  3881.     });
  3882.     
  3883.     // Fermer en cliquant en dehors
  3884.     $(document).click(function(event) {
  3885.         if (!$(event.target).closest("#favorites-sidebar").length && 
  3886.             !$(event.target).closest("#openFavoritesSidebar").length && 
  3887.             $("#favorites-sidebar").css("right") === "0px") {
  3888.             $("#favorites-sidebar").css("right", "-500px");
  3889.         }
  3890.     });
  3891. });
  3892.   </script>
  3893.   <script src="{{ asset('js/splide.min.js') }}" defer></script>
  3894.    
  3895. <script>
  3896.   document.addEventListener('DOMContentLoaded', function () {
  3897.     const openBtn = document.getElementById('openFavoritesSidebar');
  3898.     const sidebar = document.getElementById('favorites-sidebar');
  3899.     const closeBtn = document.getElementById('favorites-close');
  3900.     if (openBtn && sidebar) {
  3901.       openBtn.addEventListener('click', () => {
  3902.         if (sidebar && sidebar.classList) {
  3903.           sidebar.classList.add('active');
  3904.         }
  3905.       });
  3906.     }
  3907.     if (closeBtn && sidebar) {
  3908.       closeBtn.addEventListener('click', () => {
  3909.         if (sidebar && sidebar.classList) {
  3910.           sidebar.classList.remove('active');
  3911.         }
  3912.       });
  3913.     }
  3914.   });
  3915. </script>
  3916.  
  3917. <script>
  3918. document.addEventListener('DOMContentLoaded', function() {
  3919.   const filterBadges = document.querySelectorAll('.filter-badge');
  3920.   filterBadges.forEach(badge => {
  3921.     badge.addEventListener('click', function() {
  3922.       // Désactiver tous les badges
  3923.       filterBadges.forEach(b => {
  3924.         if (b && b.classList) {
  3925.           b.classList.remove('active');
  3926.         }
  3927.       });
  3928.       // Activer le badge cliqué
  3929.       if (this && this.classList) {
  3930.         this.classList.add('active');
  3931.       }
  3932.     });
  3933.   });
  3934. });
  3935. </script>
  3936.   <script
  3937.     type="text/javascript"
  3938.     src="{{ asset('Accueil/js/jquery.magnific-popup.min.js') }}"
  3939.   ></script>
  3940.   <script>
  3941. document.addEventListener('DOMContentLoaded', function () {
  3942.   const prevButtons = document.querySelectorAll('.btn-prev-day');
  3943.   const nextButtons = document.querySelectorAll('.btn-next-day');
  3944.   const dateCards = document.querySelectorAll('.date-card.modern-card');
  3945.   const collapseSections = document.querySelectorAll('.collapse');
  3946.   // 🎯 MEDIA TOOLBAR CAPSULE - État global et logique (Senior UX)
  3947.   (() => {
  3948.     // État global de filtre
  3949.     const MediaFilter = { current: 'all' };
  3950.     // Compteurs dynamiques
  3951.     const counters = { photo: 0, audio: 0, video: 0, fav: 0 };
  3952.     
  3953.     const updateCounts = () => {
  3954.       // Compter les médias dans le DOM
  3955.       counters.photo = document.querySelectorAll('.photo-item, [data-media-type="photo"]').length;
  3956.       counters.video = document.querySelectorAll('[data-media-type="video"]').length;
  3957.       counters.audio = document.querySelectorAll('.audio-message-item[data-type="audio"]').length;
  3958.       // NE PAS recalculer seulement les favoris - ils utilisent {{ nblikes }}
  3959.       
  3960.       // Mettre à jour l'affichage (sauf favoris)
  3961.       Object.entries(counters).forEach(([k, v]) => {
  3962.         if (k !== 'fav') { // Garder seulement le compteur favoris intact
  3963.           document.querySelectorAll(`.mtb-count[data-bind="${k}"]`).forEach(el => el.textContent = v);
  3964.         }
  3965.       });
  3966.     };
  3967.     // Simple fonction pour le filtre actuel
  3968.     function setActive(filter) {
  3969.       MediaFilter.current = filter;
  3970.       
  3971.       // Exception : ne pas ajouter de classe active au bouton photos
  3972.       if (filter === 'photos') {
  3973.         const photosBtn = document.querySelector('.mtb-btn[data-filter="photos"]');
  3974.         if (photosBtn) {
  3975.           photosBtn.classList.remove('active', 'is-active');
  3976.         }
  3977.       }
  3978.     }
  3979.     // Fonction simplifiée qui mappe et appelle filterContent
  3980.     function applyFilter(filter) {
  3981.       // Mapper les filtres de la capsule vers les filtres existants si nécessaire
  3982.       const filterMap = {
  3983.         'all': 'toutVoir',
  3984.         'photos': 'photos', 
  3985.         'audio': 'audio',
  3986.         'videos': 'videos',
  3987.         'favoris': 'favoris'
  3988.       };
  3989.       
  3990.       const mappedFilter = filterMap[filter] || filter;
  3991.       
  3992.       // Pour le filtre "all", s'assurer que la vue des jours est restaurée
  3993.       if (filter === 'all') {
  3994.         const containerGallery = document.querySelector('.container--gallery');
  3995.         if (containerGallery) {
  3996.           containerGallery.style.display = 'block';
  3997.         }
  3998.       }
  3999.       
  4000.       if (typeof window.filterContent === 'function') {
  4001.         window.filterContent(mappedFilter);
  4002.       } else {
  4003.         console.error('window.filterContent n\'est pas disponible');
  4004.       }
  4005.     }
  4006.     // Click handlers - exactement la même logique que stat-pill
  4007.     document.addEventListener('click', (e) => {
  4008.       const btn = e.target.closest('.mtb-btn');
  4009.       if (!btn) return;
  4010.       // Bouton fermer
  4011.       if (btn.classList.contains('mtb-close')) {
  4012.         document.querySelector('.media-toolbar')?.remove();
  4013.         return;
  4014.       }
  4015.       const filter = btn.dataset.filter;
  4016.       if (!filter) return;
  4017.       // Debug spécifique pour le bouton favoris
  4018.       if (filter === 'favoris') {
  4019.         console.log('🔵🔵🔵 BOUTON FAVORIS CLIQUÉ (FILTRE uniquement, PAS de modification compteur) 🔵🔵🔵', {
  4020.           btn: btn,
  4021.           filter: filter,
  4022.           hasFilterContent: typeof window.filterContent === 'function',
  4023.           mesFavCountAVANT: document.getElementById('mesFavCount')?.textContent,
  4024.           action: 'Afficher vue favoris SEULEMENT'
  4025.         });
  4026.         
  4027.         // Protection ABSOLUE : empêcher TOUTE propagation
  4028.         e.stopPropagation();
  4029.         e.stopImmediatePropagation();
  4030.         e.preventDefault();
  4031.         
  4032.         // ✅ Appeler UNIQUEMENT la fonction de filtrage (pas AddFavoris, pas supprimerFavoris)
  4033.         if (typeof window.filterContent === 'function') {
  4034.           window.filterContent('favoris');
  4035.         } else {
  4036.           console.error('❌ window.filterContent n\'est pas disponible');
  4037.         }
  4038.         
  4039.         console.log('🔵 Après filterContent, mesFavCountAPRÈS:', document.getElementById('mesFavCount')?.textContent);
  4040.         return; // Sortir IMMÉDIATEMENT pour éviter TOUT traitement supplémentaire
  4041.       }
  4042.       // Exception pour le bouton photos : ne pas modifier les états actifs
  4043.       if (btn.dataset.filter === 'photos') {
  4044.         // Pour le bouton photos, ne pas toucher aux autres boutons
  4045.         // Juste exécuter l'action sans changer les états
  4046.       } else {
  4047.         // Pour les autres boutons : gérer les états actifs normalement
  4048.         const mtbBtns = document.querySelectorAll('.mtb-btn[data-filter]');
  4049.         for (let i = 0; i < mtbBtns.length; i++) {
  4050.           const b = mtbBtns[i];
  4051.           if (b && b.classList) {
  4052.             if (b === btn) {
  4053.               b.classList.add('active');
  4054.             } else {
  4055.               b.classList.remove('active');
  4056.             }
  4057.           }
  4058.         }
  4059.       }
  4060.       
  4061.       // Déclencher le filtrage du contenu - même logique que stat-pill
  4062.       console.log('mtb-btn click:', filter, 'filterContent type:', typeof window.filterContent);
  4063.       if (typeof window.filterContent === 'function') {
  4064.         window.filterContent(filter);
  4065.       } else {
  4066.         console.error('window.filterContent n\'est pas une fonction!');
  4067.       }
  4068.     });
  4069.     // Expose un setter global
  4070.     window.setMediaFilter = (filter) => {
  4071.       // Pour le bouton photos : ne pas modifier les états
  4072.       if (filter !== 'photos') {
  4073.         setActive(filter);
  4074.       }
  4075.       applyFilter(filter);
  4076.     };
  4077.     // 🛡️ PROTECTION ABSOLUE : Empêcher TOUTE interaction directe avec le compteur favoris
  4078.     document.addEventListener('DOMContentLoaded', () => {
  4079.       const mesFavCount = document.getElementById('mesFavCount');
  4080.       if (mesFavCount) {
  4081.         // Bloquer tous les clics directs sur le compteur
  4082.         mesFavCount.addEventListener('click', (e) => {
  4083.           console.log('⚠️ Clic sur mesFavCount bloqué');
  4084.           e.stopPropagation();
  4085.           e.stopImmediatePropagation();
  4086.           e.preventDefault();
  4087.           return false;
  4088.         }, true); // Capture phase pour intercepter avant tout autre listener
  4089.         
  4090.         // Empêcher le double-clic
  4091.         mesFavCount.addEventListener('dblclick', (e) => {
  4092.           console.log('⚠️ Double-clic sur mesFavCount bloqué');
  4093.           e.stopPropagation();
  4094.           e.stopImmediatePropagation();
  4095.           e.preventDefault();
  4096.           return false;
  4097.         }, true);
  4098.         
  4099.         // Ajouter un style pour indiquer visuellement que c'est non-cliquable
  4100.         mesFavCount.style.pointerEvents = 'none';
  4101.         mesFavCount.style.userSelect = 'none';
  4102.         
  4103.         console.log('🛡️ Protection mesFavCount activée - compteur non-cliquable');
  4104.       }
  4105.     });
  4106.     
  4107.     // Mise à jour des compteurs quand les favoris changent
  4108.     window.addEventListener('favorites:updated', updateCounts);
  4109.     
  4110.     // Initialisation
  4111.     setTimeout(() => {
  4112.       updateCounts();
  4113.       setActive('all');
  4114.     }, 100);
  4115.     // Mise à jour périodique des compteurs (sauf favoris qui reste fixe)
  4116.     setInterval(updateCounts, 2000);
  4117.   })();
  4118.   function navigateToDay(index) {
  4119.      if (index >= 0 && index < collapseSections.length) {
  4120.       // Fermer tous les jours
  4121.       collapseSections.forEach(s => {
  4122.         if (s && s.classList) {
  4123.           s.classList.remove('show');
  4124.         }
  4125.       });
  4126.       dateCards.forEach(c => {
  4127.         if (c && c.classList) {
  4128.           c.classList.remove('active');
  4129.         }
  4130.       });
  4131.       // Ouvrir le bon jour
  4132.       const targetCollapse = collapseSections[index];
  4133.       const targetCard = dateCards[index];
  4134.       if (targetCollapse && targetCard) {
  4135.         if (targetCollapse.classList) {
  4136.           targetCollapse.classList.add('show');
  4137.         }
  4138.         if (targetCard.classList) {
  4139.           targetCard.classList.add('active');
  4140.         }
  4141.       }
  4142.     }
  4143.   }
  4144.   prevButtons.forEach(button => {
  4145.     button.addEventListener('click', function () {
  4146.       const targetIndex = parseInt(this.dataset.target, 10);
  4147.       navigateToDay(targetIndex);
  4148.     });
  4149.   });
  4150.   nextButtons.forEach(button => {
  4151.     button.addEventListener('click', function () {
  4152.       const targetIndex = parseInt(this.dataset.target, 10);
  4153.       navigateToDay(targetIndex);
  4154.     });
  4155.   });
  4156. });
  4157. </script>
  4158.   <script>
  4159.     document.addEventListener('DOMContentLoaded', function() {
  4160.   const style = document.createElement('style');
  4161.   style.textContent = `
  4162.     .hidden {
  4163.       display: none !important;
  4164.     }
  4165.   `;
  4166.   document.head.appendChild(style);
  4167. });
  4168.     document.addEventListener('DOMContentLoaded', function() {
  4169.     // Get the gift button
  4170.     const giftButton = document.querySelector('.gift-button');
  4171.     if (giftButton) {
  4172.       // Keep it clickable for the gift functionality
  4173.       giftButton.style.pointerEvents = 'auto';
  4174.       
  4175.       // Add click handler for gift action
  4176.       giftButton.onclick = function(e) {
  4177.         e.preventDefault();
  4178.         e.stopPropagation();
  4179.         return false;
  4180.       };
  4181.       
  4182.       // Make sure hover still works
  4183.       favoriteButton.addEventListener('mouseover', function() {
  4184.         document.getElementById('purchase-alert').style.display = 'block';
  4185.       });
  4186.       
  4187.       // Keep any existing hover functionality
  4188.       if (typeof showSelection === 'function') {
  4189.         favoriteButton.onmouseover = showSelection;
  4190.       }
  4191.     }
  4192.     
  4193.     // Make sure the purchase alert remains interactive
  4194.     const purchaseAlert = document.getElementById('purchase-alert');
  4195.     if (purchaseAlert) {
  4196.       purchaseAlert.style.pointerEvents = 'auto';
  4197.     }
  4198.   });
  4199.     // Sélection des éléments
  4200.     const purchaseAlert = document.getElementById("purchase-alert");
  4201.     const alertContent = document.getElementById("purchase-alert-content");
  4202.     const likeCountLabel = document.getElementById("likeCount");
  4203.     
  4204.     // Fonction pour obtenir le nombre actuel de favoris
  4205.     function getCurrentFavoriteCount() {
  4206.         const likeCountLabel = document.getElementById("likeCount");
  4207.         return parseInt(likeCountLabel?.textContent.trim(), 10) || 0;
  4208.     }
  4209.     // Fonction pour mettre à jour le contenu de l'alerte
  4210.     function updatePurchaseAlert(current) {
  4211.   let remainingForAlbum = Math.max(0, 20 - current);
  4212.   let remainingForPochette = Math.max(0, 12 - current);
  4213.   let remainingForPack = Math.max(0, 12 - current);
  4214.   const progressBar = (count, total, color) => `
  4215.   <div style="margin: 5px 0;">
  4216.     <div style="background-color: #e9ecef; border-radius: 5px; overflow: hidden; height: 8px;">
  4217.       <div style="width: ${
  4218.         (count / total) * 100
  4219.       }%; background-color: ${color}; height: 100%;"></div>
  4220.     </div>
  4221.     <small style="font-size: 12px;">${count}/${total} photos</small>
  4222.   </div>
  4223. `;
  4224.   // Use Twig paths here:
  4225.   const products = [
  4226.       {
  4227.       name: "Pochette photo (12 photos)",
  4228.       required: 12,
  4229.       remaining: remainingForPochette,
  4230.       image: "/images/produit/PochettePhoto5sur5-2.jpg",
  4231.       color: "#2196f3",
  4232.       link: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}",
  4233.     },
  4234.        {
  4235.       name: "Pack numérique (20 photos)",
  4236.       required: 20,
  4237.       remaining: remainingForAlbum,
  4238.       image: "/images/produit/photoNumerique.jpg",
  4239.       color: "#4caf50",
  4240.       link: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 20}) }}",
  4241.     }
  4242.   
  4243.   
  4244.     
  4245.   ].sort((a, b) => a.remaining - b.remaining);
  4246.   const productList = products
  4247.     .map((product) => {
  4248.       const count = current;
  4249.       const total = product.required;
  4250.       const remaining = product.remaining;
  4251.       return `
  4252.     <li style="margin-bottom: 15px;">
  4253.       <div style="display: flex; align-items: center; gap: 10px;">
  4254.         <img
  4255.           src="${product.image}"
  4256.           alt="${product.name}"
  4257.           style="width: 65px; height: 65px; border-radius: 5px; margin-top:-19px"
  4258.         />
  4259.         <div style="flex: 1;">
  4260.           <strong style="font-size: 14px;">${product.name}</strong>
  4261.           ${progressBar(count, total, product.color)}
  4262.           ${
  4263.             remaining > 0
  4264.               ? `<small style="color: ${product.color}; font-size: 12px;">
  4265.                    Encore ${remaining} photos ❤️ pour compléter ${product.name.toLowerCase()}
  4266.                  </small>`
  4267.               : `<button
  4268.                   style="
  4269.                     margin-top: 5px;
  4270.                     padding: 5px 8px;
  4271.                     background-color: ${product.color};
  4272.                     color: white;
  4273.                     border: none;
  4274.                     border-radius: 5px;
  4275.                     font-size: 12px;
  4276.                     cursor: pointer;
  4277.                   "
  4278.                   onclick="window.location.href='${product.link}'"
  4279.                 >
  4280.                   Commander
  4281.                 </button>`
  4282.           }
  4283.         </div>
  4284.       </div>
  4285.     </li>
  4286.   `;
  4287.     })
  4288.     .join("");
  4289.   const plusButton = `
  4290.     <li style="margin-bottom: 15px; text-align: center;">
  4291.       <button
  4292.         style="
  4293.           padding: 5px 8px;
  4294.           background-color: #F56040;
  4295.           color: white;
  4296.           border: none;
  4297.           border-radius: 5px;
  4298.           font-size: 14px;
  4299.           line-height: 1;
  4300.           width: 150px;
  4301.           height: 40px;
  4302.           cursor: pointer;
  4303.         "
  4304.         onclick="window.location.href='{{ path('boutique5sur5') }}'"
  4305.       >
  4306.         Aller à la boutique
  4307.       </button>
  4308.     </li>
  4309.   `;
  4310.   if (current === 0) {
  4311.     alertContent.innerHTML = `
  4312.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  4313.       Vous n'avez pas encore de photos favorites !
  4314.     </p>
  4315.     <p style="margin-bottom: 20px; color: #555;">
  4316.       Commencez à ajouter vos moments préférés pour profiter de nos offres.
  4317.     </p>
  4318.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  4319.   `;
  4320.   } else {
  4321.     alertContent.innerHTML = `
  4322.     <p style="font-size: 16px; font-weight: bold; color: #333;">
  4323.       Vous avez atteint ${current} photos favorites !
  4324.     </p>
  4325.     <p style="margin-bottom: 20px; color: #555;">
  4326.       Profitez de nos offres spéciales :
  4327.     </p>
  4328.     <ul style="list-style-type: none; padding: 0;">${productList}${plusButton}</ul>
  4329.   `;
  4330.   }
  4331.   if (purchaseAlert && purchaseAlert.classList) {
  4332.     purchaseAlert.classList.remove("hidden");
  4333.   }
  4334.   clearTimeout(window.purchaseAlertTimeout);
  4335.   window.purchaseAlertTimeout = setTimeout(() => {
  4336.     if (!purchaseAlert.matches(":hover")) {
  4337.       closePurchaseAlert();
  4338.     }
  4339.   }, 5000);
  4340. }
  4341.     // Fonction pour fermer l'alerte
  4342.     function closePurchaseAlert() {
  4343.       if (purchaseAlert && purchaseAlert.classList) {
  4344.         purchaseAlert.classList.add("hidden");
  4345.       }
  4346.     }
  4347.     // Événement pour mettre à jour le contenu et afficher la popover dynamiquement au hover
  4348.     document.querySelector(".gift-button").addEventListener("mouseover", () => {
  4349.       const currentCount = getCurrentFavoriteCount();
  4350.       updatePurchaseAlert(currentCount);
  4351.       if (purchaseAlert && purchaseAlert.classList) {
  4352.         purchaseAlert.classList.remove("cachee"); // Réaffiche la popover
  4353.       }
  4354.     });
  4355.     // Nouvelles fonctions pour le bouton cadeau
  4356.     function showGiftMessage() {
  4357.       const tooltip = document.getElementById("giftTooltip");
  4358.       if (tooltip) {
  4359.         if (tooltip && tooltip.classList) {
  4360.           tooltip.classList.add("show");
  4361.         }
  4362.       }
  4363.     }
  4364.     function hideGiftMessage() {
  4365.       const tooltip = document.getElementById("giftTooltip");
  4366.       if (tooltip) {
  4367.         if (tooltip && tooltip.classList) {
  4368.           tooltip.classList.remove("show");
  4369.         }
  4370.       }
  4371.     }
  4372.     function showSelection() {
  4373.       document.getElementById("purchase-alert").style.display = "block";
  4374.     }
  4375.     function hideSelection() {
  4376.       document.getElementById("selectionPopover").style.display = "none";
  4377.     }
  4378.     document.addEventListener("DOMContentLoaded", function () {
  4379.       const container = document.querySelector(".date-container");
  4380.       // Vérifie si le conteneur existe pour éviter les erreurs
  4381.       if (container) {
  4382.         container.scrollTo({
  4383.           left: container.scrollWidth, // Scroll directement à la position maximale
  4384.           behavior: "smooth", // Défilement fluide
  4385.         });
  4386.       }
  4387.     });
  4388.     document.addEventListener("DOMContentLoaded", function () {
  4389.       const container = document.querySelector(".date-container");
  4390.       const leftArrow = document.querySelector(".scroll-btn.left");
  4391.       const rightArrow = document.querySelector(".scroll-btn.right");
  4392.       // Fonction pour vérifier le débordement et activer/désactiver les flèches
  4393.       function updateArrowsVisibility() {
  4394.         const isOverflowing = container.scrollWidth > container.clientWidth; // Vérifie si débordement
  4395.         leftArrow.style.display = isOverflowing ? "flex" : "none";
  4396.         rightArrow.style.display = isOverflowing ? "flex" : "none";
  4397.       }
  4398.       // Fonction pour défiler
  4399.       function scrollContainer(direction) {
  4400.         container.scrollBy({
  4401.           left: direction === "left" ? -200 : 200, // Défiler à gauche ou à droite
  4402.           behavior: "smooth",
  4403.         });
  4404.       }
  4405.       // Ajout des événements de clic pour les flèches
  4406.       leftArrow.addEventListener("click", () => scrollContainer("left"));
  4407.       rightArrow.addEventListener("click", () => scrollContainer("right"));
  4408.       // ⚡ OPTIMISÉ: Debounce resize pour éviter surcharge
  4409.       let resizeTimeout;
  4410.       const debouncedResize = () => {
  4411.         clearTimeout(resizeTimeout);
  4412.         resizeTimeout = setTimeout(updateArrowsVisibility, 150);
  4413.       };
  4414.       
  4415.       updateArrowsVisibility();
  4416.       window.addEventListener("resize", debouncedResize, { passive: true });
  4417.     });
  4418.     document.addEventListener("DOMContentLoaded", function () {
  4419.       const container = document.querySelector(".date-container");
  4420.       const leftBtn = document.querySelector(".scroll-btn.left");
  4421.       const rightBtn = document.querySelector(".scroll-btn.right");
  4422.       leftBtn.addEventListener("click", () => {
  4423.         container.scrollBy({
  4424.           left: -200, // Défile vers la gauche
  4425.           behavior: "smooth",
  4426.         });
  4427.       });
  4428.       rightBtn.addEventListener("click", () => {
  4429.         container.scrollBy({
  4430.           left: 200, // Défile vers la droite
  4431.           behavior: "smooth",
  4432.         });
  4433.       });
  4434.     });
  4435.  document.addEventListener("DOMContentLoaded", function () {
  4436.     // Sélectionnez tous les badges de filtre
  4437.     const filterBadges = document.querySelectorAll(".filter-badge");
  4438.     // Sélectionnez tous les éléments de la galerie
  4439.     const galleryItems = document.querySelectorAll(".column");
  4440.     // Sélectionnez tous les jours
  4441.     const days = document.querySelectorAll(".collapse");
  4442.     // Fonction pour réinitialiser les filtres
  4443.     function resetFilters() {
  4444.         // Réinitialisez tous les éléments de la galerie
  4445.         galleryItems.forEach((item) => {
  4446.             item.style.display = "block";
  4447.         });
  4448.         // Réinitialisez les états des badges
  4449.         filterBadges.forEach((badge) => {
  4450.           if (badge && badge.classList) {
  4451.             badge.classList.remove("active");
  4452.           }
  4453.         });
  4454.     }
  4455.     // Ajoutez un gestionnaire d'événements pour chaque badge
  4456.     filterBadges.forEach((badge) => {
  4457.         badge.addEventListener("click", function () {
  4458.             const filter = this.getAttribute("data-filter");
  4459.             // Réinitialisez l'état actif pour tous les badges
  4460.             filterBadges.forEach((btn) => {
  4461.               if (btn && btn.classList) {
  4462.                 btn.classList.remove("active");
  4463.               }
  4464.             });
  4465.             // Ajoutez l'état actif au badge cliqué
  4466.             if (this && this.classList) {
  4467.               this.classList.add("active");
  4468.             }
  4469.             // Affichez ou masquez les éléments de la galerie
  4470.             galleryItems.forEach((item) => {
  4471.                 if (filter === "all") {
  4472.                     item.style.display = "block";
  4473.                 } else if (filter === "photo" && item.querySelector("img")) {
  4474.                     item.style.display = "block";
  4475.                 } else if (filter === "video" && item.querySelector("video")) {
  4476.                     item.style.display = "block";
  4477.                 } else if (filter === "audio" && item.classList && item.classList.contains("audio-message-item")) {
  4478.                     item.style.display = "block";
  4479.                 } else {
  4480.                     item.style.display = "none";
  4481.                 }
  4482.             });
  4483.         });
  4484.     });
  4485.     // Réinitialiser les filtres lors du changement de jour
  4486.     days.forEach((day) => {
  4487.         day.addEventListener("show.bs.collapse", function () {
  4488.             resetFilters();
  4489.         });
  4490.     });
  4491. });
  4492.     $(document).ready(function () {
  4493.       let zoomCounter = 0; // Initialize zoom counter
  4494.       let currentImageSrc = ""; // Track current image source
  4495.       let lastClickPosition = { x: 50, y: 50 }; // Default to center of image
  4496.       $(".container--gallery").magnificPopup({
  4497.         delegate: "a",
  4498.         type: "image",
  4499.         mainClass: "mfp-with-zoom mfp-img-mobile",
  4500.         image: {
  4501.           verticalFit: true,
  4502.         },
  4503.         gallery: {
  4504.           enabled: true,
  4505.           tPrev: "Previous (Left arrow key)", // Alt text on left arrow
  4506.           tNext: "Next (Right arrow key)", // Alt text on right arrow
  4507.           tCounter: "%curr% of %total%", // Markup for "1 of 7" counter
  4508.         },
  4509.         zoom: {
  4510.           enabled: true,
  4511.           duration: 300,
  4512.           opener: function (element) {
  4513.             return element.find("img");
  4514.           },
  4515.         },
  4516.         callbacks: {
  4517.           open: function () {
  4518.             // Get current image data from the link that was clicked
  4519.             const currentLink = this.currItem.el;
  4520.             const imageId =
  4521.               currentLink
  4522.                 .closest(".photo-zoom")
  4523.                 .find(".heart-icon")
  4524.                 .data("id") || "";
  4525.             const sejourId =
  4526.               currentLink
  4527.                 .closest(".photo-zoom")
  4528.                 .find(".heart-icon")
  4529.                 .data("sejour-id") || "";
  4530.             const imagePath =
  4531.               currentLink
  4532.                 .closest(".photo-zoom")
  4533.                 .find(".heart-icon")
  4534.                 .data("path") || "";
  4535.             const imageDesc =
  4536.               currentLink
  4537.                 .closest(".photo-zoom")
  4538.                 .find(".heart-icon")
  4539.                 .data("description") || "";
  4540.             const isFavorite = currentLink
  4541.               .closest(".photo-zoom")
  4542.               .find(".heart-icon i")
  4543.               .hasClass("bi-heart-fill");
  4544.             const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  4545.             const favoriteIconColor = isFavorite ? "#f56040" : "white";
  4546.             const favoriteTooltip = isFavorite
  4547.               ? "Retirer des favoris"
  4548.               : "Ajouter aux favoris";
  4549.             const zoomControls = `
  4550.           <div class="mfp-zoom-controls">
  4551.             <button class="zoom-btn zoom-out" title="Zoom Out"><i class="fa fa-search-minus"></i></button>
  4552.             <button class="zoom-btn zoom-in" title="Zoom In"><i class="fa fa-search-plus"></i></button>
  4553.           </div>
  4554.           <div class="mfp-favorite">
  4555.             <button class="favorite-btn" 
  4556.                     data-id="${imageId}" 
  4557.                     data-sejour-id="${sejourId}" 
  4558.                     data-path="${imagePath}" 
  4559.                     data-description="${imageDesc}"
  4560.                     title="${favoriteTooltip}">
  4561.               <i class="bi ${favoriteIconClass}" style="color: ${favoriteIconColor}; text-shadow: 0px 0px 3px rgba(0,0,0,0.5);"></i>
  4562.             </button>
  4563.           </div>
  4564.           <div class="mfp-counter"></div>
  4565.         `;
  4566.             $(".mfp-content").append(zoomControls);
  4567.             initializeZoomControls();
  4568.             initializeFavoriteButton();
  4569.             const intervalId = setInterval(() => {
  4570.               const newImageSrc = $(".mfp-img").attr("src");
  4571.               if (newImageSrc !== currentImageSrc) {
  4572.                 currentImageSrc = newImageSrc;
  4573.                 zoomCounter = 0;
  4574.                 lastClickPosition = { x: 50, y: 50 }; // Reset to center
  4575.                 attachZoomHandler(); // Reattach zoom handler to new image
  4576.                 $(".mfp-img").css({
  4577.                   "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4578.                   transform: `scale(1)`,
  4579.                 });
  4580.                 // Update favorite button for the new image
  4581.                 updateFavoriteButton();
  4582.                 initializeZoomControls();
  4583.                 updateCounter();
  4584.               }
  4585.             }, 100);
  4586.             this.content.on("mfpClose", function () {
  4587.               clearInterval(intervalId);
  4588.             });
  4589.             attachZoomHandler();
  4590.           },
  4591.           close: function () {
  4592.             $(".mfp-zoom-controls").remove();
  4593.             $(".mfp-favorite").remove();
  4594.             $(".mfp-counter").remove();
  4595.             zoomCounter = 0;
  4596.           },
  4597.         },
  4598.       });
  4599.       function attachZoomHandler() {
  4600.         $(".mfp-img")
  4601.           .off("click")
  4602.           .on("click", function (event) {
  4603.             event.stopPropagation(); // Prevent default navigation behavior
  4604.             // Calculate click coordinates relative to the image
  4605.             const imgOffset = $(this).offset();
  4606.             const clickX = event.pageX - imgOffset.left;
  4607.             const clickY = event.pageY - imgOffset.top;
  4608.             const imgWidth = $(this).width();
  4609.             const imgHeight = $(this).height();
  4610.             // Calculate transform-origin based on click position
  4611.             lastClickPosition = {
  4612.               x: (clickX / imgWidth) * 100,
  4613.               y: (clickY / imgHeight) * 100,
  4614.             };
  4615.             // Cycle through zoom levels: 1x, 1.5x, 2x
  4616.             zoomCounter = (zoomCounter + 1) % 3;
  4617.             const zoomLevels = [1, 1.5, 2];
  4618.             const zoomLevel = zoomLevels[zoomCounter];
  4619.             $(this).css({
  4620.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4621.               transform: `scale(${zoomLevel})`,
  4622.             });
  4623.             updateZoomButtonState();
  4624.           });
  4625.       }
  4626.       function initializeZoomControls() {
  4627.         $(".mfp-zoom-controls .zoom-in")
  4628.           .off("click")
  4629.           .on("click", function (event) {
  4630.             event.stopPropagation();
  4631.             zoomCounter = (zoomCounter + 1) % 3;
  4632.             const zoomLevels = [1, 1.5, 2];
  4633.             const zoomLevel = zoomLevels[zoomCounter];
  4634.             $(".mfp-img").css({
  4635.               "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4636.               transform: `scale(${zoomLevel})`,
  4637.             });
  4638.             updateZoomButtonState();
  4639.           });
  4640.         $(".mfp-zoom-controls .zoom-out")
  4641.           .off("click")
  4642.           .on("click", function (event) {
  4643.             event.stopPropagation();
  4644.             if (zoomCounter > 0) {
  4645.               zoomCounter -= 1;
  4646.               const zoomLevels = [1, 1.5, 2];
  4647.               const zoomLevel = zoomLevels[zoomCounter];
  4648.               $(".mfp-img").css({
  4649.                 "transform-origin": `${lastClickPosition.x}% ${lastClickPosition.y}%`,
  4650.                 transform: `scale(${zoomLevel})`,
  4651.               });
  4652.               updateZoomButtonState();
  4653.             } else {
  4654.               $.magnificPopup.close();
  4655.             }
  4656.           });
  4657.       }
  4658.       function initializeFavoriteButton() {
  4659.         $(".mfp-favorite .favorite-btn")
  4660.           .off("click")
  4661.           .on("click", function (event) {
  4662.             event.stopPropagation();
  4663.             const $this = $(this);
  4664.             const imageId = $this.data("id");
  4665.             const sejourId = $this.data("sejour-id");
  4666.             // Toggle favorite status
  4667.             const isFavorite = $this.find("i").hasClass("bi-heart-fill");
  4668.             // Update the button appearance
  4669.             if (isFavorite) {
  4670.               $this
  4671.                 .find("i")
  4672.                 .removeClass("bi-heart-fill")
  4673.                 .addClass("bi-heart")
  4674.                 .css("color", "white");
  4675.               $this.attr("title", "Ajouter aux favoris");
  4676.             } else {
  4677.               $this
  4678.                 .find("i")
  4679.                 .removeClass("bi-heart")
  4680.                 .addClass("bi-heart-fill")
  4681.                 .css("color", "#f56040");
  4682.               $this.attr("title", "Retirer des favoris");
  4683.             }
  4684.             // Update the original heart icon in the gallery
  4685.             const originalHeartIcon = $(
  4686.               `.heart-icon[data-id="${imageId}"]`
  4687.             ).find("i");
  4688.             if (isFavorite) {
  4689.               originalHeartIcon
  4690.                 .removeClass("bi-heart-fill")
  4691.                 .addClass("bi-heart")
  4692.                 .css("color", "");
  4693.             } else {
  4694.               originalHeartIcon
  4695.                 .removeClass("bi-heart")
  4696.                 .addClass("bi-heart-fill")
  4697.                 .css("color", "#f56040");
  4698.             }
  4699.             // Make AJAX call to update favorite status in the backend using Parent routes
  4700.             $.ajax({
  4701.               url: isFavorite ? "/Parent/aSupprimerFav" : "/Parent/ajouterFav",
  4702.               type: "POST",
  4703.               data: {
  4704.                 id: imageId,
  4705.                 idSejour: sejourId,
  4706.               },
  4707.               success: function (response) {
  4708.                 // Optional: Show a success message or handle response
  4709.                 console.log("Favorite status updated", response);
  4710.               },
  4711.               error: function (error) {
  4712.                 console.error("Error updating favorite status", error);
  4713.                 // Revert the icon change on error
  4714.                 if (isFavorite) {
  4715.                   $this
  4716.                     .find("i")
  4717.                     .removeClass("bi-heart")
  4718.                     .addClass("bi-heart-fill")
  4719.                     .css("color", "#f56040");
  4720.                   originalHeartIcon
  4721.                     .removeClass("bi-heart")
  4722.                     .addClass("bi-heart-fill")
  4723.                     .css("color", "#f56040");
  4724.                 } else {
  4725.                   $this
  4726.                     .find("i")
  4727.                     .removeClass("bi-heart-fill")
  4728.                     .addClass("bi-heart")
  4729.                     .css("color", "white");
  4730.                   originalHeartIcon
  4731.                     .removeClass("bi-heart-fill")
  4732.                     .addClass("bi-heart")
  4733.                     .css("color", "");
  4734.                 }
  4735.               },
  4736.             });
  4737.           });
  4738.       }
  4739.       function updateFavoriteButton() {
  4740.         // Get current image data from the current slide
  4741.         const currentSlide = $.magnificPopup.instance.currItem.el;
  4742.         const photoZoom = currentSlide.closest(".photo-zoom");
  4743.         if (photoZoom.length) {
  4744.           const heartIcon = photoZoom.find(".heart-icon");
  4745.           const imageId = heartIcon.data("id") || "";
  4746.           const sejourId = heartIcon.data("sejour-id") || "";
  4747.           const imagePath = heartIcon.data("path") || "";
  4748.           const imageDesc = heartIcon.data("description") || "";
  4749.           const isFavorite = heartIcon.find("i").hasClass("bi-heart-fill");
  4750.           const favoriteIconClass = isFavorite ? "bi-heart-fill" : "bi-heart";
  4751.           const favoriteIconColor = isFavorite ? "#f56040" : "white";
  4752.           const favoriteTooltip = isFavorite
  4753.             ? "Retirer des favoris"
  4754.             : "Ajouter aux favoris";
  4755.           // Update the favorite button
  4756.           const $favoriteBtn = $(".mfp-favorite .favorite-btn");
  4757.           $favoriteBtn.data("id", imageId);
  4758.           $favoriteBtn.data("sejour-id", sejourId);
  4759.           $favoriteBtn.data("path", imagePath);
  4760.           $favoriteBtn.data("description", imageDesc);
  4761.           $favoriteBtn.attr("title", favoriteTooltip);
  4762.           $favoriteBtn
  4763.             .find("i")
  4764.             .removeClass("bi-heart bi-heart-fill")
  4765.             .addClass(favoriteIconClass)
  4766.             .css("color", favoriteIconColor);
  4767.         }
  4768.       }
  4769.       function updateZoomButtonState() {
  4770.         const zoomLevels = [1, 1.5, 2];
  4771.         const currentZoom = zoomLevels[zoomCounter];
  4772.         $(".zoom-in").prop("disabled", currentZoom === 2);
  4773.         $(".zoom-out").prop("disabled", currentZoom === 1);
  4774.       }
  4775.       function updateCounter() {
  4776.         const counterText = $(".mfp-counter")
  4777.           .closest(".mfp-content")
  4778.           .find(".mfp-counter")
  4779.           .text();
  4780.         const matches = counterText.match(/(\d+) of (\d+)/);
  4781.         if (matches) {
  4782.           const currentIndex = matches[1];
  4783.           const totalImages = matches[2];
  4784.           $(".mfp-counter").text(`${currentIndex} of ${totalImages}`);
  4785.         }
  4786.       }
  4787.       // Add CSS for the favorite button and rounded image corners
  4788.       $("<style>")
  4789.         .prop("type", "text/css")
  4790.         .html(
  4791.           `
  4792.       .mfp-favorite {
  4793.         position: absolute;
  4794.         top: 15px;
  4795.         left: 15px;
  4796.         z-index: 1046;
  4797.       }
  4798.       .favorite-btn {
  4799.         background: transparent;
  4800.         border: none;
  4801.         font-size: 24px;
  4802.         padding: 5px;
  4803.         cursor: pointer;
  4804.         outline: none;
  4805.       }
  4806.       .favorite-btn i {
  4807.         transition: all 0.3s ease;
  4808.       }
  4809.       .favorite-btn:hover i {
  4810.         transform: scale(1.2);
  4811.       }
  4812.       /* Rounded corners for zoomed images */
  4813.       .mfp-img {
  4814.         border-radius: 8px;
  4815.       }
  4816.       /* Make sure the container doesn't clip the rounded corners */
  4817.       .mfp-figure:after {
  4818.         border-radius: 8px;
  4819.       }
  4820.     `
  4821.         )
  4822.         .appendTo("head");
  4823.     });
  4824.   </script>
  4825. <script>
  4826. document.addEventListener('DOMContentLoaded', function () {
  4827.   const openBtn = document.getElementById('openFavoritesBtn');
  4828.   const closeBtn = document.getElementById('closeSidebarBtn');
  4829.   const sidebar = document.getElementById('favoritesSidebar');
  4830.   const tbody = document.querySelector('#favoritesTable tbody');
  4831.   openBtn.addEventListener('click', async () => {
  4832.     try {
  4833.       const response = await fetch('/Parent/mes-favoris', {
  4834.         headers: {
  4835.           'Accept': 'application/json'
  4836.         }
  4837.       });
  4838.       const result = await response.json();
  4839.       if (!result.success || !Array.isArray(result.data)) {
  4840.         alert('Erreur lors du chargement des favoris.');
  4841.         return;
  4842.       }
  4843.       tbody.innerHTML = '';
  4844.       result.data.forEach((fav, index) => {
  4845.         const row = document.createElement('tr');
  4846.         row.innerHTML = `
  4847.           <td>${index + 1}</td>
  4848.           <td><img src="${fav.path}" alt="favori"></td>
  4849.           <td>${fav.descreption || '—'}</td>
  4850.           <td>${fav.created_at}</td>
  4851.         `;
  4852.         tbody.appendChild(row);
  4853.       });
  4854.       sidebar.classList.add('active');
  4855.     } catch (e) {
  4856.       console.error('Erreur réseau:', e);
  4857.       alert('Impossible de charger les favoris.');
  4858.     }
  4859.   });
  4860.   closeBtn.addEventListener('click', () => {
  4861.     sidebar.classList.remove('active');
  4862.   });
  4863. });
  4864. </script>
  4865.   <script>
  4866.         // Fonction pour vérifier et afficher l'alerte
  4867.         function checkFavoritesAlert() {
  4868.             const currentCount = window.favoriteCount || 0;
  4869.             if (currentCount >= 10) {
  4870.                 const purchaseAlert = document.getElementById('purchase-alert');
  4871.                 if (purchaseAlert) {
  4872.                     purchaseAlert.style.display = 'block'; // Affiche l'alerte
  4873.                 }
  4874.             } else {
  4875.                 purchaseAlert.style.display = 'none'; // Cache l'alerte si le nombre est réduit
  4876.             }
  4877.         }
  4878.     
  4879.         
  4880.         document.addEventListener('DOMContentLoaded', () => {
  4881.     const favoriteCount = {{ nblikes }};
  4882.     updateCardContent(favoriteCount);
  4883. });
  4884. function updateCardContent(favoriteCount) {
  4885.     const card = document.getElementById('dynamic-card');
  4886.     const cardContent = document.getElementById('dynamic-card-content');
  4887.     let produits = [];
  4888.     if (favoriteCount >= 20) {
  4889.         produits.push({
  4890.             titre: "Album débloqué !",
  4891.             bouton: "Commander",
  4892.             image: "/images/produit/Album5sur5-3.jpg",
  4893.             lien: "{{ path('EditionAlbum') }}"
  4894.         });
  4895.     }
  4896.     if (favoriteCount >= 12) {
  4897.         produits.push({
  4898.             titre: "Pochette débloquée !",
  4899.             bouton: "Commander",
  4900.             image: "/images/produit/PochettePhoto5sur5-2.jpg",
  4901.             lien: "{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}"
  4902.         });
  4903.     }
  4904.     if (favoriteCount >= 5) {
  4905.         produits.push({
  4906.             titre: "Pack numérique débloqué !",
  4907.             bouton: "Commander",
  4908.             image: "/images/produit/photoNumerique.jpg",
  4909.             lien: "{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}"
  4910.         });
  4911.     }
  4912. if (produits.length === 0) {
  4913.   cardContent.innerHTML = `
  4914.     <div style="position: relative; width: 100%; height: 140px; border-radius: 15px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.1);">
  4915.       <div style="background-image: url('/images/produit/CoffretCadeau5sur5-2.jpg'); background-size: cover; background-position: center; width: 100%; height: 100%;">
  4916.         <div style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; padding: 10px;">
  4917.           <div style="
  4918.     margin-top: 35px;
  4919.     color: white;
  4920.     font-size: 15px;
  4921.     font-weight: bold;
  4922.     text-align: center;
  4923.     line-height: 1;
  4924.     text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);">
  4925.             Ajoutez des favoris ❤️<br><span style="font-size: 16px; font-weight: normal;">pour débloquer un souvenir à offrir 🎁</span>
  4926.           </div>
  4927.         </div>
  4928.       </div>
  4929.     </div>
  4930.   `;
  4931.         return;
  4932.     }
  4933.     cardContent.innerHTML = `
  4934.     <div class="splide" id="dynamicSplide">
  4935.       <div class="splide__track">
  4936.         <ul class="splide__list">
  4937.           ${produits.map(produit => `
  4938.             <li class="splide__slide" style="position: relative;">
  4939.               <img src="${produit.image}" alt="${produit.titre}" style="width: 100%; height: 150px; object-fit: cover; border-radius: 8px;">
  4940.               <div style="position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0,0,0,0.1); color: white; padding: 10px; text-align: center; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;">
  4941.                 <div style="font-weight: bold; font-size: 14px;">${produit.titre}</div>
  4942.                 <button style="margin-top: 5px; padding: 5px 8px; background-color: #F56040; color: white; border: none; border-radius: 4px; font-size: 12px; cursor: pointer;" onclick="window.location.href='${produit.lien}'">
  4943.                   ${produit.bouton}
  4944.                 </button>
  4945.               </div>
  4946.             </li>
  4947.           `).join('')}
  4948.         </ul>
  4949.       </div>
  4950.     </div>
  4951.     `;
  4952.     // Monte le carrousel
  4953.     new Splide('#dynamicSplide', {
  4954.         type: 'loop',
  4955.         arrows: true,
  4956.         pagination: false,
  4957.         autoplay: true,
  4958.         interval: 4000,
  4959.         speed: 800,
  4960.     }).mount();
  4961. }
  4962.         function supprimerFavoris($id, $idSejour) {
  4963.             // 🚨 LOG CRITIQUE : Tracer TOUS les appels à supprimerFavoris
  4964.             console.error('🚨🚨🚨 supprimerFavoris() APPELÉ ! 🚨🚨🚨', {
  4965.                 id: $id,
  4966.                 idSejour: $idSejour,
  4967.                 stack: new Error().stack,
  4968.                 mesFavCountAvant: document.getElementById('mesFavCount')?.textContent
  4969.             });
  4970.             
  4971.             // Vider l'élément coeur pour ce favori
  4972.             const coeurElement = $('#coeur' + $id);
  4973.             coeurElement.empty();
  4974.             // Ajout d'une animation sur le bouton cadeau
  4975.             const giftButton = document.querySelector('.gift-button');
  4976.             if (giftButton) {
  4977.                 giftButton.classList.add('active');
  4978.                 // Retirer l'animation après qu'elle soit jouée
  4979.                 setTimeout(() => {
  4980.                     giftButton.classList.remove('active');
  4981.                 }, 600); // La durée doit correspondre à celle de l'animation
  4982.             }
  4983.             // Mise à jour de l'icône coeur
  4984.             const clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  4985.             coeurElement.html(
  4986.                 `<i class="bi bi-heart ${clas}" ></i>`
  4987.             );
  4988.             // METTRE À JOUR UNIQUEMENT #mesFavCount (source unique de vérité)
  4989.             // L'observer se chargera de synchroniser giftCount automatiquement
  4990.             const mesFavCount = document.getElementById('mesFavCount');
  4991.             if (mesFavCount) {
  4992.                 let currentCount = parseInt(mesFavCount.textContent || '0');
  4993.                 currentCount = Math.max(0, currentCount - 1); // Empêche d'aller en dessous de 0
  4994.                 mesFavCount.textContent = currentCount;
  4995.                 console.log('[supprimerFavoris] ✅ mesFavCount décrémenté:', currentCount);
  4996.                 
  4997.                 // L'observer MutationObserver va automatiquement :
  4998.                 // - Synchroniser giftCount
  4999.                 // - Mettre à jour le sidebar
  5000.                 // - Mettre à jour product-suggestions
  5001.             }
  5002.             // Préparation des données pour l'Ajax
  5003.             const $_data = { 'id': $id, 'idSejour': $idSejour };
  5004.             // Appel Ajax pour supprimer le favori
  5005.             $.ajax({
  5006.                 type: "POST",
  5007.                 url: "{{ path('Supprimer_fav') }}",
  5008.                 data: $_data,
  5009.                 success: function () {
  5010.                     // Réactiver les icônes après succès
  5011.                     $('.IconDelete').each(function () {
  5012.                         $(this).css('pointer-events', '');
  5013.                     });
  5014.                     
  5015.                     // Mettre à jour l'alerte avec le nouveau nombre
  5016.                     const finalCount = getCurrentFavoriteCount();
  5017.                     if (typeof updatePurchaseAlert === 'function') {
  5018.                         updatePurchaseAlert(finalCount);
  5019.                     }
  5020.                 },
  5021.                 error: function (xhr, status, error) {
  5022.                     console.error('Erreur lors de la suppression du favori :', error);
  5023.                 }
  5024.             });
  5025.         }
  5026.         function AddFavoris($id, $idSejour, $urlimg, $description) {
  5027.             // 🟢 LOG : Tracer les ajouts de favoris
  5028.             console.log('🟢 AddFavoris() APPELÉ', {
  5029.                 id: $id,
  5030.                 idSejour: $idSejour,
  5031.                 mesFavCountAvant: document.getElementById('mesFavCount')?.textContent
  5032.             });
  5033.             
  5034.             // Update heart icon
  5035.             $('#coeur' + $id).empty();
  5036.             var clas = $('.IconImag6').hasClass('active') ? "IconDelete IconDeletesix" : "IconDelete";
  5037.             $('#coeur' + $id).html("<i class=\"bi bi-heart-fill favSelect " + clas + "\" )\"></i>");
  5038.             
  5039.             // METTRE À JOUR UNIQUEMENT #mesFavCount (source unique de vérité)
  5040.             // L'observer se chargera de synchroniser giftCount automatiquement
  5041.             const mesFavCount = document.getElementById('mesFavCount');
  5042.             if (mesFavCount) {
  5043.                 let currentCount = parseInt(mesFavCount.textContent || '0');
  5044.                 currentCount++;
  5045.                 mesFavCount.textContent = currentCount;
  5046.                 console.log('[AddFavoris] ✅ mesFavCount incrémenté:', currentCount);
  5047.                 
  5048.                 // L'observer MutationObserver va automatiquement :
  5049.                 // - Synchroniser giftCount
  5050.                 // - Mettre à jour le sidebar
  5051.                 // - Mettre à jour product-suggestions
  5052.             }
  5053.             
  5054.             // Update other counters
  5055.             var $total = parseInt($("#totalLike").html()) + 1;
  5056.             $("#totalLike").html($total);
  5057.             $("#totalLikeTitle").html($total);
  5058.             $("#totalLikeMobile").html($total);
  5059.             
  5060.             // Add gift button animation
  5061.             const giftButton = document.querySelector('.gift-button');
  5062.             if (giftButton) {
  5063.                 giftButton.classList.add('active');
  5064.                 setTimeout(() => {
  5065.                     giftButton.classList.remove('active');
  5066.                 }, 600);
  5067.             }
  5068.             var $data = { 'id': $id, 'idSejour': $idSejour };
  5069.             $.ajax({
  5070.                 type: "POST",
  5071.                 url: "{{ path('Ajouter_fav') }}",
  5072.                 data: $data,
  5073.                 success: function () {
  5074.                     $('.IconDelete').each(function () {
  5075.                         $(this).css('pointer-events', '');
  5076.                     });
  5077.                     if ($description === undefined) {
  5078.                         $description = ''; // Set it to an empty string
  5079.                     }
  5080.                     $('.rowMaselection').append(
  5081.                         '<div class="column" id="column-' + $id + '">'+
  5082.                         '<a style="position: relative;" title="Enlever de ma sélection" onclick="supprimerFavoris(' + $id + ',' + $idSejour + ')" class="iconeSuppImg"><i class="bi bi-x" style="font-size:17px;cursor:pointer;color:#d30909;float:right;margin-top:-3%;margin-right:2%"></i></a>'+
  5083.                         '<a class="photo-zoom">'+
  5084.                         '<img data-idAtach="'+$id+'" id="'+$idSejour+'" src="'+$urlimg+'"></a>'+
  5085.                         ($description ? '<h4 id="commint" class="titleHeadPhoto">'+$description+'</h4>' : '')+ // Only add the <h4> if $description is not empty
  5086.                         '</div>'
  5087.                     );
  5088.                     // Directly update nbLikes count in the header
  5089.                     var currentNbLikes = parseInt($('#favoris-link-Accueilpayment .nbrpanier').text());
  5090.                     var newNbLikes = currentNbLikes + 1;
  5091.                     $('#favoris-link-Accueilpayment .nbrpanier').text(newNbLikes);
  5092.                     
  5093.                     // Mettre à jour l'alerte avec le nouveau nombre
  5094.                     const finalCount = getCurrentFavoriteCount();
  5095.                     if (typeof updatePurchaseAlert === 'function') {
  5096.                         updatePurchaseAlert(finalCount);
  5097.                     }
  5098.                 },
  5099.                 error: function (xhr, status, error) {
  5100.                     console.error('Error:', error);
  5101.                 }
  5102.             });
  5103.         }
  5104.         $(document).on('click', '.bi-heart, .bi-heart-fill', function () {
  5105.         const heartIcon       = $(this);
  5106.         const heartContainer  = heartIcon.closest('.heart-icon');
  5107.         // Extract data attributes
  5108.         const attachmentId    = heartContainer.data('id');
  5109.         const sejourId        = heartContainer.data('sejour-id');
  5110.         const path            = heartContainer.data('path');
  5111.         const description     = heartContainer.data('description');
  5112.         const isFavorite      = heartIcon.hasClass('bi-heart-fill');
  5113.         if (isFavorite) {
  5114.             // Remove from favorites
  5115.             supprimerFavoris(attachmentId, sejourId);
  5116.         } else {
  5117.             // Add to favorites
  5118.             AddFavoris(attachmentId, sejourId, path, description);
  5119.         }
  5120.         // Update UI components after the action (sans double comptage)
  5121.         setTimeout(function() {
  5122.             const likeCountLabel = document.getElementById('likeCount');
  5123.             
  5124.             if (likeCountLabel) {
  5125.                 const currentCount = parseInt(likeCountLabel.textContent, 10) || 0;
  5126.                 
  5127.                 // Update UI components seulement
  5128.                 updateCardContent(currentCount);
  5129.                 updateFavoritesSidebar();
  5130.                 $("#close-favorites-btn").click();
  5131.                 
  5132.                 // 🎯 Product suggestions sont mis à jour automatiquement par le MutationObserver
  5133.                 // dans parent-toasts.js qui observe #mesFavCount
  5134.                 // updateProductSuggestionsLive(currentCount); // ← SUPPRIMÉ (doublon)
  5135.             }
  5136.         }, 50);
  5137.     });
  5138.         // Ajoutez les événements sur les icônes de cœur
  5139.         document.querySelectorAll('.IconDelete').forEach((icon) => {
  5140.             icon.addEventListener('click', (event) => {
  5141.                 const isFavorite = icon && icon.classList && icon.classList.contains('bi-heart-fill');
  5142.                 if (isFavorite) {
  5143.                     removeFavorite();
  5144.                     if (icon.classList) {
  5145.                       icon.classList.remove('bi-heart-fill');
  5146.                       icon.classList.add('bi-heart');
  5147.                     }
  5148.                 } else {
  5149.                     addFavorite();
  5150.                     if (icon.classList) {
  5151.                       icon.classList.remove('bi-heart');
  5152.                       icon.classList.add('bi-heart-fill');
  5153.                     }
  5154.                 }
  5155.             });
  5156.         });
  5157.         // Vérifie l'état initial
  5158.         checkFavoritesAlert();
  5159.         
  5160.         
  5161.         // ⚡ OPTIMISÉ: Réduction du délai d'initialisation
  5162.       
  5163.   </script>
  5164.   <!-- Initialisation -->
  5165.   <script>
  5166.     // ⚡ OPTIMISATION: Différer l'initialisation d'AOS pour ne pas bloquer le chargement
  5167.     setTimeout(function() {
  5168.       AOS.init({
  5169.         duration: 800,
  5170.         easing: "ease-in-out"
  5171.       });
  5172.     }, 100);
  5173.     // 🎯 DAY FILTER DROPDOWN LOGIC (Senior UX)
  5174.     document.addEventListener('DOMContentLoaded', function() {
  5175.       initializeDayFilters();
  5176.       
  5177.       // Initialiser le compteur audio avec la valeur correcte
  5178.       setTimeout(function() {
  5179.         updateAudioButtonState('days');
  5180.       }, 500);
  5181.     });
  5182.     function initializeDayFilters() {
  5183.       const dropdowns = document.querySelectorAll('.day-filter-dropdown');
  5184.       
  5185.       dropdowns.forEach(dropdown => {
  5186.         const toggle = dropdown.querySelector('.filter-toggle');
  5187.         const menu = dropdown.querySelector('.filter-dropdown-menu');
  5188.         const options = dropdown.querySelectorAll('.filter-option');
  5189.         const dayIndex = dropdown.dataset.dayIndex;
  5190.         const dayContainer = document.getElementById(`demP${dayIndex}`);
  5191.         
  5192.         if (!toggle || !menu || !dayContainer) return;
  5193.         
  5194.         // Calculer et mettre à jour les compteurs initiaux
  5195.         updateDayFilterCounts(dropdown, dayContainer);
  5196.         
  5197.         // Toggle dropdown
  5198.         toggle.addEventListener('click', (e) => {
  5199.           e.stopPropagation();
  5200.           const isOpen = dropdown.classList.contains('open');
  5201.           
  5202.           // Fermer tous les autres dropdowns
  5203.           document.querySelectorAll('.day-filter-dropdown.open').forEach(d => {
  5204.             if (d !== dropdown) d.classList.remove('open');
  5205.           });
  5206.           
  5207.           // Toggle le dropdown actuel
  5208.           dropdown.classList.toggle('open', !isOpen);
  5209.         });
  5210.         
  5211.         // Gérer les clics sur les options
  5212.         options.forEach(option => {
  5213.           option.addEventListener('click', (e) => {
  5214.             e.stopPropagation();
  5215.             const filter = option.dataset.filter;
  5216.             
  5217.             // Mettre à jour l'état actif
  5218.             options.forEach(opt => opt.classList.remove('active'));
  5219.             option.classList.add('active');
  5220.             
  5221.             // Appliquer le filtre
  5222.             applyDayFilter(dayContainer, filter);
  5223.             
  5224.             // Fermer le dropdown
  5225.             dropdown.classList.remove('open');
  5226.           });
  5227.         });
  5228.       });
  5229.       
  5230.       // Fermer les dropdowns en cliquant ailleurs
  5231.       document.addEventListener('click', () => {
  5232.         document.querySelectorAll('.day-filter-dropdown.open').forEach(dropdown => {
  5233.           dropdown.classList.remove('open');
  5234.         });
  5235.       });
  5236.     }
  5237.     function updateDayFilterCounts(dropdown, dayContainer) {
  5238.       const photoItems = dayContainer.querySelectorAll('[data-type="photo"]');
  5239.       const videoItems = dayContainer.querySelectorAll('[data-type="video"]');
  5240.       // Compter les messages audio individuels plutôt que les conteneurs
  5241.       const audioMessageItems = dayContainer.querySelectorAll('.audio-message-item[data-type="audio"]');
  5242.       const audioContainers = dayContainer.querySelectorAll('.audio-messages-container[data-type="audio"]');
  5243.       const audioRestricted = dayContainer.querySelectorAll('.audio-messages-restricted[data-type="audio"]');
  5244.       
  5245.       const allItems = dayContainer.querySelectorAll('[data-type]');
  5246.       
  5247.       const countAll = dropdown.querySelector('[data-count-all]');
  5248.       const countPhoto = dropdown.querySelector('[data-count-photo]');
  5249.       const countVideo = dropdown.querySelector('[data-count-video]');
  5250.       const countAudio = dropdown.querySelector('[data-count-audio]');
  5251.       
  5252.       // Compter les messages audio individuels + conteneurs + sections restreintes
  5253.       const totalAudio = audioMessageItems.length + audioContainers.length + audioRestricted.length;
  5254.       
  5255.       if (countAll) countAll.textContent = allItems.length;
  5256.       if (countPhoto) countPhoto.textContent = photoItems.length;
  5257.       if (countVideo) countVideo.textContent = videoItems.length;
  5258.       if (countAudio) countAudio.textContent = totalAudio;
  5259.       
  5260.       // Masquer les options sans contenu
  5261.       const photoOption = dropdown.querySelector('[data-filter="photo"]');
  5262.       const videoOption = dropdown.querySelector('[data-filter="video"]');
  5263.       const audioOption = dropdown.querySelector('[data-filter="audio"]');
  5264.       
  5265.       if (photoOption) photoOption.style.display = photoItems.length > 0 ? 'flex' : 'none';
  5266.       if (videoOption) videoOption.style.display = videoItems.length > 0 ? 'flex' : 'none';
  5267.       if (audioOption) audioOption.style.display = totalAudio > 0 ? 'flex' : 'none';
  5268.     }
  5269.     function applyDayFilter(dayContainer, filter) {
  5270.       const items = dayContainer.querySelectorAll('[data-type]');
  5271.       
  5272.       items.forEach(item => {
  5273.         if (filter === 'all' || item.dataset.type === filter) {
  5274.           item.style.display = '';
  5275.           item.classList.remove('filtered-out');
  5276.         } else {
  5277.           item.style.display = 'none';
  5278.           item.classList.add('filtered-out');
  5279.         }
  5280.       });
  5281.       
  5282.       // Animation fluide pour les éléments visibles
  5283.       requestAnimationFrame(() => {
  5284.         const visibleItems = dayContainer.querySelectorAll('[data-type]:not(.filtered-out)');
  5285.         visibleItems.forEach((item, index) => {
  5286.           item.style.animation = `fadeInUp 0.3s ease forwards ${index * 0.05}s`;
  5287.         });
  5288.       });
  5289.     }
  5290.     // Animation CSS pour fadeInUp
  5291.     if (!document.querySelector('#dayFilterAnimations')) {
  5292.       const style = document.createElement('style');
  5293.       style.id = 'dayFilterAnimations';
  5294.       style.textContent = `
  5295.         @keyframes fadeInUp {
  5296.           from {
  5297.             opacity: 0;
  5298.             transform: translateY(20px);
  5299.           }
  5300.           to {
  5301.             opacity: 1;
  5302.             transform: translateY(0);
  5303.           }
  5304.         }
  5305.       `;
  5306.       document.head.appendChild(style);
  5307.     }
  5308.     document.addEventListener("DOMContentLoaded", function () {
  5309.       const dateCards = document.querySelectorAll(".date-card");
  5310.       const sections = document.querySelectorAll(".collapse");
  5311.       dateCards.forEach((card) => {
  5312.         card.addEventListener("click", function () {
  5313.           // Supprimer les classes actives des autres cartes et sections
  5314.           dateCards.forEach((c) => c.classList.remove("active"));
  5315.           sections.forEach((s) => s.classList.remove("show"));
  5316.           // Ajouter la classe active à la carte cliquée
  5317.           this.classList.add("active");
  5318.           // Récupérer la cible et afficher la bonne section
  5319.           const targetId = this.getAttribute("data-bs-target");
  5320.           const targetSection = document.querySelector(targetId);
  5321.           if (targetSection) {
  5322.             targetSection.classList.add("show");
  5323.           }
  5324.         });
  5325.       });
  5326.     });
  5327.     document.addEventListener("DOMContentLoaded", function () {
  5328.       // Initialisation du carrousel Splide
  5329.       var splide = new Splide("#imageSlider", {
  5330.         type: "loop",
  5331.         perPage: 1,
  5332.         autoplay: true,
  5333.         interval: 6000,
  5334.         pauseOnHover: false,
  5335.         pauseOnFocus: false,
  5336.         pagination: false,
  5337.         arrows: false,
  5338.       });
  5339.       splide.mount();
  5340.       // ⚠️ Scroll automatique désactivé pour meilleure UX
  5341.       // Les utilisateurs peuvent défiler manuellement quand ils le souhaitent
  5342.     });
  5343.   </script>
  5344.   <script>
  5345.    
  5346.     const giftButton = document.querySelector('.gift-button');
  5347.     if (giftButton) {
  5348.         giftButton.addEventListener('click', () => {
  5349.         // Ajouter la classe 'active' pour déclencher l'éclat
  5350.             giftButton.classList.add('active');
  5351.         // Retirer l'animation après qu'elle soit jouée
  5352.         setTimeout(() => {
  5353.                 giftButton.classList.remove('active');
  5354.         }, 600); // La durée doit correspondre à celle de l'animation
  5355.             
  5356.             // Optionnel : rediriger vers la page de commande
  5357.             // window.location.href = '/commande';
  5358.     });
  5359.     }
  5360.     
  5361.     //const HeartAddButton = document.querySelector('.IconDelete');
  5362.     $(".IconDelete").on('click', () => {
  5363.         // Ajouter la classe 'active' pour déclencher l'éclat
  5364.         favoriteButton.classList.add('active');
  5365.         // Retirer l'animation après qu'elle soit jouée
  5366.         setTimeout(() => {
  5367.             favoriteButton.classList.remove('active');
  5368.         }, 600); // La durée doit correspondre à celle de l'animation
  5369.     });
  5370.     $(document).ready(function() {
  5371.         // Attach click event to collapse triggers
  5372.         const lastCard = $('.date-card.modern-card.active');
  5373.         const lastTargetId = lastCard.attr('data-bs-target');
  5374.         if (lastTargetId) {
  5375.             $(lastTargetId).collapse('show'); // Expand the last collapse section
  5376.             LoadImagesCloud($(lastTargetId)); // Load images for the last day
  5377.         }
  5378.         $('[data-bs-toggle="collapse"]').on('click', function() {
  5379.             var targetId = $(this).attr('data-bs-target'); // Get the target ID
  5380.             $('.date-card.modern-card').removeClass('active'); // Remove 'active' class from all cards
  5381.             $(this).addClass('active'); // Add 'active' class to the clicked card
  5382.             LoadImagesCloud($(targetId)); // Ensure this function works as expected
  5383.                // Hide all other collapses except the one clicked
  5384.                $('[data-bs-target]').each(function() {
  5385.                 var currentTargetId = $(this).attr('data-bs-target');
  5386.                 // If the current collapse is not the one clicked, hide it
  5387.                 if (currentTargetId !== targetId) {
  5388.                     $(currentTargetId).collapse('hide');
  5389.                     //$('[data-bs-toggle="collapse"]').removeClass('active'); // Remove active class from all cards
  5390.                     //Modifier leurs style en non active aussi
  5391.                 }
  5392.             });
  5393.         });
  5394.     });
  5395.   
  5396.             $(document).ready(function () {
  5397.               
  5398.                 {% if app.session.get("paymentmoniteco") %}
  5399.                 {% if app.session.get("paymentmoniteco") == "succses" %}
  5400.                 Swal.fire({
  5401.                     icon: 'success',
  5402.                     title: ' succès ',
  5403.                     text: 'votre commande est validée'
  5404.                 });
  5405.                 {% endif %}
  5406.                 {% endif %}
  5407.                 if ($total1 > 0) {
  5408.                     $('.iconeFleche').first().click();
  5409.                     //  $([document.documentElement, document.body]).animate({
  5410.                     //  scrollTop: $('.iconeFleche').last().offset().top
  5411.                     //  }, );
  5412.                 }
  5413.                 else {
  5414.                     $(window).scrollTop(0);
  5415.                 }
  5416.                 var slider = $('.responsive').slick({
  5417.                     infinite: true,
  5418.                     slidesToShow: 1,
  5419.                     slidesToScroll: 1,
  5420.                     autoplay: true,
  5421.                     autoplaySpeed: 4000,
  5422.                     pauseOnFocus: false,
  5423.                     pauseOnHover: false,
  5424.                     draggable: false,
  5425.                     fade: true
  5426.                 });
  5427.                 $('.responsive').css('display', 'block');
  5428.                 $('.namePRD').css('display', 'block');
  5429.                 var currSlide = 0;
  5430.                 var nextSlide = 0;
  5431.                 slider.on('afterChange', function (event, slick, currentSlide) {
  5432.                     console.log(typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined");
  5433.                     if (typeof ($('.slick-active .slick-current').find('.imgproduit2')) != "undefined") {
  5434.                         setTimeout(function () {
  5435.                             $('.slick-active .imgproduit1').removeClass('animated fadeIn');
  5436.                             $('.slick-active .imgproduit1').addClass('animated fadeOut');
  5437.                             $('.slick-active .imgproduit1').css('display', 'none');
  5438.                             $('.slick-active .imgproduit2').css('display', 'block');
  5439.                             $('.slick-active .imgproduit2').removeClass('animated fadeOut');
  5440.                             $('.slick-active .imgproduit2').addClass('animated fadeIn');
  5441.                         }, 2000);
  5442.                     }
  5443.                 });
  5444.                 slider.on('beforeChange', function (event, slick, currentSlide, nextSlide) {
  5445.                     currSlide = currentSlide;
  5446.                     $('.imgproduit2').each(function () {
  5447.                         $(this).removeClass('animated fadeIn');
  5448.                         $(this).addClass('animated fadeOut');
  5449.                         $(this).css('display', 'none');
  5450.                     });
  5451.                     $('.imgproduit1').each(function () {
  5452.                         $(this).css('display', 'block');
  5453.                         $(this).removeClass('animated fadeOut');
  5454.                         $(this).addClass('animated fadeIn');
  5455.                     });
  5456.                 });
  5457.                 $('.columnPub').each(function () {
  5458.                     $(this).slick({
  5459.                         infinite: true,
  5460.                         speed: 50,
  5461.                         fade: true,
  5462.                         slidesToShow: 1,
  5463.                         slidesToScroll: 1,
  5464.                         autoplay: true,
  5465.                         pauseOnFocus: false,
  5466.                         pauseOnHover: false,
  5467.                         draggable: false
  5468.                     });
  5469.                     $(this).css('display', 'block');
  5470.                 });
  5471.                 $("#offrePack").click();
  5472.                 {%if app.user.showpubprod != 'false' %}
  5473.                 $('#btnPubProd').click();
  5474.                 $('.modal-backdrop').css('background-color', 'rgba(0, 0, 0, 0.2)');
  5475.                 {% endif %}
  5476.             });
  5477.             $("#closeImage").click(function () {
  5478.                 $('#myModalImage').css('display', "none");
  5479.             });
  5480.             $.ajax({
  5481.                 type: "POST",
  5482.                 url: "{{ path("delateSession_parent") }}",
  5483.                 success: function () { }
  5484.             });
  5485.             function afficheDiv(elem) {
  5486.                 $('.nav-link').each(function () {
  5487.                     $(this).removeClass('active');
  5488.                 });
  5489.                 elem.addClass('active');
  5490.                 if (elem.attr('id') === "esphoto" || elem.attr('id') === "esphotoMobile") {
  5491.                     $("#espacphoto").show();
  5492.                     $("#espacemessage").hide();
  5493.                     $("#espaceMa_selection").hide();
  5494.                     pageMenu = 'MonSejour'
  5495.                     $(this).addClass('active');
  5496.                    $('#imageActifphoto').css('display', 'block');
  5497.                      $('#imagenoActifphoto').css('display', 'none');
  5498.                    $('#VocalActivee').css('display', 'none');
  5499.                      $('#noActifVocal').css('display', 'block');
  5500.                 }
  5501.                 if (elem.attr('id') === "esmessage" || elem.attr('id') === "esmessageMobile") {
  5502.                     $("#espacphoto").hide();
  5503.                     $("#espaceMa_selection").hide();
  5504.                     $("#espacemessage").show();
  5505.                     pageMenu = 'BoiteVocale'
  5506.                     $("#espaceMa_selection").hide();
  5507.                     $(this).addClass('active');
  5508.                   $('#imageActifphoto').css('display', 'none');
  5509.                      $('#imagenoActifphoto').css('display', 'block');
  5510.                    $('#VocalActivee').css('display', 'block');
  5511.                      $('#noActifVocal').css('display', 'none');
  5512.                 }
  5513.                 if (elem.attr('id') === "esselection" || elem.attr('id') === "esselectionMobile") {
  5514.                     $("#espacphoto").hide();
  5515.                     $("#espacemessage").hide();
  5516.                     $("#espaceMa_selection").show();
  5517.                     $(homeNavmob).removeClass('bi bi-house-door-fill');
  5518.                     $(homeNavmob).addClass('bi bi-house-door');
  5519.                     $(micromob).removeClass('bi bi-mic-fill');
  5520.                     $(micromob).addClass('bi bi-mic');
  5521.                     $(selecNavmob).removeClass('bi bi-heart');
  5522.                     $(selecNavmob).addClass('bi bi-heart-fill');
  5523.                 }
  5524.             }
  5525.             function LoadImagesCloud($element) {
  5526.                 $element.find('.photo-zoom img').each(function ($this) {
  5527.                     if ($(this).attr('data-src') != $(this).attr('src')) {
  5528.                         $(this).attr('src', $(this).attr('data-src'));
  5529.                     }
  5530.                 });
  5531.             }
  5532.   </script>
  5533.   <script>
  5534.     // ⚡ OPTIMISATION: Regroupement des initialisations jQuery
  5535.     $(document).ready(function () {
  5536.       // Modal PubProd
  5537.       $("#PubProd").on("hidden.bs.modal", function () {
  5538.         $(document).trigger("modalClosed");
  5539.       });
  5540.       
  5541.       // NoShow checkbox
  5542.       $("#noShow").on("change", function () {
  5543.         if ($(this).is(":checked")) {
  5544.           $.ajax({
  5545.             url: "/Parent/showpub",
  5546.             type: "POST",
  5547.             dataType: "json",
  5548.             success: function (response) {
  5549.               if (response.status === "success") {
  5550.                 console.log("User showpubprod updated successfully.");
  5551.               } else {
  5552.                 console.log("Error:", response.message);
  5553.               }
  5554.             },
  5555.             error: function (xhr, status, error) {
  5556.               console.log("AJAX Error:", error);
  5557.             },
  5558.           });
  5559.         }
  5560.       });
  5561.     });
  5562.   </script>
  5563. </div>
  5564. <!-- Script pour la sidebar des favoris -->
  5565. <script>
  5566.   // ⚡ OPTIMISATION: Utiliser requestIdleCallback pour ne pas bloquer le thread principal
  5567.  
  5568.   
  5569.     
  5570.     // Charger les favoris
  5571.     function loadFavorites() {
  5572.       $.ajax({
  5573.         url: "/Parent/mes-favoris",
  5574.         type: "GET",
  5575.         dataType: "json",
  5576.         beforeSend: function() {
  5577.           $("#favorites-grid").html("<div style='text-align:center'>Chargement...</div>");
  5578.         },
  5579.         success: function(data) {
  5580.           $("#favorites-grid").empty();
  5581.           
  5582.           if (data.data && data.data.length > 0) {
  5583.             $("#favorites-empty-state").hide();
  5584.             
  5585.             $.each(data.data, function(i, fav) {
  5586.               var item = $("<div class='favorite-item'></div>");
  5587.               var img = $("<img>").attr("src", fav.path).attr("alt", fav.descreption || "Photo favorite");
  5588.               var overlay = $("<div class='favorite-overlay'></div>");
  5589.         
  5590.               
  5591.               btn.click(function(e) {
  5592.                 e.preventDefault();
  5593.                 e.stopPropagation();
  5594.                 removeFavorite(fav.id);
  5595.               });
  5596.               
  5597.               overlay.append(btn);
  5598.               item.append(img).append(overlay);
  5599.               $("#favorites-grid").append(item);
  5600.             });
  5601.             
  5602.             $("#favorites-counter").text(data.data.length);
  5603.             var percentage = (data.data.length / 10) * 100;
  5604.             $("#favorites-progress").css("width", percentage + "%");
  5605.             
  5606.           } else {
  5607.             $("#favorites-empty-state").show();
  5608.             $("#favorites-counter").text("0");
  5609.             $("#favorites-progress").css("width", "0%");
  5610.           }
  5611.         },
  5612.         error: function() {
  5613.           $("#favorites-grid").html("<div style='color:red;text-align:center'>Erreur de chargement</div>");
  5614.         }
  5615.       });
  5616.     }
  5617.     
  5618.     // Supprimer un favori
  5619.     function removeFavorite(id) {
  5620.       $.ajax({
  5621.         url: "/Parent/remove-favorite/" + id,
  5622.         type: "POST",
  5623.         success: function() {
  5624.           loadFavorites();
  5625.           
  5626.           // Mettre à jour tous les compteurs de favoris
  5627.           updateAllFavoriteCounters();
  5628.         },
  5629.         error: function() {
  5630.           alert("Erreur lors de la suppression du favori");
  5631.         }
  5632.       });
  5633.     }
  5634.   });
  5635. </script>
  5636. <!-- === E-COMMERCE SIDEBAR JAVASCRIPT === -->
  5637. <script>
  5638. // Configuration UX_VARIANT pour A/B testing
  5639. let UX_VARIANT = 'EMOTION'; // ou 'URGENCY'
  5640. // Variables globales e-commerce
  5641. let sejourEndDate = null; // À définir avec la vraie date de fin du séjour
  5642. // Fonction utilitaire robuste pour gérer les compteurs de favoris
  5643. window.getFavoriteCount = function getFavoriteCount() {
  5644.   const likeCountInput = document.getElementById('likeCount');
  5645.   if (likeCountInput) {
  5646.     // Priorité à la valeur de l'input
  5647.     const value = likeCountInput.value || likeCountInput.textContent || 0;
  5648.     return parseInt(value, 10) || 0;
  5649.   }
  5650.   
  5651.   // Fallback sur giftCount
  5652.   const giftCount = document.getElementById('giftCount');
  5653.   if (giftCount && giftCount.textContent) {
  5654.     return parseInt(giftCount.textContent.trim(), 10) || 0;
  5655.   }
  5656.   
  5657.   return 0;
  5658. };
  5659. window.setFavoriteCount = function setFavoriteCount(count) {
  5660.   const likeCountInput = document.getElementById('likeCount');
  5661.   if (likeCountInput) {
  5662.     // Mettre à jour les deux propriétés pour être sûr
  5663.     likeCountInput.value = count;
  5664.     likeCountInput.textContent = count;
  5665.   }
  5666.   
  5667.   // Mettre à jour les autres compteurs
  5668.   const giftCount = document.getElementById('giftCount');
  5669.   if (giftCount) {
  5670.     giftCount.textContent = count;
  5671.   }
  5672.   
  5673.   const mesFavCount = document.getElementById('mesFavCount');
  5674.   if (mesFavCount) {
  5675.     mesFavCount.textContent = count;
  5676.   }
  5677.   
  5678.   // Mettre à jour l'input hidden
  5679.   const nbFavCurrentInput = document.getElementById('nbFavCurrent');
  5680.   if (nbFavCurrentInput) {
  5681.     nbFavCurrentInput.value = count;
  5682.   }
  5683. };
  5684. // Fonction pour définir la date de fin du séjour
  5685. window.setSejourEndDate = function setSejourEndDate(dateString) {
  5686.   sejourEndDate = dateString;
  5687.   console.log('🎯 Sejour end date set to:', sejourEndDate);
  5688.   
  5689.   // Mettre à jour le countdown si le sidebar est ouvert
  5690.   updateCountdownTimer();
  5691. };
  5692. // Fonctions d'ouverture/fermeture du sidebar
  5693. window.openEcommerceSidebar = function openEcommerceSidebar() {
  5694.   const sidebar = document.getElementById('ecommerce-sidebar');
  5695.   if (!sidebar) {
  5696.     console.error('❌ E-commerce sidebar element not found!');
  5697.     return false;
  5698.   }
  5699.   
  5700.   console.log('🎁 [OPEN SIDEBAR] Ouverture du sidebar e-commerce...');
  5701.   const favCount = window.getFavoriteCount ? window.getFavoriteCount() : 0;
  5702.   console.log('🎁 [OPEN SIDEBAR] Favoris actuels:', favCount);
  5703.   
  5704.   // D'abord ouvrir le sidebar pour que les éléments soient dans le DOM
  5705.   sidebar.classList.add('active');
  5706.   console.log('🎁 [OPEN SIDEBAR] Sidebar classe "active" ajoutée');
  5707.   
  5708.   // Puis mettre à jour le contenu (petit délai pour que le DOM soit prêt)
  5709.   setTimeout(() => {
  5710.     console.log('🎁 [OPEN SIDEBAR] Mise à jour du contenu...');
  5711.     
  5712.     // Appeler la fonction de parent-toasts.js
  5713.     if (typeof window.parentConversion !== 'undefined' && typeof window.parentConversion.updateSidebar === 'function') {
  5714.       console.log('✅ [OPEN SIDEBAR] Appel de parentConversion.updateSidebar()');
  5715.       window.parentConversion.updateSidebar();
  5716.     } else {
  5717.       console.warn('⚠️ [OPEN SIDEBAR] parent-toasts.js non disponible, fallback sur fonction locale');
  5718.       // Fallback sur la fonction locale
  5719.       if (typeof window.updateEcommerceSidebarContent === 'function') {
  5720.         window.updateEcommerceSidebarContent(favCount);
  5721.       }
  5722.     }
  5723.   }, 100); // 100ms de délai pour que le sidebar soit visible
  5724.   
  5725.   return true;
  5726. };
  5727. window.closeEcommerceSidebar = function closeEcommerceSidebar() {
  5728.   const sidebar = document.getElementById('ecommerce-sidebar');
  5729.   if (sidebar) {
  5730.     sidebar.classList.remove('active');
  5731.     console.log('🎯 E-commerce sidebar closed');
  5732.   } else {
  5733.     console.error('❌ E-commerce sidebar element not found!');
  5734.   }
  5735. };
  5736. // 🚫 FONCTION LEGACY SUPPRIMÉE - Utiliser parent-toasts.js à la place
  5737. // Cette fonction était en conflit avec updateEcommerceSidebar() de parent-toasts.js
  5738. // qui gère TOUT : titre, sous-titre, compteurs, visibilité des produits, CTA, etc.
  5739. window.updateEcommerceSidebarContent = function updateEcommerceSidebarContent(favoriteCount) {
  5740.   console.warn('[LEGACY] updateEcommerceSidebarContent appelé - Redirection vers parent-toasts.js');
  5741.   
  5742.   // Rediriger vers la vraie fonction
  5743.   if (typeof window.parentConversion !== 'undefined' && typeof window.parentConversion.updateSidebar === 'function') {
  5744.     window.parentConversion.updateSidebar();
  5745.   } else {
  5746.     console.error('[LEGACY] parent-toasts.js non disponible !');
  5747.   }
  5748. }
  5749. // 🚫 FONCTION SUPPRIMÉE - Géré par parent-toasts.js
  5750. // Tout est maintenant géré dans updateEcommerceSidebar() de parent-toasts.js
  5751. // Fonction pour gérer le countdown
  5752. window.updateCountdownTimer = function updateCountdownTimer() {
  5753.   const countdownTimer = document.getElementById('countdown-timer');
  5754.   const daysLeftSpan = document.getElementById('days-left');
  5755.   
  5756.   if (!countdownTimer || !daysLeftSpan) return;
  5757.   
  5758.   // Si pas de date définie, utiliser une date par défaut (6 semaines à partir d'aujourd'hui)
  5759.   let expirationDate;
  5760.   if (sejourEndDate) {
  5761.     const endDate = new Date(sejourEndDate);
  5762.     expirationDate = new Date(endDate.getTime() + (6 * 7 * 24 * 60 * 60 * 1000)); // +6 semaines
  5763.   } else {
  5764.     // Date par défaut : 6 semaines à partir d'aujourd'hui
  5765.     expirationDate = new Date();
  5766.     expirationDate.setDate(expirationDate.getDate() + (6 * 7));
  5767.   }
  5768.   
  5769.   const now = new Date();
  5770.   const daysLeft = Math.ceil((expirationDate - now) / (24 * 60 * 60 * 1000));
  5771.   
  5772.   if (daysLeft <= 10 && daysLeft > 0) {
  5773.     countdownTimer.style.display = 'block';
  5774.     daysLeftSpan.textContent = daysLeft;
  5775.     
  5776.     // Mettre à jour les titres avec le countdown si variante URGENCY
  5777.     if (UX_VARIANT === 'URGENCY') {
  5778.       const title = document.getElementById('ecommerce-title');
  5779.       if (title) {
  5780.         const favoriteCount = parseInt(document.getElementById('giftCount')?.textContent || '0');
  5781.         if (favoriteCount > 0) {
  5782.           if (favoriteCount < 5) {
  5783.             title.innerHTML = `⏳ ${favoriteCount} souvenirs - Plus que ${daysLeft} jours !`;
  5784.           } else if (favoriteCount < 12) {
  5785.             title.innerHTML = `⏳ ${favoriteCount} magnifiques souvenirs - Plus que ${daysLeft} jours !`;
  5786.           } else {
  5787.             title.innerHTML = `⏳ Superbe collection de ${favoriteCount} photos - Plus que ${daysLeft} jours !`;
  5788.           }
  5789.         }
  5790.       }
  5791.     }
  5792.   } else {
  5793.     countdownTimer.style.display = 'none';
  5794.   }
  5795. }
  5796. // Fonction pour commander un produit
  5797. window.orderProduct = function orderProduct(productType) {
  5798.   let favoriteCount = 0;
  5799.   
  5800.   // Méthode 1: Fonction getCurrentFavoriteCount
  5801.   try {
  5802.     if (typeof getCurrentFavoriteCount === 'function') {
  5803.       favoriteCount = getCurrentFavoriteCount();
  5804.     }
  5805.   } catch (e) {
  5806.     console.log('⚠️ getCurrentFavoriteCount non disponible:', e);
  5807.   }
  5808.   
  5809.   // Méthode 2: Element giftCount
  5810.   if (favoriteCount === 0) {
  5811.     const giftCount = document.getElementById('giftCount');
  5812.     if (giftCount && giftCount.textContent) {
  5813.       favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  5814.     }
  5815.   }
  5816.   
  5817.   // Méthode 3: Récupérer depuis les éléments qui affichent le nombre de favoris
  5818.   if (favoriteCount === 0) {
  5819.     const albumFavCount = document.getElementById('album-fav-count');
  5820.     const digitalFavCount = document.getElementById('digital-fav-count');
  5821.     
  5822.     if (albumFavCount && albumFavCount.textContent) {
  5823.       favoriteCount = parseInt(albumFavCount.textContent.trim()) || 0;
  5824.     } else if (digitalFavCount && digitalFavCount.textContent) {
  5825.       favoriteCount = parseInt(digitalFavCount.textContent.trim()) || 0;
  5826.     }
  5827.   }
  5828.   
  5829.   // Méthode 4: Variable globale likes
  5830.   if (favoriteCount === 0 && typeof likes !== 'undefined' && likes) {
  5831.     favoriteCount = likes.length || 0;
  5832.   }
  5833.   
  5834.   // Méthode 5: Compter les éléments liked dans le DOM
  5835.   if (favoriteCount === 0) {
  5836.     const likedElements = document.querySelectorAll('.liked, .photo.liked, [data-liked="true"]');
  5837.     favoriteCount = likedElements.length;
  5838.   }
  5839.   
  5840.   // Debug pour voir la valeur récupérée
  5841.   console.log('🔍 Nombre de favoris détecté:', favoriteCount);
  5842.   
  5843.   if (favoriteCount === 0) {
  5844.     // Afficher une notification plus élégante
  5845.     const notification = document.createElement('div');
  5846.     notification.className = 'favorite-notification';
  5847.     notification.innerHTML = `
  5848.       <div class="notification-content">
  5849.         <i class="bi bi-heart" style="color: #e91e63; font-size: 1.5rem; margin-right: 10px;"></i>
  5850.         <span>Veuillez d'abord sélectionner des photos favorites !</span>
  5851.         <button onclick="this.parentElement.parentElement.remove()" style="background: none; border: none; color: #666; font-size: 1.2rem; margin-left: 10px;">&times;</button>
  5852.       </div>
  5853.     `;
  5854.     
  5855.     // Styles pour la notification
  5856.     notification.style.cssText = `
  5857.       position: fixed;
  5858.       top: 20px;
  5859.       right: 20px;
  5860.       background: #fff;
  5861.       border: 2px solid #e91e63;
  5862.       border-radius: 8px;
  5863.       padding: 15px;
  5864.       box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  5865.       z-index: 10000;
  5866.       animation: slideInRight 0.3s ease;
  5867.     `;
  5868.     
  5869.     // Ajouter l'animation CSS si elle n'existe pas
  5870.     if (!document.getElementById('notification-styles')) {
  5871.       const style = document.createElement('style');
  5872.       style.id = 'notification-styles';
  5873.       style.textContent = `
  5874.         @keyframes slideInRight {
  5875.           from { transform: translateX(100%); opacity: 0; }
  5876.           to { transform: translateX(0); opacity: 1; }
  5877.         }
  5878.         .notification-content {
  5879.           display: flex;
  5880.           align-items: center;
  5881.         }
  5882.       `;
  5883.       document.head.appendChild(style);
  5884.     }
  5885.     
  5886.     document.body.appendChild(notification);
  5887.     
  5888.     // Supprimer automatiquement après 5 secondes
  5889.     setTimeout(() => {
  5890.       if (notification.parentElement) {
  5891.         notification.remove();
  5892.       }
  5893.     }, 5000);
  5894.     
  5895.     return;
  5896.   }
  5897.   
  5898.   // Construire l'URL avec les favoris pré-sélectionnés
  5899.   let orderUrl = '';
  5900.   
  5901.   switch (productType) {
  5902.     case 'album':
  5903.       orderUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  5904.       break;
  5905.     case 'digital':
  5906.       orderUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  5907.       break;
  5908.     case 'prints':
  5909.       orderUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  5910.       break;
  5911.     default:
  5912.       console.error('Type de produit non reconnu:', productType);
  5913.       alert('Erreur: Type de produit non reconnu');
  5914.       return;
  5915.   }
  5916.   
  5917.   // Analytics/tracking
  5918.   console.log('Product ordered', { productType, favoriteCount, url: orderUrl });
  5919.   
  5920.   // Redirection vers la commande
  5921.   if (orderUrl) {
  5922.     // Vérifier que l'URL est valide
  5923.     try {
  5924.       new URL(orderUrl, window.location.origin);
  5925.     window.location.href = orderUrl;
  5926.     } catch (error) {
  5927.       console.error('URL invalide générée:', orderUrl, error);
  5928.       alert('Erreur: Impossible de générer le lien de commande');
  5929.     }
  5930.   } else {
  5931.     console.error('Aucune URL générée pour le produit:', productType);
  5932.     alert('Erreur: Impossible de générer le lien de commande');
  5933.   }
  5934. }
  5935. // Fonction pour tester tous les liens de produits
  5936. window.testProductLinks = function() {
  5937.   const products = ['album', 'digital', 'prints'];
  5938.   const favoriteCount = window.getFavoriteCount() || 0;
  5939.   
  5940.   console.log('🧪 Test des liens de produits avec', favoriteCount, 'favoris');
  5941.   
  5942.   products.forEach(productType => {
  5943.     try {
  5944.       let testUrl = '';
  5945.       switch (productType) {
  5946.         case 'album':
  5947.           testUrl = `{{ path('EditionAlbum') }}?favorites=${favoriteCount}`;
  5948.           break;
  5949.         case 'digital':
  5950.           testUrl = `{{ path('PackPhotosNumerique_Favoris', {'nbr': 15}) }}?favorites=${favoriteCount}`;
  5951.           break;
  5952.         case 'prints':
  5953.           testUrl = `{{ path('AjoutPochettePhotos_Favoris', {'nbr': 12}) }}?favorites=${favoriteCount}`;
  5954.           break;
  5955.       }
  5956.       
  5957.       // Vérifier que l'URL est valide
  5958.       const url = new URL(testUrl, window.location.origin);
  5959.       console.log(`✅ ${productType}: ${url.href}`);
  5960.     } catch (error) {
  5961.       console.error(`❌ ${productType}: Erreur URL`, error);
  5962.     }
  5963.   });
  5964. };
  5965. // Fonction pour valider les liens au chargement de la page
  5966. window.validateProductLinks = function() {
  5967.   // Vérifier que tous les boutons de produits existent
  5968.   const albumBtn = document.querySelector('button[onclick*="orderProduct(\'album\')"]');
  5969.   const digitalBtn = document.querySelector('button[onclick*="orderProduct(\'digital\')"]');
  5970.   const printsBtn = document.querySelector('button[onclick*="orderProduct(\'prints\')"]');
  5971.   
  5972.   if (!albumBtn) console.warn('⚠️ Bouton album non trouvé');
  5973.   if (!digitalBtn) console.warn('⚠️ Bouton digital non trouvé');
  5974.   if (!printsBtn) console.warn('⚠️ Bouton prints non trouvé');
  5975.   
  5976.   // Tester les liens
  5977.   window.testProductLinks();
  5978. };
  5979. // Fonction centrale de mise à jour de TOUS les compteurs de favoris
  5980. window.updateAllFavoriteCounters = function() {
  5981.   // Récupérer le compte depuis #mesFavCount (source unique de vérité)
  5982.   const mesFavCount = document.getElementById('mesFavCount');
  5983.   let favoriteCount = 0;
  5984.   
  5985.   if (mesFavCount) {
  5986.     favoriteCount = parseInt(mesFavCount.textContent || '0');
  5987.   }
  5988.   
  5989.   console.log('[updateAllFavoriteCounters] 💗 Synchronisation:', favoriteCount, 'favoris');
  5990.   
  5991.   // Synchroniser giftCount (bouton cadeau) avec animation
  5992.   const giftCount = document.getElementById('giftCount');
  5993.   if (giftCount) {
  5994.     giftCount.textContent = favoriteCount;
  5995.     giftCount.classList.remove('gift-count-bounce');
  5996.     setTimeout(() => giftCount.classList.add('gift-count-bounce'), 10);
  5997.   }
  5998.   
  5999.   // Mettre à jour les compteurs dans le sidebar même s'il n'est pas ouvert
  6000.   const albumCount = document.getElementById('album-count');
  6001.   const digitalCount = document.getElementById('digital-count');
  6002.   const printsCount = document.getElementById('prints-count');
  6003.   
  6004.   if (albumCount) albumCount.textContent = favoriteCount;
  6005.   if (digitalCount) digitalCount.textContent = favoriteCount;
  6006.   if (printsCount) printsCount.textContent = Math.min(favoriteCount, 12);
  6007.   
  6008.   // Mettre à jour les icônes de favoris dans les produits
  6009.   const albumFavCount = document.getElementById('album-fav-count');
  6010.   const digitalFavCount = document.getElementById('digital-fav-count');
  6011.   const printsFavCount = document.getElementById('prints-fav-count');
  6012.   
  6013.   if (albumFavCount) albumFavCount.textContent = favoriteCount;
  6014.   if (digitalFavCount) digitalFavCount.textContent = favoriteCount;
  6015.   if (printsFavCount) printsFavCount.textContent = Math.min(favoriteCount, 12);
  6016.   
  6017.   // Mettre à jour le sidebar si ouvert
  6018.   const sidebar = document.getElementById('ecommerce-sidebar');
  6019.   if (sidebar && sidebar.classList.contains('active')) {
  6020.     console.log('🔄 Sidebar is open, updating content with favoriteCount:', favoriteCount);
  6021.     window.updateEcommerceSidebarContent(favoriteCount);
  6022.   } else {
  6023.     console.log('ℹ️ Sidebar is closed, but updating content anyway for consistency');
  6024.     // Mettre à jour le contenu même si le sidebar n'est pas ouvert pour la cohérence
  6025.     window.updateEcommerceSidebarContent(favoriteCount);
  6026.   }
  6027. };
  6028. // Fonction pour retirer un favori de la grille et exécuter supprimerFavoris
  6029. window.removeFavorite = function removeFavorite(itemId) {
  6030.   // Animation simple de disparition puis suppression
  6031.   const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"]`);
  6032.  
  6033.   if (favoriteItem) {
  6034.     favoriteItem.classList.add('fade-out');
  6035.     setTimeout(() => {
  6036.       favoriteItem.remove();
  6037.       // Appeler la fonction métier si présente
  6038.       if (typeof window.supprimerFavoris === 'function') {
  6039.         const heartIcon = document.querySelector(`#coeur${itemId}`);
  6040.         const sejourId = heartIcon && heartIcon.dataset.sejourId ? heartIcon.dataset.sejourId : '';
  6041.         window.supprimerFavoris(itemId, sejourId);
  6042.       }
  6043.       // Mettre à jour les compteurs
  6044.       updateAllFavoriteCounters();
  6045.     }, 200);
  6046.   }
  6047. };
  6048. // Vérifier que la fonction est bien définie
  6049. console.log('🔍 removeFavorite function defined:', typeof window.removeFavorite);
  6050. // (Nettoyé) Pas de délégation globale ici; les boutons utilisent des onclick simples
  6051. // Fonction pour voir un favori en grand
  6052. window.viewFavorite = function viewFavorite(itemId) {
  6053.   console.log('👁️ Viewing favorite:', itemId);
  6054.   
  6055.   // Essayer d'utiliser la fonction viewImage existante
  6056.   if (typeof window.viewImage === 'function') {
  6057.     window.viewImage(itemId, null);
  6058.   } else {
  6059.     console.log('ℹ️ viewImage function not available, opening in new tab');
  6060.     // Fallback: ouvrir dans un nouvel onglet
  6061.     const favoriteItem = document.querySelector(`.favorite-item[data-id="${itemId}"] img`);
  6062.     if (favoriteItem && favoriteItem.src) {
  6063.       window.open(favoriteItem.src, '_blank');
  6064.     }
  6065.   }
  6066. };
  6067. // Fonction de debug pour tester les compteurs
  6068. window.debugFavoriteCounters = function() {
  6069.   console.log('🔍 Debug Favorite Counters:');
  6070.   console.log('- getFavoriteCount():', window.getFavoriteCount());
  6071.   console.log('- likeCount input value:', document.getElementById('likeCount')?.value);
  6072.   console.log('- likeCount textContent:', document.getElementById('likeCount')?.textContent);
  6073.   console.log('- giftCount textContent:', document.getElementById('giftCount')?.textContent);
  6074.   console.log('- album-count textContent:', document.getElementById('album-count')?.textContent);
  6075.   console.log('- digital-count textContent:', document.getElementById('digital-count')?.textContent);
  6076.   console.log('- prints-count textContent:', document.getElementById('prints-count')?.textContent);
  6077.   console.log('- Sidebar active:', document.getElementById('ecommerce-sidebar')?.classList.contains('active'));
  6078. };
  6079. // Fonction de test pour les boutons de favoris
  6080. window.testFavoriteButtons = function() {
  6081.   console.log('🧪 Testing favorite buttons:');
  6082.   const removeButtons = document.querySelectorAll('.btn-remove-favorite');
  6083.   const viewButtons = document.querySelectorAll('.btn-view-favorite');
  6084.   
  6085.   console.log('- Remove buttons found:', removeButtons.length);
  6086.   console.log('- View buttons found:', viewButtons.length);
  6087.   
  6088.   removeButtons.forEach((btn, index) => {
  6089.     console.log(`- Remove button ${index}:`, {
  6090.       itemId: btn.dataset.itemId,
  6091.       hasClickListener: btn.onclick !== null
  6092.     });
  6093.   });
  6094.   
  6095.   viewButtons.forEach((btn, index) => {
  6096.     console.log(`- View button ${index}:`, {
  6097.       itemId: btn.dataset.itemId,
  6098.       hasClickListener: btn.onclick !== null
  6099.     });
  6100.   });
  6101. };
  6102. // Fermer le sidebar en cliquant en dehors
  6103. document.addEventListener('click', function(e) {
  6104.   const sidebar = document.getElementById('ecommerce-sidebar');
  6105.   const giftButton = document.querySelector('.gift-button');
  6106.   
  6107.   if (sidebar && sidebar.classList.contains('active') && 
  6108.       !sidebar.contains(e.target) && 
  6109.       !giftButton.contains(e.target)) {
  6110.     window.closeEcommerceSidebar();
  6111.   }
  6112. });
  6113. // Fermer avec Escape
  6114. document.addEventListener('keydown', function(e) {
  6115.   if (e.key === 'Escape') {
  6116.     window.closeEcommerceSidebar();
  6117.   }
  6118. });
  6119. // Initialisation au chargement
  6120. document.addEventListener('DOMContentLoaded', function() {
  6121.   // Vérifier que les éléments existent
  6122.   const sidebar = document.getElementById('ecommerce-sidebar');
  6123.   const giftButton = document.getElementById('gift-button-trigger');
  6124.   
  6125.   // Event listener propre pour le bouton cadeau
  6126.   if (giftButton) {
  6127.     giftButton.addEventListener('click', function(e) {
  6128.       e.preventDefault();
  6129.       e.stopPropagation();
  6130.       window.openEcommerceSidebar();
  6131.     });
  6132.   }
  6133.   
  6134.   // Définir la date de fin du séjour si disponible
  6135.   {% if sejour and sejour.dateFinSejour is defined %}
  6136.     window.setSejourEndDate('{{ sejour.dateFinSejour|date("Y-m-d") }}');
  6137.   {% endif %}
  6138.   
  6139.   // Mise à jour initiale du contenu
  6140.   let favoriteCount = 0;
  6141.   try {
  6142.     if (typeof getCurrentFavoriteCount === 'function') {
  6143.       favoriteCount = getCurrentFavoriteCount();
  6144.     } else {
  6145.       // Essayer d'abord l'input likeCount
  6146.       const likeCountInput = document.getElementById('likeCount');
  6147.       if (likeCountInput && likeCountInput.value) {
  6148.         favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  6149.       } else {
  6150.         // Fallback sur giftCount
  6151.         const giftCount = document.getElementById('giftCount');
  6152.         if (giftCount && giftCount.textContent) {
  6153.           favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  6154.         }
  6155.       }
  6156.     }
  6157.   } catch (e) {
  6158.     console.log('⚠️ Fallback pour favoriteCount initial:', e);
  6159.     // Fallback sur likeCount input
  6160.     const likeCountInput = document.getElementById('likeCount');
  6161.     if (likeCountInput && likeCountInput.value) {
  6162.       favoriteCount = parseInt(likeCountInput.value, 10) || 0;
  6163.     } else {
  6164.       const giftCount = document.getElementById('giftCount');
  6165.       if (giftCount && giftCount.textContent) {
  6166.         favoriteCount = parseInt(giftCount.textContent.trim()) || 0;
  6167.       }
  6168.     }
  6169.   }
  6170.   
  6171.   window.updateEcommerceSidebarContent(favoriteCount);
  6172.   
  6173. });
  6174. </script>
  6175. <!-- 🎯 DAY FILTER POPOVER (instancié une seule fois) -->
  6176. <div id="dayFilterPopover" class="day-popover" role="dialog" aria-modal="true" aria-hidden="true">
  6177.   <div class="dp-arrow" aria-hidden="true"></div>
  6178.   <div class="dp-content" role="group" aria-label="Filtres du jour">
  6179.     <button class="dp-btn" data-filter="all" title="Tout" aria-pressed="true">
  6180.       <i class="bi bi-grid-3x3-gap-fill"></i><span class="dp-count dp-label">Tout</span>
  6181.     </button>
  6182.     <button class="dp-btn" data-filter="photos" title="Photos" aria-pressed="false">
  6183.       <i class="bi bi-images"></i><span class="dp-count" data-bind="photo">0</span>
  6184.     </button>
  6185.     <button class="dp-btn" data-filter="audio" title="Audios" aria-pressed="false">
  6186.       <i class="bi bi-mic-fill"></i><span class="dp-count" data-bind="audio">0</span>
  6187.     </button>
  6188.     <button class="dp-btn" data-filter="videos" title="Vidéos" aria-pressed="false">
  6189.       <i class="bi bi-camera-video-fill"></i><span class="dp-count" data-bind="video">0</span>
  6190.     </button>
  6191.     <button class="dp-btn" data-filter="favoris" title="Favoris" aria-pressed="false">
  6192.       <i class="bi bi-heart-fill" style="color:#f56040"></i><span class="dp-count" data-bind="fav">0</span>
  6193.     </button>
  6194.     <button class="dp-btn dp-close" title="Fermer" aria-label="Fermer">
  6195.       <i class="bi bi-x-lg"></i>
  6196.     </button>
  6197.   </div>
  6198. </div>
  6199. {# ==================== Zones B2C Suggestions ==================== #}
  6200. {# Sidebar suggestions (desktop) #}
  6201. <aside class="col-lg-3 d-none d-lg-block" style="position:sticky;top:100px;">
  6202.   <div id="parentSuggestions" class="pcb-sticky">
  6203.     {# Rempli dynamiquement par parent-b2c-nuke.js #}
  6204.   </div>
  6205. </aside>
  6206. {# Bloc suggestions mobile #}
  6207. <div class="col-12 d-lg-none mt-3">
  6208.   <div id="parentSuggestionsMobile">
  6209.     {# Rempli dynamiquement par parent-b2c-nuke.js #}
  6210.   </div>
  6211. </div>
  6212. {# ==================== Script Toasts Parents (SIMPLE) ==================== #}
  6213. <script src="{{ asset('js/parent-toasts.js') }}"></script>
  6214. {# ==================== Panneau de test B2C ==================== #}
  6215. <div id="b2cTestPanel" class="modal fade" tabindex="-1" aria-hidden="true">
  6216.   <div class="modal-dialog modal-lg">
  6217.     <div class="modal-content">
  6218.       <div class="modal-header bg-primary text-white">
  6219.         <h5 class="modal-title">
  6220.           <i class="bi bi-lightning-charge-fill"></i> Test B2C Conversion System
  6221.         </h5>
  6222.         <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
  6223.       </div>
  6224.       <div class="modal-body">
  6225.         <div class="alert alert-info">
  6226.           <strong>Système B2C actif</strong> - Testez toutes les variantes de toasts et surfaces
  6227.         </div>
  6228.         <h6 class="fw-bold mt-3 mb-2">🎯 État actuel</h6>
  6229.         <div class="row g-2 mb-3">
  6230.           <div class="col-6">
  6231.             <div class="card">
  6232.               <div class="card-body py-2">
  6233.                 <small class="text-muted">Favoris</small>
  6234.                 <div class="fw-bold" id="testCurrentFav">{{ nblikes|default(0) }}</div>
  6235.               </div>
  6236.             </div>
  6237.           </div>
  6238.           <div class="col-6">
  6239.             <div class="card">
  6240.               <div class="card-body py-2">
  6241.                 <small class="text-muted">Surface active</small>
  6242.                 <div class="fw-bold" id="testCurrentSurface">Aucune</div>
  6243.               </div>
  6244.             </div>
  6245.           </div>
  6246.         </div>
  6247.         <h6 class="fw-bold mb-2">💬 Test Toasts (8 variantes)</h6>
  6248.         <div class="d-grid gap-2 mb-3">
  6249.           <button class="btn btn-sm btn-outline-danger" onclick="testToast('last')">
  6250.             <i class="bi bi-alarm-fill"></i> Last Chance (J→J+7)
  6251.           </button>
  6252.           <button class="btn btn-sm btn-outline-info" onclick="testToast('cart')">
  6253.             <i class="bi bi-bag-check-fill"></i> Panier en attente
  6254.           </button>
  6255.           <button class="btn btn-sm btn-outline-primary" onclick="testToast('fav2')">
  6256.             <i class="bi bi-gift-fill"></i> Favoris → Produit (≥8 fav)
  6257.           </button>
  6258.           <button class="btn btn-sm btn-outline-warning" onclick="testToast('jmoins')">
  6259.             <i class="bi bi-calendar2-event-fill"></i> J-3→J-1 (fin proche)
  6260.           </button>
  6261.           <button class="btn btn-sm btn-outline-warning" style="background:#fef3c7;" onclick="testToast('welcome')">
  6262.             <i class="bi bi-emoji-smile-fill"></i> Welcome (1er jour)
  6263.           </button>
  6264.           <button class="btn btn-sm btn-outline-info" onclick="testToast('nudge')">
  6265.             <i class="bi bi-heart"></i> Nudge favoris (≤2 fav)
  6266.           </button>
  6267.           <button class="btn btn-sm btn-outline-secondary" onclick="testToast('social')">
  6268.             <i class="bi bi-stars"></i> Social proof
  6269.           </button>
  6270.         </div>
  6271.         <h6 class="fw-bold mb-2">📊 Test Surfaces</h6>
  6272.         <div class="d-grid gap-2 mb-3">
  6273.           <button class="btn btn-sm btn-outline-success" onclick="testSurface('slider')">
  6274.             <i class="bi bi-image-fill"></i> Afficher Slider Héros
  6275.           </button>
  6276.           <button class="btn btn-sm btn-outline-primary" onclick="testSurface('banner')">
  6277.             <i class="bi bi-flag-fill"></i> Afficher Bandeau 48px
  6278.           </button>
  6279.           <button class="btn btn-sm btn-outline-info" onclick="testSurface('sidebar')">
  6280.             <i class="bi bi-layout-sidebar-inset-reverse"></i> Afficher Sidebar
  6281.           </button>
  6282.         </div>
  6283.         <h6 class="fw-bold mb-2">🎨 Outils</h6>
  6284.         <div class="d-grid gap-2">
  6285.           <button class="btn btn-sm btn-warning" onclick="clearB2CStorage()">
  6286.             <i class="bi bi-trash3-fill"></i> Effacer localStorage (reset capping)
  6287.           </button>
  6288.           <button class="btn btn-sm btn-secondary" onclick="showB2CConfig()">
  6289.             <i class="bi bi-gear-fill"></i> Voir configuration
  6290.           </button>
  6291.           <button class="btn btn-sm btn-success" onclick="testPickOffers()">
  6292.             <i class="bi bi-box-seam"></i> Test pickParentOffers()
  6293.           </button>
  6294.         </div>
  6295.       </div>
  6296.     </div>
  6297.   </div>
  6298. </div>
  6299. <script>
  6300. // Ouvrir le panneau de test
  6301. document.getElementById('btnTestB2C').addEventListener('click', function() {
  6302.   const modal = new bootstrap.Modal(document.getElementById('b2cTestPanel'));
  6303.   modal.show();
  6304.   updateTestPanel();
  6305. });
  6306. function updateTestPanel() {
  6307.   const favEl = document.getElementById('testCurrentFav');
  6308.   const surfaceEl = document.getElementById('testCurrentSurface');
  6309.   if (favEl) favEl.textContent = window.PARENT_CONVERSION_CONFIG?.parent?.favorites || 0;
  6310.   if (surfaceEl) surfaceEl.textContent = window.__PROMO_STATE__?.claimed || 'Aucune';
  6311. }
  6312. function testToast(variant) {
  6313.   console.log('🧪 Test Toast:', variant);
  6314.   const C = window.PARENT_CONVERSION_CONFIG;
  6315.   if (!C) { 
  6316.     console.error('❌ Configuration B2C non chargée');
  6317.     alert('Configuration B2C non chargée'); 
  6318.     return; 
  6319.   }
  6320.   
  6321.   console.log('✅ Config B2C:', C);
  6322.   
  6323.   // Libérer la surface si elle est occupée
  6324.   if (window.PROMO_release && window.__PROMO_STATE__?.claimed) {
  6325.     console.log('🔓 Libération surface:', window.__PROMO_STATE__.claimed);
  6326.     window.PROMO_release(window.__PROMO_STATE__.claimed);
  6327.   }
  6328.   
  6329.   // Fermer le modal de test
  6330.   const modal = bootstrap.Modal.getInstance(document.getElementById('b2cTestPanel'));
  6331.   if (modal) modal.hide();
  6332.   
  6333.   // Attendre que le modal soit fermé puis afficher le toast
  6334.   setTimeout(() => {
  6335.     console.log('🚀 Affichage toast:', variant);
  6336.     
  6337.     // Vérifier que les fonctions existent
  6338.     const functions = {
  6339.       last: window.showToastLastChance,
  6340.       cart: window.showToastCart,
  6341.       fav2: window.showToastFavToProduct,
  6342.       jmoins: window.showToastAlmostEnd,
  6343.       welcome: window.showToastWelcome,
  6344.       nudge: window.showToastFavNudge,
  6345.       social: window.showToastSocial
  6346.     };
  6347.     
  6348.     console.log('📋 Fonctions disponibles:', Object.keys(functions).map(k => k + ': ' + (typeof functions[k])));
  6349.     
  6350.     // Appeler directement les fonctions de toast du script B2C
  6351.     switch(variant) {
  6352.       case 'last':
  6353.         if (typeof window.showToastLastChance === 'function') {
  6354.           console.log('✅ Appel showToastLastChance');
  6355.           window.showToastLastChance(C);
  6356.         } else {
  6357.           console.error('❌ showToastLastChance non disponible');
  6358.           alert('Fonction showToastLastChance non disponible. Vérifiez la console.');
  6359.         }
  6360.         break;
  6361.       case 'cart':
  6362.         if (typeof window.showToastCart === 'function') {
  6363.           const oldCart = C.parent.cartCount;
  6364.           C.parent.cartCount = 2;
  6365.           console.log('✅ Appel showToastCart (cartCount=2)');
  6366.           window.showToastCart(C);
  6367.           C.parent.cartCount = oldCart;
  6368.         } else {
  6369.           console.error('❌ showToastCart non disponible');
  6370.           alert('Fonction showToastCart non disponible');
  6371.         }
  6372.         break;
  6373.       case 'fav2':
  6374.         if (typeof window.showToastFavToProduct === 'function') {
  6375.           console.log('✅ Appel showToastFavToProduct');
  6376.           window.showToastFavToProduct(C);
  6377.         } else {
  6378.           console.error('❌ showToastFavToProduct non disponible');
  6379.           alert('Fonction showToastFavToProduct non disponible');
  6380.         }
  6381.         break;
  6382.       case 'jmoins':
  6383.         if (typeof window.showToastAlmostEnd === 'function') {
  6384.           console.log('✅ Appel showToastAlmostEnd');
  6385.           window.showToastAlmostEnd(C);
  6386.         } else {
  6387.           console.error('❌ showToastAlmostEnd non disponible');
  6388.           alert('Fonction showToastAlmostEnd non disponible');
  6389.         }
  6390.         break;
  6391.       case 'welcome':
  6392.         if (typeof window.showToastWelcome === 'function') {
  6393.           console.log('✅ Appel showToastWelcome');
  6394.           window.showToastWelcome(C);
  6395.         } else {
  6396.           console.error('❌ showToastWelcome non disponible');
  6397.           alert('Fonction showToastWelcome non disponible');
  6398.         }
  6399.         break;
  6400.       case 'nudge':
  6401.         if (typeof window.showToastFavNudge === 'function') {
  6402.           console.log('✅ Appel showToastFavNudge');
  6403.           window.showToastFavNudge(C);
  6404.         } else {
  6405.           console.error('❌ showToastFavNudge non disponible');
  6406.           alert('Fonction showToastFavNudge non disponible');
  6407.         }
  6408.         break;
  6409.       case 'social':
  6410.         if (typeof window.showToastSocial === 'function') {
  6411.           console.log('✅ Appel showToastSocial');
  6412.           window.showToastSocial(C);
  6413.         } else {
  6414.           console.error('❌ showToastSocial non disponible');
  6415.           alert('Fonction showToastSocial non disponible');
  6416.         }
  6417.         break;
  6418.       default:
  6419.         console.error('❌ Variante inconnue:', variant);
  6420.         alert('Variante de toast non reconnue: ' + variant);
  6421.     }
  6422.     
  6423.     console.log('✅ Toast affiché');
  6424.     updateTestPanel();
  6425.   }, 300);
  6426. }
  6427. function testSurface(surface) {
  6428.   alert(`Test surface "${surface}" - Rechargez la page pour voir l'effet`);
  6429.   localStorage.clear();
  6430.   location.reload();
  6431. }
  6432. function clearB2CStorage() {
  6433.   const keys = Object.keys(localStorage).filter(k => k.startsWith('pc_'));
  6434.   keys.forEach(k => localStorage.removeItem(k));
  6435.   alert(`${keys.length} clés B2C effacées du localStorage`);
  6436.   updateTestPanel();
  6437. }
  6438. function showB2CConfig() {
  6439.   console.log('PARENT_CONVERSION_CONFIG:', window.PARENT_CONVERSION_CONFIG);
  6440.   console.log('PROMO_STATE:', window.__PROMO_STATE__);
  6441.   alert('Configuration affichée dans la console (F12)');
  6442. }
  6443. function testPickOffers() {
  6444.   if (!window.pickParentOffers) {
  6445.     alert('Fonction pickParentOffers non chargée');
  6446.     return;
  6447.   }
  6448.   const offers = window.pickParentOffers(window.PARENT_CONVERSION_CONFIG);
  6449.   console.log('Offres suggérées:', offers);
  6450.   alert(`${offers.length} offres calculées:\n` + offers.map((o,i) => `${i+1}. ${o.title} (${o.badge})`).join('\n'));
  6451. }
  6452. </script>
  6453. {% endblock %}