- Wait for ShopifyCart to be available before initializing structured data - Add getCart() method to retrieve existing cart from Shopify API - Load cart state on page load to display correct initial cart contents Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
290 lines
8.1 KiB
JavaScript
290 lines
8.1 KiB
JavaScript
/**
|
||
* Cart Drawer Component
|
||
* Manages the cart sidebar with add/remove/update functionality
|
||
*/
|
||
(function() {
|
||
const drawer = document.getElementById('cart-drawer');
|
||
const emptyState = document.querySelector('[data-cart-empty]');
|
||
const itemsContainer = document.querySelector('[data-cart-items]');
|
||
const checkoutBtn = document.querySelector('[data-cart-checkout]');
|
||
const closeButtons = document.querySelectorAll('[data-cart-close]');
|
||
const totalDisplay = document.querySelector('[data-cart-total]');
|
||
const headerCartBtn = document.querySelector('[data-cart-open]');
|
||
const headerCartCount = document.querySelector('[data-cart-count]');
|
||
|
||
// Get translated text
|
||
const removeText = drawer.dataset.textRemove || 'Remove';
|
||
|
||
let currentCart = null;
|
||
let cartInstance = null;
|
||
|
||
// Wait for ShopifyCart to be available
|
||
function initCartDrawer() {
|
||
if (typeof ShopifyCart === 'undefined') {
|
||
setTimeout(initCartDrawer, 100);
|
||
return;
|
||
}
|
||
|
||
cartInstance = new ShopifyCart({
|
||
domain: 'nv7cqv-bu.myshopify.com',
|
||
storefrontAccessToken: 'dec3d35a2554384d149c72927d1cfd1b'
|
||
});
|
||
|
||
// Initialize event listeners
|
||
setupEventListeners();
|
||
|
||
// Load initial cart state
|
||
loadCart();
|
||
}
|
||
|
||
function setupEventListeners() {
|
||
// Close drawer
|
||
closeButtons.forEach(btn => {
|
||
btn.addEventListener('click', closeDrawer);
|
||
});
|
||
|
||
// Open drawer from header button
|
||
if (headerCartBtn) {
|
||
headerCartBtn.addEventListener('click', openDrawer);
|
||
}
|
||
|
||
// Checkout button
|
||
checkoutBtn.addEventListener('click', () => {
|
||
if (currentCart?.checkoutUrl) {
|
||
window.location.href = currentCart.checkoutUrl;
|
||
}
|
||
});
|
||
|
||
// Listen for custom cart update events
|
||
document.addEventListener('cart:updated', (e) => {
|
||
currentCart = e.detail.cart;
|
||
renderCart();
|
||
openDrawer();
|
||
});
|
||
}
|
||
|
||
async function loadCart() {
|
||
if (!cartInstance) return;
|
||
|
||
try {
|
||
const cart = await cartInstance.getCart();
|
||
currentCart = cart;
|
||
renderCart();
|
||
} catch (error) {
|
||
console.error('Error loading cart:', error);
|
||
}
|
||
}
|
||
|
||
function openDrawer() {
|
||
drawer.classList.add('is-open');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function closeDrawer() {
|
||
drawer.classList.remove('is-open');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
function calculateTotal() {
|
||
if (!currentCart || !currentCart.lines) return 0;
|
||
|
||
return currentCart.lines.edges.reduce((total, edge) => {
|
||
const item = edge.node;
|
||
const price = parseFloat(item.merchandise.price.amount);
|
||
const quantity = item.quantity;
|
||
return total + (price * quantity);
|
||
}, 0);
|
||
}
|
||
|
||
function formatPrice(amount, currency = 'EUR') {
|
||
return new Intl.NumberFormat('fr-FR', {
|
||
style: 'currency',
|
||
currency: currency
|
||
}).format(amount);
|
||
}
|
||
|
||
function updateCartCount() {
|
||
if (!currentCart || !currentCart.lines || currentCart.lines.edges.length === 0) {
|
||
if (headerCartCount) {
|
||
headerCartCount.textContent = '';
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Calculate total quantity
|
||
const totalQty = currentCart.lines.edges.reduce((sum, edge) => {
|
||
return sum + edge.node.quantity;
|
||
}, 0);
|
||
|
||
if (headerCartCount) {
|
||
headerCartCount.textContent = totalQty > 0 ? totalQty : '';
|
||
}
|
||
}
|
||
|
||
function renderCart() {
|
||
if (!currentCart || !currentCart.lines || currentCart.lines.edges.length === 0) {
|
||
emptyState.classList.remove('hidden');
|
||
itemsContainer.classList.add('hidden');
|
||
checkoutBtn.disabled = true;
|
||
if (totalDisplay) {
|
||
totalDisplay.textContent = '0,00 €';
|
||
}
|
||
updateCartCount();
|
||
return;
|
||
}
|
||
|
||
emptyState.classList.add('hidden');
|
||
itemsContainer.classList.remove('hidden');
|
||
checkoutBtn.disabled = false;
|
||
|
||
// Calculate and display total
|
||
const total = calculateTotal();
|
||
const currency = currentCart.lines.edges[0]?.node.merchandise.price.currencyCode || 'EUR';
|
||
if (totalDisplay) {
|
||
totalDisplay.textContent = formatPrice(total, currency);
|
||
}
|
||
|
||
// Update header cart count
|
||
updateCartCount();
|
||
|
||
// Render cart items
|
||
itemsContainer.innerHTML = currentCart.lines.edges.map(edge => {
|
||
const item = edge.node;
|
||
const merchandise = item.merchandise;
|
||
|
||
return `
|
||
<div class="cart-item" data-line-id="${item.id}">
|
||
<div class="cart-item__details">
|
||
<h4 class="cart-item__title">${merchandise.product.title}</h4>
|
||
${merchandise.title !== 'Default Title' ? `<p class="cart-item__variant">${merchandise.title}</p>` : ''}
|
||
<p class="cart-item__price">${formatPrice(parseFloat(merchandise.price.amount), merchandise.price.currencyCode)}</p>
|
||
|
||
<div class="cart-item__quantity">
|
||
<button class="cart-item__qty-btn" data-action="decrease" data-line-id="${item.id}">−</button>
|
||
<span class="cart-item__qty-value">${item.quantity}</span>
|
||
<button class="cart-item__qty-btn" data-action="increase" data-line-id="${item.id}">+</button>
|
||
</div>
|
||
|
||
<button class="cart-item__remove" data-action="remove" data-line-id="${item.id}">
|
||
${removeText}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
// Attach event listeners to quantity buttons
|
||
attachQuantityListeners();
|
||
}
|
||
|
||
function attachQuantityListeners() {
|
||
const buttons = itemsContainer.querySelectorAll('[data-action]');
|
||
|
||
buttons.forEach(btn => {
|
||
btn.addEventListener('click', async (e) => {
|
||
const action = e.target.dataset.action;
|
||
const lineId = e.target.dataset.lineId;
|
||
|
||
await handleQuantityChange(action, lineId);
|
||
});
|
||
});
|
||
}
|
||
|
||
async function handleQuantityChange(action, lineId) {
|
||
if (!cartInstance || !currentCart) return;
|
||
|
||
// Find the line item
|
||
const line = currentCart.lines.edges.find(edge => edge.node.id === lineId);
|
||
if (!line) return;
|
||
|
||
const currentQty = line.node.quantity;
|
||
let newQty = currentQty;
|
||
|
||
if (action === 'increase') {
|
||
newQty = currentQty + 1;
|
||
} else if (action === 'decrease') {
|
||
newQty = Math.max(0, currentQty - 1);
|
||
} else if (action === 'remove') {
|
||
newQty = 0;
|
||
}
|
||
|
||
// Update cart via API
|
||
try {
|
||
itemsContainer.classList.add('is-loading');
|
||
|
||
const query = `
|
||
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
||
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
||
cart {
|
||
id
|
||
checkoutUrl
|
||
lines(first: 10) {
|
||
edges {
|
||
node {
|
||
id
|
||
quantity
|
||
merchandise {
|
||
... on ProductVariant {
|
||
id
|
||
title
|
||
price {
|
||
amount
|
||
currencyCode
|
||
}
|
||
product {
|
||
title
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
userErrors {
|
||
field
|
||
message
|
||
}
|
||
}
|
||
}
|
||
`;
|
||
|
||
const data = await cartInstance.query(query, {
|
||
cartId: currentCart.id,
|
||
lines: [{
|
||
id: lineId,
|
||
quantity: newQty
|
||
}]
|
||
});
|
||
|
||
if (data.cartLinesUpdate.userErrors.length > 0) {
|
||
throw new Error(data.cartLinesUpdate.userErrors[0].message);
|
||
}
|
||
|
||
currentCart = data.cartLinesUpdate.cart;
|
||
renderCart();
|
||
|
||
} catch (error) {
|
||
console.error('Error updating cart:', error);
|
||
alert('Erreur lors de la mise à jour du panier');
|
||
} finally {
|
||
itemsContainer.classList.remove('is-loading');
|
||
}
|
||
}
|
||
|
||
// Public API
|
||
window.CartDrawer = {
|
||
open: openDrawer,
|
||
close: closeDrawer,
|
||
updateCart: (cart) => {
|
||
currentCart = cart;
|
||
renderCart();
|
||
}
|
||
};
|
||
|
||
// Initialize when DOM is ready
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', initCartDrawer);
|
||
} else {
|
||
initCartDrawer();
|
||
}
|
||
})();
|