document.addEventListener('DOMContentLoaded', async () => { // ------------------------------------------------------------- // DOM entry point and shared asset path. // ------------------------------------------------------------- const detailContainer = document.getElementById('detail-view'); const locationIconPath = 'assets/location-pin.svg'; // Read event id from query string (detail page deep-link support). const params = new URLSearchParams(window.location.search); const eventId = parseInt(params.get('id')); if (!eventId) { window.location.href = 'event_overview.html'; return; } // Fetch data source and resolve the matching event record. try { const response = await fetch('data/events.json'); const allEvents = await response.json(); 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); } // Format localized date token into full readable date. function formatEventDate(dateString) { 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 casing for UI consistency. function formatEventTime(timeString) { return timeString.replace('UHR', 'Uhr').trim(); } // Map diet keys to readable labels while keeping unknown values untouched. function getDietLabel(diet) { const labels = { VEGGIE: 'Vegetarisch', VEGAN: 'Vegan', FLEISCH: 'Fleisch', FISCH: 'Fisch' }; return labels[diet] || diet; } // Compose and inject the full detail UI for a single event. function renderDetailPage(event) { // Core display values and resilient fallbacks for optional data fields. const displayDate = formatEventDate(event.date); const displayTime = formatEventTime(event.time); const dietLabel = getDietLabel(event.diet); const eventCategory = event.category || 'EVENT'; const hostName = event.host?.name || 'Host'; const hostInitial = (event.host?.initial || hostName.charAt(0) || 'H').charAt(0).toUpperCase(); 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 Kuerze bekannt gegeben.']; const specifications = Array.isArray(event.specifications) && event.specifications.length > 0 ? event.specifications : []; const participants = Array.isArray(event.participants) ? event.participants : []; const galleryImages = Array.isArray(event.gallery) && event.gallery.length > 0 ? event.gallery : [event.image, event.image, event.image]; const visibleParticipants = participants.slice(0, 6); const remainingParticipants = Math.max(0, participants.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 detailChips = [ `${eventCategory}`, `${dietLabel}`, ...specifications.map(item => `${item}`) ].join(''); // Render complete detail page layout including: // hero metadata, host card, menu, participants, gallery and sticky action bar. detailContainer.innerHTML = `
Alle Events
${event.location}

${displayDate} | ${displayTime} | ${confirmedGuests}/${totalGuests} Gaeste

${event.title}

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

${paragraph}

`).join('')}

Menue

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

Teilnehmer

Alle ansehen
${visibleParticipants.map(name => `${name.charAt(0).toUpperCase()}`).join('')} ${remainingParticipants > 0 ? `+${remainingParticipants}` : ''}
${event.location} | ${displayDate} | ${displayTime} | ${confirmedGuests}/${totalGuests} Gaeste ${event.title}
${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plaetze frei`}
`; // --------------------------------------------------------- // Lightbox behavior for gallery images: // open on image click, close via backdrop, close button or ESC. // --------------------------------------------------------- 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'); // Central close helper to keep all close paths consistent. function closeLightbox() { if (!lightbox) { return; } lightbox.classList.remove('is-open'); lightbox.setAttribute('aria-hidden', 'true'); } if (lightbox && lightboxImage) { // Open with selected image source. 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'); }); }); // Close when user clicks on backdrop. lightbox.addEventListener('click', event => { const target = event.target; if (target instanceof HTMLElement && target.hasAttribute('data-close-lightbox')) { closeLightbox(); } }); // Close via dedicated icon/button. lightboxClose?.addEventListener('click', closeLightbox); // Close with keyboard for accessibility. document.addEventListener('keydown', event => { if (event.key === 'Escape') { closeLightbox(); } }); } } });