Social_Cooking/js/my_profil.js
viiivo 1efa4dcd39 feat: Implement user profile management and navigation updates
- Added a new profile page (my_profil.html) for users to manage their events and personal information.
- Introduced a new CSS file (my_profil.css) for styling the profile page.
- Created a JavaScript file (my_profil.js) to handle profile data retrieval, event registration management, and form submission.
- Updated navigation logic (navigation.js) to dynamically display login/signup or event management links based on user authentication status.
- Enhanced event creation and detail pages to support user-specific actions (registration/unregistration).
- Improved login and signup processes to handle user data more robustly, including fallback user creation.
- Refactored event overview to show user-specific events and registrations.
- Added error handling and validation for user input in profile management.
2026-04-10 16:28:44 +02:00

368 lines
14 KiB
JavaScript

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 fuer klare, testbare Funktionen.
const loggedOutState = document.getElementById('logged-out-state');
const loggedInContent = document.getElementById('logged-in-content');
const profileHeadline = document.getElementById('profile-headline');
const profileSubline = document.getElementById('profile-subline');
const logoutButton = document.getElementById('logout-button');
const myEventsCount = document.getElementById('my-events-count');
const myRegistrationsCount = document.getElementById('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();
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));
}
// 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.';
}
// Fuellt Ueberschriften 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);
[vornameInput, nachnameInput, emailInput, passwortInput].forEach(input => {
input.addEventListener('input', () => {
input.parentElement.classList.remove('has-error');
profileFeedback.textContent = '';
});
});
logoutButton.addEventListener('click', () => {
localStorage.removeItem(CURRENT_USER_KEY);
window.location.href = 'login.html';
});
}
// 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 || !currentUser?.email) {
return;
}
const eventId = Number(unregisterButton.getAttribute('data-unregister-id'));
if (!Number.isFinite(eventId)) {
return;
}
unregisterFromEvent(eventId, currentUser.email);
}
// 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 Profilaenderungen lokal und synchronisiert auch den Benutzerkatalog.
function handleProfileSubmit(event) {
event.preventDefault();
if (!validateProfileForm()) {
profileFeedback.textContent = 'Bitte pruefe 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 geaendert 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 anhand Host-E-Mail oder Host-Vorname.
function getMyHostedEvents(events, user) {
const userFirstName = normalizeText(user.vorname);
return events.filter(event => {
const hostEmail = normalizeText(event.hostEmail || '');
const hostName = normalizeText(event.host?.name || '');
if (hostEmail && hostEmail === normalizeText(user.email)) {
return true;
}
return userFirstName && hostName === userFirstName;
});
}
// Ermittelt angemeldete Events ueber die Registration-Map.
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)));
return events.filter(event => idSet.has(Number(event.id)));
}
// Rendert gehostete Events inkl. Zaehler.
function renderMyEvents(events, user) {
const hostedEvents = getMyHostedEvents(events, user);
myEventsCount.textContent = String(hostedEvents.length);
renderEventCards(myEventsList, hostedEvents, 'Du hast noch kein eigenes Event erstellt.', false);
}
// Rendert angemeldete Events inkl. Zaehler.
function renderMyRegistrations(events, user) {
const registeredEvents = getMyRegisteredEvents(events, user);
myRegistrationsCount.textContent = String(registeredEvents.length);
renderEventCards(myRegistrationsList, registeredEvents, 'Du bist aktuell bei keinem Event angemeldet.', true);
}
// Baut die Eventkarten fuer beide Listen in einheitlichem Markup.
function renderEventCards(container, events, emptyText, withUnregisterButton) {
container.innerHTML = '';
if (events.length === 0) {
const emptyElement = document.createElement('p');
emptyElement.className = 'profile-empty';
emptyElement.textContent = emptyText;
container.appendChild(emptyElement);
return;
}
events.forEach(event => {
const card = document.createElement('article');
card.className = 'profile-event-card';
const actionMarkup = withUnregisterButton
? `
<div class="profile-event-actions">
<a class="profile-event-link" href="event_detail.html?id=${event.id}">Zum Event</a>
<button class="profile-unregister-btn" type="button" data-unregister-id="${event.id}">Abmelden</button>
</div>
`
: `<a class="profile-event-link" href="event_detail.html?id=${event.id}">Zum Event</a>`;
card.innerHTML = `
<div>
<h3 class="profile-event-title">${event.title}</h3>
<p class="profile-event-meta">${event.location} | ${formatEventDate(event.date)} | ${formatEventTime(event.time)}</p>
</div>
${actionMarkup}
`;
container.appendChild(card);
});
}
// Formatiert ein Eventdatum konsistent fuer die Profilkarten.
function formatEventDate(dateString) {
if (!dateString) {
return 'Kein Datum';
}
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
const [year, month, day] = dateString.split('-');
const monthLabel = ['Januar', 'Februar', 'Maerz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'][Number(month) - 1];
return `${Number(day)}. ${monthLabel} ${year}`;
}
return dateString;
}
// Vereinheitlicht die Zeitanzeige fuer die Profilseite.
function formatEventTime(timeString) {
if (!timeString) {
return 'Keine Uhrzeit';
}
return timeString.includes('UHR') ? timeString.replace('UHR', 'Uhr').trim() : timeString;
}
// Normalisiert Vergleichswerte fuer robuste String-Matches.
function normalizeText(value) {
return String(value || '').trim().toLowerCase();
}
});