Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| const productGrid = document.getElementById('product-grid'); | |
| const loader = document.getElementById('loader'); | |
| let products = []; | |
| // Cart State | |
| let cartCount = 0; | |
| // --- Event Listeners for Filters --- | |
| document.getElementById('filter-all').addEventListener('click', () => filterProducts('all')); | |
| document.getElementById('filter-tech').addEventListener('click', () => filterProducts('tech')); | |
| document.getElementById('filter-design').addEventListener('click', () => filterProducts('design')); | |
| // --- Function: Fetch Data --- | |
| async function loadProducts() { | |
| // Show Loader | |
| loader.style.display = 'flex'; | |
| productGrid.innerHTML = ''; | |
| try { | |
| // Attempt to fetch from local data.json file | |
| const response = await fetch('data.json'); | |
| if (!response.ok) throw new Error("HTTP error"); | |
| products = await response.json(); | |
| } catch (error) { | |
| console.warn('Could not load data.json (likely due to CORS on local file system). Using fallback data.', error); | |
| // Fallback Data if JSON fetch fails | |
| products = [ | |
| { id: 1, name: "Wireless Headphones", price: 299, category: "tech", image: "http://static.photos/technology/640x360/1", rating: 4.8 }, | |
| { id: 2, name: "Mechanical Keyboard", price: 149, category: "tech", image: "http://static.photos/workspace/640x360/2", rating: 4.7 }, | |
| { id: 3, name: "Modern Lamp", price: 89, category: "design", image: "http://static.photos/interior/640x360/3", rating: 4.5 }, | |
| { id: 4, name: "Smart Watch", price: 399, category: "tech", image: "http://static.photos/technology/640x360/6", rating: 4.6 } | |
| ]; | |
| } finally { | |
| // Hide Loader | |
| loader.style.display = 'none'; | |
| renderProducts(products); | |
| } | |
| } | |
| // --- Function: Render Products --- | |
| function renderProducts(items) { | |
| productGrid.innerHTML = ''; | |
| items.forEach(product => { | |
| const card = document.createElement('shop-card'); | |
| // Set attributes for the web component | |
| card.setAttribute('name', product.name); | |
| card.setAttribute('price', product.price); | |
| card.setAttribute('category', product.category); | |
| card.setAttribute('image', product.image); | |
| card.setAttribute('rating', product.rating); | |
| productGrid.appendChild(card); | |
| }); | |
| } | |
| // --- Function: Filter Products --- | |
| function filterProducts(category) { | |
| // Update active button styles | |
| const buttons = document.querySelectorAll('button[id^="filter-"]'); | |
| buttons.forEach(btn => { | |
| if (btn.id === `filter-${category}`) { | |
| btn.classList.remove('bg-white', 'text-gray-600'); | |
| btn.classList.add('bg-primary', 'text-white'); | |
| } else { | |
| btn.classList.remove('bg-primary', 'text-white'); | |
| btn.classList.add('bg-white', 'text-gray-600'); | |
| } | |
| }); | |
| if (category === 'all') { | |
| renderProducts(products); | |
| } else { | |
| const filtered = products.filter(p => p.category === category); | |
| renderProducts(filtered); | |
| } | |
| } | |
| // --- Global Event Listener: Add to Cart --- | |
| // We listen for a custom event dispatched from inside the Shadow DOM of the Web Component | |
| window.addEventListener('add-to-cart', (e) => { | |
| cartCount++; | |
| updateCartDisplay(); | |
| showToast(`Added ${e.detail.productName} to cart!`); | |
| }); | |
| // --- Global Event Listener: Remove Product --- | |
| window.addEventListener('remove-product', (e) => { | |
| const card = e.detail.element; | |
| card.style.opacity = '0'; | |
| card.style.transform = 'scale(0.9)'; | |
| setTimeout(() => { | |
| card.remove(); | |
| }, 300); | |
| showToast('Product removed.'); | |
| }); | |
| function updateCartDisplay() { | |
| // We dispatch an event to the navbar since we can't easily access its shadow DOM internals directly | |
| window.dispatchEvent(new CustomEvent('update-cart-count', { | |
| detail: { count: cartCount } | |
| })); | |
| } | |
| // --- Function: Show Toast Notification --- | |
| function showToast(message) { | |
| const container = document.getElementById('toast-container'); | |
| const toast = document.createElement('div'); | |
| toast.className = 'toast-enter bg-dark text-white px-6 py-3 rounded-lg shadow-xl flex items-center gap-3'; | |
| toast.innerHTML = ` | |
| <i data-feather="check-circle" class="text-green-400"></i> | |
| <span class="font-medium">${message}</span> | |
| `; | |
| container.appendChild(toast); | |
| feather.replace(); // Refresh icons in new element | |
| // Remove after 3 seconds | |
| setTimeout(() => { | |
| toast.classList.remove('toast-enter'); | |
| toast.classList.add('toast-exit'); | |
| setTimeout(() => { | |
| toast.remove(); | |
| }, 300); | |
| }, 3000); | |
| } | |
| // Initialize | |
| loadProducts(); | |
| }); |