351 lines
8.4 KiB
JavaScript
351 lines
8.4 KiB
JavaScript
/**
|
|
* 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);
|
|
}
|
|
})();
|