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