Add i18n support and cart total to Shopify integration
Implement multilingual support for shop interface and add total calculation to cart drawer: - Add FR/EN translations for all shop-related texts (cart, checkout, stock status) - Update templates and JavaScript to use translation system - Add cart total calculation with formatted currency display - Refactor cart drawer styles to SASS with improved button styling (black borders on +/-) - Fix English product content (replace JSON with proper HTML) - Extract cart drawer to separate snippet for better organization Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
28501fec7c
commit
b3940bba08
12 changed files with 586 additions and 451 deletions
|
|
@ -8,6 +8,10 @@
|
|||
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]');
|
||||
|
||||
// Get translated text
|
||||
const removeText = drawer.dataset.textRemove || 'Remove';
|
||||
|
||||
let currentCart = null;
|
||||
let cartInstance = null;
|
||||
|
|
@ -59,11 +63,32 @@
|
|||
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 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 €';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +96,13 @@
|
|||
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);
|
||||
}
|
||||
|
||||
// Render cart items
|
||||
itemsContainer.innerHTML = currentCart.lines.edges.map(edge => {
|
||||
const item = edge.node;
|
||||
|
|
@ -81,7 +113,7 @@
|
|||
<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>
|
||||
<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>
|
||||
|
|
@ -90,7 +122,7 @@
|
|||
</div>
|
||||
|
||||
<button class="cart-item__remove" data-action="remove" data-line-id="${item.id}">
|
||||
Retirer
|
||||
${removeText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@
|
|||
const variantId = addToCartBtn.dataset.variantId;
|
||||
const stockDisplay = document.querySelector('[data-product-stock]');
|
||||
|
||||
// Get translated texts
|
||||
const texts = {
|
||||
add: addToCartBtn.dataset.textAdd || 'Add to cart',
|
||||
adding: addToCartBtn.dataset.textAdding || 'Adding...',
|
||||
added: addToCartBtn.dataset.textAdded || 'Added! ✓',
|
||||
error: addToCartBtn.dataset.textError || 'Error - Try again',
|
||||
outOfStock: addToCartBtn.dataset.textOutOfStock || 'Out of stock',
|
||||
inStock: addToCartBtn.dataset.textInStock || 'In stock'
|
||||
};
|
||||
|
||||
// Load product data to check availability
|
||||
async function loadProductData() {
|
||||
try {
|
||||
|
|
@ -49,16 +59,16 @@
|
|||
// Update button based on availability
|
||||
if (!variant.availableForSale) {
|
||||
addToCartBtn.disabled = true;
|
||||
addToCartBtn.textContent = 'Rupture de stock';
|
||||
addToCartBtn.textContent = texts.outOfStock;
|
||||
addToCartBtn.classList.add('out-of-stock');
|
||||
if (stockDisplay) {
|
||||
stockDisplay.textContent = 'Rupture de stock';
|
||||
stockDisplay.textContent = texts.outOfStock;
|
||||
stockDisplay.classList.add('out-of-stock');
|
||||
}
|
||||
} else {
|
||||
// Show in stock
|
||||
if (stockDisplay) {
|
||||
stockDisplay.textContent = 'En stock';
|
||||
stockDisplay.textContent = texts.inStock;
|
||||
stockDisplay.classList.add('in-stock');
|
||||
}
|
||||
}
|
||||
|
|
@ -78,14 +88,14 @@
|
|||
// Disable button during request
|
||||
addToCartBtn.disabled = true;
|
||||
const originalText = addToCartBtn.textContent;
|
||||
addToCartBtn.textContent = 'Ajout en cours...';
|
||||
addToCartBtn.textContent = texts.adding;
|
||||
|
||||
try {
|
||||
const variantId = this.dataset.variantId;
|
||||
const cartResult = await cart.addToCart(variantId, 1);
|
||||
|
||||
// Show success feedback
|
||||
addToCartBtn.textContent = 'Ajouté ! ✓';
|
||||
addToCartBtn.textContent = texts.added;
|
||||
addToCartBtn.classList.add('success');
|
||||
|
||||
// Dispatch event to open cart drawer
|
||||
|
|
@ -104,7 +114,7 @@
|
|||
console.error('Error adding to cart:', error);
|
||||
|
||||
// Show error feedback
|
||||
addToCartBtn.textContent = 'Erreur - Réessayer';
|
||||
addToCartBtn.textContent = texts.error;
|
||||
addToCartBtn.classList.add('error');
|
||||
|
||||
// Re-enable button after delay
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue