document.addEventListener('DOMContentLoaded', () => { const EVENTS_STORAGE_KEY = 'socialCookingEvents'; const CURRENT_USER_KEY = 'socialCookingCurrentUser'; const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations'; // ------------------------------------------------------------- // DOM references used throughout the page lifecycle. // ------------------------------------------------------------- const eventGrid = document.getElementById('event-grid'); const filterButtons = document.querySelectorAll('.category-item'); const locationFilter = document.getElementById('location-filter'); const dateFilter = document.getElementById('date-filter'); const locationIconPath = 'assets/location-pin.svg'; // ------------------------------------------------------------- // In-memory state for fetched events and currently active category. // ------------------------------------------------------------- let allEvents = []; let activeCategory = 'ALLE'; const currentUser = getCurrentUser(); function getCurrentUser() { try { const stored = localStorage.getItem(CURRENT_USER_KEY); return stored ? JSON.parse(stored) : null; } catch (error) { console.error('Aktueller Benutzer konnte nicht gelesen werden.', error); return null; } } // Prueft, ob ein Event dem aktuellen Benutzer gehoert. function isEventOwnedByCurrentUser(event, user) { if (!event || !user) { return false; } const userEmail = String(user.email || '').trim().toLowerCase(); const hostEmail = String(event.hostEmail || '').trim().toLowerCase(); if (userEmail && hostEmail) { return userEmail === hostEmail; } const userFirstName = String(user.vorname || '').trim().toLowerCase(); const hostName = String(event.host?.name || '').trim().toLowerCase(); return Boolean(userFirstName && hostName && userFirstName === hostName); } function getStoredEvents() { try { const stored = localStorage.getItem(EVENTS_STORAGE_KEY); return stored ? JSON.parse(stored) : []; } catch (error) { console.error('Lokale Events konnten nicht gelesen werden.', error); return []; } } function getRegistrationMap() { try { const stored = localStorage.getItem(REGISTRATION_STORAGE_KEY); return stored ? JSON.parse(stored) : {}; } catch (error) { console.error('Anmeldedaten konnten nicht gelesen werden.', error); return {}; } } function setRegistrationMap(registrationMap) { localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap)); } // ------------------------------------------------------------- // Initial data bootstrap: // 1) fetch JSON, // 2) populate select options, // 3) restore filter state from sessionStorage, // 4) render filtered list. // ------------------------------------------------------------- async function fetchEvents() { try { const response = await fetch('data/events.json'); const apiEvents = await response.json(); const localEvents = getStoredEvents(); allEvents = [...localEvents, ...apiEvents]; populateMetaFilters(); const savedCategory = sessionStorage.getItem('activeFilter') || 'ALLE'; const savedLocation = sessionStorage.getItem('activeLocation') || 'ALLE_ORTE'; const savedDate = sessionStorage.getItem('activeDate') || ''; activeCategory = savedCategory; if (locationFilter) { locationFilter.value = hasOption(locationFilter, savedLocation) ? savedLocation : 'ALLE_ORTE'; } if (dateFilter) { dateFilter.value = savedDate; } applyFilters(); } catch (error) { console.error('Fehler:', error); eventGrid.innerHTML = '
Events konnten nicht geladen werden.
'; } } // Build location options dynamically from loaded events. function populateMetaFilters() { const locations = [...new Set(allEvents.map(event => event.location))].sort(); if (locationFilter) { locations.forEach(location => { const option = document.createElement('option'); option.value = location; option.textContent = location; locationFilter.appendChild(option); }); } } // Convert localized event date (e.g. 19. MÄR. 2026) into ISO format for date input comparison. function parseEventDateToIso(dateString) { if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) { return dateString; } const months = { JAN: '01', FEB: '02', 'MÄR': '03', MRZ: '03', APR: '04', MAI: '05', JUN: '06', JUL: '07', AUG: '08', SEP: '09', OKT: '10', NOV: '11', DEZ: '12' }; const match = dateString.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/); if (!match) { return ''; } const day = String(match[1]).padStart(2, '0'); const month = months[match[2]]; const year = match[3]; return month ? `${year}-${month}-${day}` : ''; } // Convert short month notation into full German month label for UI display. function formatEventDate(dateString) { if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) { const [year, month, day] = dateString.split('-'); return `${Number(day)}. ${['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'][Number(month) - 1]} ${year}`; } const labels = { JAN: 'Januar', FEB: 'Februar', 'MÄR': 'März', MRZ: 'März', APR: 'April', MAI: 'Mai', JUN: 'Juni', JUL: 'Juli', AUG: 'August', SEP: 'September', OKT: 'Oktober', NOV: 'November', DEZ: 'Dezember' }; const match = dateString.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/); if (!match) { return dateString; } const day = Number(match[1]); const monthLabel = labels[match[2]]; const year = match[3]; return monthLabel ? `${day}. ${monthLabel} ${year}` : dateString; } // Normalize time label from UHR to Uhr for consistent typography. function formatEventTime(timeString) { if (!timeString) { return ''; } return timeString.includes('UHR') ? timeString.replace('UHR', 'Uhr').trim() : `${timeString} Uhr`; } // Baut aus Eventdatum/-zeit ein Date-Objekt fuer Fristlogik und Vergleiche. function parseEventDateTime(event) { if (!event?.date) { return null; } const dateValue = String(event.date).trim(); const isoDateMatch = dateValue.match(/^(\d{4})-(\d{2})-(\d{2})$/); let year; let month; let day; if (isoDateMatch) { year = Number(isoDateMatch[1]); month = Number(isoDateMatch[2]); day = Number(isoDateMatch[3]); } else { const monthMap = { JAN: 1, FEB: 2, 'MÄR': 3, MRZ: 3, APR: 4, MAI: 5, JUN: 6, JUL: 7, AUG: 8, SEP: 9, OKT: 10, NOV: 11, DEZ: 12 }; const localizedMatch = dateValue.match(/^(\d{1,2})\.\s*([A-ZÄÖÜ]{3})\.\s*(\d{4})$/); if (!localizedMatch) { return null; } day = Number(localizedMatch[1]); month = monthMap[localizedMatch[2]]; year = Number(localizedMatch[3]); if (!month) { return null; } } const timeMatch = String(event.time || '').match(/(\d{1,2}):(\d{2})/); const hours = timeMatch ? Number(timeMatch[1]) : 0; const minutes = timeMatch ? Number(timeMatch[2]) : 0; return new Date(year, month - 1, day, hours, minutes, 0, 0); } // Zaehlt eindeutige Registrierungen eines Events ueber alle Benutzer. function countRegistrationsForEvent(registrationMap, eventId) { return Object.values(registrationMap).reduce((count, ids) => { const hasEvent = Array.isArray(ids) && ids.map(id => Number(id)).includes(Number(eventId)); return hasEvent ? count + 1 : count; }, 0); } // Schliesst neue Anmeldungen ab 12h vor Start (inkl. bereits gestarteter Events). function isRegistrationClosedForEvent(event) { const eventDateTime = parseEventDateTime(event); if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) { return false; } const msUntilStart = eventDateTime.getTime() - Date.now(); const twelveHoursInMs = 12 * 60 * 60 * 1000; return msUntilStart <= twelveHoursInMs; } // Safely verify whether a value exists in the given select element. function hasOption(selectElement, value) { return Array.from(selectElement.options).some(option => option.value === value); } // Apply all filters together (category, location, date), update button state, render and persist. function applyFilters() { const selectedLocation = locationFilter ? locationFilter.value : 'ALLE_ORTE'; const selectedDate = dateFilter ? dateFilter.value : ''; filterButtons.forEach(btn => { if (btn.getAttribute('data-cat') === activeCategory) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); const filtered = allEvents.filter(event => { // Lokal erstellte Events werden nicht in der allgemeinen Event-Uebersicht angezeigt. if (event.source === 'local') { return false; } const categoryMatch = activeCategory === 'ALLE' || event.category === activeCategory; const locationMatch = selectedLocation === 'ALLE_ORTE' || event.location === selectedLocation; const eventDateIso = parseEventDateToIso(event.date); const dateMatch = !selectedDate || eventDateIso === selectedDate; return categoryMatch && locationMatch && dateMatch; }); renderEvents(filtered); sessionStorage.setItem('activeFilter', activeCategory); sessionStorage.setItem('activeLocation', selectedLocation); sessionStorage.setItem('activeDate', selectedDate); } // Render either: // - empty state call-to-action when no results match, // - or event cards with status and metadata. function renderEvents(events) { eventGrid.innerHTML = ''; const registrationMap = getRegistrationMap(); const userRegistrationSet = currentUser?.email && Array.isArray(registrationMap[currentUser.email]) ? new Set(registrationMap[currentUser.email].map(id => Number(id))) : new Set(); if (events.length === 0) { eventGrid.innerHTML = `Keine Treffer
Starte dein eigenes Dinner und bringe die Community an deinen Tisch.
${displayDate} | ${displayTime} | ${bookedSeats}/${totalCapacity} Gaeste