index-shop/assets/js/cart-drawer.js
isUnknown 28501fec7c Implement custom Shopify cart with drawer UI
Replace Shopify Buy Button iframe with custom implementation using Storefront API 2026-01. Create interactive cart drawer with full product management capabilities (add, remove, update quantities) and seamless checkout flow.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-14 11:26:14 +01:00

214 lines
6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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]');
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();
}
function setupEventListeners() {
// Close drawer
closeButtons.forEach(btn => {
btn.addEventListener('click', closeDrawer);
});
// 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();
});
}
function openDrawer() {
drawer.classList.add('is-open');
document.body.style.overflow = 'hidden';
}
function closeDrawer() {
drawer.classList.remove('is-open');
document.body.style.overflow = '';
}
function renderCart() {
if (!currentCart || !currentCart.lines || currentCart.lines.edges.length === 0) {
emptyState.classList.remove('hidden');
itemsContainer.classList.add('hidden');
checkoutBtn.disabled = true;
return;
}
emptyState.classList.add('hidden');
itemsContainer.classList.remove('hidden');
checkoutBtn.disabled = false;
// 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">${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}">
Retirer
</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();
}
})();