/** * Main Application JavaScript * Handles global state management, API integration, and core functionality */ (function () { "use strict"; console.log("[main.js] Loading..."); // Global state management window.AppState = { cart: [], wishlist: [], products: [], settings: null, user: null, _saveCartTimeout: null, _saveWishlistTimeout: null, // Initialize state from localStorage init() { console.log("[AppState] Initializing..."); console.log("[AppState] window.AppState exists:", !!window.AppState); this.loadCart(); this.loadWishlist(); this.updateUI(); console.log( "[AppState] Initialized - Cart:", this.cart.length, "items, Wishlist:", this.wishlist.length, "items" ); }, // Cart management loadCart() { try { const saved = localStorage.getItem("cart"); this.cart = saved ? JSON.parse(saved) : []; } catch (error) { console.error("Error loading cart:", error); this.cart = []; } }, saveCart() { // Debounce saves to reduce localStorage writes if (this._saveCartTimeout) { clearTimeout(this._saveCartTimeout); } this._saveCartTimeout = setTimeout(() => { try { localStorage.setItem("cart", JSON.stringify(this.cart)); this.updateUI(); } catch (error) { console.error("Error saving cart:", error); } }, 100); }, addToCart(product, quantity = 1) { console.log( "[AppState] addToCart called:", product, "quantity:", quantity ); const existing = this.cart.find((item) => item.id === product.id); if (existing) { console.log("[AppState] Product exists in cart, updating quantity"); existing.quantity += quantity; } else { console.log("[AppState] Adding new product to cart"); this.cart.push({ ...product, quantity }); } console.log("[AppState] Cart after add:", this.cart); this.saveCart(); this.showNotification( `${product.name || product.title || "Item"} added to cart`, "success" ); }, removeFromCart(productId) { this.cart = this.cart.filter((item) => item.id !== productId); this.saveCart(); this.showNotification("Removed from cart", "info"); }, updateCartQuantity(productId, quantity) { const item = this.cart.find((item) => item.id === productId); // Dispatch custom event for cart dropdown window.dispatchEvent( new CustomEvent("cart-updated", { detail: this.cart }) ); if (item) { item.quantity = Math.max(1, quantity); this.saveCart(); } }, getCartTotal() { return this.cart.reduce( (sum, item) => sum + item.price * item.quantity, 0 ); }, getCartCount() { return this.cart.reduce((sum, item) => sum + item.quantity, 0); }, // Wishlist management loadWishlist() { try { const saved = localStorage.getItem("wishlist"); this.wishlist = saved ? JSON.parse(saved) : []; } catch (error) { console.error("Error loading wishlist:", error); this.wishlist = []; } }, saveWishlist() { try { localStorage.setItem("wishlist", JSON.stringify(this.wishlist)); this.updateUI(); } catch (error) { console.error("Error saving wishlist:", error); } }, addToWishlist(product) { if (!this.wishlist.find((item) => item.id === product.id)) { this.wishlist.push(product); this.saveWishlist(); this.showNotification( `${product.name || product.title || "Item"} added to wishlist`, "success" ); // Dispatch custom event for wishlist dropdown window.dispatchEvent( new CustomEvent("wishlist-updated", { detail: this.wishlist }) ); } else { this.showNotification("Already in wishlist", "info"); } }, removeFromWishlist(productId) { this.wishlist = this.wishlist.filter((item) => item.id !== productId); this.saveWishlist(); this.showNotification("Removed from wishlist", "info"); }, isInWishlist(productId) { return this.wishlist.some((item) => item.id === productId); }, // UI updates updateUI() { this.updateCartUI(); this.updateWishlistUI(); }, updateCartUI() { const count = this.getCartCount(); console.log("[AppState] Updating cart UI, count:", count); const badge = document.getElementById("cartCount"); if (badge) { badge.textContent = count; if (count > 0) { badge.classList.add("show"); } else { badge.classList.remove("show"); } console.log("[AppState] Cart badge updated"); } else { console.warn("[AppState] Cart badge element not found"); } // Also trigger cart dropdown update window.dispatchEvent( new CustomEvent("cart-updated", { detail: this.cart }) ); }, updateWishlistUI() { const count = this.wishlist.length; console.log("[AppState] Updating wishlist UI, count:", count); const badge = document.getElementById("wishlistCount"); if (badge) { badge.textContent = count; if (count > 0) { badge.classList.add("show"); } else { badge.classList.remove("show"); } console.log("[AppState] Wishlist badge updated"); } else { console.warn("[AppState] Wishlist badge element not found"); } // Also trigger wishlist dropdown update window.dispatchEvent( new CustomEvent("wishlist-updated", { detail: this.wishlist }) ); }, // Notifications showNotification(message, type = "info") { const notification = document.createElement("div"); notification.className = `notification notification-${type}`; notification.textContent = message; notification.setAttribute("role", "alert"); notification.setAttribute("aria-live", "polite"); document.body.appendChild(notification); setTimeout(() => notification.classList.add("show"), 10); setTimeout(() => { notification.classList.remove("show"); setTimeout(() => notification.remove(), 300); }, 3000); }, }; // API Client window.API = { baseURL: "/api", async request(endpoint, options = {}) { try { const response = await fetch(this.baseURL + endpoint, { ...options, headers: { "Content-Type": "application/json", ...options.headers, }, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { console.error("API request failed:", error); throw error; } }, // Product endpoints async getProducts(filters = {}) { const params = new URLSearchParams(filters); return this.request(`/products?${params}`); }, async getProduct(id) { return this.request(`/products/${id}`); }, async getFeaturedProducts() { return this.request("/products/featured"); }, // Settings endpoint async getSettings() { return this.request("/settings"); }, // Homepage endpoint async getHomepageSettings() { return this.request("/homepage/settings"); }, // Menu endpoint async getMenu() { return this.request("/menu"); }, // Blog endpoints async getBlogPosts() { return this.request("/blog"); }, async getBlogPost(id) { return this.request(`/blog/${id}`); }, // Portfolio endpoints async getPortfolioProjects() { return this.request("/portfolio"); }, async getPortfolioProject(id) { return this.request(`/portfolio/${id}`); }, // Pages endpoints async getPages() { return this.request("/pages"); }, async getPage(slug) { return this.request(`/pages/${slug}`); }, }; // Utility functions window.Utils = { // Format currency formatCurrency(amount) { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(amount); }, // Format date formatDate(date) { return new Intl.DateTimeFormat("en-US", { year: "numeric", month: "long", day: "numeric", }).format(new Date(date)); }, // Debounce function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, // Get URL parameter getUrlParameter(name) { const params = new URLSearchParams(window.location.search); return params.get(name); }, // Safe HTML encode escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; }, // Show loading state showLoading(element) { if (element) { element.classList.add("loading"); element.setAttribute("aria-busy", "true"); } }, hideLoading(element) { if (element) { element.classList.remove("loading"); element.setAttribute("aria-busy", "false"); } }, }; // Initialize on DOM ready console.log( "[main.js] Script loaded, document.readyState:", document.readyState ); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { console.log("[main.js] DOMContentLoaded fired"); window.AppState.init(); }); } else { console.log("[main.js] DOM already loaded, initializing immediately"); window.AppState.init(); } // Add notification styles if not exists if (!document.getElementById("notification-styles")) { const style = document.createElement("style"); style.id = "notification-styles"; style.textContent = ` .notification { position: fixed; top: 20px; right: 20px; padding: 15px 20px; background: white; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10000; opacity: 0; transform: translateX(400px); transition: all 0.3s ease; max-width: 300px; } .notification.show { opacity: 1; transform: translateX(0); } .notification-success { border-left: 4px solid #28a745; } .notification-error { border-left: 4px solid #dc3545; } .notification-info { border-left: 4px solid #17a2b8; } .notification-warning { border-left: 4px solid #ffc107; } `; document.head.appendChild(style); } })();