function readStorageJson(key, fallbackValue) { const raw = localStorage.getItem(key); if (!raw || raw === "undefined" || raw === "null") { return fallbackValue; } try { return JSON.parse(raw); } catch (error) { console.warn(`Konnte LocalStorage-Wert fuer ${key} nicht lesen.`, error); return fallbackValue; } } function normalizeUser(user) { if (!user || typeof user !== "object") { return null; } return { salutation: user.salutation || "", firstName: user.firstName || "", lastName: user.lastName || "", birthday: user.birthday || "", street: user.street || "", houseNumber: user.houseNumber || "", zip: user.zip || "", city: user.city || "", country: user.country || "Deutschland", email: user.email || "", passwordHash: user.passwordHash || (user.password ? hashPassword(user.password) : ""), orders: Array.isArray(user.orders) ? user.orders : [], paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : [] }; } function hashPassword(password) { let hashA = 0xdeadbeef; let hashB = 0x41c6ce57; const text = String(password || ""); for (let index = 0; index < text.length; index += 1) { const charCode = text.charCodeAt(index); hashA = Math.imul(hashA ^ charCode, 2654435761); hashB = Math.imul(hashB ^ charCode, 1597334677); } hashA = Math.imul(hashA ^ (hashA >>> 16), 2246822507) ^ Math.imul(hashB ^ (hashB >>> 13), 3266489909); hashB = Math.imul(hashB ^ (hashB >>> 16), 2246822507) ^ Math.imul(hashA ^ (hashA >>> 13), 3266489909); return `hash$${(hashB >>> 0).toString(16).padStart(8, "0")}${(hashA >>> 0).toString(16).padStart(8, "0")}`; } function escapeHtml(value) { return String(value || "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function formatEuro(value) { return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`; } function persistUsers() { localStorage.setItem("eagleUsers", JSON.stringify(users)); } function persistCurrentUser() { if (currentUser) { localStorage.setItem("currentUser", JSON.stringify(currentUser)); } else { localStorage.removeItem("currentUser"); } } let users = readStorageJson("eagleUsers", []); if (!Array.isArray(users)) { users = []; } users = users.map(normalizeUser).filter(Boolean); persistUsers(); let currentUser = normalizeUser(readStorageJson("currentUser", null)); if (currentUser && currentUser.email) { const storedMatch = users.find((user) => user.email === currentUser.email); if (storedMatch) { currentUser = storedMatch; persistCurrentUser(); } else { users.push(currentUser); persistUsers(); persistCurrentUser(); } } function registerUser() { if (!validateRegisterForm()) { return; } const salutation = document.getElementById("reg-salutation")?.value.trim() || ""; const firstName = document.getElementById("reg-firstname")?.value.trim() || ""; const lastName = document.getElementById("reg-lastname")?.value.trim() || ""; const birthday = document.getElementById("reg-birthday")?.value.trim() || ""; const street = document.getElementById("reg-street")?.value.trim() || ""; const houseNumber = document.getElementById("reg-house-number")?.value.trim() || ""; const zip = document.getElementById("reg-zip")?.value.trim() || ""; const city = document.getElementById("reg-city")?.value.trim() || ""; const country = document.getElementById("reg-country")?.value.trim() || ""; const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase(); const password = document.getElementById("reg-password")?.value || ""; const existingUser = users.find((user) => user.email.toLowerCase() === email); if (existingUser) { setFieldError(document.getElementById("reg-email"), "Diese E-Mail ist bereits registriert."); return; } const newUser = { salutation, firstName, lastName, birthday, street, houseNumber, zip, city, country, email, passwordHash: hashPassword(password), orders: [], paymentMethods: [] }; users.push(newUser); currentUser = newUser; persistUsers(); persistCurrentUser(); document.getElementById("register-modal")?.classList.add("hidden"); openAccountDashboard(); } function loginUser() { if (!validateLoginForm()) { return; } const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase(); const password = document.getElementById("login-password")?.value || ""; const passwordHash = hashPassword(password); const user = users.find( (entry) => entry.email.toLowerCase() === email && entry.passwordHash === passwordHash ); if (!user) { document.getElementById("login-error")?.classList.remove("hidden"); setFieldError(document.getElementById("login-email"), "E-Mail oder Passwort stimmen nicht."); setFieldError(document.getElementById("login-password"), "E-Mail oder Passwort stimmen nicht."); return; } currentUser = user; persistCurrentUser(); openAccountDashboard(); } function openAccountDashboard() { const accountView = document.getElementById("account-view"); if (!accountView) { return; } if (!currentUser) { accountView.innerHTML = "

Mein Konto

Bitte melde dich an oder registriere dich.

"; return; } accountView.innerHTML = `

Mein Konto

`; renderPersonalInfo(); } function renderPersonalInfo(isEditing = false) { const target = document.getElementById("account-tab-content"); if (!target || !currentUser) { return; } if (isEditing) { target.innerHTML = `

Persönliche Daten bearbeiten

E-Mail bleibt unverändert
`; return; } target.innerHTML = `

Persönliche Daten

Anrede${escapeHtml(currentUser.salutation || "-")}

Vorname${escapeHtml(currentUser.firstName || "-")}

Nachname${escapeHtml(currentUser.lastName || "-")}

Geburtstag${escapeHtml(currentUser.birthday || "-")}

Straße${escapeHtml(currentUser.street || "-")}

Nr.${escapeHtml(currentUser.houseNumber || "-")}

PLZ${escapeHtml(currentUser.zip || "-")}

Ort${escapeHtml(currentUser.city || "-")}

Land${escapeHtml(currentUser.country || "-")}

E-Mail${escapeHtml(currentUser.email || "-")}

`; } function savePersonalInfo() { if (!currentUser) { return; } const requiredFields = ["account-edit-firstname", "account-edit-lastname"]; const isValid = requiredFields .map((id) => validateAccountField(document.getElementById(id))) .every(Boolean); if (!isValid) { return; } currentUser.salutation = document.getElementById("account-edit-salutation")?.value || ""; currentUser.firstName = document.getElementById("account-edit-firstname")?.value.trim() || ""; currentUser.lastName = document.getElementById("account-edit-lastname")?.value.trim() || ""; currentUser.birthday = document.getElementById("account-edit-birthday")?.value || ""; currentUser.street = document.getElementById("account-edit-street")?.value.trim() || ""; currentUser.houseNumber = document.getElementById("account-edit-house-number")?.value.trim() || ""; currentUser.zip = document.getElementById("account-edit-zip")?.value.trim() || ""; currentUser.city = document.getElementById("account-edit-city")?.value.trim() || ""; currentUser.country = document.getElementById("account-edit-country")?.value || "Deutschland"; const userIndex = users.findIndex((user) => user.email === currentUser.email); if (userIndex >= 0) { users[userIndex] = currentUser; } persistUsers(); persistCurrentUser(); renderPersonalInfo(); } function getFieldWrapper(input) { if (!input) { return null; } return input.closest(".auth-field") || document.querySelector(`[data-field-for="${input.id}"]`); } function setFieldError(input, message) { const wrapper = getFieldWrapper(input); if (!wrapper) { return; } input.dataset.touched = "true"; wrapper.classList.add("is-invalid"); const error = wrapper.querySelector(".field-error"); if (error) { error.textContent = message; } } function clearFieldError(input) { const wrapper = getFieldWrapper(input); if (!wrapper) { return; } wrapper.classList.remove("is-invalid"); const error = wrapper.querySelector(".field-error"); if (error) { error.textContent = ""; } } function validateAccountField(input) { if (!input) { return true; } const value = (input.value || "").trim(); if (input.required && !value) { setFieldError(input, input.dataset.requiredMessage || "Dieses Feld ist ein Pflichtfeld."); return false; } if (input.type === "email" && value && !value.includes("@")) { setFieldError(input, input.dataset.emailMessage || "Bitte gib eine gültige E-Mail-Adresse ein."); return false; } if (input.type === "password" && value && (value.length < 8 || value.length > 32)) { setFieldError(input, input.dataset.passwordMessage || "Passwort muss 8 bis 32 Zeichen haben."); return false; } clearFieldError(input); return true; } function validateRegisterForm() { const fieldIds = [ "reg-salutation", "reg-firstname", "reg-lastname", "reg-birthday", "reg-street", "reg-house-number", "reg-zip", "reg-city", "reg-country", "reg-email", "reg-password" ]; return fieldIds .map((id) => validateAccountField(document.getElementById(id))) .every(Boolean); } function validateLoginForm() { return ["login-email", "login-password"] .map((id) => validateAccountField(document.getElementById(id))) .every(Boolean); } function initAccountFieldValidation() { const fieldSelector = [ "#login-email", "#login-password", "#reg-salutation", "#reg-firstname", "#reg-lastname", "#reg-birthday", "#reg-street", "#reg-house-number", "#reg-zip", "#reg-city", "#reg-country", "#reg-email", "#reg-password" ].join(","); document.querySelectorAll(fieldSelector).forEach((input) => { input.addEventListener("blur", () => { input.dataset.touched = "true"; validateAccountField(input); }); input.addEventListener("input", () => { input.dataset.touched = "true"; if (input.type !== "hidden") { validateAccountField(input); } }); input.addEventListener("change", () => { input.dataset.touched = "true"; validateAccountField(input); }); }); document.querySelectorAll(".gender-selector button").forEach((button) => { button.addEventListener("click", () => { const salutationInput = document.getElementById("reg-salutation"); if (!salutationInput) { return; } salutationInput.value = button.dataset.gender || ""; salutationInput.dataset.touched = "true"; button.closest(".gender-selector")?.querySelectorAll("button").forEach((item) => { item.classList.toggle("active", item === button); }); validateAccountField(salutationInput); }); }); } function renderOrders() { const target = document.getElementById("account-tab-content"); if (!target || !currentUser) { return; } const orders = Array.isArray(currentUser.orders) ? currentUser.orders : []; if (!orders.length) { target.innerHTML = `

Meine Bestellungen

Noch keine Bestellungen vorhanden.

`; return; } const orderHtml = orders .map((order, index) => { const movieItems = Array.isArray(order.items) ? order.items.filter((item) => item.category === "movie") : []; const previewItem = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null); const previewTitle = previewItem?.title || "Bestellung"; const ticketsCount = movieItems.length || (Array.isArray(order.items) ? order.items.length : 0); return ` `; }) .join(""); target.innerHTML = `

Meine Bestellungen

Klicke auf eine Bestellung, um genauere Informationen zu sehen.

${orderHtml}
`; const detailTarget = document.getElementById("order-ticket-details"); const orderButtons = Array.from(target.querySelectorAll(".order-item-btn")); const renderOrderTicket = (orderIndex) => { const order = orders[orderIndex]; if (!order || !detailTarget) { return; } const movieItems = Array.isArray(order.items) ? order.items.filter((item) => item.category === "movie") : []; const primaryMovie = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null); const poster = primaryMovie?.img || ""; const seats = movieItems.map((item) => item.seatId).filter(Boolean).join(", ") || "-"; const ticketCount = movieItems.length || (Array.isArray(order.items) ? order.items.length : 0); const hall = primaryMovie?.hall || "-"; const time = primaryMovie?.time ? `${primaryMovie.time} Uhr` : "-"; detailTarget.innerHTML = `
${poster ? `${escapeHtml(primaryMovie?.title || ` : `
Kein Poster
`}
EAGLE'S IMAX | Bestell-Details

${escapeHtml(primaryMovie?.title || "Bestellung")}

Datum${escapeHtml(order.date || "-")}

Saal${escapeHtml(hall)}

Uhrzeit${escapeHtml(time)}

Tickets${ticketCount}x

Sitze${escapeHtml(seats)}

Gesamt${formatEuro(order.total || 0)}

`; detailTarget.classList.remove("hidden"); orderButtons.forEach((button) => { button.classList.toggle("active", Number(button.dataset.orderIndex) === orderIndex); }); }; orderButtons.forEach((button) => { button.addEventListener("click", () => { const orderIndex = Number(button.dataset.orderIndex || -1); if (orderIndex >= 0) { renderOrderTicket(orderIndex); } }); }); } function renderPayments() { const target = document.getElementById("account-tab-content"); if (!target || !currentUser) { return; } target.innerHTML = `

Zahlungsmethoden

Hier kannst du deine bevorzugten Zahlungsmethoden verwalten.

`; const modals = Array.from(target.querySelectorAll(".pay-modal-overlay")); const triggers = Array.from(target.querySelectorAll(".account-pay-trigger")); const closeButtons = Array.from(target.querySelectorAll("[data-pay-close]")); const closeAllPaymentModals = () => { modals.forEach((modal) => modal.classList.add("hidden")); document.body.style.overflow = "auto"; }; triggers.forEach((trigger) => { trigger.addEventListener("click", () => { closeAllPaymentModals(); const targetId = trigger.getAttribute("data-pay-modal"); const modal = targetId ? target.querySelector(`#${targetId}`) : null; if (modal) { modal.classList.remove("hidden"); document.body.style.overflow = "hidden"; } }); }); closeButtons.forEach((button) => { button.addEventListener("click", closeAllPaymentModals); }); modals.forEach((modal) => { modal.addEventListener("click", (event) => { if (event.target === modal) { closeAllPaymentModals(); } }); }); } function logoutUser() { currentUser = null; persistCurrentUser(); window.location.reload(); }