document.addEventListener('DOMContentLoaded', () => { const EVENTS_STORAGE_KEY = 'socialCookingEvents'; const USERS_STORAGE_KEY = 'socialCookingUsers'; const CURRENT_USER_KEY = 'socialCookingCurrentUser'; const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations'; // Zentrale DOM-Referenzen für klare, testbare Funktionen. const loggedOutState = document.getElementById('logged-out-state'); const loggedInContent = document.getElementById('logged-in-content'); const profileHeadline = document.getElementById('headline'); const profileSubline = document.getElementById('profile-subline'); const logoutButton = document.getElementById('logout-button'); const profileTabButtons = Array.from(document.querySelectorAll('[data-category-item]')); const profileTabPanels = Array.from(document.querySelectorAll('[data-profile-panel]')); const myEventsCount = document.getElementById('my-events-count'); const myEventsBtnCount = document.getElementById('btn-my-events-count'); const myRegistrationsCount = document.getElementById('my-registrations-count'); const myRegistrationsBtnCount = document.getElementById('btn-my-registrations-count'); const myEventsList = document.getElementById('my-events-list'); const myRegistrationsList = document.getElementById('my-registrations-list'); const profileForm = document.getElementById('profile-form'); const profileFeedback = document.getElementById('profile-feedback'); const vornameInput = document.getElementById('vorname'); const nachnameInput = document.getElementById('nachname'); const emailInput = document.getElementById('email'); const passwortInput = document.getElementById('passwort'); let currentUser = getCurrentUser(); let allEvents = []; init(); async function init() { if (!currentUser) { renderLoggedOutState(); return; } renderLoggedInState(currentUser); bindFormHandlers(); activateProfileTab('hosting'); allEvents = await loadAllEvents(); renderMyEvents(allEvents, currentUser); renderMyRegistrations(allEvents, currentUser); } // Liest den aktuell eingeloggten Benutzer robust aus dem Storage. function getCurrentUser() { try { const raw = localStorage.getItem(CURRENT_USER_KEY); return raw ? JSON.parse(raw) : null; } catch (error) { console.error('Der aktuelle Benutzer konnte nicht geladen werden.', error); return null; } } // Liest lokal erstellte Events aus dem Storage. function getStoredEvents() { try { const raw = localStorage.getItem(EVENTS_STORAGE_KEY); return raw ? JSON.parse(raw) : []; } catch (error) { console.error('Lokale Events konnten nicht gelesen werden.', error); return []; } } // Liest den Anmeldestatus pro Benutzer-E-Mail. function getRegistrationMap() { try { const raw = localStorage.getItem(REGISTRATION_STORAGE_KEY); return raw ? JSON.parse(raw) : {}; } catch (error) { console.error('Anmeldedaten konnten nicht gelesen werden.', error); return {}; } } // Schreibt den gesamten Registrierungszustand in localStorage. function setRegistrationMap(registrationMap) { localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap)); } // Schreibt die lokal erstellten Events in den Storage. function setStoredEvents(events) { localStorage.setItem(EVENTS_STORAGE_KEY, JSON.stringify(events)); } // Fuehrt JSON-Daten und lokal erstellte Events in einer Liste zusammen. async function loadAllEvents() { try { const response = await fetch('data/events.json'); const apiEvents = await response.json(); return [...getStoredEvents(), ...apiEvents]; } catch (error) { console.error('Events konnten nicht geladen werden.', error); return getStoredEvents(); } } // Schaltet in den ausgeloggten Zustand und blendet geschuetzte Inhalte aus. function renderLoggedOutState() { loggedOutState.classList.remove('hidden'); loggedInContent.classList.add('hidden'); logoutButton.classList.add('hidden'); profileHeadline.textContent = 'Mein Profil'; profileSubline.textContent = 'Bitte logge dich ein, um deinen Bereich zu sehen.'; } // Füllt Überschriften und Formular mit den aktuellen Benutzerdaten. function renderLoggedInState(user) { loggedOutState.classList.add('hidden'); loggedInContent.classList.remove('hidden'); logoutButton.classList.remove('hidden'); profileHeadline.textContent = `Hallo ${user.vorname || 'Gast'}`; profileSubline.textContent = 'Hier kannst du deine Events und Anmeldungen verwalten.'; vornameInput.value = user.vorname || ''; nachnameInput.value = user.nachname || ''; emailInput.value = user.email || ''; } // Bindet Submit-, Input- und Logout-Verhalten an die Profilseite. function bindFormHandlers() { profileForm.addEventListener('submit', handleProfileSubmit); myRegistrationsList.addEventListener('click', handleRegistrationListClick); myEventsList.addEventListener('click', handleHostedListClick); profileTabButtons.forEach(button => { button.addEventListener('click', () => { const tabName = button.getAttribute('data-category-item'); if (!tabName) { return; } activateProfileTab(tabName); }); }); [vornameInput, nachnameInput, emailInput, passwortInput].forEach(input => { input.addEventListener('input', () => { input.parentElement.classList.remove('has-error'); profileFeedback.textContent = ''; }); }); logoutButton.addEventListener('click', () => { const logoutModal = document.getElementById('logoutModal'); logoutModal.classList.add('show'); document.body.style.overflow = 'hidden'; }); } // Globale Funktionen für das Logout-Modal. window.closeLogoutModal = function() { const logoutModal = document.getElementById('logoutModal'); logoutModal.classList.remove('show'); document.body.style.overflow = 'auto'; }; window.confirmLogout = function() { localStorage.removeItem(CURRENT_USER_KEY); window.location.href = 'index.html'; }; // Reagiert auf Aktionen in der Liste "Meine Events" per Event Delegation. function handleHostedListClick(event) { const target = event.target; if (!(target instanceof HTMLElement)) { return; } const cancelButton = target.closest('[data-cancel-event-id]'); if (cancelButton && currentUser?.email) { const eventId = Number(cancelButton.getAttribute('data-cancel-event-id')); if (Number.isFinite(eventId)) { openCancelEventModal(eventId); } return; } if (target.closest('a, button')) { return; } const card = target.closest('[data-event-id]'); if (!card) { return; } const eventId = Number(card.getAttribute('data-event-id')); if (!Number.isFinite(eventId)) { return; } window.location.href = `event_detail.html?id=${eventId}`; } // Schaltet den sichtbaren Profilbereich per Tabname um. function activateProfileTab(tabName) { profileTabButtons.forEach(button => { const isActive = button.getAttribute('data-category-item') === tabName; button.classList.toggle('is-active', isActive); button.setAttribute('aria-selected', isActive ? 'true' : 'false'); }); profileTabPanels.forEach(panel => { const isActive = panel.getAttribute('data-profile-panel') === tabName; panel.classList.toggle('hidden', !isActive); }); if (tabName === 'teilnehmen') { const registeredEvents = getMyRegisteredEvents(allEvents, currentUser); markRegistrationsAsRead(registeredEvents); } } // Reagiert auf Aktionen in der Liste "Meine Anmeldungen" per Event Delegation. function handleRegistrationListClick(event) { const target = event.target; if (!(target instanceof HTMLElement)) { return; } const unregisterButton = target.closest('[data-unregister-id]'); if (unregisterButton) { if (!currentUser?.email) { return; } const eventId = Number(unregisterButton.getAttribute('data-unregister-id')); if (!Number.isFinite(eventId)) { return; } unregisterFromEvent(eventId, currentUser.email); // Snackbar: Feedback bei erfolgreicher Abmeldung. 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); }, 3000); } return; } if (target.closest('a, button')) { return; } const card = target.closest('[data-event-id]'); if (!card) { return; } const eventId = Number(card.getAttribute('data-event-id')); if (!Number.isFinite(eventId)) { return; } window.location.href = `event_detail.html?id=${eventId}`; } // Sagt ein gehostetes Event ab (aus eigener Profilansicht entfernen). let pendingCancelEventId = null; function openCancelEventModal(eventId) { pendingCancelEventId = eventId; const modal = document.getElementById('cancelEventModal'); modal.classList.add('show'); } window.closeCancelEventModal = function() { pendingCancelEventId = null; const modal = document.getElementById('cancelEventModal'); modal.classList.remove('show'); }; document.getElementById('confirmCancelEventBtn').addEventListener('click', function() { if (pendingCancelEventId !== null && currentUser?.email) { cancelHostedEvent(pendingCancelEventId, currentUser.email); } closeCancelEventModal(); }); // Schliesst das Modal bei Klick ausserhalb des Inhalts. document.getElementById('cancelEventModal').addEventListener('click', function(e) { if (e.target === this) { closeCancelEventModal(); } }); function cancelHostedEvent(eventId, userEmail) { // Lokal erstellte, eigene Events werden direkt aus dem Storage geloescht. const storedEvents = getStoredEvents(); const nextStoredEvents = storedEvents.filter(event => { const isTargetEvent = Number(event.id) === eventId; const isOwnedByUser = normalizeText(event.hostEmail || '') === normalizeText(userEmail) || normalizeText(event.host?.name || '') === normalizeText(currentUser?.vorname || ''); return !(isTargetEvent && isOwnedByUser); }); setStoredEvents(nextStoredEvents); // Event-ID für alle Benutzer aus den Anmeldungen entfernen. const registrationMap = getRegistrationMap(); Object.keys(registrationMap).forEach(email => { const ids = Array.isArray(registrationMap[email]) ? registrationMap[email].map(id => Number(id)).filter(Number.isFinite) : []; registrationMap[email] = ids.filter(id => id !== eventId); }); setRegistrationMap(registrationMap); allEvents = allEvents.filter(event => Number(event.id) !== eventId); renderMyEvents(allEvents, currentUser); renderMyRegistrations(allEvents, currentUser); profileFeedback.textContent = 'Event wurde abgesagt und aus deinem Hosting entfernt.'; } // Entfernt eine Event-ID aus der Benutzerliste und aktualisiert die UI sofort. function unregisterFromEvent(eventId, userEmail) { const registrationMap = getRegistrationMap(); const currentIds = Array.isArray(registrationMap[userEmail]) ? registrationMap[userEmail] : []; const nextIds = currentIds .map(id => Number(id)) .filter(id => Number.isFinite(id) && id !== eventId); registrationMap[userEmail] = nextIds; setRegistrationMap(registrationMap); renderMyRegistrations(allEvents, currentUser); profileFeedback.textContent = 'Du wurdest von dem Event abgemeldet.'; } // Validiert Profildaten konsistent und liefert true/false zur Submit-Steuerung. function validateProfileForm() { let isValid = true; const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!vornameInput.value.trim()) { vornameInput.parentElement.classList.add('has-error'); isValid = false; } if (!nachnameInput.value.trim()) { nachnameInput.parentElement.classList.add('has-error'); isValid = false; } if (!emailRegex.test(emailInput.value.trim())) { emailInput.parentElement.classList.add('has-error'); isValid = false; } if (passwortInput.value && passwortInput.value.length < 6) { passwortInput.parentElement.classList.add('has-error'); isValid = false; } return isValid; } // Speichert Profiländerungen lokal und synchronisiert auch den Benutzerkatalog. function handleProfileSubmit(event) { event.preventDefault(); if (!validateProfileForm()) { profileFeedback.textContent = 'Bitte prüfe die markierten Felder.'; return; } const previousEmail = currentUser.email; const nextUser = { ...currentUser, vorname: vornameInput.value.trim(), nachname: nachnameInput.value.trim(), email: emailInput.value.trim(), passwort: passwortInput.value ? passwortInput.value : currentUser.passwort, updatedAt: new Date().toISOString() }; currentUser = nextUser; localStorage.setItem(CURRENT_USER_KEY, JSON.stringify(nextUser)); syncUserInUserStore(previousEmail, nextUser); // Falls sich die E-Mail geändert hat, verschieben wir bestehende Anmeldungen auf die neue E-Mail. migrateRegistrationEmail(previousEmail, nextUser.email); passwortInput.value = ''; profileHeadline.textContent = `Hallo ${nextUser.vorname}`; profileFeedback.textContent = 'Profil erfolgreich gespeichert.'; } // Synchronisiert einen Benutzer im zentralen User-Array. function syncUserInUserStore(previousEmail, nextUser) { let users = []; try { const raw = localStorage.getItem(USERS_STORAGE_KEY); users = raw ? JSON.parse(raw) : []; } catch (error) { console.error('Benutzerdaten konnten nicht gelesen werden.', error); } const nextUsers = users.filter(user => user.email !== previousEmail && user.email !== nextUser.email); nextUsers.unshift(nextUser); localStorage.setItem(USERS_STORAGE_KEY, JSON.stringify(nextUsers)); } // Migriert bestehende Registrierungen, falls die E-Mail aktualisiert wurde. function migrateRegistrationEmail(previousEmail, nextEmail) { if (!previousEmail || !nextEmail || previousEmail === nextEmail) { return; } const map = getRegistrationMap(); const existingRegistrations = Array.isArray(map[previousEmail]) ? map[previousEmail] : []; const alreadyPresent = Array.isArray(map[nextEmail]) ? map[nextEmail] : []; map[nextEmail] = Array.from(new Set([...alreadyPresent, ...existingRegistrations])); delete map[previousEmail]; localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(map)); } // Ermittelt gehostete Events aus lokal erstellten Daten des aktuellen Benutzers. function getMyHostedEvents(events, user) { const userFirstName = normalizeText(user.vorname || ''); const userEmail = normalizeText(user.email || ''); return events.filter(event => { if (event.source !== 'local') { return false; } const hostEmail = normalizeText(event.hostEmail || ''); const hostName = normalizeText(event.host?.name || ''); if (hostEmail && hostEmail === userEmail) { return true; } return userFirstName && hostName === userFirstName; }); } // Ermittelt angemeldete Events über die Registration-Map und participants-Liste. function getMyRegisteredEvents(events, user) { const registrationMap = getRegistrationMap(); const registeredIds = Array.isArray(registrationMap[user.email]) ? registrationMap[user.email] : []; const idSet = new Set(registeredIds.map(id => Number(id))); const userFirstName = String(user.vorname || '').trim().toLowerCase(); const userFullName = `${String(user.vorname || '').trim()} ${String(user.nachname || '').trim()}`.trim().toLowerCase(); return events.filter(event => { if (idSet.has(Number(event.id))) { return true; } if (Array.isArray(event.participants)) { const participantSet = new Set(event.participants.map(name => String(name || '').trim().toLowerCase()).filter(Boolean)); if ((userFirstName && participantSet.has(userFirstName)) || (userFullName && participantSet.has(userFullName))) { return true; } } return false; }); } // Rendert angemeldete Events inkl. Zähler. function renderMyEvents(events, user) { const hostedEvents = getMyHostedEvents(events, user); const count = hostedEvents.length; myEventsCount.textContent = String(count); if (myEventsBtnCount) myEventsBtnCount.textContent = String(count); renderEventCards(myEventsList, hostedEvents, { title: 'Noch kein eigenes Event', text: 'Starte dein erstes Dinner und lade die Community an deinen Tisch ein.', buttonLabel: 'Event erstellen', href: 'event_create.html' }, 'hosting'); } function getSeenAddresses() { try { const raw = localStorage.getItem('socialCookingSeenAddresses'); return raw ? JSON.parse(raw) : []; } catch (err) { return []; } } function markRegistrationsAsRead(events) { const seen = getSeenAddresses(); let changed = false; events.forEach(event => { if (isAddressVisibleWindow(event) && !seen.includes(Number(event.id))) { seen.push(Number(event.id)); changed = true; } }); if (changed) { localStorage.setItem('socialCookingSeenAddresses', JSON.stringify(seen)); // Remove dots from UI const tabDot = document.querySelector('[data-category-item="teilnehmen"] .notification-dot'); if (tabDot) tabDot.remove(); const navDot = document.querySelector('.profile-pill .notification-dot'); if (navDot) navDot.remove(); } } // Rendert angemeldete Events inkl. Zähler. function renderMyRegistrations(events, user) { const registeredEvents = getMyRegisteredEvents(events, user); const count = registeredEvents.length; myRegistrationsCount.textContent = String(count); if (myRegistrationsBtnCount) myRegistrationsBtnCount.textContent = String(count); const seenAddresses = getSeenAddresses(); const unreadEvents = registeredEvents.filter(e => isAddressVisibleWindow(e) && !seenAddresses.includes(Number(e.id))); const hasNotifications = unreadEvents.length > 0; const tabButton = document.querySelector('[data-category-item="teilnehmen"]'); if (tabButton) { let dot = tabButton.querySelector('.notification-dot'); if (hasNotifications) { if (!dot) { dot = document.createElement('span'); dot.className = 'notification-dot'; tabButton.appendChild(dot); } } else if (dot) { dot.remove(); } } renderEventCards(myRegistrationsList, registeredEvents, { title: 'Noch keine Anmeldungen', text: 'Entdecke spannende Dinner in deiner Naehe und melde dich direkt an.', buttonLabel: 'Events entdecken', href: 'event_overview.html' }, 'registrations', seenAddresses); // Falls wir bereits auf dem Tab sind, direkt als gelesen markieren const activeTab = document.querySelector('[data-category-item="teilnehmen"].is-active'); if (activeTab && hasNotifications) { // Kurze Verzögerung, damit UI sich erst aufbaut setTimeout(() => markRegistrationsAsRead(registeredEvents), 500); } } // Gibt true zurück, wenn die Abmeldung gesperrt ist (innerhalb von 24h oder in der Vergangenheit). function isDeregistrationClosed(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; } // Baut die Eventkarten für beide Listen in einheitlichem Markup. function renderEventCards(container, events, emptyStateConfig, mode, seenAddresses = []) { container.innerHTML = ''; if (events.length === 0) { const emptyElement = document.createElement('div'); emptyElement.className = 'profile-empty-state'; emptyElement.innerHTML = `
Keine Treffer
${emptyStateConfig.text}
${emptyStateConfig.buttonLabel} `; container.appendChild(emptyElement); return; } events.forEach(event => { const card = document.createElement('article'); card.className = 'profile-event-card profile-event-card-clickable'; card.setAttribute('data-event-id', String(event.id)); const isCanceled = event.status === 'canceled'; if (isCanceled) { card.style.opacity = '0.6'; } let addressMessage = 'Vielen Dank für die Anmeldung! Die Adresse für diesen Event wird 24 Stunden vorher genau hier sichtbar sein.'; if (isEventPastAddressWindow(event)) { addressMessage = 'Vielen Dank, dass du an diesem Event teilgenommen hast.'; } let addressMarkup = ''; if (mode === 'registrations' && event.address) { if (isCanceled) { addressMarkup = `Adresse
Dieses Event wurde leider vom Gastgeber abgesagt.
Adresse
${event.address}
Adresse
${addressMessage}