/* ============================================ SKY ART SHOP - MODERN THEME JAVASCRIPT Complete Frontend Functionality ============================================ */ // Global State const SkyArtShop = { cart: JSON.parse(localStorage.getItem("skyart_cart") || "[]"), wishlist: JSON.parse(localStorage.getItem("skyart_wishlist") || "[]"), // Initialize init() { this.initNavbar(); // Delay slider init slightly to ensure DOM is ready requestAnimationFrame(() => { this.initSlider(); }); this.initCart(); this.initWishlist(); this.initWishlistDrawer(); this.initProducts(); this.initAnimations(); this.updateCartCount(); this.updateWishlistCount(); }, // Navbar Functionality initNavbar() { const navbar = document.querySelector(".nav-wrapper"); const mobileToggle = document.querySelector(".nav-mobile-toggle"); const navMenu = document.querySelector(".nav-menu"); // Scroll effect if (navbar) { window.addEventListener("scroll", () => { if (window.scrollY > 50) { navbar.classList.add("scrolled"); } else { navbar.classList.remove("scrolled"); } }); } // Mobile/Tablet menu toggle if (mobileToggle && navMenu) { // Create overlay element for background dimming let overlay = document.querySelector(".nav-menu-overlay"); if (!overlay) { overlay = document.createElement("div"); overlay.className = "nav-menu-overlay"; document.body.appendChild(overlay); } // Create close button inside menu let closeBtn = navMenu.querySelector(".nav-menu-close"); if (!closeBtn) { closeBtn = document.createElement("button"); closeBtn.className = "nav-menu-close"; closeBtn.innerHTML = ''; closeBtn.setAttribute("aria-label", "Close menu"); navMenu.insertBefore(closeBtn, navMenu.firstChild); } // Function to open menu const openMenu = () => { navMenu.classList.add("open"); mobileToggle.classList.add("active"); overlay.classList.add("active"); document.body.classList.add("nav-menu-open"); }; // Function to close menu const closeMenu = () => { navMenu.classList.remove("open"); mobileToggle.classList.remove("active"); overlay.classList.remove("active"); document.body.classList.remove("nav-menu-open"); }; // Toggle button click mobileToggle.addEventListener("click", (e) => { e.stopPropagation(); if (navMenu.classList.contains("open")) { closeMenu(); } else { openMenu(); } }); // Close button click closeBtn.addEventListener("click", (e) => { e.stopPropagation(); closeMenu(); }); // Overlay click to close overlay.addEventListener("click", () => { closeMenu(); }); // Close menu when clicking a link navMenu.querySelectorAll(".nav-link").forEach((link) => { link.addEventListener("click", () => { closeMenu(); }); }); // Close menu when clicking outside document.addEventListener("click", (e) => { if ( navMenu.classList.contains("open") && !navMenu.contains(e.target) && !mobileToggle.contains(e.target) ) { closeMenu(); } }); // Close menu when touching outside (for mobile/tablet) document.addEventListener( "touchstart", (e) => { if ( navMenu.classList.contains("open") && !navMenu.contains(e.target) && !mobileToggle.contains(e.target) ) { closeMenu(); } }, { passive: true }, ); // Close menu on escape key document.addEventListener("keydown", (e) => { if (e.key === "Escape" && navMenu.classList.contains("open")) { closeMenu(); } }); } // Set active nav link const currentPath = window.location.pathname; document.querySelectorAll(".nav-link").forEach((link) => { if (link.getAttribute("href") === currentPath) { link.classList.add("active"); } }); }, // Hero Slider sliderInitialized: false, sliderInterval: null, initSlider() { const slider = document.querySelector(".hero-slider"); if (!slider) return; // Prevent multiple initializations if (this.sliderInitialized || slider.dataset.initialized === "true") { return; } this.sliderInitialized = true; slider.dataset.initialized = "true"; const slides = slider.querySelectorAll(".slide"); const dots = slider.querySelectorAll(".slider-dot"); const prevBtn = slider.querySelector(".slider-arrow.prev"); const nextBtn = slider.querySelector(".slider-arrow.next"); // Need at least 2 slides for auto-play to make sense if (slides.length < 2) return; let currentSlide = 0; let isAnimating = false; const self = this; // Clear any existing interval if (this.sliderInterval) { clearInterval(this.sliderInterval); this.sliderInterval = null; } // Initialize slides - first slide active, others positioned off-screen right slides.forEach((slide, i) => { slide.classList.remove("active", "outgoing"); slide.style.transition = "none"; if (i === 0) { slide.classList.add("active"); } }); // Force reflow then re-enable transitions void slider.offsetWidth; slides.forEach((slide) => (slide.style.transition = "")); if (dots[0]) dots[0].classList.add("active"); const showSlide = (index) => { if (isAnimating) return; const prevIndex = currentSlide; currentSlide = (index + slides.length) % slides.length; if (prevIndex === currentSlide) return; isAnimating = true; const oldSlide = slides[prevIndex]; const newSlide = slides[currentSlide]; // Update dots dots.forEach((dot, i) => dot.classList.toggle("active", i === currentSlide), ); // Position new slide off-screen to the right (no transition) newSlide.style.transition = "none"; newSlide.classList.remove("outgoing"); newSlide.style.transform = "translateX(100%)"; // Force browser to register the position void newSlide.offsetWidth; // Re-enable transition and animate newSlide.style.transition = ""; newSlide.style.transform = ""; newSlide.classList.add("active"); // Old slide moves out to the left oldSlide.classList.remove("active"); oldSlide.classList.add("outgoing"); // Cleanup after animation (800ms matches CSS) setTimeout(() => { oldSlide.classList.remove("outgoing"); oldSlide.style.transform = ""; isAnimating = false; }, 850); }; const nextSlide = () => showSlide(currentSlide + 1); const prevSlide = () => showSlide(currentSlide - 1); // Auto-play with 7 second intervals (7000ms) const startAutoPlay = () => { // Clear any existing interval first if (self.sliderInterval) { clearInterval(self.sliderInterval); } self.sliderInterval = setInterval(nextSlide, 7000); }; const stopAutoPlay = () => { if (self.sliderInterval) { clearInterval(self.sliderInterval); self.sliderInterval = null; } }; // Event listeners if (prevBtn) prevBtn.addEventListener("click", () => { stopAutoPlay(); prevSlide(); startAutoPlay(); }); if (nextBtn) nextBtn.addEventListener("click", () => { stopAutoPlay(); nextSlide(); startAutoPlay(); }); dots.forEach((dot, i) => { dot.addEventListener("click", () => { stopAutoPlay(); showSlide(i); startAutoPlay(); }); }); // Start auto-play immediately (first slide already initialized) startAutoPlay(); // Pause on hover slider.addEventListener("mouseenter", stopAutoPlay); slider.addEventListener("mouseleave", startAutoPlay); // Pause when tab is not visible, resume when visible document.addEventListener("visibilitychange", () => { if (document.hidden) { stopAutoPlay(); } else { startAutoPlay(); } }); }, // Cart Functionality initCart() { const cartBtn = document.querySelector(".cart-btn"); const cartDrawer = document.querySelector(".cart-drawer"); const cartOverlay = document.querySelector(".cart-overlay"); const cartClose = document.querySelector(".cart-close"); if (cartBtn && cartDrawer) { cartBtn.addEventListener("click", () => this.openCart()); } if (cartClose) cartClose.addEventListener("click", () => this.closeCart()); if (cartOverlay) cartOverlay.addEventListener("click", () => this.closeCart()); // Close on escape document.addEventListener("keydown", (e) => { if (e.key === "Escape") this.closeCart(); }); }, openCart() { const cartDrawer = document.querySelector(".cart-drawer"); const cartOverlay = document.querySelector(".cart-overlay"); if (cartDrawer) cartDrawer.classList.add("open"); if (cartOverlay) cartOverlay.classList.add("open"); document.body.style.overflow = "hidden"; this.renderCart(); }, closeCart() { const cartDrawer = document.querySelector(".cart-drawer"); const cartOverlay = document.querySelector(".cart-overlay"); if (cartDrawer) cartDrawer.classList.remove("open"); if (cartOverlay) cartOverlay.classList.remove("open"); document.body.style.overflow = ""; }, addToCart(product) { const existingItem = this.cart.find((item) => item.id === product.id); if (existingItem) { existingItem.quantity += 1; } else { this.cart.push({ ...product, quantity: 1 }); } this.saveCart(); this.updateCartCount(); this.showNotification(`${product.name} added to cart!`); this.openCart(); }, removeFromCart(productId) { this.cart = this.cart.filter((item) => item.id !== productId); this.saveCart(); this.updateCartCount(); this.renderCart(); }, updateCartQty(productId, change) { const item = this.cart.find((item) => item.id === productId); if (item) { item.quantity += change; if (item.quantity <= 0) { this.removeFromCart(productId); } else { this.saveCart(); this.renderCart(); } } }, saveCart() { localStorage.setItem("skyart_cart", JSON.stringify(this.cart)); }, updateCartCount() { const count = this.cart.reduce((sum, item) => sum + item.quantity, 0); document.querySelectorAll(".cart-count").forEach((el) => { el.textContent = count; el.style.display = count > 0 ? "flex" : "none"; }); }, getCartTotal() { return this.cart.reduce( (sum, item) => sum + parseFloat(item.price) * item.quantity, 0, ); }, renderCart() { const cartItems = document.querySelector(".cart-items"); const cartTotal = document.querySelector(".cart-total-amount"); if (!cartItems) return; if (this.cart.length === 0) { cartItems.innerHTML = `

Your cart is empty

Continue Shopping
`; } else { cartItems.innerHTML = this.cart .map( (item) => `
${
              item.name
            }
${item.name}
${ item.color ? `
Color: ${item.color}
` : "" }
$${parseFloat(item.price).toFixed( 2, )}
${item.quantity}
`, ) .join(""); } if (cartTotal) { cartTotal.textContent = `$${this.getCartTotal().toFixed(2)}`; } }, // Wishlist Functionality initWishlist() { this.updateWishlistCount(); }, toggleWishlist(product) { const index = this.wishlist.findIndex((item) => item.id === product.id); if (index > -1) { this.wishlist.splice(index, 1); this.showNotification(`${product.name} removed from wishlist`); } else { this.wishlist.push(product); this.showNotification(`${product.name} added to wishlist!`); } this.saveWishlist(); this.updateWishlistCount(); this.updateWishlistButtons(); }, isInWishlist(productId) { return this.wishlist.some((item) => item.id === productId); }, saveWishlist() { localStorage.setItem("skyart_wishlist", JSON.stringify(this.wishlist)); }, updateWishlistCount() { const count = this.wishlist.length; document.querySelectorAll(".wishlist-count").forEach((el) => { el.textContent = count; el.style.display = count > 0 ? "flex" : "none"; }); }, updateWishlistButtons() { document.querySelectorAll(".wishlist-btn").forEach((btn) => { const productId = btn.dataset.productId; if (this.isInWishlist(productId)) { btn.classList.add("active"); btn.innerHTML = ''; } else { btn.classList.remove("active"); btn.innerHTML = ''; } }); }, // Wishlist Drawer initWishlistDrawer() { const wishlistBtn = document.querySelector(".wishlist-btn-nav"); const wishlistDrawer = document.querySelector(".wishlist-drawer"); const wishlistOverlay = document.querySelector(".wishlist-overlay"); const wishlistClose = document.querySelector(".wishlist-close"); if (wishlistBtn && wishlistDrawer) { wishlistBtn.addEventListener("click", (e) => { e.preventDefault(); this.openWishlist(); }); } if (wishlistClose) wishlistClose.addEventListener("click", () => this.closeWishlist()); if (wishlistOverlay) wishlistOverlay.addEventListener("click", () => this.closeWishlist()); // Close on escape document.addEventListener("keydown", (e) => { if (e.key === "Escape") this.closeWishlist(); }); }, openWishlist() { const wishlistDrawer = document.querySelector(".wishlist-drawer"); const wishlistOverlay = document.querySelector(".wishlist-overlay"); if (wishlistDrawer) wishlistDrawer.classList.add("open"); if (wishlistOverlay) wishlistOverlay.classList.add("open"); document.body.style.overflow = "hidden"; this.renderWishlist(); }, closeWishlist() { const wishlistDrawer = document.querySelector(".wishlist-drawer"); const wishlistOverlay = document.querySelector(".wishlist-overlay"); if (wishlistDrawer) wishlistDrawer.classList.remove("open"); if (wishlistOverlay) wishlistOverlay.classList.remove("open"); document.body.style.overflow = ""; }, renderWishlist() { const wishlistItems = document.querySelector(".wishlist-items"); if (!wishlistItems) return; if (this.wishlist.length === 0) { wishlistItems.innerHTML = `

Your wishlist is empty

Browse our products and add items you love!

`; return; } wishlistItems.innerHTML = this.wishlist .map( (item) => `
${
              item.name
            }
${item.name}
${ item.color ? `
Color: ${item.color}
` : "" }
$${parseFloat(item.price).toFixed( 2, )}
`, ) .join(""); }, moveToCart(productId) { const item = this.wishlist.find((item) => item.id === productId); if (item) { // Pass the full item including color and image this.addToCart({ id: item.id, productId: item.productId || item.id, name: item.name, price: item.price, image: item.image, color: item.color || null, }); this.removeFromWishlistById(productId); } }, removeFromWishlistById(productId) { const index = this.wishlist.findIndex((item) => item.id === productId); if (index > -1) { const item = this.wishlist[index]; this.wishlist.splice(index, 1); this.saveWishlist(); this.updateWishlistCount(); this.updateWishlistButtons(); this.renderWishlist(); this.showNotification(`${item.name} removed from wishlist`); } }, // Products initProducts() { // Attach event listeners to product cards document.querySelectorAll(".add-to-cart-btn").forEach((btn) => { btn.addEventListener("click", (e) => { e.preventDefault(); const card = btn.closest(".product-card"); const product = this.getProductFromCard(card); this.addToCart(product); }); }); document.querySelectorAll(".wishlist-btn").forEach((btn) => { btn.addEventListener("click", (e) => { e.preventDefault(); const card = btn.closest(".product-card"); const product = this.getProductFromCard(card); this.toggleWishlist(product); }); }); this.updateWishlistButtons(); }, getProductFromCard(card) { return { id: card.dataset.productId, name: card.querySelector(".product-name a")?.textContent || card.querySelector(".product-name")?.textContent || "Product", price: card.dataset.productPrice || card.querySelector(".price-current")?.textContent?.replace("$", "") || "0", image: card.querySelector(".product-image img")?.src || "", }; }, // Animations initAnimations() { // Intersection Observer for scroll animations const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add("fade-in"); observer.unobserve(entry.target); } }); }, { threshold: 0.1 }, ); document .querySelectorAll(".section, .product-card, .blog-card, .portfolio-card") .forEach((el) => { observer.observe(el); }); }, // Notifications showNotification(message, type = "success") { // Remove existing notifications document.querySelectorAll(".notification").forEach((n) => n.remove()); const notification = document.createElement("div"); notification.className = `notification notification-${type}`; notification.innerHTML = ` ${message} `; notification.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: ${type === "success" ? "#202023" : "#e74c3c"}; color: white; padding: 16px 24px; border-radius: 12px; display: flex; align-items: center; gap: 10px; font-weight: 500; box-shadow: 0 8px 32px rgba(0,0,0,0.2); z-index: 9999; animation: slideIn 0.3s ease; `; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = "slideOut 0.3s ease forwards"; setTimeout(() => notification.remove(), 300); }, 3000); }, }; // Add notification animations const style = document.createElement("style"); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } .cart-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; text-align: center; color: var(--text-light); } `; document.head.appendChild(style); // Initialize on DOM ready document.addEventListener("DOMContentLoaded", () => { SkyArtShop.init(); }); // API Functions const API = { baseUrl: "/api", async get(endpoint, noCache = false) { try { const url = noCache ? `${this.baseUrl}${endpoint}${ endpoint.includes("?") ? "&" : "?" }_t=${Date.now()}` : `${this.baseUrl}${endpoint}`; const response = await fetch(url); const data = await response.json(); return data.success ? data : null; } catch (error) { console.error("API Error:", error); return null; } }, async loadFeaturedProducts() { const data = await this.get("/products/featured?limit=4"); return data?.products || []; }, async loadAllProducts() { const data = await this.get("/products"); return data?.products || []; }, async loadProduct(slug) { // Always fetch fresh product data to get latest color variants const data = await this.get(`/products/${slug}`, true); return data?.product || null; }, async loadHomepageSections() { const data = await this.get("/homepage/sections"); return data?.sections || []; }, async loadBlogPosts() { const data = await this.get("/blog/posts"); return data?.posts || []; }, async loadPortfolioProjects() { const data = await this.get("/portfolio/projects", true); return data?.projects || []; }, async loadTeamMembers() { const data = await this.get("/team-members"); return data?.teamMembers || []; }, async loadCategories() { const data = await this.get("/categories"); return data?.categories || []; }, }; // Product Renderer const ProductRenderer = { renderCard(product) { const primaryImage = product.images?.find((img) => img.is_primary) || product.images?.[0]; const imageUrl = primaryImage?.image_url || product.imageurl || "/assets/images/placeholder.jpg"; const inWishlist = SkyArtShop.isInWishlist(product.id); return `
${product.name} ${ product.isfeatured ? '
Featured
' : "" }
${product.category || "General"}

${product.name}

$${parseFloat(product.price).toFixed( 2, )}
`; }, async renderProducts(container, products) { if (!container) return; if (products.length === 0) { container.innerHTML = '

No products found.

'; return; } container.innerHTML = products.map((p) => this.renderCard(p)).join(""); SkyArtShop.initProducts(); }, }; // Blog Renderer const BlogRenderer = { renderCard(post) { const date = new Date(post.createdat).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", }); return `
${post.title}
${date}

${post.title}

${post.excerpt || ""}

Read More
`; }, }; // Portfolio Renderer const PortfolioRenderer = { renderCard(project) { return `
${project.title}

${project.title}

${project.description || ""}

`; }, }; // ============================================ // AUTO-REFRESH SYSTEM // Automatically refresh frontend when admin makes changes // ============================================ const AutoRefresh = { lastChecked: Date.now(), checkInterval: 5000, // Check every 5 seconds init() { // Listen for BroadcastChannel messages from admin try { const channel = new BroadcastChannel("skyartshop_updates"); channel.onmessage = (event) => { console.log("[AutoRefresh] Received update:", event.data); this.handleUpdate(event.data.type); }; } catch (e) { // Fallback to localStorage polling if BroadcastChannel not supported this.startPolling(); } // Also start polling as backup this.startPolling(); // Check on visibility change (when user returns to tab) document.addEventListener("visibilitychange", () => { if (!document.hidden) { this.checkForUpdates(); } }); }, startPolling() { setInterval(() => { if (!document.hidden) { this.checkForUpdates(); } }, this.checkInterval); }, checkForUpdates() { const lastChange = localStorage.getItem("skyartshop_last_change"); if (lastChange && parseInt(lastChange) > this.lastChecked) { this.lastChecked = parseInt(lastChange); // Determine what changed const productsChanged = localStorage.getItem( "skyartshop_change_products", ); const settingsChanged = localStorage.getItem( "skyartshop_change_settings", ); const pagesChanged = localStorage.getItem("skyartshop_change_pages"); if ( productsChanged && parseInt(productsChanged) > this.lastChecked - this.checkInterval ) { this.handleUpdate("products"); } if ( settingsChanged && parseInt(settingsChanged) > this.lastChecked - this.checkInterval ) { this.handleUpdate("settings"); } if ( pagesChanged && parseInt(pagesChanged) > this.lastChecked - this.checkInterval ) { this.handleUpdate("pages"); } } }, handleUpdate(type) { console.log(`[AutoRefresh] Handling ${type} update`); switch (type) { case "products": this.refreshProducts(); break; case "settings": this.refreshSettings(); break; case "pages": // Soft reload for page content changes location.reload(); break; case "all": location.reload(); break; default: // For unknown types, refresh products as default this.refreshProducts(); } }, async refreshProducts() { // Refresh featured products on homepage const featuredGrid = document.querySelector(".featured-products-grid"); if (featuredGrid) { try { const response = await fetch(`/api/products/featured?_t=${Date.now()}`); const data = await response.json(); if (data.success && data.products) { featuredGrid.innerHTML = data.products .map((p) => ProductRenderer.renderCard(p)) .join(""); console.log("[AutoRefresh] Featured products refreshed"); } } catch (e) { console.error("[AutoRefresh] Failed to refresh products:", e); } } // Refresh products page if we're on it const productsGrid = document.querySelector(".products-grid"); if (productsGrid && window.loadProducts) { window.loadProducts(); console.log("[AutoRefresh] Products page refreshed"); } }, async refreshSettings() { // Settings changes might affect header/footer, just reload location.reload(); }, }; // Initialize auto-refresh on page load document.addEventListener("DOMContentLoaded", () => { AutoRefresh.init(); }); // Export for global use window.SkyArtShop = SkyArtShop; window.API = API; window.ProductRenderer = ProductRenderer; window.BlogRenderer = BlogRenderer; window.PortfolioRenderer = PortfolioRenderer; window.AutoRefresh = AutoRefresh; // ============================================ // MOBILE TOUCH OPTIMIZATION // Prevent double-tap behavior on mobile devices // ============================================ (function () { "use strict"; // Only run on touch devices if (!("ontouchstart" in window)) return; // Optimize touch interaction on mobile devices document.addEventListener("DOMContentLoaded", function () { // Add mobile-optimized class to body for CSS targeting document.body.classList.add("mobile-optimized"); // Override hover states on touch devices if (window.matchMedia("(hover: none)").matches) { const style = document.createElement("style"); style.id = "mobile-touch-override"; style.textContent = ` /* Remove hover delays on touch devices */ .product-card, .portfolio-card, .blog-card, .btn, .nav-link, .filter-btn, .add-to-cart-btn, .product-action-btn, .view-project, .nav-icon-btn, .social-link, .footer-links a, .inspiration-card, .featured-post, .faq-question, .user-btn, .back-to-top { -webkit-tap-highlight-color: transparent !important; touch-action: manipulation !important; } /* Immediate visual feedback on touch */ .product-card:active, .portfolio-card:active, .blog-card:active, .btn:active, .nav-link:active, .filter-btn:active, .add-to-cart-btn:active, .product-action-btn:active { transform: scale(0.98) !important; opacity: 0.85 !important; transition: transform 0.1s ease, opacity 0.1s ease !important; } `; document.head.appendChild(style); } // Fast click handling for immediate response let touchStartTime = 0; let touchStartX = 0; let touchStartY = 0; document.addEventListener( "touchstart", function (e) { touchStartTime = Date.now(); touchStartX = e.touches[0].clientX; touchStartY = e.touches[0].clientY; }, { passive: true }, ); document.addEventListener( "touchend", function (e) { const touchEndTime = Date.now(); const touchDuration = touchEndTime - touchStartTime; // Only process quick taps (not long presses) if (touchDuration < 300) { const touchEndX = e.changedTouches[0].clientX; const touchEndY = e.changedTouches[0].clientY; // Check if it's a tap (not a swipe) const deltaX = Math.abs(touchEndX - touchStartX); const deltaY = Math.abs(touchEndY - touchStartY); if (deltaX < 10 && deltaY < 10) { const target = e.target.closest( "a, button, [data-bs-toggle], .product-card, .portfolio-card, .blog-card, .nav-link, .btn, .filter-btn, .add-to-cart-btn", ); if (target && !target.disabled) { // Add immediate visual feedback target.style.transform = "scale(0.95)"; target.style.transition = "transform 0.1s ease"; // Reset after short delay setTimeout(() => { target.style.transform = ""; target.style.transition = ""; }, 100); } } } }, { passive: false }, ); }); })(); // Load and display social media links in footer (function loadFooterSocialLinks() { document.addEventListener("DOMContentLoaded", async () => { try { const response = await fetch("/api/settings"); if (!response.ok) return; const settings = await response.json(); // Map of social platform to element ID and URL format const socialMap = { socialFacebook: { id: "footerFacebook", url: (v) => v }, socialInstagram: { id: "footerInstagram", url: (v) => v }, socialTwitter: { id: "footerTwitter", url: (v) => v }, socialYoutube: { id: "footerYoutube", url: (v) => v }, socialPinterest: { id: "footerPinterest", url: (v) => v }, socialTiktok: { id: "footerTiktok", url: (v) => v }, socialWhatsapp: { id: "footerWhatsapp", url: (v) => v.startsWith("http") ? v : `https://wa.me/${v.replace(/[^0-9]/g, "")}`, }, socialLinkedin: { id: "footerLinkedin", url: (v) => v }, }; for (const [key, config] of Object.entries(socialMap)) { const value = settings[key]; const el = document.getElementById(config.id); if (el && value && value.trim()) { el.href = config.url(value.trim()); el.target = "_blank"; el.rel = "noopener noreferrer"; el.style.display = ""; } } } catch (error) { console.log("Could not load social links:", error); } }); })();