diff --git a/assets/reserve.js b/assets/reserve.js
index e79d458..228caa4 100644
--- a/assets/reserve.js
+++ b/assets/reserve.js
@@ -1,6 +1,8 @@
import './reserve.scss';
import { UtmEvent, UtmAccount } from "./tools/UtmEvent.js";
import { CookieBanner } from "./tools/CookieBanner.js";
+import { FlowReserve } from "./tools/FlowReserve.js";
+import { FlowDatePicker } from "./tools/FlowDatePicker.js";
import * as Turbo from "@hotwired/turbo";
import { onLCP, onINP, onCLS } from 'web-vitals';
import AOS from 'aos';
@@ -251,6 +253,12 @@ const initRegisterLogic = () => {
const registerComponents = () => {
const comps = [['utm-event', UtmEvent], ['utm-account', UtmAccount], ['cookie-banner', CookieBanner]];
comps.forEach(([name, cl]) => { if (!customElements.get(name)) customElements.define(name, cl); });
+
+ if(!customElements.get('flow-reserve'))
+ customElements.define('flow-reserve',FlowReserve,{extends:'a'})
+
+ if(!customElements.get('flow-datepicker'))
+ customElements.define('flow-datepicker',FlowDatePicker)
};
document.addEventListener('DOMContentLoaded', () => {
diff --git a/assets/tools/FlowDatePicker.js b/assets/tools/FlowDatePicker.js
new file mode 100644
index 0000000..0929969
--- /dev/null
+++ b/assets/tools/FlowDatePicker.js
@@ -0,0 +1,173 @@
+export class FlowDatePicker extends HTMLElement {
+ constructor() {
+ super();
+ this.modalId = 'flow-datepicker-modal';
+ }
+
+ connectedCallback() {
+ // Find the button inside this element and attach click listener
+ const btn = this.querySelector('button');
+ if (btn) {
+ btn.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.openModal();
+ });
+ }
+
+ // Listen for global open event
+ document.addEventListener('open-date-picker', () => this.openModal());
+
+ // Update header if dates exist in localStorage
+ const storedDates = JSON.parse(localStorage.getItem('reservation_dates') || '{}');
+ if (storedDates.start && storedDates.end) {
+ const headerDisplay = document.getElementById('header-date-display');
+ if (headerDisplay) {
+ const formatDate = (dateStr) => {
+ if (!dateStr) return '';
+ const [y, m, d] = dateStr.split('-');
+ return `${d}/${m}/${y}`;
+ };
+ headerDisplay.textContent = `${formatDate(storedDates.start)} au ${formatDate(storedDates.end)}`;
+ }
+ }
+ }
+
+ openModal() {
+ this.ensureModalExists();
+ const modal = document.getElementById(this.modalId);
+ const backdrop = modal.querySelector('.backdrop');
+ const panel = modal.querySelector('.panel');
+
+ modal.classList.remove('pointer-events-none');
+ backdrop.classList.remove('opacity-0');
+ panel.classList.remove('translate-y-full', 'opacity-0');
+ panel.classList.add('translate-y-0', 'opacity-100');
+ }
+
+ closeModal() {
+ const modal = document.getElementById(this.modalId);
+ if (!modal) return;
+
+ const backdrop = modal.querySelector('.backdrop');
+ const panel = modal.querySelector('.panel');
+
+ backdrop.classList.add('opacity-0');
+ panel.classList.remove('translate-y-0', 'opacity-100');
+ panel.classList.add('translate-y-full', 'opacity-0');
+
+ setTimeout(() => {
+ modal.classList.add('pointer-events-none');
+ }, 300);
+ }
+
+ ensureModalExists() {
+ if (document.getElementById(this.modalId)) return;
+
+ // Recuperation des dates du localStorage
+ const storedDates = JSON.parse(localStorage.getItem('reservation_dates') || '{}');
+ const defaultStart = storedDates.start || '';
+ const defaultEnd = storedDates.end || '';
+
+ // Calculer la date minimum (Aujourd'hui + 7 jours)
+ const minDate = new Date();
+ minDate.setDate(minDate.getDate() + 7);
+ const minDateString = minDate.toISOString().split('T')[0];
+
+ const template = `
+
+ `;
+ document.body.insertAdjacentHTML('beforeend', template);
+
+ // Bind Close Events
+ const modal = document.getElementById(this.modalId);
+ modal.querySelector('.backdrop').addEventListener('click', () => this.closeModal());
+ modal.querySelector('#datepicker-close').addEventListener('click', () => this.closeModal());
+
+ // Basic Logic: Ensure End Date >= Start Date & Save to LocalStorage
+ const startInput = modal.querySelector('#start_date');
+ const endInput = modal.querySelector('#end_date');
+
+ const saveToLocalStorage = () => {
+ localStorage.setItem('reservation_dates', JSON.stringify({
+ start: startInput.value,
+ end: endInput.value
+ }));
+ };
+
+ modal.querySelector('form').addEventListener('submit', (evt) => {
+ evt.preventDefault();
+ saveToLocalStorage();
+
+ // Update Header Display
+ const headerDisplay = document.getElementById('header-date-display');
+ if (headerDisplay) {
+ const formatDate = (dateStr) => {
+ if (!dateStr) return '';
+ const [y, m, d] = dateStr.split('-');
+ return `${d}/${m}/${y}`;
+ };
+ headerDisplay.textContent = `${formatDate(startInput.value)} au ${formatDate(endInput.value)}`;
+ }
+
+ this.closeModal();
+ });
+
+ startInput.addEventListener('change', () => {
+ if (endInput.value < startInput.value) {
+ endInput.value = startInput.value;
+ }
+ endInput.min = startInput.value;
+ saveToLocalStorage();
+ });
+
+ endInput.addEventListener('change', () => {
+ saveToLocalStorage();
+ });
+ }
+}
diff --git a/assets/tools/FlowReserve.js b/assets/tools/FlowReserve.js
new file mode 100644
index 0000000..41c41b3
--- /dev/null
+++ b/assets/tools/FlowReserve.js
@@ -0,0 +1,264 @@
+
+export class FlowReserve extends HTMLAnchorElement {
+ constructor() {
+ super();
+ this.sidebarId = 'flow-reserve-sidebar';
+ this.storageKey = 'pl_list';
+ this.apiUrl = '/basket/json';
+ this.isOpen = false;
+ }
+
+ connectedCallback() {
+ this.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.toggleSidebar();
+ });
+
+ // Listen for updates to the cart from other components
+ window.addEventListener('cart:updated', () => this.updateBadge());
+
+ // Initial badge update
+ this.updateBadge();
+ }
+
+ /**
+ * Updates the notification badge on the icon
+ */
+ updateBadge() {
+ const list = this.getList();
+ const badge = this.querySelector('[data-count]');
+ if (badge) {
+ badge.innerText = list.length;
+ if (list.length > 0) {
+ badge.classList.remove('hidden');
+ } else {
+ badge.classList.add('hidden');
+ }
+ }
+ }
+
+ getList() {
+ try {
+ return JSON.parse(localStorage.getItem(this.storageKey) || '[]');
+ } catch (e) {
+ return [];
+ }
+ }
+
+ removeFromList(id) {
+ let list = this.getList();
+ list = list.filter(itemId => itemId.toString() !== id.toString());
+ localStorage.setItem(this.storageKey, JSON.stringify(list));
+ window.dispatchEvent(new CustomEvent('cart:updated'));
+ this.refreshContent(); // Re-fetch and render
+ }
+
+ toggleSidebar() {
+ if (this.isOpen) {
+ this.close();
+ } else {
+ this.open();
+ }
+ }
+
+ open() {
+ this.ensureSidebarExists();
+ const sidebar = document.getElementById(this.sidebarId);
+ const backdrop = sidebar.querySelector('.backdrop');
+ const panel = sidebar.querySelector('.panel');
+
+ sidebar.classList.remove('pointer-events-none');
+ backdrop.classList.remove('opacity-0');
+ panel.classList.remove('translate-x-full');
+
+ this.isOpen = true;
+ this.refreshContent();
+ }
+
+ close() {
+ const sidebar = document.getElementById(this.sidebarId);
+ if (!sidebar) return;
+
+ const backdrop = sidebar.querySelector('.backdrop');
+ const panel = sidebar.querySelector('.panel');
+
+ backdrop.classList.add('opacity-0');
+ panel.classList.add('translate-x-full');
+
+ setTimeout(() => {
+ sidebar.classList.add('pointer-events-none');
+ }, 300); // Match transition duration
+
+ this.isOpen = false;
+ }
+
+ ensureSidebarExists() {
+ if (document.getElementById(this.sidebarId)) return;
+
+ const template = `
+
+ `;
+ document.body.insertAdjacentHTML('beforeend', template);
+
+ // Bind events
+ const sidebar = document.getElementById(this.sidebarId);
+ sidebar.querySelector('.backdrop').addEventListener('click', () => this.close());
+ sidebar.querySelector('#flow-reserve-close').addEventListener('click', () => this.close());
+ }
+ async refreshContent() {
+ const container = document.getElementById('flow-reserve-content');
+ const footer = document.getElementById('flow-reserve-footer');
+
+ // Loader
+ container.innerHTML = `
+
+ `;
+ footer.innerHTML = '';
+
+ const ids = this.getList();
+
+ if (ids.length === 0) {
+ this.renderEmpty(container, footer);
+ return;
+ }
+
+ try {
+ const response = await fetch(this.apiUrl, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ ids })
+ });
+
+ if (!response.ok) throw new Error('Erreur réseau');
+
+ const data = await response.json();
+ this.renderList(container, footer, data);
+
+ } catch (error) {
+ console.error(error);
+ container.innerHTML = `
+
+
Une erreur est survenue lors du chargement de votre panier.
+
+ `;
+ }
+ }
+
+ renderEmpty(container, footer) {
+ container.innerHTML = `
+
+
+
Votre panier est vide
+
Ajoutez du bonheur à votre événement !
+
+ Voir le catalogue
+
+
+ `;
+ footer.innerHTML = '';
+ container.querySelector('#flow-empty-catalog-link').addEventListener('click', () => this.close());
+ }
+
+ renderList(container, footer, data) {
+ // --- HEADER DATES ---
+ let datesHtml = '';
+ if (data.start_date && data.end_date) {
+ datesHtml = `
+
+
Votre période
+
+
${data.start_date}
+
+
${data.end_date}
+
+
+ `;
+ }
+
+ // --- RENDER PRODUCTS ---
+ const productsHtml = data.products.map(product => `
+
+
+

+
+
+
+
${product.name}
+
+ 1J: ${this.formatPrice(product.priceHt1Day)} HT
+ ${product.priceHTSupDay ? `|Sup: ${this.formatPrice(product.priceHTSupDay)} HT` : ''}
+
+
+
+
${this.formatPrice(product.priceTTC1Day || product.priceHt1Day)} /j
+
+
+
+
+ `).join('');
+
+ container.innerHTML = `${datesHtml}${productsHtml}
`;
+
+ // --- RENDER FOOTER (TOTALS) ---
+ const total = data.total || {};
+ const hasTva = total.totalTva > 0;
+
+ footer.innerHTML = `
+
+
+ Total HT
+ ${this.formatPrice(total.totalHT)}
+
+ ${hasTva ? `
+
+ TVA
+ ${this.formatPrice(total.totalTva)}
+
+ ` : ''}
+
+ Total ${hasTva ? 'TTC' : 'HT'}
+ ${this.formatPrice(total.totalTTC || total.totalHT)}
+
+
+
+ Valider ma demande
+
+ `;
+ }
+
+ formatPrice(amount) {
+ if (amount === undefined || amount === null) return '-';
+ return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(amount);
+ }
+}
diff --git a/templates/revervation/base.twig b/templates/revervation/base.twig
index 0133969..fcecbe3 100644
--- a/templates/revervation/base.twig
+++ b/templates/revervation/base.twig
@@ -51,7 +51,7 @@
{% macro nav_link(route_name, label_key, is_external = false) %}
+ class="text-gray-700 hover:text-[#f39e36] font-medium transition-colors">
{{ label_key|trans }}
{% endmacro %}
@@ -90,7 +90,14 @@
{{ macros.nav_link('reservation_workflow', 'nav.how_to_book') }}
{{ macros.nav_link('reservation_contact', 'nav.contact') }}
-
+
+
+ 0
+
+
+
@@ -138,6 +145,10 @@
{{ macros.mobile_nav_link('/provider/Catalogue.pdf', 'Catalogue', true) }}
{{ macros.mobile_nav_link('reservation_workflow', 'Comment reserver') }}
{{ macros.mobile_nav_link('reservation_search', 'Rechercher') }}
+
+ Panier
+ 0
+