<script src="notification-handler.js"></script> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Wishlist - SetupGuru</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <style> :root { --primary: #2E1A5F; --secondary: #C49B46; --text-dark: #1a1a1a; --text-light: #666; --bg-light: #f9f9f9; --white: #ffffff; --shadow: 0 4px 20px rgba(0, 0, 0, 0.08); --shadow-hover: 0 8px 30px rgba(0, 0, 0, 0.12); --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', sans-serif; background-color: var(--bg-light); color: var(--text-dark); padding-bottom: 80px; } .header { background: var(--white); padding: 1rem 2rem; box-shadow: var(--shadow); position: sticky; top: 0; z-index: 100; } .header-content { max-width: 1400px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; } .back-btn { background: none; border: none; font-size: 1.5rem; color: var(--primary); cursor: pointer; display: flex; align-items: center; gap: 10px; } .page-title { font-family: 'Playfair Display', serif; font-size: 1.8rem; color: var(--primary); } .notification-bell { position: relative; font-size: 1.5rem; color: var(--primary); cursor: pointer; transition: var(--transition); } .notification-bell:hover { transform: scale(1.1); } .notification-badge { position: absolute; top: -5px; right: -5px; background: #e74c3c; color: white; font-size: 0.7rem; padding: 2px 6px; border-radius: 10px; font-weight: 700; } .container { max-width: 1400px; margin: 2rem auto; padding: 0 2rem; display: grid; grid-template-columns: 1fr 350px; gap: 2rem; } .main-content { min-height: 500px; } .wishlist-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; } .wishlist-count { color: var(--text-light); font-size: 1.1rem; } .clear-all-btn { background: var(--primary); color: var(--white); border: none; padding: 10px 20px; border-radius: 50px; cursor: pointer; font-weight: 600; transition: var(--transition); } .clear-all-btn:hover { background: #3a217a; transform: translateY(-2px); } .wishlist-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 2rem; } .wishlist-card { background: var(--white); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); transition: var(--transition); position: relative; } .wishlist-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-hover); } .remove-btn { position: absolute; top: 10px; right: 10px; background: rgba(255, 255, 255, 0.9); border: none; width: 35px; height: 35px; border-radius: 50%; cursor: pointer; z-index: 2; display: flex; align-items: center; justify-content: center; transition: var(--transition); } .remove-btn:hover { background: #e74c3c; color: var(--white); } .product-image { width: 100%; aspect-ratio: 1; object-fit: cover; } .product-info { padding: 1.5rem; } .product-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 10px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .product-price { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; } .current-price { font-size: 1.3rem; font-weight: 700; color: var(--primary); } .original-price { text-decoration: line-through; color: var(--text-light); } .discount-badge { position: absolute; top: 10px; left: 10px; background: var(--secondary); color: var(--primary); padding: 5px 10px; border-radius: 5px; font-size: 0.8rem; font-weight: 700; z-index: 2; } .action-buttons { display: flex; gap: 10px; } .btn { flex: 1; padding: 10px; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; transition: var(--transition); } .btn-primary { background: var(--primary); color: var(--white); } .btn-primary:hover { background: #3a217a; } .btn-secondary { background: var(--bg-light); color: var(--primary); } .btn-secondary:hover { background: #e0e0e0; } .empty-wishlist { text-align: center; padding: 4rem 2rem; } .empty-icon { font-size: 5rem; color: var(--text-light); margin-bottom: 1rem; } .empty-wishlist h2 { font-family: 'Playfair Display', serif; color: var(--primary); margin-bottom: 1rem; } .empty-wishlist p { color: var(--text-light); margin-bottom: 2rem; } .shop-now-btn { display: inline-block; background: var(--primary); color: var(--white); padding: 12px 30px; border-radius: 50px; text-decoration: none; font-weight: 600; transition: var(--transition); } .shop-now-btn:hover { background: #3a217a; transform: translateY(-2px); } /* Notifications Sidebar */ .notifications-sidebar { background: var(--white); border-radius: 12px; padding: 1.5rem; box-shadow: var(--shadow); height: fit-content; position: sticky; top: 100px; } .notifications-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 2px solid var(--bg-light); } .notifications-header h3 { font-family: 'Playfair Display', serif; color: var(--primary); font-size: 1.3rem; } .mark-read-btn { background: none; border: none; color: var(--text-light); font-size: 0.85rem; cursor: pointer; transition: var(--transition); } .mark-read-btn:hover { color: var(--primary); } .notifications-list { max-height: 600px; overflow-y: auto; } .notification-item { padding: 1rem; border-radius: 8px; margin-bottom: 1rem; background: var(--bg-light); transition: var(--transition); cursor: pointer; position: relative; } .notification-item.unread { background: #e8f4ff; border-left: 3px solid var(--primary); } .notification-item:hover { transform: translateX(5px); box-shadow: var(--shadow); } .notification-icon { width: 40px; height: 40px; border-radius: 50%; background: var(--primary); color: var(--white); display: flex; align-items: center; justify-content: center; margin-bottom: 10px; } .notification-title { font-weight: 600; margin-bottom: 5px; color: var(--text-dark); } .notification-message { font-size: 0.9rem; color: var(--text-light); margin-bottom: 8px; } .notification-time { font-size: 0.75rem; color: var(--text-light); } .notification-action { margin-top: 10px; } .notification-btn { background: var(--primary); color: var(--white); border: none; padding: 8px 16px; border-radius: 20px; font-size: 0.85rem; cursor: pointer; transition: var(--transition); } .notification-btn:hover { background: #3a217a; } .empty-notifications { text-align: center; padding: 2rem; color: var(--text-light); } @media (max-width: 1024px) { .container { grid-template-columns: 1fr; } .notifications-sidebar { position: relative; top: 0; } } /* Skeleton Loading */ .skeleton-card { background: var(--white); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); } .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .skeleton-image { width: 100%; aspect-ratio: 1; } .skeleton-text { height: 16px; margin: 10px 15px; border-radius: 4px; } .skeleton-price { height: 24px; width: 60%; margin: 10px 15px; border-radius: 4px; } .skeleton-btn { height: 40px; margin: 15px; border-radius: 8px; } /* Notification Skeleton */ .skeleton-notification { padding: 1rem; border-radius: 8px; margin-bottom: 1rem; background: var(--bg-light); } .skeleton-notif-icon { width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; } .skeleton-notif-title { height: 18px; width: 70%; margin-bottom: 8px; border-radius: 4px; } .skeleton-notif-text { height: 14px; width: 90%; margin-bottom: 6px; border-radius: 4px; } /* Enhanced Notifications */ .notifications-sidebar { background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%); border-radius: 16px; padding: 1.5rem; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); height: fit-content; position: sticky; top: 100px; border: 1px solid rgba(46, 26, 95, 0.1); } .notifications-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 2px solid rgba(46, 26, 95, 0.1); } .notifications-header h3 { font-family: 'Playfair Display', serif; color: var(--primary); font-size: 1.4rem; display: flex; align-items: center; gap: 8px; } .mark-read-btn { background: rgba(46, 26, 95, 0.1); border: none; color: var(--primary); font-size: 0.8rem; cursor: pointer; transition: var(--transition); padding: 6px 12px; border-radius: 20px; font-weight: 600; } .mark-read-btn:hover { background: var(--primary); color: var(--white); transform: scale(1.05); } .notifications-list { max-height: 600px; overflow-y: auto; padding-right: 5px; } .notifications-list::-webkit-scrollbar { width: 6px; } .notifications-list::-webkit-scrollbar-track { background: var(--bg-light); border-radius: 10px; } .notifications-list::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 10px; } .notification-item { padding: 1.2rem; border-radius: 12px; margin-bottom: 1rem; background: var(--white); transition: var(--transition); cursor: pointer; position: relative; border: 1px solid transparent; } .notification-item.unread { background: linear-gradient(135deg, #e8f4ff 0%, #f0f8ff 100%); border-left: 4px solid var(--primary); box-shadow: 0 2px 8px rgba(46, 26, 95, 0.1); } .notification-item:hover { transform: translateX(5px); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); border-color: var(--primary); } .notification-icon { width: 45px; height: 45px; border-radius: 50%; background: linear-gradient(135deg, var(--primary) 0%, #3a217a 100%); color: var(--white); display: flex; align-items: center; justify-content: center; margin-bottom: 12px; font-size: 1.2rem; box-shadow: 0 4px 12px rgba(46, 26, 95, 0.3); } .notification-title { font-weight: 700; margin-bottom: 6px; color: var(--text-dark); font-size: 1rem; } .notification-message { font-size: 0.9rem; color: var(--text-light); margin-bottom: 10px; line-height: 1.5; } .notification-time { font-size: 0.75rem; color: var(--text-light); display: flex; align-items: center; gap: 5px; } .notification-time::before { content: 'a"’òÉ'; } .notification-action { margin-top: 12px; } .notification-btn { background: linear-gradient(135deg, var(--primary) 0%, #3a217a 100%); color: var(--white); border: none; padding: 10px 20px; border-radius: 25px; font-size: 0.85rem; cursor: pointer; transition: var(--transition); font-weight: 600; box-shadow: 0 4px 12px rgba(46, 26, 95, 0.3); } .notification-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(46, 26, 95, 0.4); } .empty-notifications { text-align: center; padding: 3rem 1rem; color: var(--text-light); } .empty-notifications i { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; } @media (max-width: 768px) { .container { padding: 0 1rem; } .wishlist-grid { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1.5rem; } .wishlist-header { flex-direction: column; gap: 1rem; align-items: flex-start; } } </style> </head> <body> <header class="header"> <div class="header-content"> <button class="back-btn" onclick="window.history.back()"> <i class="fas fa-arrow-left"></i> <span>Back</span> </button> <h1 class="page-title">My Wishlist</h1> <div class="notification-bell" id="notification-bell"> <i class="fas fa-bell"></i> <span class="notification-badge" id="notification-count">0</span> </div> </div> </header> <div class="container"> <div class="main-content"> <div class="wishlist-header"> <div class="wishlist-count" id="wishlist-count-text">0 items in wishlist</div> <button class="clear-all-btn" id="clear-all-btn">Clear All</button> </div> <div class="wishlist-grid" id="wishlist-grid"></div> <div class="empty-wishlist" id="empty-wishlist" style="display: none;"> <div class="empty-icon"> <i class="fas fa-heart-broken"></i> </div> <h2>Your Wishlist is Empty</h2> <p>Add items you love to your wishlist and shop them later!</p> <a href="items.html" class="shop-now-btn">Start Shopping</a> </div> </div> <div class="notifications-sidebar"> <div class="notifications-header"> <h3>a"’öö Notifications</h3> <button class="mark-read-btn" id="mark-read-btn">Mark all read</button> </div> <div class="notifications-list" id="notifications-list"></div> </div> </div> <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-app-compat.js"></script> <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-auth-compat.js"></script> <script src="https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore-compat.js"></script> <script> const firebaseConfig = { apiKey: "AIzaSyAMweRCGXZCfuRvtlVO0acLbKmY4heV08s", authDomain: "setupguru-217af.firebaseapp.com", projectId: "setupguru-217af", storageBucket: "setupguru-217af.firebasestorage.app", messagingSenderId: "377386830551", appId: "1:377386830551:web:39909b0add3f2bc630be92" }; firebase.initializeApp(firebaseConfig); const db = firebase.firestore(); const auth = firebase.auth(); const wishlistGrid = document.getElementById('wishlist-grid'); const emptyWishlist = document.getElementById('empty-wishlist'); const wishlistCountText = document.getElementById('wishlist-count-text'); const clearAllBtn = document.getElementById('clear-all-btn'); const notificationsList = document.getElementById('notifications-list'); const notificationCount = document.getElementById('notification-count'); const markReadBtn = document.getElementById('mark-read-btn'); let wishlist = JSON.parse(localStorage.getItem('wishlist')) || []; let notifications = JSON.parse(localStorage.getItem('notifications')) || []; function showSkeletonLoader(count = 4) { wishlistGrid.innerHTML = ''; for (let i = 0; i < count; i++) { const skeleton = document.createElement('div'); skeleton.className = 'skeleton-card'; skeleton.innerHTML = ` <div class="skeleton skeleton-image"></div> <div class="skeleton skeleton-text"></div> <div class="skeleton skeleton-text" style="width: 80%;"></div> <div class="skeleton skeleton-price"></div> <div class="skeleton skeleton-btn"></div> `; wishlistGrid.appendChild(skeleton); } } function showNotificationSkeleton(count = 3) { notificationsList.innerHTML = ''; for (let i = 0; i < count; i++) { const skeleton = document.createElement('div'); skeleton.className = 'skeleton-notification'; skeleton.innerHTML = ` <div class="skeleton skeleton-notif-icon"></div> <div class="skeleton skeleton-notif-title"></div> <div class="skeleton skeleton-notif-text"></div> <div class="skeleton skeleton-notif-text" style="width: 60%;"></div> `; notificationsList.appendChild(skeleton); } } async function loadWishlist() { if (wishlist.length === 0) { wishlistGrid.style.display = 'none'; emptyWishlist.style.display = 'block'; clearAllBtn.style.display = 'none'; return; } wishlistGrid.style.display = 'grid'; emptyWishlist.style.display = 'none'; clearAllBtn.style.display = 'block'; wishlistCountText.textContent = `${wishlist.length} item${wishlist.length > 1 ? 's' : ''} in wishlist`; showSkeletonLoader(wishlist.length); wishlistGrid.innerHTML = ''; for (const itemId of wishlist) { try { const doc = await db.collection('items').doc(itemId).get(); if (doc.exists) { const item = { id: doc.id, ...doc.data() }; displayWishlistItem(item); } } catch (error) { console.error('Error loading item:', error); } } } function displayWishlistItem(item) { const discountPercent = item.discountPercent || 0; const currentPrice = item.price * (1 - discountPercent / 100); const card = document.createElement('div'); card.className = 'wishlist-card'; card.innerHTML = ` ${discountPercent > 0 ? `<div class="discount-badge">-${discountPercent}%</div>` : ''} <button class="remove-btn" data-id="${item.id}"> <i class="fas fa-times"></i> </button> <img src="${item.imageURLs?.[0] || 'https://via.placeholder.com/300'}" alt="${item.title}" class="product-image"> <div class="product-info"> <h3 class="product-title">${item.title}</h3> <div class="product-price"> <span class="current-price">“éc%${Math.floor(currentPrice)}</span> ${discountPercent > 0 ? `<span class="original-price">“éc%${Math.floor(item.price)}</span>` : ''} </div> <div class="action-buttons"> <button class="btn btn-primary" onclick="window.location.href='card.html?id=${item.id}'">View Details</button> <button class="btn btn-secondary add-to-cart-btn" data-id="${item.id}">Add to Cart</button> </div> </div> `; const removeBtn = card.querySelector('.remove-btn'); removeBtn.addEventListener('click', () => removeFromWishlist(item.id)); const addToCartBtn = card.querySelector('.add-to-cart-btn'); addToCartBtn.addEventListener('click', () => addToCart(item.id)); wishlistGrid.appendChild(card); } function removeFromWishlist(itemId) { wishlist = wishlist.filter(id => id !== itemId); localStorage.setItem('wishlist', JSON.stringify(wishlist)); loadWishlist(); } async function addToCart(itemId) { const user = auth.currentUser; if (!user) { alert('Please login to add items to cart'); window.location.href = 'login.html'; return; } try { await db.collection('users').doc(user.uid).collection('cart').doc(itemId).set({ itemId: itemId, quantity: 1, addedAt: firebase.firestore.FieldValue.serverTimestamp() }); alert('Added to cart!'); } catch (error) { console.error('Error adding to cart:', error); alert('Failed to add to cart'); } } clearAllBtn.addEventListener('click', () => { if (confirm('Are you sure you want to clear your entire wishlist?')) { wishlist = []; localStorage.setItem('wishlist', JSON.stringify(wishlist)); loadWishlist(); } }); // Notifications System function loadNotifications() { const user = auth.currentUser; showNotificationSkeleton(); if (user) { // Load from Firestore for logged-in users db.collection('notifications') .where('userId', '==', user.uid) .orderBy('createdAt', 'desc') .limit(10) .onSnapshot((snapshot) => { notifications = []; snapshot.forEach(doc => { notifications.push({ id: doc.id, ...doc.data() }); }); displayNotifications(); }); } else { // Load from localStorage for guests setTimeout(() => displayNotifications(), 500); } } function displayNotifications() { if (notifications.length === 0) { notificationsList.innerHTML = '<div class="empty-notifications"><i class="fas fa-bell-slash" style="font-size:2rem;margin-bottom:10px;"></i><p>No notifications yet</p></div>'; notificationCount.textContent = '0'; return; } const unreadCount = notifications.filter(n => !n.read).length; notificationCount.textContent = unreadCount; notificationCount.style.display = unreadCount > 0 ? 'block' : 'none'; notificationsList.innerHTML = notifications.map(notif => ` <div class="notification-item ${notif.read ? '' : 'unread'}" data-id="${notif.id}"> <div class="notification-icon"> <i class="${notif.icon || 'fas fa-bell'}"></i> </div> <div class="notification-title">${notif.title}</div> <div class="notification-message">${notif.message}</div> <div class="notification-time">${formatTime(notif.createdAt)}</div> ${notif.productId ? ` <div class="notification-action"> <button class="notification-btn" onclick="window.location.href='card.html?id=${notif.productId}'"> ${notif.actionText || 'Buy Now'} </button> </div> ` : ''} </div> `).join(''); // Mark as read on click document.querySelectorAll('.notification-item').forEach(item => { item.addEventListener('click', () => markAsRead(item.dataset.id)); }); } function formatTime(timestamp) { if (!timestamp) return 'Just now'; let date; if (timestamp.toDate) { date = timestamp.toDate(); } else if (typeof timestamp === 'string') { date = new Date(timestamp); } else { date = new Date(timestamp); } const now = new Date(); const diff = Math.floor((now - date) / 1000); if (diff < 60) return 'Just now'; if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; return `${Math.floor(diff / 86400)}d ago`; } function markAsRead(notifId) { notifications = notifications.map(n => n.id === notifId ? { ...n, read: true } : n ); localStorage.setItem('notifications', JSON.stringify(notifications)); displayNotifications(); } markReadBtn.addEventListener('click', () => { notifications = notifications.map(n => ({ ...n, read: true })); localStorage.setItem('notifications', JSON.stringify(notifications)); displayNotifications(); }); // Generate sample notifications if empty function generateSampleNotifications() { const storedNotifs = JSON.parse(localStorage.getItem('notifications')) || []; if (storedNotifs.length === 0) { const sampleNotifs = [ { id: 'notif-' + Date.now() + 1, title: 'a"’Äë Welcome to SetupGuru!', message: 'Explore our amazing collection of products. Special discounts available!', icon: 'fas fa-gift', read: false, createdAt: new Date().toISOString(), actionText: 'Shop Now' }, { id: 'notif-' + Date.now() + 2, title: '“Üí Flash Sale Alert', message: 'Limited time offer! Get up to 50% off on selected items.', icon: 'fas fa-bolt', read: false, createdAt: new Date(Date.now() - 1800000).toISOString(), actionText: 'View Deals' }, { id: 'notif-' + Date.now() + 3, title: 'a"’Æ¥ Wishlist Reminder', message: 'Don\'t forget to check your wishlist items. Some are running low on stock!', icon: 'fas fa-heart', read: false, createdAt: new Date(Date.now() - 7200000).toISOString() } ]; if (wishlist.length > 0) { sampleNotifs.push({ id: 'notif-' + Date.now() + 4, title: 'a"’öÑ Price Drop Alert', message: 'Great news! Items in your wishlist are now on sale.', icon: 'fas fa-tag', read: false, createdAt: new Date(Date.now() - 3600000).toISOString(), productId: wishlist[0], actionText: 'Buy Now' }); } notifications = sampleNotifs; localStorage.setItem('notifications', JSON.stringify(notifications)); } else { notifications = storedNotifs; } } // Initialize auth.onAuthStateChanged((user) => { loadWishlist(); if (!user) { generateSampleNotifications(); } loadNotifications(); }); </script> </body> </html>