Files

351 lines
8.4 KiB
JavaScript
Raw Permalink Normal View History

2026-01-01 22:24:30 -06:00
/**
* Main Application JavaScript
* Handles global state management, API integration, and core functionality
*/
(function () {
"use strict";
// Global state management
window.AppState = {
cart: [],
wishlist: [],
products: [],
settings: null,
user: null,
// Initialize state from localStorage
init() {
this.loadCart();
this.loadWishlist();
this.updateUI();
},
// 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() {
try {
localStorage.setItem("cart", JSON.stringify(this.cart));
this.updateUI();
} catch (error) {
console.error("Error saving cart:", error);
}
},
addToCart(product, quantity = 1) {
const existing = this.cart.find((item) => item.id === product.id);
if (existing) {
existing.quantity += quantity;
} else {
this.cart.push({ ...product, quantity });
}
this.saveCart();
this.showNotification("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);
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("Added to wishlist", "success");
}
},
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();
const badge = document.getElementById("cartCount");
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? "flex" : "none";
}
},
updateWishlistUI() {
const count = this.wishlist.length;
const badge = document.getElementById("wishlistCount");
if (badge) {
badge.textContent = count;
badge.style.display = count > 0 ? "flex" : "none";
}
},
// 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
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
window.AppState.init();
});
} else {
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);
}
})();