Files
SkyArtShop/website/public/assets/js/cart.js

320 lines
9.4 KiB
JavaScript
Raw Normal View History

2026-01-01 22:24:30 -06:00
/**
* Shopping Cart Component
* Handles cart dropdown, updates, and interactions
*/
(function () {
"use strict";
class ShoppingCart {
constructor() {
this.cartToggle = document.getElementById("cartToggle");
this.cartPanel = document.getElementById("cartPanel");
this.cartContent = document.getElementById("cartContent");
this.cartClose = document.getElementById("cartClose");
this.isOpen = false;
this.init();
}
init() {
this.setupEventListeners();
this.render();
}
setupEventListeners() {
if (this.cartToggle) {
this.cartToggle.addEventListener("click", () => this.toggle());
}
if (this.cartClose) {
this.cartClose.addEventListener("click", () => this.close());
}
// Close when clicking outside
document.addEventListener("click", (e) => {
if (this.isOpen && !e.target.closest(".cart-dropdown-wrapper")) {
this.close();
}
});
// Listen for cart updates
window.addEventListener("cart-updated", () => this.render());
}
toggle() {
this.isOpen ? this.close() : this.open();
}
open() {
if (this.cartPanel) {
this.cartPanel.classList.add("active");
this.cartPanel.setAttribute("aria-hidden", "false");
this.isOpen = true;
this.render();
}
}
close() {
if (this.cartPanel) {
this.cartPanel.classList.remove("active");
this.cartPanel.setAttribute("aria-hidden", "true");
this.isOpen = false;
}
}
render() {
if (!this.cartContent) return;
const cart = window.AppState.cart;
if (cart.length === 0) {
this.cartContent.innerHTML =
'<p class="empty-state">Your cart is empty</p>';
this.updateFooter(null);
return;
}
const html = cart.map((item) => this.renderCartItem(item)).join("");
this.cartContent.innerHTML = html;
// Add event listeners to cart items
this.setupCartItemListeners();
// Update footer with total
this.updateFooter(window.AppState.getCartTotal());
}
renderCartItem(item) {
const 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(item.price || 0);
const subtotal = window.Utils.formatCurrency(
(item.price || 0) * item.quantity
);
return `
<div class="cart-item" data-id="${item.id}">
<img src="${imageUrl}" alt="${title}" class="cart-item-image" loading="lazy">
<div class="cart-item-details">
<h4 class="cart-item-title">${title}</h4>
<p class="cart-item-price">${price}</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">${item.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}</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>
`;
}
setupCartItemListeners() {
// Remove buttons
this.cartContent.querySelectorAll(".cart-item-remove").forEach((btn) => {
btn.addEventListener("click", (e) => {
const id = parseInt(e.currentTarget.dataset.id);
window.AppState.removeFromCart(id);
this.render();
});
});
// Quantity buttons
this.cartContent.querySelectorAll(".quantity-minus").forEach((btn) => {
btn.addEventListener("click", (e) => {
const id = parseInt(e.currentTarget.dataset.id);
const item = window.AppState.cart.find((item) => item.id === id);
if (item && item.quantity > 1) {
window.AppState.updateCartQuantity(id, item.quantity - 1);
this.render();
}
});
});
this.cartContent.querySelectorAll(".quantity-plus").forEach((btn) => {
btn.addEventListener("click", (e) => {
const id = parseInt(e.currentTarget.dataset.id);
const item = window.AppState.cart.find((item) => item.id === id);
if (item) {
window.AppState.updateCartQuantity(id, item.quantity + 1);
this.render();
}
});
});
}
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 = `
<div class="cart-total">
<span>Total:</span>
<strong>${window.Utils.formatCurrency(total)}</strong>
</div>
<a href="/shop" class="btn-text">Continue Shopping</a>
<button class="btn-primary-full" onclick="alert('Checkout coming soon!')">
Proceed to Checkout
</button>
`;
}
}
}
// Wishlist Component
class Wishlist {
constructor() {
this.wishlistToggle = document.getElementById("wishlistToggle");
this.wishlistPanel = document.getElementById("wishlistPanel");
this.wishlistContent = document.getElementById("wishlistContent");
this.wishlistClose = document.getElementById("wishlistClose");
this.isOpen = false;
this.init();
}
init() {
this.setupEventListeners();
this.render();
}
setupEventListeners() {
if (this.wishlistToggle) {
this.wishlistToggle.addEventListener("click", () => this.toggle());
}
if (this.wishlistClose) {
this.wishlistClose.addEventListener("click", () => this.close());
}
// Close when clicking outside
document.addEventListener("click", (e) => {
if (this.isOpen && !e.target.closest(".wishlist-dropdown-wrapper")) {
this.close();
}
});
// Listen for wishlist updates
window.addEventListener("wishlist-updated", () => this.render());
}
toggle() {
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.wishlistContent) return;
const wishlist = window.AppState.wishlist;
if (wishlist.length === 0) {
this.wishlistContent.innerHTML =
'<p class="empty-state">Your wishlist is empty</p>';
return;
}
const html = wishlist
.map((item) => this.renderWishlistItem(item))
.join("");
this.wishlistContent.innerHTML = html;
// Add event listeners
this.setupWishlistItemListeners();
}
renderWishlistItem(item) {
const 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(item.price || 0);
return `
<div class="wishlist-item" data-id="${item.id}">
<img src="${imageUrl}" alt="${title}" class="wishlist-item-image" loading="lazy">
<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}">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() {
// Remove buttons
this.wishlistContent
.querySelectorAll(".wishlist-item-remove")
.forEach((btn) => {
btn.addEventListener("click", (e) => {
const id = parseInt(e.currentTarget.dataset.id);
window.AppState.removeFromWishlist(id);
this.render();
});
});
// Add to cart buttons
this.wishlistContent
.querySelectorAll(".btn-add-to-cart")
.forEach((btn) => {
btn.addEventListener("click", (e) => {
const id = parseInt(e.currentTarget.dataset.id);
const item = window.AppState.wishlist.find(
(item) => item.id === id
);
if (item) {
window.AppState.addToCart(item);
}
});
});
}
}
// Initialize when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
new ShoppingCart();
new Wishlist();
});
} else {
new ShoppingCart();
new Wishlist();
}
})();