const API_BASE = '/api'; function getOrCreateSessionId() { let sessionId = localStorage.getItem('cart_session_id'); if (!sessionId) { sessionId = 'sess_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36); localStorage.setItem('cart_session_id', sessionId); } return sessionId; } function getSelectedRegion() { const stored = localStorage.getItem('selectedRegion'); return stored ? JSON.parse(stored) : null; } async function getExchangeRate(fromCurrency, toCurrency) { if (fromCurrency === toCurrency) { return 1.0; } try { const response = await fetch(`${API_BASE}/currency/rate/${fromCurrency}/${toCurrency}`); const data = await response.json(); if (data.success && data.rate) { return data.rate; } } catch (err) { console.error('Failed to get exchange rate:', err); } return 1.0; // Fallback } // Cart management (same as app.js) const cart = { get() { try { return JSON.parse(localStorage.getItem('cart') || '[]'); } catch { return []; } }, set(items) { localStorage.setItem('cart', JSON.stringify(items)); updateCartCount(); }, update(productId, quantity) { const items = this.get(); // Convert both to strings for comparison to handle type mismatches const item = items.find(i => String(i.productId) === String(productId)); if (item) { if (quantity <= 0) { this.remove(productId); } else { item.quantity = quantity; this.set(items); } } }, remove(productId) { const items = this.get().filter(item => String(item.productId) !== String(productId)); this.set(items); }, clear() { this.set([]); } }; function updateCartCount() { const count = cart.get().reduce((sum, item) => sum + item.quantity, 0); const cartBadge = document.getElementById('cartCount'); if (cartBadge) { cartBadge.textContent = count; cartBadge.style.display = count > 0 ? 'flex' : 'none'; } } async function renderCart() { const content = document.getElementById('cartContent'); const items = cart.get(); if (items.length === 0) { content.innerHTML = `
🛒

Your cart is empty

Add some products to get started

Continue Shopping
`; return; } // Get user's selected region for currency const selectedRegion = getSelectedRegion(); const targetCurrency = selectedRegion ? selectedRegion.currency : 'USD'; // Get exchange rate once for all products const exchangeRate = await getExchangeRate('USD', targetCurrency); // Fetch product details and calculate totals (all in cents) // Use current prices from API, not stored prices let subtotal = 0; const itemsHtml = await Promise.all(items.map(async (item) => { try { const headers = {}; if (selectedRegion) { headers['X-User-Region'] = selectedRegion.code; } const response = await fetch(`${API_BASE}/products/${item.productId}`, { headers }); const data = await response.json(); if (data.success) { const product = data.product; // Convert price to target currency const currentPriceUSD = product.price; // In cents const currentPrice = Math.round(currentPriceUSD * exchangeRate); // Converted to target currency const itemTotal = currentPrice * item.quantity; subtotal += itemTotal; const image = product.image || ''; const imageElement = image && image.trim() && (image.startsWith('http') || image.startsWith('/')) ? `${product.name}` : ''; return `
${imageElement || '
No Image
'}
${escapeHtml(product.name)}
${targetCurrency} ${(currentPrice / 100).toFixed(2)} each
${targetCurrency} ${(itemTotal / 100).toFixed(2)}
`; } } catch (error) { console.error('Error loading product:', error); return ''; } })); // Tax cannot be calculated without country - show as uncalculated const shipping = 0; // Can be calculated based on shipping info (in cents) const tax = 0; // Tax cannot be calculated without country const total = subtotal + shipping; // Tax will be calculated at checkout content.innerHTML = `
${itemsHtml.join('')}

Order Summary

Subtotal ${targetCurrency} ${(subtotal / 100).toFixed(2)}
Shipping ${targetCurrency} ${(shipping / 100).toFixed(2)}
Tax Uncalculated
Total ${targetCurrency} ${(total / 100).toFixed(2)}
Proceed to Checkout Continue Shopping
`; } // Debounce timer for quantity updates let updateQuantityTimer = null; async function updateQuantity(productId, quantity) { const qty = parseInt(quantity) || 1; // Update localStorage immediately cart.update(productId, qty); // Re-render immediately to show the updated quantity and price await renderCart(); // Clear existing timer for API call if (updateQuantityTimer) { clearTimeout(updateQuantityTimer); } // Debounce the API call for stock reservation updateQuantityTimer = setTimeout(async () => { try { const sessionId = getOrCreateSessionId(); // Try to reserve stock const response = await fetchWithCsrf(`${API_BASE}/cart/reserve`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-session-id': sessionId }, body: JSON.stringify({ productId, quantity: qty }) }); const data = await response.json(); if (!data.success) { showNotification(data.error || 'Failed to update quantity', 'error'); // Reload cart to reset to available stock await renderCart(); } } catch (error) { showNotification(`Error: ${error.message}`, 'error'); } }, 500); // Wait 500ms after last change before making API call } async function removeItem(productId) { const confirmed = await showConfirm('Remove this item from cart?', 'Remove Item'); if (confirmed) { try { const sessionId = getOrCreateSessionId(); await fetchWithCsrf(`${API_BASE}/cart/release`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-session-id': sessionId }, body: JSON.stringify({ productId }) }); } catch (error) { console.error('Error releasing stock:', error); } cart.remove(productId); await renderCart(); showNotification('Item removed from cart', 'success', 3000); } } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize document.addEventListener('DOMContentLoaded', () => { updateCartCount(); renderCart(); refreshReservations(); }); async function refreshReservations() { const items = cart.get(); if (items.length === 0) return; const sessionId = getOrCreateSessionId(); // Refresh reservations for all items in cart // We don't block UI for this, just try to secure the stock items.forEach(item => { fetchWithCsrf(`${API_BASE}/cart/reserve`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-session-id': sessionId }, body: JSON.stringify({ productId: item.productId, quantity: item.quantity }) }).catch(console.error); }); } window.updateQuantity = updateQuantity; window.removeItem = removeItem;