```
✨ feat(reservation): Ajoute le panier et sélection de date globale
Ajoute un composant de panier accessible depuis toutes les pages de réservation et un sélecteur de date global.
```
This commit is contained in:
@@ -3,6 +3,7 @@ import { UtmEvent, UtmAccount } from "./tools/UtmEvent.js";
|
|||||||
import { CookieBanner } from "./tools/CookieBanner.js";
|
import { CookieBanner } from "./tools/CookieBanner.js";
|
||||||
import { FlowReserve } from "./tools/FlowReserve.js";
|
import { FlowReserve } from "./tools/FlowReserve.js";
|
||||||
import { FlowDatePicker } from "./tools/FlowDatePicker.js";
|
import { FlowDatePicker } from "./tools/FlowDatePicker.js";
|
||||||
|
import { FlowAddToCart } from "./tools/FlowAddToCart.js";
|
||||||
import * as Turbo from "@hotwired/turbo";
|
import * as Turbo from "@hotwired/turbo";
|
||||||
import { onLCP, onINP, onCLS } from 'web-vitals';
|
import { onLCP, onINP, onCLS } from 'web-vitals';
|
||||||
import AOS from 'aos';
|
import AOS from 'aos';
|
||||||
@@ -259,6 +260,9 @@ const registerComponents = () => {
|
|||||||
|
|
||||||
if(!customElements.get('flow-datepicker'))
|
if(!customElements.get('flow-datepicker'))
|
||||||
customElements.define('flow-datepicker',FlowDatePicker)
|
customElements.define('flow-datepicker',FlowDatePicker)
|
||||||
|
|
||||||
|
if(!customElements.get('flow-add-to-cart'))
|
||||||
|
customElements.define('flow-add-to-cart',FlowAddToCart)
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|||||||
72
assets/tools/FlowAddToCart.js
Normal file
72
assets/tools/FlowAddToCart.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
export class FlowAddToCart extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.btn = this.querySelector('button');
|
||||||
|
if (!this.btn) return;
|
||||||
|
|
||||||
|
this.productId = this.getAttribute('product-id');
|
||||||
|
this.btn.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.addToCart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async addToCart() {
|
||||||
|
// 1. Check Dates
|
||||||
|
const dates = JSON.parse(localStorage.getItem('reservation_dates') || '{}');
|
||||||
|
if (!dates.start || !dates.end) {
|
||||||
|
// Open Date Picker if no dates
|
||||||
|
document.dispatchEvent(new CustomEvent('open-date-picker'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Loading State
|
||||||
|
const originalContent = this.btn.innerHTML;
|
||||||
|
this.btn.disabled = true;
|
||||||
|
this.btn.innerHTML = '<span class="animate-spin inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full"></span>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3. Check Availability
|
||||||
|
const response = await fetch('/produit/check', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: this.productId,
|
||||||
|
start: dates.start,
|
||||||
|
end: dates.end
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Network error');
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.dispo) {
|
||||||
|
// 4. Add to Cart
|
||||||
|
const list = JSON.parse(localStorage.getItem('pl_list') || '[]');
|
||||||
|
if (!list.includes(this.productId)) {
|
||||||
|
list.push(this.productId);
|
||||||
|
localStorage.setItem('pl_list', JSON.stringify(list));
|
||||||
|
window.dispatchEvent(new CustomEvent('cart:updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open Cart
|
||||||
|
const cart = document.querySelector('[is="flow-reserve"]');
|
||||||
|
if (cart) cart.open();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 5. Not Available
|
||||||
|
alert("Ce produit n'est pas disponible pour les dates sélectionnées.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
alert("Une erreur est survenue.");
|
||||||
|
} finally {
|
||||||
|
this.btn.disabled = false;
|
||||||
|
this.btn.innerHTML = originalContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -228,12 +228,17 @@
|
|||||||
Structure soumise à des conditions spéciales.
|
Structure soumise à des conditions spéciales.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<a href="{{ path('reservation_contact', {id: product.id}) }}"
|
<a href="{{ path('reservation_contact', {id: product.id}) }}"
|
||||||
class="flex items-center justify-center w-full py-6 md:py-8 bg-slate-900 text-white rounded-[2.5rem] font-black uppercase text-[11px] md:text-[12px] tracking-[0.3em] hover:bg-[#f39e36] transition-all shadow-2xl hover:scale-[1.02] active:scale-95">
|
class="flex items-center justify-center w-full py-6 md:py-8 bg-slate-900 text-white rounded-[2.5rem] font-black uppercase text-[11px] md:text-[12px] tracking-[0.3em] hover:bg-[#f39e36] transition-all shadow-2xl hover:scale-[1.02] active:scale-95">
|
||||||
{{ (product.category == "2-7 ans") ? 'Nous contacter' : 'Vérifier la disponibilité' }}
|
Nous contacter
|
||||||
</a>
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<flow-add-to-cart product-id="{{ product.id }}">
|
||||||
|
<button class="flex items-center justify-center w-full py-6 md:py-8 bg-slate-900 text-white rounded-[2.5rem] font-black uppercase text-[11px] md:text-[12px] tracking-[0.3em] hover:bg-[#f39e36] transition-all shadow-2xl hover:scale-[1.02] active:scale-95">
|
||||||
|
Vérifier la disponibilité
|
||||||
|
</button>
|
||||||
|
</flow-add-to-cart>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<p class="text-center text-[9px] font-black text-slate-300 uppercase tracking-widest italic">
|
<p class="text-center text-[9px] font-black text-slate-300 uppercase tracking-widest italic">
|
||||||
Devis gratuit • Ludikevent • Qualité Pro
|
Devis gratuit • Ludikevent • Qualité Pro
|
||||||
|
|||||||
Reference in New Issue
Block a user