541 lines
17 KiB
JavaScript
541 lines
17 KiB
JavaScript
/**
|
|
* Shared HTML Components
|
|
* Eliminates duplication across HTML files
|
|
*/
|
|
|
|
// Component templates
|
|
const Components = {
|
|
/**
|
|
* Render cart drawer HTML
|
|
* @returns {string} Cart drawer HTML
|
|
*/
|
|
cartDrawer: () => `
|
|
<div id="cartDrawer" class="cart-drawer">
|
|
<div class="cart-drawer-header">
|
|
<h3>Shopping Cart</h3>
|
|
<button id="closeCart" class="close-cart" aria-label="Close cart">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div id="cartItems" class="cart-items"></div>
|
|
<div class="cart-footer">
|
|
<div class="cart-total">
|
|
<span>Total:</span>
|
|
<span id="cartTotal">$0.00</span>
|
|
</div>
|
|
<button id="checkoutBtn" class="btn btn-primary checkout-btn">
|
|
Proceed to Checkout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`,
|
|
|
|
/**
|
|
* Render wishlist drawer HTML
|
|
* @returns {string} Wishlist drawer HTML
|
|
*/
|
|
wishlistDrawer: () => `
|
|
<div id="wishlistDrawer" class="wishlist-drawer">
|
|
<div class="wishlist-drawer-header">
|
|
<h3>Wishlist</h3>
|
|
<button id="closeWishlist" class="close-wishlist" aria-label="Close wishlist">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div id="wishlistItems" class="wishlist-items"></div>
|
|
</div>
|
|
`,
|
|
|
|
/**
|
|
* Render navbar HTML
|
|
* @param {Object} options - Navbar options
|
|
* @param {string} options.activePage - Current active page
|
|
* @returns {string} Navbar HTML
|
|
*/
|
|
navbar: ({ activePage = "" } = {}) => {
|
|
const isActive = (page) => (page === activePage ? "active" : "");
|
|
return `
|
|
<nav class="navbar">
|
|
<div class="nav-container">
|
|
<a href="/" class="nav-logo">
|
|
<img src="/assets/images/logo.png" alt="SkyArtShop Logo" class="logo-img" />
|
|
<span>SkyArtShop</span>
|
|
</a>
|
|
|
|
<button class="mobile-menu-toggle" id="mobileMenuToggle" aria-label="Toggle menu">
|
|
<span></span>
|
|
<span></span>
|
|
<span></span>
|
|
</button>
|
|
|
|
<div class="nav-menu" id="navMenu">
|
|
<a href="/" class="nav-link ${isActive("home")}">Home</a>
|
|
<a href="/shop" class="nav-link ${isActive("shop")}">Shop</a>
|
|
<a href="/portfolio" class="nav-link ${isActive(
|
|
"portfolio"
|
|
)}">Portfolio</a>
|
|
<a href="/blog" class="nav-link ${isActive("blog")}">Blog</a>
|
|
<a href="/about" class="nav-link ${isActive("about")}">About</a>
|
|
<a href="/contact" class="nav-link ${isActive(
|
|
"contact"
|
|
)}">Contact</a>
|
|
|
|
<div class="nav-actions">
|
|
<button id="wishlistBtn" class="icon-btn" aria-label="Wishlist">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
|
</svg>
|
|
<span id="wishlistBadge" class="badge">0</span>
|
|
</button>
|
|
|
|
<button id="cartBtn" class="icon-btn" aria-label="Shopping cart">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="9" cy="21" r="1"></circle>
|
|
<circle cx="20" cy="21" r="1"></circle>
|
|
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
|
|
</svg>
|
|
<span id="cartBadge" class="badge">0</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
`;
|
|
},
|
|
|
|
/**
|
|
* Render footer HTML
|
|
* @returns {string} Footer HTML
|
|
*/
|
|
footer: () => `
|
|
<footer class="footer">
|
|
<div class="footer-container">
|
|
<div class="footer-section">
|
|
<h3>About SkyArtShop</h3>
|
|
<p>Your premier destination for unique art pieces and custom designs.</p>
|
|
</div>
|
|
|
|
<div class="footer-section">
|
|
<h3>Quick Links</h3>
|
|
<ul>
|
|
<li><a href="/">Home</a></li>
|
|
<li><a href="/shop">Shop</a></li>
|
|
<li><a href="/portfolio">Portfolio</a></li>
|
|
<li><a href="/blog">Blog</a></li>
|
|
<li><a href="/about">About</a></li>
|
|
<li><a href="/contact">Contact</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="footer-section">
|
|
<h3>Customer Service</h3>
|
|
<ul>
|
|
<li><a href="/faq">FAQ</a></li>
|
|
<li><a href="/shipping-info">Shipping Info</a></li>
|
|
<li><a href="/returns">Returns</a></li>
|
|
<li><a href="/privacy">Privacy Policy</a></li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="footer-section">
|
|
<h3>Connect With Us</h3>
|
|
<div class="social-links">
|
|
<a href="#" aria-label="Facebook">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>
|
|
</svg>
|
|
</a>
|
|
<a href="#" aria-label="Instagram">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
|
|
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
|
|
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
|
|
</svg>
|
|
</a>
|
|
<a href="#" aria-label="Twitter">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="footer-bottom">
|
|
<p>© ${new Date().getFullYear()} SkyArtShop. All rights reserved.</p>
|
|
</div>
|
|
</footer>
|
|
`,
|
|
|
|
/**
|
|
* Render notification container
|
|
* @returns {string} Notification container HTML
|
|
*/
|
|
notificationContainer: () => `
|
|
<div id="notificationContainer" class="notification-container"></div>
|
|
`,
|
|
};
|
|
|
|
/**
|
|
* Initialize components in the DOM
|
|
* @param {Object} options - Component options
|
|
* @param {boolean} options.navbar - Include navbar
|
|
* @param {boolean} options.footer - Include footer
|
|
* @param {boolean} options.cart - Include cart drawer
|
|
* @param {boolean} options.wishlist - Include wishlist drawer
|
|
* @param {boolean} options.notifications - Include notifications
|
|
* @param {string} options.activePage - Active page for navbar
|
|
*/
|
|
function initializeComponents({
|
|
navbar = true,
|
|
footer = true,
|
|
cart = true,
|
|
wishlist = true,
|
|
notifications = true,
|
|
activePage = "",
|
|
} = {}) {
|
|
// Inject components into DOM
|
|
if (navbar) {
|
|
const navPlaceholder = document.getElementById("navbar-placeholder");
|
|
if (navPlaceholder) {
|
|
navPlaceholder.innerHTML = Components.navbar({ activePage });
|
|
}
|
|
}
|
|
|
|
if (footer) {
|
|
const footerPlaceholder = document.getElementById("footer-placeholder");
|
|
if (footerPlaceholder) {
|
|
footerPlaceholder.innerHTML = Components.footer();
|
|
}
|
|
}
|
|
|
|
if (cart) {
|
|
const cartPlaceholder = document.getElementById("cart-drawer-placeholder");
|
|
if (cartPlaceholder) {
|
|
cartPlaceholder.innerHTML = Components.cartDrawer();
|
|
}
|
|
}
|
|
|
|
if (wishlist) {
|
|
const wishlistPlaceholder = document.getElementById(
|
|
"wishlist-drawer-placeholder"
|
|
);
|
|
if (wishlistPlaceholder) {
|
|
wishlistPlaceholder.innerHTML = Components.wishlistDrawer();
|
|
}
|
|
}
|
|
|
|
if (notifications) {
|
|
const notificationPlaceholder = document.getElementById(
|
|
"notification-placeholder"
|
|
);
|
|
if (notificationPlaceholder) {
|
|
notificationPlaceholder.innerHTML = Components.notificationContainer();
|
|
}
|
|
}
|
|
|
|
// Initialize mobile menu toggle
|
|
initMobileMenu();
|
|
|
|
// Initialize cart and wishlist interactions
|
|
if (cart) initCartDrawer();
|
|
if (wishlist) initWishlistDrawer();
|
|
}
|
|
|
|
/**
|
|
* Initialize mobile menu functionality
|
|
*/
|
|
function initMobileMenu() {
|
|
const menuToggle = document.getElementById("mobileMenuToggle");
|
|
const navMenu = document.getElementById("navMenu");
|
|
|
|
if (menuToggle && navMenu) {
|
|
menuToggle.addEventListener("click", () => {
|
|
navMenu.classList.toggle("active");
|
|
menuToggle.classList.toggle("active");
|
|
});
|
|
|
|
// Close menu when clicking outside
|
|
document.addEventListener("click", (e) => {
|
|
if (
|
|
!menuToggle.contains(e.target) &&
|
|
!navMenu.contains(e.target) &&
|
|
navMenu.classList.contains("active")
|
|
) {
|
|
navMenu.classList.remove("active");
|
|
menuToggle.classList.remove("active");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize cart drawer functionality
|
|
*/
|
|
function initCartDrawer() {
|
|
const cartBtn = document.getElementById("cartBtn");
|
|
const cartDrawer = document.getElementById("cartDrawer");
|
|
const closeCart = document.getElementById("closeCart");
|
|
|
|
if (cartBtn && cartDrawer) {
|
|
cartBtn.addEventListener("click", () => {
|
|
cartDrawer.classList.add("active");
|
|
document.body.style.overflow = "hidden";
|
|
updateCartUI();
|
|
});
|
|
|
|
if (closeCart) {
|
|
closeCart.addEventListener("click", () => {
|
|
cartDrawer.classList.remove("active");
|
|
document.body.style.overflow = "";
|
|
});
|
|
}
|
|
|
|
// Close on overlay click
|
|
cartDrawer.addEventListener("click", (e) => {
|
|
if (e.target === cartDrawer) {
|
|
cartDrawer.classList.remove("active");
|
|
document.body.style.overflow = "";
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize checkout button
|
|
const checkoutBtn = document.getElementById("checkoutBtn");
|
|
if (checkoutBtn) {
|
|
checkoutBtn.addEventListener("click", () => {
|
|
window.location.href = "/checkout";
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize wishlist drawer functionality
|
|
*/
|
|
function initWishlistDrawer() {
|
|
const wishlistBtn = document.getElementById("wishlistBtn");
|
|
const wishlistDrawer = document.getElementById("wishlistDrawer");
|
|
const closeWishlist = document.getElementById("closeWishlist");
|
|
|
|
if (wishlistBtn && wishlistDrawer) {
|
|
wishlistBtn.addEventListener("click", () => {
|
|
wishlistDrawer.classList.add("active");
|
|
document.body.style.overflow = "hidden";
|
|
updateWishlistUI();
|
|
});
|
|
|
|
if (closeWishlist) {
|
|
closeWishlist.addEventListener("click", () => {
|
|
wishlistDrawer.classList.remove("active");
|
|
document.body.style.overflow = "";
|
|
});
|
|
}
|
|
|
|
// Close on overlay click
|
|
wishlistDrawer.addEventListener("click", (e) => {
|
|
if (e.target === wishlistDrawer) {
|
|
wishlistDrawer.classList.remove("active");
|
|
document.body.style.overflow = "";
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update cart UI with current items
|
|
*/
|
|
function updateCartUI() {
|
|
if (typeof CartUtils === "undefined") return;
|
|
|
|
const cart = CartUtils.getCart();
|
|
const cartItems = document.getElementById("cartItems");
|
|
const cartTotal = document.getElementById("cartTotal");
|
|
const cartBadge = document.getElementById("cartBadge");
|
|
|
|
if (cartBadge) {
|
|
cartBadge.textContent = cart.length;
|
|
cartBadge.style.display = cart.length > 0 ? "flex" : "none";
|
|
}
|
|
|
|
if (!cartItems) return;
|
|
|
|
if (cart.length === 0) {
|
|
cartItems.innerHTML = '<p class="empty-cart">Your cart is empty</p>';
|
|
if (cartTotal) cartTotal.textContent = "$0.00";
|
|
return;
|
|
}
|
|
|
|
cartItems.innerHTML = cart
|
|
.map(
|
|
(item) => `
|
|
<div class="cart-item" data-id="${item.id}">
|
|
<img src="${item.image}" alt="${item.name}" class="cart-item-image" />
|
|
<div class="cart-item-details">
|
|
<h4>${item.name}</h4>
|
|
<p class="cart-item-price">${formatPrice(item.price)}</p>
|
|
<div class="quantity-controls">
|
|
<button class="qty-btn" onclick="decreaseQuantity('${
|
|
item.id
|
|
}')">-</button>
|
|
<span class="quantity">${item.quantity}</span>
|
|
<button class="qty-btn" onclick="increaseQuantity('${
|
|
item.id
|
|
}')">+</button>
|
|
</div>
|
|
</div>
|
|
<button class="remove-item" onclick="removeFromCart('${
|
|
item.id
|
|
}')" aria-label="Remove item">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
|
|
if (cartTotal) {
|
|
cartTotal.textContent = formatPrice(CartUtils.getCartTotal());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update wishlist UI with current items
|
|
*/
|
|
function updateWishlistUI() {
|
|
if (typeof WishlistUtils === "undefined") return;
|
|
|
|
const wishlist = WishlistUtils.getWishlist();
|
|
const wishlistItems = document.getElementById("wishlistItems");
|
|
const wishlistBadge = document.getElementById("wishlistBadge");
|
|
|
|
if (wishlistBadge) {
|
|
wishlistBadge.textContent = wishlist.length;
|
|
wishlistBadge.style.display = wishlist.length > 0 ? "flex" : "none";
|
|
}
|
|
|
|
if (!wishlistItems) return;
|
|
|
|
if (wishlist.length === 0) {
|
|
wishlistItems.innerHTML =
|
|
'<p class="empty-wishlist">Your wishlist is empty</p>';
|
|
return;
|
|
}
|
|
|
|
wishlistItems.innerHTML = wishlist
|
|
.map(
|
|
(item) => `
|
|
<div class="wishlist-item" data-id="${item.id}">
|
|
<img src="${item.image}" alt="${
|
|
item.name
|
|
}" class="wishlist-item-image" />
|
|
<div class="wishlist-item-details">
|
|
<h4>${item.name}</h4>
|
|
<p class="wishlist-item-price">${formatPrice(item.price)}</p>
|
|
<button class="btn-sm btn-primary" onclick="moveToCart('${item.id}')">
|
|
Add to Cart
|
|
</button>
|
|
</div>
|
|
<button class="remove-item" onclick="removeFromWishlist('${
|
|
item.id
|
|
}')" aria-label="Remove from wishlist">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
// Global functions for inline event handlers
|
|
window.increaseQuantity = (productId) => {
|
|
if (typeof CartUtils !== "undefined") {
|
|
const cart = CartUtils.getCart();
|
|
const item = cart.find((item) => item.id === productId);
|
|
if (item) {
|
|
CartUtils.updateQuantity(productId, item.quantity + 1);
|
|
updateCartUI();
|
|
}
|
|
}
|
|
};
|
|
|
|
window.decreaseQuantity = (productId) => {
|
|
if (typeof CartUtils !== "undefined") {
|
|
const cart = CartUtils.getCart();
|
|
const item = cart.find((item) => item.id === productId);
|
|
if (item && item.quantity > 1) {
|
|
CartUtils.updateQuantity(productId, item.quantity - 1);
|
|
updateCartUI();
|
|
}
|
|
}
|
|
};
|
|
|
|
window.removeFromCart = (productId) => {
|
|
if (typeof CartUtils !== "undefined") {
|
|
CartUtils.removeFromCart(productId);
|
|
updateCartUI();
|
|
showNotification("Item removed from cart", "info");
|
|
}
|
|
};
|
|
|
|
window.removeFromWishlist = (productId) => {
|
|
if (typeof WishlistUtils !== "undefined") {
|
|
WishlistUtils.removeFromWishlist(productId);
|
|
updateWishlistUI();
|
|
showNotification("Item removed from wishlist", "info");
|
|
}
|
|
};
|
|
|
|
window.moveToCart = (productId) => {
|
|
if (
|
|
typeof WishlistUtils !== "undefined" &&
|
|
typeof CartUtils !== "undefined"
|
|
) {
|
|
const wishlist = WishlistUtils.getWishlist();
|
|
const item = wishlist.find((item) => item.id === productId);
|
|
if (item) {
|
|
CartUtils.addToCart(item);
|
|
WishlistUtils.removeFromWishlist(productId);
|
|
updateWishlistUI();
|
|
updateCartUI();
|
|
showNotification("Item moved to cart", "success");
|
|
}
|
|
}
|
|
};
|
|
|
|
// Initialize on DOM load
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
// Auto-initialize if data attribute is set
|
|
const autoInit = document.body.dataset.autoInitComponents;
|
|
if (autoInit !== "false") {
|
|
initializeComponents({
|
|
activePage: document.body.dataset.activePage || "",
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
// Already loaded
|
|
const autoInit = document.body.dataset.autoInitComponents;
|
|
if (autoInit !== "false") {
|
|
initializeComponents({
|
|
activePage: document.body.dataset.activePage || "",
|
|
});
|
|
}
|
|
}
|
|
|
|
// Export for manual initialization
|
|
window.Components = Components;
|
|
window.initializeComponents = initializeComponents;
|