document.addEventListener('DOMContentLoaded', async () => { const EVENTS_STORAGE_KEY = 'socialCookingEvents'; const CURRENT_USER_KEY = 'socialCookingCurrentUser'; const USERS_STORAGE_KEY = 'socialCookingUsers'; const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations'; const detailcontainer = document.getElementById('detail-view'); const locationIconPath = 'assets/icon_location.svg'; const calendarIconPath = 'assets/icon_calendar.svg'; const gastIconPath = 'assets/icon_gast.svg'; const currentUser = getCurrentUser(); const params = new URLSearchParams(window.location.search); const eventId = parseInt(params.get('id')); if (!eventId) { window.location.href = 'event_overview.html'; return; } 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 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; } } 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 getStoredUsers() { try { const stored = localStorage.getItem(USERS_STORAGE_KEY); return stored ? JSON.parse(stored) : []; } catch (error) { console.error('Benutzerdaten konnten nicht gelesen werden.', error); return []; } } function getUserDisplayName(user) { if (!user) return ''; const firstName = String(user.vorname || '').trim(); const lastName = String(user.nachname || '').trim(); const fullName = `${firstName} ${lastName}`.trim(); return (fullName || firstName || String(user.email || '').trim()).trim(); } function getResolvedParticipants(event, registrationMap) { const baseParticipants = Array.isArray(event.participants) ? event.participants.map(name => String(name || '').trim()).filter(Boolean) : []; const usersByEmail = new Map( getStoredUsers().map(user => [String(user.email || '').trim().toLowerCase(), user]) ); const participantLookup = new Set(baseParticipants.map(name => name.toLowerCase())); Object.entries(registrationMap || {}).forEach(([email, ids]) => { const isRegisteredForEvent = Array.isArray(ids) && ids.map(id => Number(id)).includes(Number(event.id)); if (!isRegisteredForEvent) return; const user = usersByEmail.get(String(email || '').trim().toLowerCase()); const displayName = getUserDisplayName(user) || String(email || '').trim(); const normalizedName = displayName.toLowerCase(); if (displayName && !participantLookup.has(normalizedName)) { baseParticipants.push(displayName); participantLookup.add(normalizedName); } }); return baseParticipants; } function getParticipantNameForViewer(name, canSeeLastName) { const rawName = String(name || '').trim(); if (!rawName) return ''; if (canSeeLastName) return rawName; if (rawName.includes('@')) return rawName.split('@')[0].trim() || rawName; return rawName.split(/\s+/)[0]; } function setRegistrationMap(registrationMap) { localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap)); } function getRegistrationIdsForUser(registrationMap, user) { const userEmail = String(user?.email || '').trim().toLowerCase(); if (!userEmail) return []; const matchingIds = Object.entries(registrationMap || {}) .filter(([email]) => String(email || '').trim().toLowerCase() === userEmail) .flatMap(([, ids]) => (Array.isArray(ids) ? ids : [])) .map(id => Number(id)) .filter(id => Number.isFinite(id)); return Array.from(new Set(matchingIds)); } 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, month, day; if (isoDateMatch) { year = Number(isoDateMatch[1]); month = Number(isoDateMatch[2]); day = Number(isoDateMatch[3]); } else { const monthMap = { jan: 1, januar: 1, feb: 2, februar: 2, 'mär': 3, mrz: 3, mar: 3, maerz: 3, märz: 3, apr: 4, april: 4, mai: 5, jun: 6, juni: 6, jul: 7, juli: 7, aug: 8, august: 8, sep: 9, sept: 9, september: 9, okt: 10, oktober: 10, nov: 11, november: 11, dez: 12, dezember: 12 }; const localizedMatch = dateValue.match(/^(\d{1,2})\.\s*([A-Za-zÄÖÜäöü]{3,9})\.?\s*(\d{4})$/); if (!localizedMatch) return null; day = Number(localizedMatch[1]); month = monthMap[String(localizedMatch[2]).toLowerCase()]; 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); } function isRegistrationClosedForEvent(event) { const eventDateTime = parseEventDateTime(event); if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) return false; const msUntilStart = eventDateTime.getTime() - Date.now(); return msUntilStart <= 24 * 60 * 60 * 1000; } function getDeregistrationInfo(event) { const eventDateTime = parseEventDateTime(event); if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) return { daysLeft: null, isClosed: false }; const oneDayMs = 24 * 60 * 60 * 1000; const msUntilDeadline = (eventDateTime.getTime() - oneDayMs) - Date.now(); if (msUntilDeadline <= 0) return { daysLeft: 0, isClosed: true }; return { daysLeft: Math.ceil(msUntilDeadline / oneDayMs), isClosed: false }; } function isAddressVisibleWindow(event) { const eventDateTime = parseEventDateTime(event); if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) return false; const now = Date.now(); const start = eventDateTime.getTime(); const revealStart = start - (24 * 60 * 60 * 1000); const revealEnd = start + (1 * 60 * 60 * 1000); return now >= revealStart && now <= revealEnd; } function isEventPastAddressWindow(event) { const eventDateTime = parseEventDateTime(event); if (!eventDateTime || Number.isNaN(eventDateTime.getTime())) return false; const revealEnd = eventDateTime.getTime() + (1 * 60 * 60 * 1000); return Date.now() > revealEnd; } 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 isUserListedInEventParticipants(event, user) { if (!event || !user || !Array.isArray(event.participants)) return false; const participantSet = new Set( event.participants.map(name => String(name || '').trim().toLowerCase()).filter(Boolean) ); const userFirstName = String(user.vorname || '').trim().toLowerCase(); const userFullName = `${String(user.vorname || '').trim()} ${String(user.nachname || '').trim()}`.trim().toLowerCase(); return Boolean( (userFirstName && participantSet.has(userFirstName)) || (userFullName && participantSet.has(userFullName)) ); } 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 monthLabel = labels[match[2]]; return monthLabel ? `${Number(match[1])}. ${monthLabel} ${match[3]}` : dateString; } function formatEventTime(timeString) { return timeString.replace('UHR', 'Uhr').trim(); } function getDietLabel(diet) { const labels = { FLEISCH:'Fleisch', FISCH:'Fisch', VEGGIE:'Vegetarisch', VEGAN:'Vegan' }; return labels[diet] || diet; } function getPlaceholderImageByEventType(event) { const rawType = String(event?.eventType || event?.category || '') .trim() .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/[+&/_-]/g, ' ') .replace(/\s+/g, ' '); if (rawType.includes('brunch')) { return 'assets/platzhalter_brunch.jpeg'; } if (rawType.includes('lunch')) { return 'assets/platzhalter_lunch.jpeg'; } if ( rawType.includes('kaffee') || rawType.includes('coffee') || rawType.includes('cafe') || rawType.includes('kuchen') ) { return 'assets/platzhalter_kaffee.jpeg'; } if (rawType.includes('dinner')) { return 'assets/platzhalter_dinner.jpeg'; } return 'assets/platzhalter_dinner.jpeg'; } // Fetch data source and resolve the matching event record. try { const response = await fetch('data/events.json'); const apiEvents = await response.json(); const allEvents = [...getStoredEvents(), ...apiEvents]; const event = allEvents.find(e => e.id === eventId); if (event) { renderDetailPage(event); } else { detailcontainer.innerHTML = "

Event wurde nicht gefunden.

Zurück zur Übersicht"; } } catch (error) { console.error("Fehler beim Laden der Details:", error); } function renderDetailPage(event) { const displayDate = formatEventDate(event.date); const displayTime = formatEventTime(event.time); const eventCategory = event.category || 'EVENT'; const hostName = event.host?.name || 'Host'; const hostMessage = Array.isArray(event.hostMessage) && event.hostMessage.length > 0 ? event.hostMessage : ['Der Host hat für dieses Event noch keine Nachricht hinterlegt.']; const menuItems = Array.isArray(event.menu) && event.menu.length > 0 ? event.menu : ['Menü wird in Kürze bekannt gegeben.']; const specifications = Array.isArray(event.specifications) && event.specifications.length > 0 ? event.specifications : []; const registrationMap = getRegistrationMap(); const participants = getResolvedParticipants(event, registrationMap); const isOwnEvent = isEventOwnedByCurrentUser(event, currentUser); const participantNamesForView = participants .map(name => getParticipantNameForViewer(name, isOwnEvent)) .filter(Boolean); const galleryImages = Array.isArray(event.gallery) ? event.gallery.filter(Boolean) : []; const resolvedGalleryImages = galleryImages.length > 0 ? galleryImages : [getPlaceholderImageByEventType(event)]; const galleryLayoutClass = resolvedGalleryImages.length === 1 ? 'detail-gallery detail-gallery-large detail-gallery-large--single' : 'detail-gallery detail-gallery-large'; const galleryMarkup = resolvedGalleryImages.length > 0 ? `
${resolvedGalleryImages.slice(0, 9).map((img, index) => ` `).join('')}
` : ''; const visibleParticipants = participantNamesForView.slice(0, 6); const remainingParticipants = Math.max(0, participantNamesForView.length - visibleParticipants.length); const totalGuests = Number.isFinite(event.spots) ? event.spots : 0; const confirmedGuests = participants.length; const freePlaces = Math.max(0, totalGuests - confirmedGuests); const isFull = freePlaces === 0; const isRegistrationClosed = isRegistrationClosedForEvent(event); const deregInfo = getDeregistrationInfo(event); const userRegistrations = getRegistrationIdsForUser(registrationMap, currentUser); const isRegistered = userRegistrations.includes(Number(event.id)); const isListedParticipant = isUserListedInEventParticipants(event, currentUser); const hasAddressAccess = isRegistered || isListedParticipant || isOwnEvent; const isCanceled = event.status === 'canceled'; const actionButtonLabel = isCanceled ? 'Abgesagt' : isOwnEvent ? 'Dein Event!' : !currentUser ? 'Einloggen' : isRegistered ? (deregInfo.isClosed ? 'Abmeldung geschlossen' : 'Abmelden') : isRegistrationClosed ? 'Anmeldung geschlossen' : 'Anmelden'; const actionButtonDisabled = isCanceled || isOwnEvent || (!isRegistered && (isFull || isRegistrationClosed)) || (isRegistered && deregInfo.isClosed); const actionButtonVariantClass = isOwnEvent ? ' button-primary-eigener-event' : (isRegistered || isRegistrationClosed) ? ' button-primary-abmelden ' : ' button-primary '; const shouldRevealAddress = Boolean(event.address) && isAddressVisibleWindow(event) && hasAddressAccess; let addressMessage = 'Wenn du dich anmeldest, wird die Adresse für diesen Event wird 24 Stunden vorher genau hier sichtbar sein.'; if (isCanceled) { addressMessage = 'Dieses Event wurde leider vom Gastgeber abgesagt.'; } else if (isOwnEvent) { addressMessage = 'Deine Adresse für diesen Event wird 24 Stunden vorher genau hier für alle Teilnehmer sichtbar sein'; } else if (hasAddressAccess) { addressMessage = 'Vielen Dank für die Anmeldung! Die Adresse für diesen Event wird 24 Stunden vorher genau hier sichtbar sein.'; } if (!isCanceled && isEventPastAddressWindow(event)) { addressMessage = 'Vielen Dank, dass du an diesem Event teilgenommen hast.'; } const addressPanelMarkup = shouldRevealAddress ? `

Adresse

${event.address}

` : `

Adresse

${addressMessage}

`; const detailChips = [ `${eventCategory}`, ...event.diet.split(', ').filter(d => d.trim() && d !== 'Keine Angabe').map(d => `${getDietLabel(d.trim())}`), ...specifications.map(item => `${item}`) ].join(''); detailcontainer.innerHTML = `
${event.location} ${displayDate} | ${displayTime} ${confirmedGuests}/${totalGuests}

${event.title}

${detailChips}
Host ${hostName}
${hostMessage.map(paragraph => `

${paragraph}

`).join('')}

Menu

    ${menuItems.map(item => `
  • ${item}
  • `).join('')}

Teilnehmer

${visibleParticipants.map(name => `${name.charAt(0).toUpperCase()}`).join('')} ${remainingParticipants > 0 ? `+${remainingParticipants}` : ''}
${addressPanelMarkup}
${galleryMarkup}
${event.location} ${displayDate} | ${displayTime} ${confirmedGuests}/${totalGuests} ${event.title}
${isFull ? ` ` : ` `} ${isRegistered && deregInfo.daysLeft !== null ? ` ${deregInfo.isClosed ? 'Abmeldefrist abgelaufen' : deregInfo.daysLeft === 1 ? 'Noch 1 Tag zur Abmeldung' : `Noch ${deregInfo.daysLeft} Tage zur Abmeldung`} ` : ''}
`; // DOM references after render const lightbox = detailcontainer.querySelector('.detail-lightbox'); const lightboxImage = detailcontainer.querySelector('.detail-lightbox-image'); const lightboxClose = detailcontainer.querySelector('.detail-lightbox-close'); const galleryButtons = detailcontainer.querySelectorAll('.detail-gallery-item'); const registerButton = detailcontainer.querySelector('[data-register-button]'); // Eigene Events immer deaktiviert if (registerButton && isOwnEvent) { registerButton.disabled = true; registerButton.textContent = 'Dein Event!'; registerButton.setAttribute('aria-disabled', 'true'); } // Anmeldung / Abmeldung mit Bestätigungs-Modal if (registerButton) { registerButton.addEventListener('click', () => { if (isOwnEvent) return; if (!currentUser || !currentUser.email) { window.location.href = 'login.html'; return; } const alreadyRegistered = (() => { const map = getRegistrationMap(); const ids = Array.isArray(map[currentUser.email]) ? map[currentUser.email].map(id => Number(id)) : []; return ids.includes(Number(event.id)); })(); if (alreadyRegistered) { const modal = document.getElementById('unregister-confirm-modal'); if (modal) modal.classList.add('show'); document.getElementById('confirm-unregister-btn').onclick = () => { modal.classList.remove('show'); const map = getRegistrationMap(); const ids = new Set((map[currentUser.email] || []).map(id => Number(id))); ids.delete(Number(event.id)); map[currentUser.email] = Array.from(ids); setRegistrationMap(map); const snackbar = document.getElementById('snackbar'); if (snackbar) { snackbar.textContent = 'Du wurdest erfolgreich abgemeldet.'; snackbar.classList.add('snackbar--danger', 'snackbar--visible'); setTimeout(() => { snackbar.classList.remove('snackbar--visible'); setTimeout(() => snackbar.classList.remove('snackbar--danger'), 400); }, 4000); } renderDetailPage(event); }; document.getElementById('unregister-modal-close').onclick = () => modal.classList.remove('show'); document.getElementById('unregister-modal-cancel').onclick = () => modal.classList.remove('show'); modal.addEventListener('click', e => { if (e.target === modal) modal.classList.remove('show'); }); } else if (!isFull && !isRegistrationClosed) { const modal = document.getElementById('register-confirm-modal'); if (modal) modal.classList.add('show'); document.getElementById('confirm-register-btn').onclick = () => { modal.classList.remove('show'); const map = getRegistrationMap(); const ids = new Set((map[currentUser.email] || []).map(id => Number(id))); ids.add(Number(event.id)); map[currentUser.email] = Array.from(ids); setRegistrationMap(map); const snackbar = document.getElementById('snackbar'); if (snackbar) { snackbar.textContent = 'Du wurdest erfolgreich angemeldet.'; snackbar.classList.add('snackbar--visible'); setTimeout(() => snackbar.classList.remove('snackbar--visible'), 4000); } renderDetailPage(event); }; document.getElementById('register-modal-close').onclick = () => modal.classList.remove('show'); document.getElementById('register-modal-cancel').onclick = () => modal.classList.remove('show'); modal.addEventListener('click', e => { if (e.target === modal) modal.classList.remove('show'); }); } }); } // "Alle ansehen": Teilnehmerliste aufklappen / zuklappen const showAllBtn = detailcontainer.querySelector('[data-show-all-participants]'); const avatarRow = detailcontainer.querySelector('[data-participants-row]'); const fullList = detailcontainer.querySelector('[data-participants-full]'); if (showAllBtn && avatarRow && fullList) { showAllBtn.addEventListener('click', () => { const isExpanded = !fullList.classList.contains('hidden'); fullList.classList.toggle('hidden'); avatarRow.classList.toggle('hidden'); showAllBtn.textContent = isExpanded ? 'Alle ansehen' : 'Weniger anzeigen'; }); } // Lightbox function closeLightbox() { if (!lightbox) return; lightbox.classList.remove('is-open'); lightbox.setAttribute('aria-hidden', 'true'); } if (lightbox && lightboxImage) { galleryButtons.forEach(button => { button.addEventListener('click', () => { const imageSrc = button.getAttribute('data-fullsrc'); if (!imageSrc) return; lightboxImage.src = imageSrc; lightbox.classList.add('is-open'); lightbox.setAttribute('aria-hidden', 'false'); }); }); lightbox.addEventListener('click', event => { const target = event.target; if (target instanceof HTMLElement && target.hasAttribute('data-close-lightbox')) { closeLightbox(); } }); lightboxClose?.addEventListener('click', closeLightbox); document.addEventListener('keydown', event => { if (event.key === 'Escape') closeLightbox(); }); } } });