2026-01-01 22:24:30 -06:00
|
|
|
/**
|
|
|
|
|
* Global State Management
|
|
|
|
|
* Centralized state for cart, wishlist, and user preferences
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
class StateManager {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.state = {
|
|
|
|
|
cart: [],
|
|
|
|
|
wishlist: [],
|
|
|
|
|
user: null,
|
|
|
|
|
preferences: {},
|
|
|
|
|
};
|
|
|
|
|
this.listeners = {};
|
|
|
|
|
this.init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init() {
|
|
|
|
|
this.loadFromStorage();
|
|
|
|
|
this.setupStorageSync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadFromStorage() {
|
|
|
|
|
try {
|
|
|
|
|
this.state.cart = JSON.parse(localStorage.getItem("cart") || "[]");
|
|
|
|
|
this.state.wishlist = JSON.parse(
|
|
|
|
|
localStorage.getItem("wishlist") || "[]"
|
|
|
|
|
);
|
|
|
|
|
this.state.preferences = JSON.parse(
|
|
|
|
|
localStorage.getItem("preferences") || "{}"
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("State load error:", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
saveToStorage() {
|
|
|
|
|
try {
|
|
|
|
|
localStorage.setItem("cart", JSON.stringify(this.state.cart));
|
|
|
|
|
localStorage.setItem("wishlist", JSON.stringify(this.state.wishlist));
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
"preferences",
|
|
|
|
|
JSON.stringify(this.state.preferences)
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("State save error:", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setupStorageSync() {
|
|
|
|
|
window.addEventListener("storage", (e) => {
|
|
|
|
|
if (e.key === "cart" || e.key === "wishlist") {
|
|
|
|
|
this.loadFromStorage();
|
|
|
|
|
this.emit("stateChanged", { key: e.key });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cart methods
|
|
|
|
|
addToCart(product, quantity = 1) {
|
2026-01-04 17:52:37 -06:00
|
|
|
const existing = this._findById(this.state.cart, product.id);
|
2026-01-01 22:24:30 -06:00
|
|
|
|
|
|
|
|
if (existing) {
|
|
|
|
|
existing.quantity += quantity;
|
|
|
|
|
} else {
|
|
|
|
|
this.state.cart.push({
|
|
|
|
|
...product,
|
|
|
|
|
quantity,
|
|
|
|
|
addedAt: Date.now(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-04 17:52:37 -06:00
|
|
|
this._updateState("cart");
|
2026-01-01 22:24:30 -06:00
|
|
|
return this.state.cart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeFromCart(productId) {
|
2026-01-04 17:52:37 -06:00
|
|
|
this.state.cart = this.state.cart.filter(
|
|
|
|
|
(item) => String(item.id) !== String(productId)
|
|
|
|
|
);
|
|
|
|
|
this._updateState("cart");
|
2026-01-01 22:24:30 -06:00
|
|
|
return this.state.cart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateCartQuantity(productId, quantity) {
|
2026-01-04 17:52:37 -06:00
|
|
|
const item = this._findById(this.state.cart, productId);
|
2026-01-01 22:24:30 -06:00
|
|
|
if (item) {
|
|
|
|
|
item.quantity = Math.max(0, quantity);
|
|
|
|
|
if (item.quantity === 0) {
|
|
|
|
|
return this.removeFromCart(productId);
|
|
|
|
|
}
|
2026-01-04 17:52:37 -06:00
|
|
|
this._updateState("cart");
|
2026-01-01 22:24:30 -06:00
|
|
|
}
|
|
|
|
|
return this.state.cart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getCart() {
|
|
|
|
|
return this.state.cart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getCartTotal() {
|
2026-01-04 17:52:37 -06:00
|
|
|
return this._calculateTotal(this.state.cart);
|
2026-01-01 22:24:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getCartCount() {
|
2026-01-04 17:52:37 -06:00
|
|
|
return this._calculateCount(this.state.cart);
|
2026-01-01 22:24:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clearCart() {
|
|
|
|
|
this.state.cart = [];
|
2026-01-04 17:52:37 -06:00
|
|
|
this._updateState("cart");
|
2026-01-01 22:24:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wishlist methods
|
|
|
|
|
addToWishlist(product) {
|
|
|
|
|
const exists = this.state.wishlist.find((item) => item.id === product.id);
|
|
|
|
|
|
|
|
|
|
if (!exists) {
|
|
|
|
|
this.state.wishlist.push({
|
|
|
|
|
...product,
|
|
|
|
|
addedAt: Date.now(),
|
|
|
|
|
});
|
|
|
|
|
this.saveToStorage();
|
|
|
|
|
this.emit("wishlistUpdated", this.state.wishlist);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeFromWishlist(productId) {
|
|
|
|
|
this.state.wishlist = this.state.wishlist.filter(
|
|
|
|
|
(item) => item.id !== productId
|
|
|
|
|
);
|
|
|
|
|
this.saveToStorage();
|
|
|
|
|
this.emit("wishlistUpdated", this.state.wishlist);
|
|
|
|
|
return this.state.wishlist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getWishlist() {
|
|
|
|
|
return this.state.wishlist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isInWishlist(productId) {
|
|
|
|
|
return this.state.wishlist.some((item) => item.id === productId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Event system
|
|
|
|
|
on(event, callback) {
|
|
|
|
|
if (!this.listeners[event]) {
|
|
|
|
|
this.listeners[event] = [];
|
|
|
|
|
}
|
|
|
|
|
this.listeners[event].push(callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
off(event, callback) {
|
|
|
|
|
if (this.listeners[event]) {
|
|
|
|
|
this.listeners[event] = this.listeners[event].filter(
|
|
|
|
|
(cb) => cb !== callback
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit(event, data) {
|
|
|
|
|
if (this.listeners[event]) {
|
|
|
|
|
this.listeners[event].forEach((callback) => {
|
|
|
|
|
try {
|
|
|
|
|
callback(data);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(`Error in ${event} listener:`, e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-04 17:52:37 -06:00
|
|
|
|
|
|
|
|
// Helper methods
|
|
|
|
|
_findById(collection, id) {
|
|
|
|
|
return collection.find((item) => String(item.id) === String(id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_updateState(type) {
|
|
|
|
|
this.saveToStorage();
|
|
|
|
|
this.emit(`${type}Updated`, this.state[type]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_calculateTotal(items) {
|
|
|
|
|
return items.reduce((sum, item) => {
|
|
|
|
|
const price = parseFloat(item.price) || 0;
|
|
|
|
|
const quantity = parseInt(item.quantity) || 0;
|
|
|
|
|
return sum + price * quantity;
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_calculateCount(items) {
|
|
|
|
|
return items.reduce((sum, item) => {
|
|
|
|
|
const quantity = parseInt(item.quantity) || 0;
|
|
|
|
|
return sum + quantity;
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
2026-01-01 22:24:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create global instance
|
|
|
|
|
window.StateManager = window.StateManager || new StateManager();
|
|
|
|
|
|
|
|
|
|
// Expose helper functions for backward compatibility
|
|
|
|
|
window.addToCart = function (productId, name, price, imageurl) {
|
|
|
|
|
const product = { id: productId, name, price: parseFloat(price), imageurl };
|
|
|
|
|
window.StateManager.addToCart(product, 1);
|
|
|
|
|
if (window.showNotification) {
|
|
|
|
|
window.showNotification(`${name} added to cart!`, "success");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.addToWishlist = function (productId, name, price, imageurl) {
|
|
|
|
|
const product = { id: productId, name, price: parseFloat(price), imageurl };
|
|
|
|
|
const added = window.StateManager.addToWishlist(product);
|
|
|
|
|
if (window.showNotification) {
|
|
|
|
|
window.showNotification(
|
|
|
|
|
added ? `${name} added to wishlist!` : "Already in wishlist!",
|
|
|
|
|
added ? "success" : "info"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update badges on state changes
|
|
|
|
|
window.StateManager.on("cartUpdated", () => {
|
2026-01-14 07:16:04 -06:00
|
|
|
const badges = document.querySelectorAll(".cart-badge, #cartCount");
|
|
|
|
|
const count = window.StateManager.getCartCount();
|
|
|
|
|
badges.forEach((badge) => {
|
|
|
|
|
if (badge) {
|
|
|
|
|
badge.textContent = count;
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
badge.classList.add("show");
|
|
|
|
|
} else {
|
|
|
|
|
badge.classList.remove("show");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-01 22:24:30 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.StateManager.on("wishlistUpdated", () => {
|
2026-01-14 07:16:04 -06:00
|
|
|
const badges = document.querySelectorAll(".wishlist-badge, #wishlistCount");
|
|
|
|
|
const count = window.StateManager.getWishlist().length;
|
|
|
|
|
badges.forEach((badge) => {
|
|
|
|
|
if (badge) {
|
|
|
|
|
badge.textContent = count;
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
badge.classList.add("show");
|
|
|
|
|
} else {
|
|
|
|
|
badge.classList.remove("show");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-01-01 22:24:30 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initialize badges
|
|
|
|
|
if (document.readyState === "loading") {
|
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
|
|
|
window.StateManager.emit("cartUpdated");
|
|
|
|
|
window.StateManager.emit("wishlistUpdated");
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
window.StateManager.emit("cartUpdated");
|
|
|
|
|
window.StateManager.emit("wishlistUpdated");
|
|
|
|
|
}
|
|
|
|
|
})();
|