/** * Shopping Cart Component * Handles cart dropdown, updates, and interactions */ (function () { "use strict"; // Base Dropdown Component class BaseDropdown { constructor(config) { this.toggleBtn = document.getElementById(config.toggleId); this.panel = document.getElementById(config.panelId); this.content = document.getElementById(config.contentId); this.closeBtn = document.getElementById(config.closeId); this.wrapperClass = config.wrapperClass; this.eventName = config.eventName; this.emptyMessage = config.emptyMessage; this.isOpen = false; this.init(); } init() { this.setupEventListeners(); this.render(); } setupEventListeners() { if (this.toggleBtn) { this.toggleBtn.addEventListener("click", () => this.toggle()); } if (this.closeBtn) { this.closeBtn.addEventListener("click", () => this.close()); } document.addEventListener("click", (e) => { if (this.isOpen && !e.target.closest(this.wrapperClass)) { this.close(); } }); window.addEventListener(this.eventName, () => { console.log(`[${this.constructor.name}] ${this.eventName} received`); this.render(); }); } toggle() { this.isOpen ? this.close() : this.open(); } open() { if (this.panel) { this.panel.classList.add("active"); this.panel.setAttribute("aria-hidden", "false"); this.isOpen = true; this.render(); } } close() { if (this.panel) { this.panel.classList.remove("active"); this.panel.setAttribute("aria-hidden", "true"); this.isOpen = false; } } renderEmpty() { if (this.content) { this.content.innerHTML = this.emptyMessage; } } } class ShoppingCart extends BaseDropdown { constructor() { super({ toggleId: "cartToggle", panelId: "cartPanel", contentId: "cartContent", closeId: "cartClose", wrapperClass: ".cart-dropdown-wrapper", eventName: "cart-updated", emptyMessage: '


Your cart is empty

' }); } render() { if (!this.content) return; try { if (!window.AppState) { return; } const cart = window.AppState.cart; if (!Array.isArray(cart)) { this.content.innerHTML = '

Error loading cart

'; return; } if (cart.length === 0) { this.renderEmpty(); this.updateFooter(null); return; } const validItems = this._filterValidItems(cart); if (validItems.length === 0) { this.renderEmpty(); this.updateFooter(null); return; } this.content.innerHTML = validItems.map(item => this.renderCartItem(item)).join(""); this.setupCartItemListeners(); const total = this._calculateTotal(validItems); this.updateFooter(total); } catch (error) { this.content.innerHTML = '

Error loading cart

'; } } _filterValidItems(items) { return items.filter(item => item && item.id && typeof item.price !== 'undefined'); } _calculateTotal(items) { if (window.AppState.getCartTotal) { return window.AppState.getCartTotal(); } return items.reduce((sum, item) => { const price = parseFloat(item.price) || 0; const quantity = parseInt(item.quantity) || 0; return sum + (price * quantity); }, 0); } renderCartItem(item) { try { // Validate item and Utils availability if (!item || !item.id) { return ''; } if (!window.Utils) { return '

Error loading item

'; } // Sanitize and validate item data with defensive checks const imageUrl = item.imageurl || item.imageUrl || item.image_url || "/assets/images/placeholder.svg"; const title = window.Utils.escapeHtml( item.title || item.name || "Product" ); const price = parseFloat(item.price) || 0; const quantity = Math.max(1, parseInt(item.quantity) || 1); const subtotal = price * quantity; const priceFormatted = window.Utils.formatCurrency(price); const subtotalFormatted = window.Utils.formatCurrency(subtotal); return `
${title}

${title}

${priceFormatted}

${quantity}

Subtotal: ${subtotalFormatted}

`; } catch (error) { return ''; } } setupCartItemListeners() { try { this._setupRemoveButtons(); this._setupQuantityButtons(); } catch (error) { console.error("[ShoppingCart] Error setting up listeners:", error); } } _setupRemoveButtons() { this.content.querySelectorAll(".cart-item-remove").forEach((btn) => { btn.addEventListener("click", (e) => { e.stopPropagation(); this._handleAction(e, () => { const id = e.currentTarget.dataset.id; if (id && window.AppState?.removeFromCart) { window.AppState.removeFromCart(id); this.render(); } }); }); }); } _setupQuantityButtons() { this._setupQuantityButton(".quantity-minus", -1); this._setupQuantityButton(".quantity-plus", 1); } _setupQuantityButton(selector, delta) { this.content.querySelectorAll(selector).forEach((btn) => { btn.addEventListener("click", (e) => { e.stopPropagation(); this._handleAction(e, () => { const id = e.currentTarget.dataset.id; if (!window.AppState?.cart) return; const item = window.AppState.cart.find( (item) => String(item.id) === String(id) ); if (!item || !window.AppState.updateCartQuantity) return; const newQuantity = delta > 0 ? Math.min(item.quantity + delta, 999) : Math.max(item.quantity + delta, 1); if (delta < 0 && item.quantity <= 1) return; window.AppState.updateCartQuantity(id, newQuantity); this.render(); }); }); }); } _handleAction(event, callback) { try { callback(); } catch (error) { console.error("[ShoppingCart] Action error:", error); } } updateFooter(total) { const footer = this.cartPanel?.querySelector(".dropdown-foot"); if (!footer) return; if (total === null) { footer.innerHTML = 'Continue Shopping'; } else { footer.innerHTML = `
Total: ${window.Utils.formatCurrency(total)}
Continue Shopping `; } } } // Wishlist Component class Wishlist extends BaseDropdown { constructor() { super({ toggleId: "wishlistToggle", panelId: "wishlistPanel", contentId: "wishlistContent", closeId: "wishlistClose", wrapperClass: ".wishlist-dropdown-wrapper", eventName: "wishlist-updated", emptyMessage: '


Your wishlist is empty

' }); } this.isOpen ? this.close() : this.open(); } open() { if (this.wishlistPanel) { this.wishlistPanel.classList.add("active"); this.wishlistPanel.setAttribute("aria-hidden", "false"); this.isOpen = true; this.render(); } } close() { if (this.wishlistPanel) { this.wishlistPanel.classList.remove("active"); this.wishlistPanel.setAttribute("aria-hidden", "true"); this.isOpen = false; } } render() { if (!this.content) return; if (!window.AppState) { console.warn("[Wishlist] AppState not available yet"); return; } const wishlist = window.AppState.wishlist; if (wishlist.length === 0) { this.renderEmpty(); return; } this.content.innerHTML = wishlist .map((item) => this.renderWishlistItem(item)) .join(""); this.setupWishlistItemListeners(); } renderWishlistItem(item) { if (!window.Utils) { console.error("[Wishlist] Utils not available"); return '

Error loading item

'; } const imageUrl = item.imageurl || item.imageUrl || item.image_url || "/assets/images/placeholder.jpg"; const title = window.Utils.escapeHtml( item.title || item.name || "Product" ); const price = window.Utils.formatCurrency(parseFloat(item.price) || 0); return `
${title}

${title}

${price}

`; } setupWishlistItemListeners() { this._setupRemoveButtons(); this._setupAddToCartButtons(); } _setupRemoveButtons() { this.content.querySelectorAll(".wishlist-item-remove").forEach((btn) => { btn.addEventListener("click", (e) => { e.stopPropagation(); const id = e.currentTarget.dataset.id; if (window.AppState?.removeFromWishlist) { window.AppState.removeFromWishlist(id); this.render(); } }); }); } _setupAddToCartButtons() { this.content.querySelectorAll(".btn-add-to-cart").forEach((btn) => { btn.addEventListener("click", (e) => { e.stopPropagation(); const id = e.currentTarget.dataset.id; const item = window.AppState?.wishlist.find( (item) => String(item.id) === String(id) ); if (item && window.AppState?.addToCart) { window.AppState.addToCart(item); } }); }); } } // Initialize when DOM is ready const initializeComponents = () => { console.log("[cart.js] Initializing ShoppingCart and Wishlist components"); new ShoppingCart(); new Wishlist(); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeComponents); } else { initializeComponents(); } })();