Phase 1: Remove obsolete files and standardize all pages
- Standardize script loading on faq, privacy, returns, shipping-info pages - Archive 14 unused JS files (cart-functions, shopping, cart.js, enhanced versions, etc.) - Archive 2 unused CSS files (responsive-enhanced, responsive-fixes) - All pages now use consistent script loading order - Eliminated shopping.js dependency (not needed after standardization)
This commit is contained in:
@@ -1,406 +0,0 @@
|
||||
/**
|
||||
* 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:
|
||||
'<p class="empty-state"><i class="bi bi-cart-x"></i><br>Your cart is empty</p>',
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.content) return;
|
||||
|
||||
try {
|
||||
if (!window.AppState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cart = window.AppState.cart;
|
||||
|
||||
if (!Array.isArray(cart)) {
|
||||
this.content.innerHTML =
|
||||
'<p class="empty-state">Error loading cart</p>';
|
||||
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 =
|
||||
'<p class="empty-state">Error loading cart</p>';
|
||||
}
|
||||
}
|
||||
|
||||
_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 '<p class="error-message">Error loading item</p>';
|
||||
}
|
||||
|
||||
// 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 `
|
||||
<div class="cart-item" data-id="${item.id}">
|
||||
<img src="${imageUrl}" alt="${title}" class="cart-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
|
||||
<div class="cart-item-details">
|
||||
<h4 class="cart-item-title">${title}</h4>
|
||||
<p class="cart-item-price">${priceFormatted}</p>
|
||||
<div class="cart-item-quantity">
|
||||
<button class="quantity-btn quantity-minus" data-id="${item.id}" aria-label="Decrease quantity">
|
||||
<i class="bi bi-dash"></i>
|
||||
</button>
|
||||
<span class="quantity-value">${quantity}</span>
|
||||
<button class="quantity-btn quantity-plus" data-id="${item.id}" aria-label="Increase quantity">
|
||||
<i class="bi bi-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p class="cart-item-subtotal">Subtotal: ${subtotalFormatted}</p>
|
||||
</div>
|
||||
<button class="cart-item-remove" data-id="${item.id}" aria-label="Remove from cart">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
} 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 =
|
||||
'<a href="/shop" class="btn-outline">Continue Shopping</a>';
|
||||
} else {
|
||||
footer.innerHTML = `
|
||||
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wishlist Component
|
||||
class Wishlist extends BaseDropdown {
|
||||
constructor() {
|
||||
super({
|
||||
toggleId: "wishlistToggle",
|
||||
panelId: "wishlistPanel",
|
||||
contentId: "wishlistContent",
|
||||
closeId: "wishlistClose",
|
||||
wrapperClass: ".wishlist-dropdown-wrapper",
|
||||
eventName: "wishlist-updated",
|
||||
emptyMessage:
|
||||
'<p class="empty-state"><i class="bi bi-heart"></i><br>Your wishlist is empty</p>',
|
||||
});
|
||||
}
|
||||
|
||||
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 '<p class="error-message">Error loading item</p>';
|
||||
}
|
||||
|
||||
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 `
|
||||
<div class="wishlist-item" data-id="${item.id}">
|
||||
<img src="${imageUrl}" alt="${title}" class="wishlist-item-image" loading="lazy" onerror="this.src='/assets/images/placeholder.svg'">
|
||||
<div class="wishlist-item-details">
|
||||
<h4 class="wishlist-item-title">${title}</h4>
|
||||
<p class="wishlist-item-price">${price}</p>
|
||||
<button class="btn-add-to-cart" data-id="${item.id}">
|
||||
<i class="bi bi-cart-plus"></i> Add to Cart
|
||||
</button>
|
||||
</div>
|
||||
<button class="wishlist-item-remove" data-id="${item.id}" aria-label="Remove from wishlist">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
// Skip if shop-system.js already initialized
|
||||
if (window.ShopSystem?.isInitialized) {
|
||||
console.log("[cart.js] Skipping initialization - shop-system.js already loaded");
|
||||
return;
|
||||
}
|
||||
console.log("[cart.js] Initializing ShoppingCart and Wishlist components");
|
||||
new ShoppingCart();
|
||||
new Wishlist();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", initializeComponents);
|
||||
} else {
|
||||
initializeComponents();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user