BYAMN Festhub Festivals

Celebrate Every Moment

Create and share beautiful festival greetings with your loved ones

Browse by Category

Upcoming Events

// Current filter state let currentCategory = 'all'; let currentSearch = ''; let currentRating = 0; let currentFestival = null; let scrollInterval = null; let favorites = JSON.parse(localStorage.getItem('festhub-favorites') || '[]'); let notificationsData = []; let isNotificationSectionOpen = false; let notificationCheckInterval = null; // Initialize the page document.addEventListener('DOMContentLoaded', function() { renderWebsites(); renderEvents(); setupEventListeners(); setupNavigationEventListeners(); setupNotificationSystem(); loadNotifications(); checkTodayFestivals(); startAutoScroll(); updateFavoritesCount(); updateNotificationCount(); // Track page view if (typeof gtag !== 'undefined') { gtag('event', 'page_view', { 'page_title': 'BYAMN Festhub Homepage', 'page_location': window.location.href, 'page_path': window.location.pathname }); } }); // Render websites based on current filters function renderWebsites() { websitesGrid.innerHTML = ''; const filteredWebsites = websitesData.filter(website => { const matchesCategory = currentCategory === 'all' || website.category === currentCategory; const matchesSearch = website.title.toLowerCase().includes(currentSearch.toLowerCase()) || website.description.toLowerCase().includes(currentSearch.toLowerCase()); return matchesCategory && matchesSearch; }); if (filteredWebsites.length === 0) { websitesGrid.innerHTML = `

No results found

Try adjusting your search or filter criteria

`; return; } filteredWebsites.forEach(website => { let badgeClass = ''; let btnClass = 'btn-primary'; switch(website.category) { case 'hindu': badgeClass = 'hindu-badge'; btnClass = 'btn-hindu'; break; case 'muslim': badgeClass = 'muslim-badge'; btnClass = 'btn-muslim'; break; case 'christian': badgeClass = 'christian-badge'; btnClass = 'btn-christian'; break; default: badgeClass = 'other-badge'; btnClass = 'btn-other'; } const websiteCard = document.createElement('div'); websiteCard.className = 'website-card'; websiteCard.innerHTML = `
${website.category.charAt(0).toUpperCase() + website.category.slice(1)}

${website.title}

${website.description}

`; websitesGrid.appendChild(websiteCard); }); } // Render events slider function renderEvents() { eventsSlider.innerHTML = ''; eventsData.forEach(event => { let bgColor = 'var(--primary)'; switch(event.category) { case 'hindu': bgColor = 'var(--hindu)'; break; case 'muslim': bgColor = 'var(--muslim)'; break; case 'christian': bgColor = 'var(--christian)'; break; case 'other': bgColor = 'var(--other)'; } const eventCard = document.createElement('div'); eventCard.className = 'event-card'; eventCard.innerHTML = `
${event.day}
${event.month}

${event.title}

${event.description}

View Wishes
`; eventsSlider.appendChild(eventCard); }); } // Set up event listeners function setupEventListeners() { // Category filter buttons categoryBtns.forEach(btn => { btn.addEventListener('click', function() { categoryBtns.forEach(b => b.classList.remove('active')); this.classList.add('active'); currentCategory = this.dataset.category; // Track category filter if (typeof gtag !== 'undefined') { gtag('event', 'select_content', { 'content_type': 'category', 'item_id': currentCategory }); } renderWebsites(); }); }); // Search functionality searchBtn.addEventListener('click', performSearch); searchInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { performSearch(); } }); // Newsletter form newsletterForm.addEventListener('submit', function(e) { e.preventDefault(); const email = this.querySelector('input').value; if (email) { // Track newsletter signup if (typeof gtag !== 'undefined') { gtag('event', 'generate_lead', { 'method': 'newsletter_signup' }); } alert(`Thank you for subscribing to BYAMN Festhub!\nYou'll receive updates on upcoming festivals.`); this.querySelector('input').value = ''; } }); // Modal close button modalClose.addEventListener('click', closeFestivalModal); // Close modal when clicking outside festivalModal.addEventListener('click', function(e) { if (e.target === this) { closeFestivalModal(); } }); // Wish Now button modalWishNow.addEventListener('click', function(e) { e.preventDefault(); if (currentFestival) { // Track wish now action if (typeof gtag !== 'undefined') { gtag('event', 'select_content', { 'content_type': 'festival_wish', 'item_id': currentFestival.title }); } alert(`Redirecting to ${currentFestival.title} wishes page...`); closeFestivalModal(); } }); // Leave Review button modalLeaveReview.addEventListener('click', function() { closeFestivalModal(); openReviewModal(); }); // Review modal close button reviewModalClose.addEventListener('click', closeReviewModal); // Close review modal when clicking outside reviewModal.addEventListener('click', function(e) { if (e.target === this) { closeReviewModal(); } }); // Rating stars const stars = ratingStars.querySelectorAll('.rating-star'); stars.forEach(star => { star.addEventListener('click', function() { const rating = parseInt(this.dataset.rating); setRating(rating); }); star.addEventListener('mouseover', function() { const rating = parseInt(this.dataset.rating); highlightStars(rating); }); star.addEventListener('mouseout', function() { highlightStars(currentRating); }); }); // Submit review submitReview.addEventListener('click', function() { if (currentRating === 0) { alert('Please select a rating'); return; } const review = reviewText.value.trim(); if (!review) { alert('Please write your review'); return; } // In a real app, you would send this to your backend console.log('Review submitted:', { festival: currentFestival.title, rating: currentRating, review: review }); // Track review submission if (typeof gtag !== 'undefined') { gtag('event', 'rate_content', { 'content_type': 'festival', 'item_id': currentFestival.title, 'rating': currentRating }); } alert('Thank you for your review!'); closeReviewModal(); }); } // Setup navigation event listeners function setupNavigationEventListeners() { // Header search button headerSearchBtn.addEventListener('click', function() { searchInput.focus(); searchInput.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); // Quick navigation pills quickNavPills.forEach(pill => { pill.addEventListener('click', function(e) { e.preventDefault(); const category = this.dataset.category; // Update active category categoryBtns.forEach(btn => { btn.classList.remove('active'); if (btn.dataset.category === category) { btn.classList.add('active'); } }); currentCategory = category; updateBreadcrumb(this.textContent); renderWebsites(); // Scroll to categories section document.querySelector('.categories-section').scrollIntoView({ behavior: 'smooth', block: 'start' }); }); }); // Favorites button favoritesBtn.addEventListener('click', function() { showFavoritesModal(); }); // Notifications button notificationsBtn.addEventListener('click', function() { toggleNotificationSection(); }); // Menu toggle menuToggle.addEventListener('click', function(e) { e.stopPropagation(); navMenu.classList.toggle('show'); }); // Close menu when clicking outside document.addEventListener('click', function(e) { if (!navMenu.contains(e.target) && e.target !== menuToggle) { navMenu.classList.remove('show'); } }); // Menu item clicks document.querySelectorAll('.fx-nav-item').forEach(item => { item.addEventListener('click', function(e) { e.preventDefault(); const href = this.getAttribute('href'); if (href === '#categories') { document.querySelector('.categories-section').scrollIntoView({ behavior: 'smooth', block: 'start' }); } else if (href === '#calendar') { document.querySelector('.events-section').scrollIntoView({ behavior: 'smooth', block: 'start' }); } else if (href === '#trending') { currentCategory = 'all'; currentSearch = ''; searchInput.value = ''; updateBreadcrumb('Trending'); renderWebsites(); } else if (href === 'file.html') { window.open('file.html', '_blank'); } else { // Handle other navigation items console.log('Navigating to:', href); alert(`Navigating to ${this.textContent.trim()}`); } navMenu.classList.remove('show'); }); }); } // Update breadcrumb function updateBreadcrumb(pageName) { currentPageBreadcrumb.textContent = pageName; } // Update favorites count function updateFavoritesCount() { favoritesCount.textContent = favorites.length; if (favorites.length === 0) { favoritesCount.style.display = 'none'; } else { favoritesCount.style.display = 'flex'; } } // Update notification count function updateNotificationCount() { const unreadCount = notificationsData.filter(n => !n.read).length; notificationCount.textContent = unreadCount; if (unreadCount === 0) { notificationCount.style.display = 'none'; } else { notificationCount.style.display = 'flex'; } } // Setup notification system event listeners function setupNotificationSystem() { // Close notification section notificationCloseBtn.addEventListener('click', function() { closeNotificationSection(); }); // Mark all as read markAllReadBtn.addEventListener('click', function() { markAllNotificationsAsRead(); }); // Clear all notifications clearAllBtn.addEventListener('click', function() { if (confirm('Are you sure you want to clear all notifications?')) { clearAllNotifications(); } }); // Toggle older notifications olderNotificationsToggle.addEventListener('click', function() { toggleOlderNotifications(); }); // Close notification section when clicking outside document.addEventListener('click', function(e) { if (isNotificationSectionOpen && !notificationSection.contains(e.target) && e.target !== notificationsBtn) { closeNotificationSection(); } }); } // Load notifications from JSON file async function loadNotifications() { try { const response = await fetch('./notifications.json'); if (response.ok) { notificationsData = await response.json(); // Load read status from localStorage const readNotifications = JSON.parse(localStorage.getItem('festhub-read-notifications') || '[]'); notificationsData.forEach(notification => { if (readNotifications.includes(notification.id)) { notification.read = true; } }); updateNotificationCount(); renderNotifications(); // Start checking for new notifications startNotificationChecking(); } else { console.warn('Failed to load notifications'); } } catch (error) { console.error('Error loading notifications:', error); // Use fallback notifications notificationsData = [ { id: 1, title: '🎄 Welcome to BYAMN Festhub!', message: 'Explore our amazing collection of festival wishes and greeting cards.', type: 'info', timestamp: new Date().toISOString(), link: '#categories', read: false } ]; updateNotificationCount(); renderNotifications(); } } // Toggle notification section visibility function toggleNotificationSection() { if (isNotificationSectionOpen) { closeNotificationSection(); } else { openNotificationSection(); } } // Open notification section function openNotificationSection() { notificationSection.classList.add('show'); isNotificationSectionOpen = true; renderNotifications(); } // Close notification section function closeNotificationSection() { notificationSection.classList.remove('show'); isNotificationSectionOpen = false; } // Render notifications in the list function renderNotifications() { if (!notificationsData || notificationsData.length === 0) { notificationList.innerHTML = `

No notifications yet

`; notificationSummary.innerHTML = 'No notifications'; olderNotificationsSection.style.display = 'none'; return; } // Sort notifications by timestamp (most recent first) const sortedNotifications = [...notificationsData].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp) ); // Split into recent and older notifications const recentNotifications = sortedNotifications.slice(0, 5); const olderNotifications = sortedNotifications.slice(5); // Update summary const unreadCount = notificationsData.filter(n => !n.read).length; const totalCount = notificationsData.length; notificationSummary.innerHTML = `${unreadCount} unread of ${totalCount} total notifications`; // Render recent notifications notificationList.innerHTML = ''; recentNotifications.forEach(notification => { const notificationElement = createNotificationElement(notification); notificationList.appendChild(notificationElement); }); // Handle older notifications section if (olderNotifications.length > 0) { olderNotificationsSection.style.display = 'block'; olderNotificationsToggle.querySelector('span').textContent = `Show ${olderNotifications.length} older notifications`; // Render older notifications in collapsed content olderNotificationsContent.innerHTML = ''; olderNotifications.forEach(notification => { const notificationElement = createNotificationElement(notification); olderNotificationsContent.appendChild(notificationElement); }); } else { olderNotificationsSection.style.display = 'none'; } } // Create a notification DOM element function createNotificationElement(notification) { const notificationDiv = document.createElement('div'); notificationDiv.className = `notification-item ${!notification.read ? 'unread' : ''}`; notificationDiv.dataset.notificationId = notification.id; const timeAgo = getTimeAgo(notification.timestamp); const typeIcon = getNotificationTypeIcon(notification.type); notificationDiv.innerHTML = `
${notification.image ? `Notification image` : ''}

${notification.title}

${notification.message}

${notification.link ? ` View Details ` : ''}
${timeAgo} ${typeIcon} ${notification.type}
${!notification.read ? `` : ''}
`; // Add click handler to mark as read when clicked (but not on links or buttons) notificationDiv.addEventListener('click', function(e) { if (!e.target.closest('.notification-actions-item') && !e.target.closest('.notification-link') && !notification.read) { markNotificationAsRead(notification.id); } }); return notificationDiv; } // Get notification type icon function getNotificationTypeIcon(type) { const icons = { 'info': 'ℹ️', 'success': '✅', 'warning': '⚠️', 'error': '❌' }; return icons[type] || 'ℹ️'; } // Get human-readable time ago function getTimeAgo(timestamp) { const now = new Date(); const notificationTime = new Date(timestamp); const diffInSeconds = Math.floor((now - notificationTime) / 1000); if (diffInSeconds < 60) { return 'Just now'; } else if (diffInSeconds < 3600) { const minutes = Math.floor(diffInSeconds / 60); return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`; } else if (diffInSeconds < 86400) { const hours = Math.floor(diffInSeconds / 3600); return `${hours} hour${hours !== 1 ? 's' : ''} ago`; } else if (diffInSeconds < 604800) { const days = Math.floor(diffInSeconds / 86400); return `${days} day${days !== 1 ? 's' : ''} ago`; } else { return notificationTime.toLocaleDateString(); } } // Mark a specific notification as read function markNotificationAsRead(notificationId) { const notification = notificationsData.find(n => n.id === notificationId); if (notification && !notification.read) { notification.read = true; saveReadNotifications(); updateNotificationCount(); renderNotifications(); } } // Mark all notifications as read function markAllNotificationsAsRead() { notificationsData.forEach(notification => { notification.read = true; }); saveReadNotifications(); updateNotificationCount(); renderNotifications(); } // Dismiss a specific notification function dismissNotification(notificationId) { notificationsData = notificationsData.filter(n => n.id !== notificationId); updateNotificationCount(); renderNotifications(); // Also remove from read notifications localStorage const readNotifications = JSON.parse(localStorage.getItem('festhub-read-notifications') || '[]'); const updatedReadNotifications = readNotifications.filter(id => id !== notificationId); localStorage.setItem('festhub-read-notifications', JSON.stringify(updatedReadNotifications)); } // Clear all notifications function clearAllNotifications() { notificationsData = []; updateNotificationCount(); renderNotifications(); localStorage.removeItem('festhub-read-notifications'); } // Toggle older notifications visibility function toggleOlderNotifications() { const content = olderNotificationsContent; const icon = olderNotificationsToggle.querySelector('i'); if (content.classList.contains('expanded')) { content.classList.remove('expanded'); icon.classList.remove('fa-chevron-up'); icon.classList.add('fa-chevron-down'); olderNotificationsToggle.querySelector('span').textContent = olderNotificationsToggle.querySelector('span').textContent.replace('Hide', 'Show'); } else { content.classList.add('expanded'); icon.classList.remove('fa-chevron-down'); icon.classList.add('fa-chevron-up'); olderNotificationsToggle.querySelector('span').textContent = olderNotificationsToggle.querySelector('span').textContent.replace('Show', 'Hide'); } } // Save read notifications to localStorage function saveReadNotifications() { const readNotifications = notificationsData .filter(n => n.read) .map(n => n.id); localStorage.setItem('festhub-read-notifications', JSON.stringify(readNotifications)); } // Show notification toast function showNotificationToast(notification) { toastTitle.textContent = notification.title; toastMessage.textContent = notification.message; // Set icon based on type const iconElement = toastIcon.querySelector('i'); toastIcon.className = `toast-icon ${notification.type}`; const iconMap = { 'info': 'fas fa-info-circle', 'success': 'fas fa-check-circle', 'warning': 'fas fa-exclamation-triangle', 'error': 'fas fa-exclamation-circle' }; iconElement.className = iconMap[notification.type] || 'fas fa-bell'; // Show toast notificationToast.classList.add('show'); // Hide toast after 5 seconds setTimeout(() => { notificationToast.classList.remove('show'); }, 5000); } // Start checking for new notifications (simulate real-time updates) function startNotificationChecking() { // Check every 30 seconds for new notifications notificationCheckInterval = setInterval(async () => { try { const response = await fetch('./notifications.json'); if (response.ok) { const freshNotifications = await response.json(); // Check for new notifications const currentIds = notificationsData.map(n => n.id); const newNotifications = freshNotifications.filter(n => !currentIds.includes(n.id)); if (newNotifications.length > 0) { // Add new notifications notificationsData = [...newNotifications, ...notificationsData]; updateNotificationCount(); // Show toast for the most recent new notification if (newNotifications.length > 0) { const latestNotification = newNotifications.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp) )[0]; showNotificationToast(latestNotification); } // Re-render if notification section is open if (isNotificationSectionOpen) { renderNotifications(); } } } } catch (error) { console.error('Error checking for new notifications:', error); } }, 30000); // Check every 30 seconds } // Make functions globally accessible window.markNotificationAsRead = markNotificationAsRead; window.dismissNotification = dismissNotification; window.handleNotificationLink = handleNotificationLink; // Handle notification link clicks function handleNotificationLink(event, link, notificationId) { event.preventDefault(); event.stopPropagation(); // Mark notification as read when link is clicked markNotificationAsRead(notificationId); // Handle different types of links if (link.startsWith('#')) { // Internal anchor link const element = document.querySelector(link); if (element) { closeNotificationSection(); element.scrollIntoView({ behavior: 'smooth', block: 'start' }); } else { console.warn('Anchor element not found:', link); } } else if (link.startsWith('http://') || link.startsWith('https://')) { // External link window.open(link, '_blank'); closeNotificationSection(); } else { // Internal page link closeNotificationSection(); window.location.href = link; } } // Show favorites modal function showFavoritesModal() { if (favorites.length === 0) { alert('You haven\'t added any festivals to favorites yet!\n\nClick the heart icon on any festival card to add it to your favorites.'); return; } const favoriteItems = favorites.map(fav => `• ${fav.title}`).join('\n'); alert(`Your Favorite Festivals:\n\n${favoriteItems}`); } // Add to favorites function addToFavorites(festival) { if (!favorites.find(f => f.id === festival.id)) { favorites.push(festival); localStorage.setItem('festhub-favorites', JSON.stringify(favorites)); updateFavoritesCount(); return true; } return false; } // Remove from favorites function removeFromFavorites(festivalId) { favorites = favorites.filter(f => f.id !== festivalId); localStorage.setItem('festhub-favorites', JSON.stringify(favorites)); updateFavoritesCount(); } // Global toggle favorite function (called from card onclick) function toggleFavorite(festivalId) { const festival = websitesData.find(w => w.id === festivalId); if (!festival) return; const favoriteBtn = document.querySelector(`[onclick="toggleFavorite(${festivalId})"]`); if (favorites.find(f => f.id === festivalId)) { removeFromFavorites(festivalId); favoriteBtn.classList.remove('active'); favoriteBtn.title = 'Add to favorites'; } else { addToFavorites(festival); favoriteBtn.classList.add('active'); favoriteBtn.title = 'Remove from favorites'; } } // Make toggleFavorite globally accessible window.toggleFavorite = toggleFavorite; // Perform search function performSearch() { currentSearch = searchInput.value.trim(); // Track search queries if (typeof gtag !== 'undefined' && currentSearch) { gtag('event', 'search', { 'search_term': currentSearch }); } renderWebsites(); } // Check for festivals happening today function checkTodayFestivals() { const today = new Date(); const currentMonth = today.toLocaleString('default', { month: 'short' }); const currentDay = today.getDate().toString(); const todaysFestivals = eventsData.filter(event => { return event.day === currentDay && event.month === currentMonth; }); if (todaysFestivals.length > 0) { // Show a popup for the first festival happening today showFestivalModal(todaysFestivals[0]); } } // Show festival modal function showFestivalModal(festival) { currentFestival = festival; // Set modal content modalFestivalTitle.textContent = festival.title; modalFestivalDate.textContent = `${festival.day} ${festival.month} 2025`; modalFestivalDesc.textContent = festival.description; // Set modal color based on category let bgColor = 'var(--primary)'; switch(festival.category) { case 'hindu': bgColor = 'var(--hindu)'; break; case 'muslim': bgColor = 'var(--muslim)'; break; case 'christian': bgColor = 'var(--christian)'; break; case 'other': bgColor = 'var(--other)'; } modalFestivalDate.style.background = bgColor; modalWishNow.style.background = bgColor; // Show modal festivalModal.classList.add('active'); document.body.style.overflow = 'hidden'; } // Close festival modal function closeFestivalModal() { festivalModal.classList.remove('active'); document.body.style.overflow = ''; } // Open review modal function openReviewModal() { reviewModal.classList.add('active'); document.body.style.overflow = 'hidden'; } // Close review modal function closeReviewModal() { reviewModal.classList.remove('active'); document.body.style.overflow = ''; resetReviewForm(); } // Set rating function setRating(rating) { currentRating = rating; highlightStars(rating); } // Highlight stars up to the given rating function highlightStars(rating) { const stars = ratingStars.querySelectorAll('.rating-star'); stars.forEach((star, index) => { if (index < rating) { star.classList.remove('far'); star.classList.add('fas'); } else { star.classList.remove('fas'); star.classList.add('far'); } }); } // Reset review form function resetReviewForm() { currentRating = 0; highlightStars(0); reviewText.value = ''; } // Auto-scroll events function startAutoScroll() { // Clone the events to create a seamless loop const eventsContainer = eventsSlider; const events = Array.from(eventsContainer.children); // Duplicate events for seamless looping events.forEach(event => { const clone = event.cloneNode(true); eventsContainer.appendChild(clone); }); let scrollAmount = 0; const scrollSpeed = 1; // pixels per interval scrollInterval = setInterval(() => { scrollAmount += scrollSpeed; eventsContainer.scrollLeft = scrollAmount; // Reset scroll position when reaching the end if (scrollAmount >= eventsContainer.scrollWidth / 2) { scrollAmount = 0; eventsContainer.scrollLeft = 0; } }, 30); // Pause on hover eventsContainer.addEventListener('mouseenter', () => { clearInterval(scrollInterval); }); eventsContainer.addEventListener('mouseleave', () => { scrollInterval = setInterval(() => { scrollAmount += scrollSpeed; eventsContainer.scrollLeft = scrollAmount; // Reset scroll position when reaching the end if (scrollAmount >= eventsContainer.scrollWidth / 2) { scrollAmount = 0; eventsContainer.scrollLeft = 0; } }, 30); }); }