master #9

Closed
Jannis wants to merge 22 commits from Jannis/Kino-Website-Framework:master into master
11 changed files with 149 additions and 2444 deletions
Showing only changes of commit 40ee49aff5 - Show all commits

3
.gitignore vendored
View File

@@ -1 +1,2 @@
img/* node_modules
dist/

View File

@@ -1,460 +0,0 @@
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 {
firstName: user.firstName || "",
lastName: user.lastName || "",
email: user.email || "",
hashedPassword: user.hashedPassword || "",
orders: Array.isArray(user.orders) ? user.orders : [],
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
};
}
function escapeHtml(value) {
return String(value || "")
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
}
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);
let currentUser = normalizeUser(readStorageJson("currentUser", null));
if (currentUser && currentUser.email) {
const storedMatch = users.find((user) => user.email === currentUser.email);
if (storedMatch) {
currentUser = storedMatch;
} else {
users.push(currentUser);
persistUsers();
}
}
async function hashMessage(message) {
const msgBuffer = new TextEncoder().encode(message); // Encode as UTF-8
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); // Hash
const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert to bytes
return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // Hex string
}
async function registerUser() {
const firstName = document.getElementById("reg-firstname")?.value.trim() || "";
const lastName = document.getElementById("reg-lastname")?.value.trim() || "";
const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("reg-password")?.value || "";
if (!firstName || !lastName || !email || !password) {
alert("Bitte fuelle alle Felder aus.");
return;
}
if (!email.includes("@")) {
alert("Bitte gib eine gueltige E-Mail-Adresse ein.");
return;
}
const existingUser = users.find((user) => user.email.toLowerCase() === email);
if (existingUser) {
alert("E-Mail bereits registriert");
return;
}
const hashedPassword = await hashMessage(password);
const newUser = {
firstName,
lastName,
email,
hashedPassword,
orders: [],
paymentMethods: []
};
users.push(newUser);
currentUser = newUser;
persistUsers();
persistCurrentUser();
alert("Registrierung erfolgreich");
document.getElementById("register-modal")?.classList.add("hidden");
openAccountDashboard();
}
async function loginUser() {
const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("login-password")?.value || "";
const hashedPassword = await hashMessage(password);
const user = users.find(
(entry) => entry.email.toLowerCase() === email && entry.hashedPassword === hashedPassword
);
if (!user) {
document.getElementById("login-error")?.classList.remove("hidden");
return;
}
currentUser = user;
persistCurrentUser();
openAccountDashboard();
}
function openAccountDashboard() {
const accountView = document.getElementById("account-view");
if (!accountView) {
return;
}
if (!currentUser) {
accountView.innerHTML = "<div class='account-login-box'><h2>Mein Konto</h2><p>Bitte melde dich an oder registriere dich.</p></div>";
return;
}
accountView.innerHTML = /*html*/`
<div class="account-panel">
<div class="account-panel-header">
<h2>Mein Konto</h2>
<button class="account-logout-btn" onclick="logoutUser()">Abmelden</button>
</div>
<div class="account-tabs">
<button class="account-tab-btn" onclick="renderPersonalInfo()">Persönliche Daten</button>
<button class="account-tab-btn" onclick="renderOrders()">Meine Bestellungen</button>
<button class="account-tab-btn" onclick="renderPayments()">Zahlungsmethoden</button>
</div>
<div id="account-tab-content"></div>
</div>
`;
renderPersonalInfo();
}
function renderPersonalInfo() {
const target = document.getElementById("account-tab-content");
if (!target || !currentUser) {
return;
}
target.innerHTML = `
<div class="account-card">
<p><strong>Vorname:</strong> ${currentUser.firstName || "-"}</p>
<p><strong>Nachname:</strong> ${currentUser.lastName || "-"}</p>
<p><strong>E-Mail:</strong> ${currentUser.email || "-"}</p>
</div>
`;
}
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 = `
<div class="account-card">
<h3>Meine Bestellungen</h3>
<p>Noch keine Bestellungen vorhanden.</p>
</div>
`;
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 `
<button type="button" class="order-box order-item-btn" data-order-index="${index}">
<div class="order-item-head">
<h4>${escapeHtml(previewTitle)}</h4>
<span>${formatEuro(order.total || 0)}</span>
</div>
<p><strong>Datum:</strong> ${escapeHtml(order.date || "-")}</p>
<p><strong>Anzahl:</strong> ${ticketsCount}x</p>
</button>
`;
})
.join("");
target.innerHTML = `
<div class="account-orders-shell">
<h3>Meine Bestellungen</h3>
<p class="account-payments-note">Klicke auf eine Bestellung, um dein Ticket-Detail zu sehen.</p>
<div class="account-orders-grid">${orderHtml}</div>
<div id="order-ticket-details" class="order-ticket-details hidden"></div>
</div>
`;
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 = `
<article class="order-ticket-card">
<div class="order-ticket-poster">
${poster
? `<img src="${escapeHtml(poster)}" alt="${escapeHtml(primaryMovie?.title || "Film")}">`
: `<div class="order-ticket-poster-fallback">Kein Poster</div>`}
</div>
<div class="order-ticket-content">
<div class="order-ticket-brand">EAGLE'S IMAX | Bestell-Details</div>
<h4>${escapeHtml(primaryMovie?.title || "Bestellung")}</h4>
<div class="order-ticket-grid">
<p><span>Datum</span><strong>${escapeHtml(order.date || "-")}</strong></p>
<p><span>Saal</span><strong>${escapeHtml(hall)}</strong></p>
<p><span>Uhrzeit</span><strong>${escapeHtml(time)}</strong></p>
<p><span>Tickets</span><strong>${ticketCount}x</strong></p>
<p><span>Sitze</span><strong>${escapeHtml(seats)}</strong></p>
<p><span>Gesamt</span><strong>${formatEuro(order.total || 0)}</strong></p>
</div>
</div>
</article>
`;
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 = /*html*/`
<div class="account-card">
<h3>Zahlungsmethoden</h3>
<p class="account-payments-note">Platzhalter zum Hinterlegen deiner Logos oder Anbieter-Informationen.</p>
<div class="account-payment-grid">
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-card">
<div class="payment-logo-slot">
<img src="img/mastercard.png" alt="Mastercard">
</div>
<h4>Visa / Mastercard</h4>
<p>Karteninformationen hinterlegen</p>
</button>
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-paypal">
<div class="payment-logo-slot">
<img src="img/paypal.png" alt="PayPal">
</div>
<h4>PayPal</h4>
<p>Konto verbinden</p>
</button>
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-apple">
<div class="payment-logo-slot">
<img src="img/applepay.png" alt="Apple Pay">
</div>
<h4>Apple Pay</h4>
<p>Geraet freischalten</p>
</button>
<button type="button" class="account-payment-card account-pay-trigger" data-pay-modal="pay-modal-google">
<div class="payment-logo-slot">
<img src="img/googlepay.png" alt="Google Pay">
</div>
<h4>Google Pay</h4>
<p>Wallet verknuepfen</p>
</button>
</div>
</div>
<div id="pay-modal-card" class="pay-modal-overlay hidden">
<div class="pay-modal-panel pay-modal-card-style">
<button type="button" class="pay-close-btn" data-pay-close>&times;</button>
<div class="pay-modal-head">
<img src="img/mastercard.png" alt="Kreditkarte">
<h4>Kreditkarte hinterlegen</h4>
</div>
<div class="pay-form-grid">
<label>Kartennummer
<input type="text" placeholder="1234 5678 9012 3456">
</label>
<div class="pay-form-row">
<label>Exp. Datum
<input type="text" placeholder="MM/JJ">
</label>
<label>CVV
<input type="text" placeholder="123">
</label>
</div>
<label>Name auf Karte
<input type="text" placeholder="Max Mustermann">
</label>
</div>
<button type="button" class="pay-submit-btn">Karte speichern</button>
</div>
</div>
<div id="pay-modal-paypal" class="pay-modal-overlay hidden">
<div class="pay-modal-panel pay-modal-paypal-style">
<button type="button" class="pay-close-btn" data-pay-close>&times;</button>
<div class="pay-modal-head">
<img src="img/paypal.png" alt="PayPal">
<h4>PayPal verbinden</h4>
</div>
<p>Einloggen und dein PayPal-Konto mit deinem Kino-Account verbinden.</p>
<label>E-Mail
<input type="email" placeholder="name@beispiel.de">
</label>
<label>Passwort
<input type="password" placeholder="Passwort">
</label>
<button type="button" class="pay-submit-btn paypal-btn">Mit PayPal fortfahren</button>
</div>
</div>
<div id="pay-modal-apple" class="pay-modal-overlay hidden">
<div class="pay-modal-panel pay-modal-apple-style">
<button type="button" class="pay-close-btn" data-pay-close>&times;</button>
<div class="pay-modal-head">
<img src="img/applepay.png" alt="Apple Pay">
<h4>Apple Pay einrichten</h4>
</div>
<p>Apple Pay wirkt schlicht, klar und fokussiert. Hinterlege hier die bevorzugte Karte für schnelle Zahlungen.</p>
<label>Apple-ID E-Mail
<input type="email" placeholder="appleid@beispiel.de">
</label>
<label>Bevorzugte Karte
<input type="text" placeholder="Visa, Mastercard, ...">
</label>
<button type="button" class="pay-submit-btn apple-btn">Zu Wallet hinzufügen</button>
</div>
</div>
<div id="pay-modal-google" class="pay-modal-overlay hidden">
<div class="pay-modal-panel pay-modal-google-style">
<button type="button" class="pay-close-btn" data-pay-close>&times;</button>
<div class="pay-modal-head">
<img src="img/googlepay.png" alt="Google Pay">
<h4>Google Pay einrichten</h4>
</div>
<p>Verbinde deine Wallet, damit zukünftige Bestellungen in wenigen Klicks abgeschlossen werden.</p>
<label>Google-Konto
<input type="email" placeholder="konto@gmail.com">
</label>
<label>Standard-Zahlungsquelle
<input type="text" placeholder="z. B. Visa 1234">
</label>
<button type="button" class="pay-submit-btn google-btn">Wallet verbinden</button>
</div>
</div>
`;
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();
}

199
cart.js
View File

@@ -1,199 +0,0 @@
function formatEuro(value) {
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
}
function escapeHtml(value) {
return String(value || "")
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
}
function buildCartKey(item) {
const infoText = item.category === "movie"
? `Sitz: ${item.seatId} (${item.hall})`
: item.time;
return `${item.title}-${item.hall}-${infoText}`;
}
function isDrinkItem(item) {
if (item.category !== "snack") {
return false;
}
const title = String(item.title || "").toLowerCase();
const size = String(item.hall || "").toLowerCase();
const drinkKeywords = [
"cola",
"sprite",
"fanta",
"mezzo",
"fuze",
"wasser",
"getraenk",
"drink"
];
return drinkKeywords.some((word) => title.includes(word)) || size.includes("l");
}
function buildItemInfo(item) {
if (item.category === "movie") {
return `
<div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div>
<div>Saal: ${escapeHtml(item.hall || "-")}</div>
<div>Uhrzeit: ${escapeHtml(item.time || "-")} Uhr</div>
`;
}
if (isDrinkItem(item)) {
return `
<div>Variante: ${escapeHtml(item.time || "-")}</div>
<div>Groesse: ${escapeHtml(item.hall || "-")}</div>
`;
}
return `
<div>Kategorie: Snack</div>
<div>Variante: ${escapeHtml(item.time || "-")}</div>
<div>Groesse: ${escapeHtml(item.hall || "-")}</div>
`;
}
function groupCartItems() {
const groups = new Map();
cart.forEach((item) => {
const key = buildCartKey(item);
if (!groups.has(key)) {
groups.set(key, {
key,
quantity: 0,
total: 0,
item
});
}
const group = groups.get(key);
group.quantity += 1;
group.total += Number(item.price || 0);
});
return Array.from(groups.values());
}
function saveCart() {
localStorage.setItem("eagleCart", JSON.stringify(cart));
updateCartBadge();
}
function updateCartBadge() {
const cartBadge = document.getElementById("cart-badge");
if (!cartBadge) {
return;
}
cartBadge.innerText = cart.length;
cartBadge.classList.toggle("hidden", cart.length === 0);
}
function renderCart() {
const cartList = document.getElementById("cart-items-list");
const totalEl = document.getElementById("cart-total-right");
const vatEl = document.getElementById("cart-vat-right");
if (!cartList || !totalEl || !vatEl) {
return;
}
if (!Array.isArray(cart) || cart.length === 0) {
cartList.innerHTML = '<p>Dein Warenkorb ist leer.</p>';
totalEl.innerText = formatEuro(0);
vatEl.innerText = `inkl. 19% MwSt: ${formatEuro(0)}`;
return;
}
const groupedItems = groupCartItems();
const header = /*html*/`
<div class="cart-header-row">
<div class="col-amount">MENGE</div>
<div class="col-img">VORSCHAU</div>
<div class="col-product">NAME</div>
<div class="col-details">INFO</div>
<div class="col-price">PREIS</div>
<div class="col-action">AKTION</div>
</div>
`;
const rows = groupedItems
.map((group) => {
const imageHtml = group.item.img
? /*html*/`<img class="cart-img-small" src="${escapeHtml(group.item.img)}" alt="${escapeHtml(group.item.title)}">`
: /*html*/`<div class="cart-img-fallback">Kein Bild</div>`;
const quantityHtml = group.item.category === "movie"
? /*html*/`<div class="qty-static" aria-label="Feste Ticketanzahl">${group.quantity}x</div>`
: /*html*/`
<div class="qty-stepper">
<button class="btn-qty" data-action="minus" data-key="${escapeHtml(group.key)}">-</button>
<span>${group.quantity}</span>
<button class="btn-qty" data-action="plus" data-key="${escapeHtml(group.key)}">+</button>
</div>
`;
return /*html*/`
<div class="cart-item-row">
<div class="col-amount">
${quantityHtml}
</div>
<div class="col-img">${imageHtml}</div>
<div class="col-product">${escapeHtml(group.item.title)}</div>
<div class="col-details cart-item-info">${buildItemInfo(group.item)}</div>
<div class="col-price">${formatEuro(group.total)}</div>
<div class="col-action">
<button class="btn-delete-item" data-key="${escapeHtml(group.key)}" aria-label="Eintrag entfernen"><span aria-hidden="true">🗑️</span></button>
</div>
</div>
`;
})
.join("");
cartList.innerHTML = header + rows;
const total = cart.reduce((sum, item) => sum + Number(item.price || 0), 0);
const vat = total - total / 1.19;
totalEl.innerText = formatEuro(total);
vatEl.innerText = `inkl. 19% MwSt: ${formatEuro(vat)}`;
saveCart();
}
window.removeItem = function removeItem(id) {
cart = cart.filter((item) => item.id !== id);
saveCart();
renderCart();
};
window.changeQty = function changeQty(title, delta) {
if (delta > 0) {
const item = cart.find((entry) => entry.title === title);
if (item) {
cart.push({ ...item, id: Date.now() + Math.random() });
}
} else {
const index = cart
.map((entry) => entry.title)
.lastIndexOf(title);
if (index !== -1) {
cart.splice(index, 1);
}
}
saveCart();
renderCart();
};

View File

@@ -1,232 +0,0 @@
function formatCheckoutEuro(value) {
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
}
let selectedPaymentMethod = "";
let checkoutEventsBound = false;
function setCheckoutStep(step) {
const step1 = document.getElementById("checkout-step-1");
const step2 = document.getElementById("checkout-step-2");
const step3 = document.getElementById("checkout-step-3");
step1?.classList.toggle("hidden", step !== 1);
step2?.classList.toggle("hidden", step !== 2);
step3?.classList.toggle("hidden", step !== 3);
const line1 = document.getElementById("line-1");
const line2 = document.getElementById("line-2");
const indicator1 = document.getElementById("step-1-indicator");
const indicator2 = document.getElementById("step-2-indicator");
const indicator3 = document.getElementById("step-3-indicator");
indicator1?.classList.add("active");
indicator2?.classList.toggle("active", step >= 2);
indicator3?.classList.toggle("active", step >= 3);
line1?.classList.toggle("active", step >= 2);
line2?.classList.toggle("active", step >= 3);
}
function renderCheckout() {
const summaryList = document.getElementById("checkout-summary-list");
const totalDisplay = document.getElementById("checkout-total-display");
const vatDisplay = document.getElementById("checkout-vat-display");
const nextButton = document.getElementById("btn-next-step-2");
if (!summaryList) {
return;
}
summaryList.innerHTML = "";
const safeCart = Array.isArray(cart) ? cart : [];
const total = safeCart.reduce((sum, item) => sum + Number(item.price || 0), 0);
const vat = total - total / 1.19;
safeCart.forEach((item) => {
const row = document.createElement("div");
row.style.cssText = "display:flex; justify-content:space-between; gap:12px; margin-bottom:10px; font-size:0.95rem;";
const infoText = item.category === "movie"
? `Sitz ${item.seatId || "-"} | ${item.hall || "-"} | ${item.time || "-"} Uhr`
: `${item.time || "Standard"} | ${item.hall || "-"}`;
row.innerHTML = `<span>${item.title} (${infoText})</span><span>${formatCheckoutEuro(item.price)}</span>`;
summaryList.appendChild(row);
});
if (totalDisplay) {
totalDisplay.innerText = `Gesamtbetrag: ${formatCheckoutEuro(total)}`;
}
if (vatDisplay) {
vatDisplay.innerText = `inkl. 19% MwSt: ${formatCheckoutEuro(vat)}`;
}
selectedPaymentMethod = "";
document.querySelectorAll(".payment-method").forEach((method) => {
method.classList.remove("selected");
});
nextButton?.classList.add("hidden");
setCheckoutStep(1);
}
function generateTicket() {
const ticketContainer = document.getElementById("ticket-container");
if (!ticketContainer) {
return;
}
const moviesInCart = (Array.isArray(cart) ? cart : []).filter((item) => item.category === "movie");
if (!moviesInCart.length) {
ticketContainer.innerHTML = "<p>Danke fuer deinen Einkauf!</p>";
return;
}
const mainMovie = moviesInCart[0];
const matchingMovieSeats = moviesInCart
.filter((item) => item.title === mainMovie.title && item.time === mainMovie.time)
.map((item) => item.seatId)
.join(", ");
const qrData = encodeURIComponent(`EAGLE-IMAX|${mainMovie.title}|${mainMovie.hall}|${matchingMovieSeats}`);
const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${qrData}&bgcolor=ffffff`;
ticketContainer.innerHTML = /*html*/`
<div class="luxury-ticket">
<div class="ticket-left">
<img src="${mainMovie.img}" class="ticket-poster" alt="${mainMovie.title}">
</div>
<div class="ticket-right">
<div class="ticket-brand">EAGLE'S IMAX PREMIUM</div>
<h2 class="ticket-title">${mainMovie.title}</h2>
<div class="ticket-details">
<p><span>SAAL</span> <strong>${mainMovie.hall}</strong></p>
<p><span>ZEIT</span> <strong>${mainMovie.time} Uhr</strong></p>
<p><span>SITZE</span> <strong>${matchingMovieSeats || "-"}</strong></p>
</div>
<div class="ticket-footer">
<img src="${qrUrl}" class="ticket-qr" alt="QR Code">
<div class="ticket-code">#${Math.floor(Math.random() * 90000) + 10000}</div>
</div>
</div>
</div>
`;
}
function saveOrderForCurrentUser(orderItems, orderTotal) {
if (typeof currentUser === "undefined" || !currentUser) {
return;
}
if (typeof users === "undefined" || !Array.isArray(users)) {
return;
}
const order = {
date: new Date().toLocaleString("de-DE"),
items: orderItems,
total: orderTotal,
paymentMethod: selectedPaymentMethod || "-"
};
const userIndex = users.findIndex((entry) => entry.email === currentUser.email);
if (userIndex === -1) {
return;
}
if (!Array.isArray(users[userIndex].orders)) {
users[userIndex].orders = [];
}
users[userIndex].orders.push(order);
localStorage.setItem("eagleUsers", JSON.stringify(users));
}
function reserveSeatsAfterPayment(orderItems) {
const movieItems = orderItems.filter((item) => item.category === "movie");
movieItems.forEach((item) => {
const key = `${item.hall}-${item.time}`;
if (!occupiedSeatsData[key]) {
occupiedSeatsData[key] = [];
}
occupiedSeatsData[key].push(item.seatId);
});
localStorage.setItem("eagleOccupied", JSON.stringify(occupiedSeatsData));
}
function completeCheckout() {
const orderItems = Array.isArray(cart) ? [...cart] : [];
const orderTotal = orderItems.reduce((sum, item) => sum + Number(item.price || 0), 0);
saveOrderForCurrentUser(orderItems, orderTotal);
reserveSeatsAfterPayment(orderItems);
cart = [];
saveCart?.();
renderCart?.();
}
function bindCheckoutEvents() {
if (checkoutEventsBound) {
return;
}
checkoutEventsBound = true;
const nextButton = document.getElementById("btn-next-step-2");
const backButton = document.getElementById("btn-back-to-step1");
const payNowButton = document.getElementById("btn-pay-now");
document.querySelectorAll(".payment-method").forEach((method) => {
method.addEventListener("click", () => {
document.querySelectorAll(".payment-method").forEach((entry) => {
entry.classList.remove("selected");
});
method.classList.add("selected");
selectedPaymentMethod = method.dataset.method || "";
nextButton?.classList.remove("hidden");
});
});
nextButton?.addEventListener("click", () => {
if (!selectedPaymentMethod) {
alert("Bitte waehle zuerst eine Zahlungsmethode aus.");
return;
}
setCheckoutStep(2);
});
backButton?.addEventListener("click", () => {
setCheckoutStep(1);
});
payNowButton?.addEventListener("click", () => {
if (!Array.isArray(cart) || !cart.length) {
alert("Dein Warenkorb ist leer.");
return;
}
payNowButton.disabled = true;
payNowButton.innerText = "Verarbeite...";
payNowButton.style.opacity = "0.7";
setTimeout(() => {
setCheckoutStep(3);
generateTicket();
completeCheckout();
payNowButton.disabled = false;
payNowButton.innerText = "Jetzt Bezahlen";
payNowButton.style.opacity = "1";
}, 1200);
});
}
document.addEventListener("DOMContentLoaded", bindCheckoutEvents);

View File

@@ -51,14 +51,16 @@ function persistCurrentUser() {
} }
} }
let users = readStorageJson("eagleUsers", []); export let users = readStorageJson("eagleUsers", []);
if (!Array.isArray(users)) { if (!Array.isArray(users)) {
users = []; users = [];
} }
users = users.map(normalizeUser).filter(Boolean); users = users.map(normalizeUser).filter(Boolean);
const rawCurrentUser = readStorageJson("currentUser", null); const rawCurrentUser = readStorageJson("currentUser", null);
var currentUser: User | null = rawCurrentUser ? normalizeUser(rawCurrentUser) : null;
export var currentUser: User | null = rawCurrentUser ? normalizeUser(rawCurrentUser) : null;
if (currentUser && currentUser.email) { if (currentUser && currentUser.email) {
const currentEmail = currentUser.email; const currentEmail = currentUser.email;
const storedMatch = users.find((user: { email: string; }) => { const storedMatch = users.find((user: { email: string; }) => {
@@ -84,7 +86,7 @@ function getInputValue(id: string): string {
return el?.value.trim() ?? ""; return el?.value.trim() ?? "";
} }
async function registerUser() { export async function registerUser() {
const firstName = getInputValue("reg-firstname"); const firstName = getInputValue("reg-firstname");
const lastName = getInputValue("reg-lastname"); const lastName = getInputValue("reg-lastname");
const email = getInputValue("reg-email").toLowerCase(); const email = getInputValue("reg-email").toLowerCase();
@@ -129,7 +131,7 @@ const password = document.querySelector<HTMLInputElement>("#reg-password")?.valu
openAccountDashboard(); openAccountDashboard();
} }
async function loginUser() { export async function loginUser() {
const email = (document.querySelector<HTMLInputElement>("#login-email")?.value.trim() || "").toLowerCase(); const email = (document.querySelector<HTMLInputElement>("#login-email")?.value.trim() || "").toLowerCase();
const password = document.querySelector<HTMLInputElement>("#login-password")?.value || ""; const password = document.querySelector<HTMLInputElement>("#login-password")?.value || "";
const hashedPassword = await hashMessage(password); const hashedPassword = await hashMessage(password);
@@ -148,7 +150,7 @@ async function loginUser() {
openAccountDashboard(); openAccountDashboard();
} }
function openAccountDashboard() { export function openAccountDashboard() {
const accountView = document.getElementById("account-view"); const accountView = document.getElementById("account-view");
if (!accountView) { if (!accountView) {
return; return;
@@ -464,3 +466,4 @@ function logoutUser() {
persistCurrentUser(); persistCurrentUser();
window.location.reload(); window.location.reload();
} }

View File

@@ -1,352 +0,0 @@
let currentBookingContext = null;
let currentHallLayout = null;
function openBooking(movie, hall, time) {
const titleEl = document.getElementById("modal-movie-title");
const infoEl = document.getElementById("modal-info-text");
if (titleEl) {
titleEl.innerText = movie;
}
if (infoEl) {
infoEl.innerText = `${hall}${time} Uhr`;
}
currentBookingContext = { movie, hall, time };
createSeats(hall, time);
renderBookingLegend();
updateBookingSummary();
document.getElementById("booking-modal")?.classList.remove("hidden");
}
function getRowLabel(rowIndex) {
return String(rowIndex + 1);
}
function buildHallLayout(hallName, baseConfig) {
const rows = Number(baseConfig.rows || 0);
const totalCols = Number(baseConfig.left || 0) + Number(baseConfig.right || 0);
const isDeluxe = /deluxe/i.test(hallName);
const left = isDeluxe
? Math.max(3, Number(baseConfig.left || 0) - 1)
: Number(baseConfig.left || 0);
const right = Math.max(0, totalCols - left);
const vipRows = rows > 0 ? [rows] : [];
const dboxMap = new Set();
const markDboxRange = (rowNumber, startCol, width) => {
if (!rowNumber || width <= 0) {
return;
}
const maxCol = Math.min(totalCols, startCol + width - 1);
for (let col = startCol; col <= maxCol; col++) {
dboxMap.add(`${rowNumber}-${col}`);
}
};
if (isDeluxe) {
const configuredDboxSeats = Array.isArray(baseConfig.dbox)
? baseConfig.dbox.reduce((sum, section) => sum + Number(section.w || 0), 0)
: 0;
const totalDboxSeats = Math.max(4, configuredDboxSeats || 0);
const firstRow = Math.max(1, rows - 2);
const secondRow = Math.max(1, rows - 1);
const targetRows = [firstRow, secondRow]
.filter((rowNumber, index, arr) => arr.indexOf(rowNumber) === index)
.filter((rowNumber) => !vipRows.includes(rowNumber));
const rowCount = Math.max(1, targetRows.length);
const seatsPerFirstRows = Math.ceil(totalDboxSeats / rowCount);
let remaining = totalDboxSeats;
targetRows.forEach((rowNumber, index) => {
const seatsForRow = index === targetRows.length - 1
? remaining
: Math.min(seatsPerFirstRows, remaining);
remaining -= seatsForRow;
const startCol = left + Math.max(1, Math.floor((right - seatsForRow) / 2) + 1);
markDboxRange(rowNumber, startCol, seatsForRow);
});
} else if (Array.isArray(baseConfig.dbox)) {
baseConfig.dbox.forEach((section) => {
const rowNumber = Number(section.r || 0);
const width = Number(section.w || 0);
const startCol = Number(section.c || 0);
markDboxRange(rowNumber, startCol, width);
});
}
return {
rows,
left,
right,
totalCols,
vipRows,
dboxMap,
isImax: Boolean(baseConfig.isImax)
};
}
function getSeatType(layout, rowNumber, colNumber) {
if (layout.dboxMap.has(`${rowNumber}-${colNumber}`)) {
return "dbox";
}
if (layout.vipRows.includes(rowNumber)) {
return "vip";
}
if (layout.isImax) {
return "imax";
}
return "normal";
}
function createSeatElement({ seatId, seatType, occupiedSeats }) {
const seat = document.createElement("button");
seat.type = "button";
seat.classList.add("seat", seatType);
seat.dataset.seatId = seatId;
seat.dataset.type = seatType;
seat.title = `${seatId} (${seatType.toUpperCase()})`;
if (occupiedSeats.has(seatId)) {
seat.classList.add("occupied");
seat.disabled = true;
seat.setAttribute("aria-label", `${seatId} belegt`);
return seat;
}
seat.setAttribute("aria-label", `${seatId} frei`);
seat.addEventListener("click", () => {
seat.classList.toggle("selected");
updateBookingSummary();
});
return seat;
}
function createSeats(hallName, time) {
const seatGrid = document.getElementById("seat-grid");
if (!seatGrid) {
return;
}
seatGrid.innerHTML = "";
const baseConfig = seatLayouts[hallName];
if (!baseConfig) {
currentHallLayout = null;
return;
}
currentHallLayout = buildHallLayout(hallName, baseConfig);
const occupiedKey = `${hallName}-${time}`;
const occupiedSeats = new Set(Array.isArray(occupiedSeatsData?.[occupiedKey]) ? occupiedSeatsData[occupiedKey] : []);
for (let rowIndex = 0; rowIndex < currentHallLayout.rows; rowIndex++) {
const rowNumber = rowIndex + 1;
const rowLabel = getRowLabel(rowIndex);
const perspectiveFactor = (currentHallLayout.rows - rowNumber) / Math.max(currentHallLayout.rows - 1, 1);
const rowIndent = Math.round(18 * perspectiveFactor);
const row = document.createElement("div");
row.className = "seat-row cinema-row";
row.style.setProperty("--row-indent", `${rowIndent}px`);
const leftLabel = document.createElement("div");
leftLabel.className = "row-label";
leftLabel.textContent = rowLabel;
const rightLabel = document.createElement("div");
rightLabel.className = "row-label row-label-right";
rightLabel.textContent = rowLabel;
const leftBlock = document.createElement("div");
leftBlock.className = "row-seat-block left-block";
const rightBlock = document.createElement("div");
rightBlock.className = "row-seat-block right-block";
for (let col = 1; col <= currentHallLayout.totalCols; col++) {
const seatId = `R${rowNumber}-P${col}`;
const seatType = getSeatType(currentHallLayout, rowNumber, col);
const seat = createSeatElement({ seatId, seatType, occupiedSeats });
if (col <= currentHallLayout.left) {
leftBlock.appendChild(seat);
} else {
rightBlock.appendChild(seat);
}
}
const aisle = document.createElement("div");
aisle.className = "aisle-gap";
row.append(leftLabel, leftBlock, aisle, rightBlock, rightLabel);
seatGrid.appendChild(row);
}
}
function renderBookingLegend() {
const legend = document.getElementById("dynamic-legend");
if (!legend || !currentHallLayout) {
return;
}
const legendItems = [
{ type: "normal", label: "Standard" },
{ type: "selected", label: "Ausgewählt" },
{ type: "occupied", label: "Belegt" }
];
if (currentHallLayout.isImax) {
legendItems.unshift({ type: "imax", label: "IMAX" });
}
if (currentHallLayout.vipRows.length > 0) {
legendItems.unshift({ type: "vip", label: "VIP" });
}
if (currentHallLayout.dboxMap.size > 0) {
legendItems.unshift({ type: "dbox", label: "D-BOX" });
}
legend.innerHTML = legendItems
.map((item) => `
<div class="item">
<span class="seat ${item.type}"></span>
<span>${item.label}</span>
</div>
`)
.join("");
}
function updateBookingSummary() {
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected"));
const summaryPanel = document.getElementById("booking-summary");
const summaryItems = document.getElementById("summary-items");
const totalEl = document.getElementById("total-price");
let total = 0;
if (summaryItems) {
summaryItems.innerHTML = selectedSeats
.map((seat) => {
const type = seat.dataset.type || "normal";
const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11);
total += seatPrice;
return `
<div class="summary-row">
<span>${seat.dataset.seatId}</span>
<span>${seatPrice.toFixed(2).replace(".", ",")} EUR</span>
</div>
`;
})
.join("");
} else {
selectedSeats.forEach((seat) => {
const type = seat.dataset.type || "normal";
const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11);
total += seatPrice;
});
}
if (totalEl) {
totalEl.innerText = `${total.toFixed(2).replace(".", ",")} EUR`;
}
summaryPanel?.classList.toggle("hidden", selectedSeats.length === 0);
}
function findMoviePoster(movieTitle) {
const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card"));
const normalizedTarget = String(movieTitle || "").trim().toLowerCase();
for (const card of cards) {
const title = card.querySelector("h2, h3")?.innerText?.trim().toLowerCase();
if (title === normalizedTarget) {
const imageSrc = card.querySelector("img")?.src;
if (imageSrc) {
return imageSrc;
}
}
}
return "";
}
function confirmSelectedSeats() {
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected"));
if (!currentBookingContext || selectedSeats.length === 0) {
alert("Bitte waehle mindestens einen Platz aus.");
return;
}
const moviePoster = findMoviePoster(currentBookingContext.movie);
const addedSeats = [];
selectedSeats.forEach((seat) => {
const seatId = seat.dataset.seatId;
const seatType = seat.dataset.type || "normal";
const alreadyInCart = cart.some((item) =>
item.category === "movie" &&
item.title === currentBookingContext.movie &&
item.hall === currentBookingContext.hall &&
item.time === currentBookingContext.time &&
item.seatId === seatId
);
if (alreadyInCart) {
return;
}
cart.push({
id: Date.now() + Math.random(),
category: "movie",
title: currentBookingContext.movie,
hall: currentBookingContext.hall,
time: currentBookingContext.time,
seatId,
type: seatType.toUpperCase(),
price: Number(prices?.[seatType] ?? prices?.normal ?? 11),
img: moviePoster
});
addedSeats.push(seatId);
});
if (!addedSeats.length) {
alert("Diese Plaetze sind bereits im Warenkorb.");
return;
}
saveCart?.();
renderCart?.();
renderCheckout?.();
document.getElementById("booking-modal")?.classList.add("hidden");
const snackOverlay = document.getElementById("snack-prompt-overlay");
snackOverlay?.classList.remove("hidden");
document.body.style.overflow = "hidden";
}
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("btn-confirm-seats")?.addEventListener("click", confirmSelectedSeats);
});

View File

@@ -1,32 +1,36 @@
let currentBookingContext = null; import { seatLayouts, occupiedSeatsData, prices, cart } from "./main"
let currentHallLayout = null; import { renderCart, saveCart } from "./cart";
import { renderCheckout } from "./checkout";
function openBooking(movie, hall, time) { let currentBookingContext: any = null;
const titleEl = document.getElementById("modal-movie-title"); let currentHallLayout: any = null;
const infoEl = document.getElementById("modal-info-text");
if (titleEl) { export function openBooking(movie: string, hall: string, time: any) {
titleEl.innerText = movie; const titleEl = document.getElementById("modal-movie-title");
} const infoEl = document.getElementById("modal-info-text");
if (infoEl) { if (titleEl) {
infoEl.innerText = `${hall}${time} Uhr`; titleEl.innerText = movie;
} }
currentBookingContext = { movie, hall, time }; if (infoEl) {
infoEl.innerText = `${hall}${time} Uhr`;
}
createSeats(hall, time); currentBookingContext = { movie, hall, time };
renderBookingLegend();
updateBookingSummary();
document.getElementById("booking-modal")?.classList.remove("hidden"); createSeats(hall, time);
} renderBookingLegend();
updateBookingSummary();
function getRowLabel(rowIndex) { document.getElementById("booking-modal")?.classList.remove("hidden");
}
function getRowLabel(rowIndex: number) {
return String(rowIndex + 1); return String(rowIndex + 1);
} }
function buildHallLayout(hallName, baseConfig) { function buildHallLayout(hallName: string, baseConfig:any) {
const rows = Number(baseConfig.rows || 0); const rows = Number(baseConfig.rows || 0);
const totalCols = Number(baseConfig.left || 0) + Number(baseConfig.right || 0); const totalCols = Number(baseConfig.left || 0) + Number(baseConfig.right || 0);
const isDeluxe = /deluxe/i.test(hallName); const isDeluxe = /deluxe/i.test(hallName);
@@ -39,7 +43,7 @@ function buildHallLayout(hallName, baseConfig) {
const vipRows = rows > 0 ? [rows] : []; const vipRows = rows > 0 ? [rows] : [];
const dboxMap = new Set(); const dboxMap = new Set();
const markDboxRange = (rowNumber, startCol, width) => { const markDboxRange = (rowNumber: number, startCol: number, width: number) => {
if (!rowNumber || width <= 0) { if (!rowNumber || width <= 0) {
return; return;
} }
@@ -52,7 +56,7 @@ function buildHallLayout(hallName, baseConfig) {
if (isDeluxe) { if (isDeluxe) {
const configuredDboxSeats = Array.isArray(baseConfig.dbox) const configuredDboxSeats = Array.isArray(baseConfig.dbox)
? baseConfig.dbox.reduce((sum, section) => sum + Number(section.w || 0), 0) ? baseConfig.dbox.reduce((sum: number, section: any) => sum + Number(section.w || 0), 0)
: 0; : 0;
const totalDboxSeats = Math.max(4, configuredDboxSeats || 0); const totalDboxSeats = Math.max(4, configuredDboxSeats || 0);
@@ -77,7 +81,7 @@ function buildHallLayout(hallName, baseConfig) {
markDboxRange(rowNumber, startCol, seatsForRow); markDboxRange(rowNumber, startCol, seatsForRow);
}); });
} else if (Array.isArray(baseConfig.dbox)) { } else if (Array.isArray(baseConfig.dbox)) {
baseConfig.dbox.forEach((section) => { baseConfig.dbox.forEach((section: any) => {
const rowNumber = Number(section.r || 0); const rowNumber = Number(section.r || 0);
const width = Number(section.w || 0); const width = Number(section.w || 0);
const startCol = Number(section.c || 0); const startCol = Number(section.c || 0);
@@ -96,7 +100,7 @@ function buildHallLayout(hallName, baseConfig) {
}; };
} }
function getSeatType(layout, rowNumber, colNumber) { function getSeatType(layout: any, rowNumber: number, colNumber: number) {
if (layout.dboxMap.has(`${rowNumber}-${colNumber}`)) { if (layout.dboxMap.has(`${rowNumber}-${colNumber}`)) {
return "dbox"; return "dbox";
} }
@@ -112,7 +116,7 @@ function getSeatType(layout, rowNumber, colNumber) {
return "normal"; return "normal";
} }
function createSeatElement({ seatId, seatType, occupiedSeats }) { function createSeatElement({seatId, seatType, occupiedSeats }:any) {
const seat = document.createElement("button"); const seat = document.createElement("button");
seat.type = "button"; seat.type = "button";
seat.classList.add("seat", seatType); seat.classList.add("seat", seatType);
@@ -136,7 +140,7 @@ function createSeatElement({ seatId, seatType, occupiedSeats }) {
return seat; return seat;
} }
function createSeats(hallName, time) { function createSeats(hallName: string, time: any) {
const seatGrid = document.getElementById("seat-grid"); const seatGrid = document.getElementById("seat-grid");
if (!seatGrid) { if (!seatGrid) {
return; return;
@@ -144,7 +148,8 @@ function createSeats(hallName, time) {
seatGrid.innerHTML = ""; seatGrid.innerHTML = "";
const baseConfig = seatLayouts[hallName]; const arrIndex = hallName as keyof typeof seatLayouts;
const baseConfig: any = seatLayouts[arrIndex];
if (!baseConfig) { if (!baseConfig) {
currentHallLayout = null; currentHallLayout = null;
return; return;
@@ -235,7 +240,7 @@ function renderBookingLegend() {
} }
function updateBookingSummary() { function updateBookingSummary() {
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")); const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")) as HTMLElement[];;
const summaryPanel = document.getElementById("booking-summary"); const summaryPanel = document.getElementById("booking-summary");
const summaryItems = document.getElementById("summary-items"); const summaryItems = document.getElementById("summary-items");
const totalEl = document.getElementById("total-price"); const totalEl = document.getElementById("total-price");
@@ -245,7 +250,7 @@ function updateBookingSummary() {
if (summaryItems) { if (summaryItems) {
summaryItems.innerHTML = selectedSeats summaryItems.innerHTML = selectedSeats
.map((seat) => { .map((seat) => {
const type = seat.dataset.type || "normal"; const type = (seat.dataset.type || "normal") as keyof typeof prices;
const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11); const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11);
total += seatPrice; total += seatPrice;
@@ -272,12 +277,13 @@ function updateBookingSummary() {
summaryPanel?.classList.toggle("hidden", selectedSeats.length === 0); summaryPanel?.classList.toggle("hidden", selectedSeats.length === 0);
} }
function findMoviePoster(movieTitle) { function findMoviePoster(movieTitle: string) {
const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card")); const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card"));
const normalizedTarget = String(movieTitle || "").trim().toLowerCase(); const normalizedTarget = String(movieTitle || "").trim().toLowerCase();
for (const card of cards) { for (const card of cards) {
const title = card.querySelector("h2, h3")?.innerText?.trim().toLowerCase(); const currentCard = card.querySelector("h2, h3") as HTMLElement;
const title = currentCard.innerText?.trim().toLowerCase();
if (title === normalizedTarget) { if (title === normalizedTarget) {
const imageSrc = card.querySelector("img")?.src; const imageSrc = card.querySelector("img")?.src;
if (imageSrc) { if (imageSrc) {
@@ -290,7 +296,7 @@ function findMoviePoster(movieTitle) {
} }
function confirmSelectedSeats() { function confirmSelectedSeats() {
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")); const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")) as HTMLElement[];
if (!currentBookingContext || selectedSeats.length === 0) { if (!currentBookingContext || selectedSeats.length === 0) {
alert("Bitte waehle mindestens einen Platz aus."); alert("Bitte waehle mindestens einen Platz aus.");
@@ -304,7 +310,7 @@ function confirmSelectedSeats() {
const seatId = seat.dataset.seatId; const seatId = seat.dataset.seatId;
const seatType = seat.dataset.type || "normal"; const seatType = seat.dataset.type || "normal";
const alreadyInCart = cart.some((item) => const alreadyInCart = cart.some((item: any) =>
item.category === "movie" && item.category === "movie" &&
item.title === currentBookingContext.movie && item.title === currentBookingContext.movie &&
item.hall === currentBookingContext.hall && item.hall === currentBookingContext.hall &&

View File

@@ -1,8 +1,10 @@
function formatEuro(value) { import { cart } from "./main";
function formatEuro(value: number) {
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`; return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
} }
function escapeHtml(value) { function escapeHtml(value: any) {
return String(value || "") return String(value || "")
.replaceAll("&", "&amp;") .replaceAll("&", "&amp;")
.replaceAll("<", "&lt;") .replaceAll("<", "&lt;")
@@ -11,14 +13,14 @@ function escapeHtml(value) {
.replaceAll("'", "&#39;"); .replaceAll("'", "&#39;");
} }
function buildCartKey(item) { function buildCartKey(item: { category: string; seatId: any; hall: any; time: any; title: any; }) {
const infoText = item.category === "movie" const infoText = item.category === "movie"
? `Sitz: ${item.seatId} (${item.hall})` ? `Sitz: ${item.seatId} (${item.hall})`
: item.time; : item.time;
return `${item.title}-${item.hall}-${infoText}`; return `${item.title}-${item.hall}-${infoText}`;
} }
function isDrinkItem(item) { function isDrinkItem(item: { category: string; title: any; hall: any; }) {
if (item.category !== "snack") { if (item.category !== "snack") {
return false; return false;
} }
@@ -39,7 +41,7 @@ function isDrinkItem(item) {
return drinkKeywords.some((word) => title.includes(word)) || size.includes("l"); return drinkKeywords.some((word) => title.includes(word)) || size.includes("l");
} }
function buildItemInfo(item) { function buildItemInfo(item: { category: any; seatId?: any; hall: any; time?: any; title: any; }) {
if (item.category === "movie") { if (item.category === "movie") {
return ` return `
<div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div> <div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div>
@@ -65,7 +67,7 @@ function buildItemInfo(item) {
function groupCartItems() { function groupCartItems() {
const groups = new Map(); const groups = new Map();
cart.forEach((item) => { cart.forEach((item: { price?: any; category: string; seatId: any; hall: any; time: any; title: any; }) => {
const key = buildCartKey(item); const key = buildCartKey(item);
if (!groups.has(key)) { if (!groups.has(key)) {
@@ -85,12 +87,12 @@ function groupCartItems() {
return Array.from(groups.values()); return Array.from(groups.values());
} }
function saveCart() { export function saveCart() {
localStorage.setItem("eagleCart", JSON.stringify(cart)); localStorage.setItem("eagleCart", JSON.stringify(cart));
updateCartBadge(); updateCartBadge();
} }
function updateCartBadge() { export function updateCartBadge() {
const cartBadge = document.getElementById("cart-badge"); const cartBadge = document.getElementById("cart-badge");
if (!cartBadge) { if (!cartBadge) {
@@ -101,7 +103,7 @@ function updateCartBadge() {
cartBadge.classList.toggle("hidden", cart.length === 0); cartBadge.classList.toggle("hidden", cart.length === 0);
} }
function renderCart() { export function renderCart() {
const cartList = document.getElementById("cart-items-list"); const cartList = document.getElementById("cart-items-list");
const totalEl = document.getElementById("cart-total-right"); const totalEl = document.getElementById("cart-total-right");
const vatEl = document.getElementById("cart-vat-right"); const vatEl = document.getElementById("cart-vat-right");
@@ -172,22 +174,23 @@ function renderCart() {
saveCart(); saveCart();
} }
//@ts-ignore
window.removeItem = function removeItem(id) { window.removeItem = function removeItem(id: any) {
cart = cart.filter((item) => item.id !== id); var localCart = cart.filter((item: { id: any; }) => item.id !== id);
saveCart(); saveCart();
renderCart(); renderCart();
}; };
window.changeQty = function changeQty(title, delta) { //@ts-ignore
window.changeQty = function changeQty(title, delta): void {
if (delta > 0) { if (delta > 0) {
const item = cart.find((entry) => entry.title === title); const item = cart.find((entry: { title: any; }) => entry.title === title);
if (item) { if (item) {
cart.push({ ...item, id: Date.now() + Math.random() }); cart.push({ ...item, id: Date.now() + Math.random() });
} }
} else { } else {
const index = cart const index = cart
.map((entry) => entry.title) .map((entry: { title: any; }) => entry.title)
.lastIndexOf(title); .lastIndexOf(title);
if (index !== -1) { if (index !== -1) {
cart.splice(index, 1); cart.splice(index, 1);
@@ -197,3 +200,4 @@ window.changeQty = function changeQty(title, delta) {
saveCart(); saveCart();
renderCart(); renderCart();
}; };

View File

@@ -1,11 +1,15 @@
function formatCheckoutEuro(value) { import { currentUser, users } from "./account";
import { renderCart, saveCart } from "./cart";
import { cart, emptyCart, occupiedSeatsData } from "./main";
function formatCheckoutEuro(value: number) {
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`; return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
} }
let selectedPaymentMethod = ""; let selectedPaymentMethod = "";
let checkoutEventsBound = false; let checkoutEventsBound = false;
function setCheckoutStep(step) { function setCheckoutStep(step: number) {
const step1 = document.getElementById("checkout-step-1"); const step1 = document.getElementById("checkout-step-1");
const step2 = document.getElementById("checkout-step-2"); const step2 = document.getElementById("checkout-step-2");
const step3 = document.getElementById("checkout-step-3"); const step3 = document.getElementById("checkout-step-3");
@@ -27,7 +31,7 @@ function setCheckoutStep(step) {
line2?.classList.toggle("active", step >= 3); line2?.classList.toggle("active", step >= 3);
} }
function renderCheckout() { export function renderCheckout() {
const summaryList = document.getElementById("checkout-summary-list"); const summaryList = document.getElementById("checkout-summary-list");
const totalDisplay = document.getElementById("checkout-total-display"); const totalDisplay = document.getElementById("checkout-total-display");
const vatDisplay = document.getElementById("checkout-vat-display"); const vatDisplay = document.getElementById("checkout-vat-display");
@@ -115,7 +119,7 @@ function generateTicket() {
`; `;
} }
function saveOrderForCurrentUser(orderItems, orderTotal) { function saveOrderForCurrentUser(orderItems: any[], orderTotal: any) {
if (typeof currentUser === "undefined" || !currentUser) { if (typeof currentUser === "undefined" || !currentUser) {
return; return;
} }
@@ -131,6 +135,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
paymentMethod: selectedPaymentMethod || "-" paymentMethod: selectedPaymentMethod || "-"
}; };
//@ts-ignore
const userIndex = users.findIndex((entry) => entry.email === currentUser.email); const userIndex = users.findIndex((entry) => entry.email === currentUser.email);
if (userIndex === -1) { if (userIndex === -1) {
return; return;
@@ -144,7 +149,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
localStorage.setItem("eagleUsers", JSON.stringify(users)); localStorage.setItem("eagleUsers", JSON.stringify(users));
} }
function reserveSeatsAfterPayment(orderItems) { function reserveSeatsAfterPayment(orderItems: any[]) {
const movieItems = orderItems.filter((item) => item.category === "movie"); const movieItems = orderItems.filter((item) => item.category === "movie");
movieItems.forEach((item) => { movieItems.forEach((item) => {
@@ -166,7 +171,7 @@ function completeCheckout() {
saveOrderForCurrentUser(orderItems, orderTotal); saveOrderForCurrentUser(orderItems, orderTotal);
reserveSeatsAfterPayment(orderItems); reserveSeatsAfterPayment(orderItems);
cart = []; emptyCart?.()
saveCart?.(); saveCart?.();
renderCart?.(); renderCart?.();
} }
@@ -180,7 +185,7 @@ function bindCheckoutEvents() {
const nextButton = document.getElementById("btn-next-step-2"); const nextButton = document.getElementById("btn-next-step-2");
const backButton = document.getElementById("btn-back-to-step1"); const backButton = document.getElementById("btn-back-to-step1");
const payNowButton = document.getElementById("btn-pay-now"); const payNowButton = document.getElementById("btn-pay-now") as HTMLButtonElement;
document.querySelectorAll(".payment-method").forEach((method) => { document.querySelectorAll(".payment-method").forEach((method) => {
method.addEventListener("click", () => { method.addEventListener("click", () => {
@@ -189,6 +194,7 @@ function bindCheckoutEvents() {
}); });
method.classList.add("selected"); method.classList.add("selected");
//@ts-ignore
selectedPaymentMethod = method.dataset.method || ""; selectedPaymentMethod = method.dataset.method || "";
nextButton?.classList.remove("hidden"); nextButton?.classList.remove("hidden");
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,18 @@
// Shared app state for legacy script files (account.js, booking.js, cart.js, checkout.js) import { currentUser, loginUser, openAccountDashboard, registerUser } from "./account";
var prices = { normal: 11.0, imax: 15.0, vip: 12.0, dbox: 16.0 }; import { openBooking } from "./booking";
var seatLayouts = { import { renderCart, saveCart, updateCartBadge } from "./cart";
import { renderCheckout } from "./checkout";
// Shared app state for legacy script files (account.js, booking.js, cart.js, checkout.js)
export const prices: Record<string, number> = { normal: 11.0, imax: 15.0, vip: 12.0, dbox: 16.0 };
export var seatLayouts = {
"Kino 1": { rows: 6, left: 3, right: 7, vipRows: [5], dbox: [] }, "Kino 1": { rows: 6, left: 3, right: 7, vipRows: [5], dbox: [] },
"Kino 2": { rows: 7, left: 5, right: 5, vipRows: [6], dbox: [] }, "Kino 2": { rows: 7, left: 5, right: 5, vipRows: [6], dbox: [] },
"Deluxe 1": { rows: 10, left: 7, right: 8, vipRows: [9], dbox: [{ r: 4, c: 5, w: 4 }] }, "Deluxe 1": { rows: 10, left: 7, right: 8, vipRows: [9], dbox: [{ r: 4, c: 5, w: 4 }] },
IMAX: { rows: 15, left: 10, right: 10, vipRows: [], dbox: [], isImax: true } IMAX: { rows: 15, left: 10, right: 10, vipRows: [], dbox: [], isImax: true }
}; };
var cart = JSON.parse(localStorage.getItem("eagleCart")) || []; export var cart = JSON.parse(localStorage.getItem("eagleCart") || '{}');
var occupiedSeatsData = JSON.parse(localStorage.getItem("eagleOccupied")) || {}; export var occupiedSeatsData = JSON.parse(localStorage.getItem("eagleOccupied") || '{}');
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const views = { const views = {
@@ -326,10 +331,10 @@ document.addEventListener("DOMContentLoaded", () => {
["13:00", "16:00", "18:20", "20:40"] ["13:00", "16:00", "18:20", "20:40"]
]; ];
let movieProgram = []; let movieProgram: any = []; // TODO: Find type
let heroItems = []; let heroItems: any = []; // TODO: find Type
let heroIndex = 0; let heroIndex = 0;
let heroTimer = null; let heroTimer:any = null; // TODO: find type
const weekdayShort = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]; const weekdayShort = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
@@ -348,7 +353,7 @@ document.addEventListener("DOMContentLoaded", () => {
window.scrollTo({ top: 0, behavior: "smooth" }); window.scrollTo({ top: 0, behavior: "smooth" });
}; };
const showMovieList = (programIndexToFocus = null) => { const showMovieList = (programIndexToFocus:number = NaN) => {
hideAllViews(); hideAllViews();
views.list?.classList.remove("hidden"); views.list?.classList.remove("hidden");
@@ -365,7 +370,7 @@ document.addEventListener("DOMContentLoaded", () => {
} }
}; };
const showStaticView = (viewElement) => { const showStaticView = (viewElement: HTMLElement) => {
if (!viewElement) { if (!viewElement) {
return; return;
} }
@@ -394,20 +399,20 @@ document.addEventListener("DOMContentLoaded", () => {
ui.bookingModal?.classList.add("hidden"); ui.bookingModal?.classList.add("hidden");
}; };
const escapeHtml = (value) => String(value || "") const escapeHtml = (value: string) => String(value || "")
.replaceAll("&", "&amp;") .replaceAll("&", "&amp;")
.replaceAll("<", "&lt;") .replaceAll("<", "&lt;")
.replaceAll(">", "&gt;") .replaceAll(">", "&gt;")
.replaceAll('"', "&quot;") .replaceAll('"', "&quot;")
.replaceAll("'", "&#39;"); .replaceAll("'", "&#39;");
const formatDateShort = (dateObj) => { const formatDateShort = (dateObj: any) => {
const day = String(dateObj.getDate()).padStart(2, "0"); const day = String(dateObj.getDate()).padStart(2, "0");
const month = String(dateObj.getMonth() + 1).padStart(2, "0"); const month = String(dateObj.getMonth() + 1).padStart(2, "0");
return `${day}.${month}.`; return `${day}.${month}.`;
}; };
const buildDayMeta = (offset) => { const buildDayMeta = (offset: number) => {
const date = new Date(); const date = new Date();
date.setHours(0, 0, 0, 0); date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() + offset); date.setDate(date.getDate() + offset);
@@ -441,14 +446,15 @@ document.addEventListener("DOMContentLoaded", () => {
}; };
}; };
const buildScheduleForMovie = (movieIndex) => { const buildScheduleForMovie = (movieIndex: number) => {
return Array.from({ length: 7 }, (_, dayOffset) => { return Array.from({ length: 7 }, (_, dayOffset) => {
const dayMeta = buildDayMeta(dayOffset); const dayMeta = buildDayMeta(dayOffset);
const pattern = timePatterns[(movieIndex + dayOffset) % timePatterns.length]; const pattern = timePatterns[(movieIndex + dayOffset) % timePatterns.length] || "Error reading";
const desiredCount = 4 + ((movieIndex + dayOffset) % 2); const desiredCount = 4 + ((movieIndex + dayOffset) % 2);
const showCount = Math.min(pattern.length, desiredCount); const showCount = Math.min(pattern.length, desiredCount);
const showings = pattern.slice(0, showCount).map((time, slotIndex) => { //@ts-ignore
const showings = pattern.slice(0, showCount).map((time: any, slotIndex: number) => { // TODO: fix map issue
const hall = hallRotation[(movieIndex + dayOffset + slotIndex) % hallRotation.length]; const hall = hallRotation[(movieIndex + dayOffset + slotIndex) % hallRotation.length];
return { time, hall }; return { time, hall };
}); });
@@ -468,7 +474,7 @@ document.addEventListener("DOMContentLoaded", () => {
heroItems = movieProgram.slice(0, 5); heroItems = movieProgram.slice(0, 5);
}; };
const setHeroSlide = (index) => { const setHeroSlide = (index: number) => {
if (!heroItems.length || !ui.heroSlider) { if (!heroItems.length || !ui.heroSlider) {
return; return;
} }
@@ -497,17 +503,18 @@ document.addEventListener("DOMContentLoaded", () => {
return; return;
} }
ui.heroSlider.innerHTML = heroItems.map((movie, index) => ` ui.heroSlider.innerHTML = heroItems.map((movie: any, index: number) => `
<div class="hero-slide ${index === 0 ? "active" : ""}" style="background-image: linear-gradient(118deg, rgba(0,0,0,0.34), rgba(0,0,0,0.04)), url('${escapeHtml(movie.backdrop || movie.poster)}');"></div> <div class="hero-slide ${index === 0 ? "active" : ""}" style="background-image: linear-gradient(118deg, rgba(0,0,0,0.34), rgba(0,0,0,0.04)), url('${escapeHtml(movie.backdrop || movie.poster)}');"></div>
`).join(""); `).join("");
if (ui.heroDots) { if (ui.heroDots) {
ui.heroDots.innerHTML = heroItems.map((_, index) => ` ui.heroDots.innerHTML = heroItems.map((_:any, index: number) => `
<button type="button" class="hero-dot ${index === 0 ? "active" : ""}" data-hero-index="${index}"></button> <button type="button" class="hero-dot ${index === 0 ? "active" : ""}" data-hero-index="${index}"></button>
`).join(""); `).join("");
ui.heroDots.addEventListener("click", (event) => { ui.heroDots.addEventListener("click", (event: any) => {
const dot = event.target.closest(".hero-dot"); const dotTarget = event.target || 0;
const dot = dotTarget.closest(".hero-dot");
if (!dot) { if (!dot) {
return; return;
} }
@@ -538,7 +545,8 @@ document.addEventListener("DOMContentLoaded", () => {
return; return;
} }
ui.nowRunningRow.innerHTML = movieProgram.map((movie, index) => /*html*/` // TODO: implement movie interface
ui.nowRunningRow.innerHTML = movieProgram.map((movie: any, index: number) => /*html*/`
<article class="running-poster"> <article class="running-poster">
<img src="${escapeHtml(movie.poster)}" alt="${escapeHtml(movie.title)}"> <img src="${escapeHtml(movie.poster)}" alt="${escapeHtml(movie.title)}">
<div class="running-meta"> <div class="running-meta">
@@ -550,7 +558,7 @@ document.addEventListener("DOMContentLoaded", () => {
`).join(""); `).join("");
}; };
const renderScheduleRows = (programIndex, dayIndex) => { const renderScheduleRows = (programIndex: number, dayIndex: number) => {
const movie = movieProgram[programIndex]; const movie = movieProgram[programIndex];
if (!movie) { if (!movie) {
return; return;
@@ -562,7 +570,7 @@ document.addEventListener("DOMContentLoaded", () => {
return; return;
} }
body.innerHTML = day.showings.map((showing) => /*html*/` body.innerHTML = day.showings.map((showing: { hall: string; time: string; }) => /*html*/`
<button class="schedule-row time-chip program-time-row" data-movie="${escapeHtml(movie.title)}" data-hall="${escapeHtml(showing.hall)}" data-time="${escapeHtml(showing.time)}"> <button class="schedule-row time-chip program-time-row" data-movie="${escapeHtml(movie.title)}" data-hall="${escapeHtml(showing.hall)}" data-time="${escapeHtml(showing.time)}">
<span>${escapeHtml(day.long)}</span> <span>${escapeHtml(day.long)}</span>
<span class="hall-pill">${escapeHtml(showing.hall)}</span> <span class="hall-pill">${escapeHtml(showing.hall)}</span>
@@ -576,7 +584,7 @@ document.addEventListener("DOMContentLoaded", () => {
return; return;
} }
ui.movieProgramList.innerHTML = movieProgram.map((movie, programIndex) => { ui.movieProgramList.innerHTML = movieProgram.map((movie: { schedule: any[]; poster: string; title: string; fsk: string; duration: any; genre: string; description: string; }, programIndex: any) => {
const dayTabs = movie.schedule.map((day, dayIndex) => /*html*/` const dayTabs = movie.schedule.map((day, dayIndex) => /*html*/`
<button type="button" class="program-day-tab ${dayIndex === 0 ? "active" : ""}" data-program-index="${programIndex}" data-day-index="${dayIndex}"> <button type="button" class="program-day-tab ${dayIndex === 0 ? "active" : ""}" data-program-index="${programIndex}" data-day-index="${dayIndex}">
<span>${escapeHtml(day.short)}</span> <span>${escapeHtml(day.short)}</span>
@@ -610,7 +618,7 @@ document.addEventListener("DOMContentLoaded", () => {
`; `;
}).join(""); }).join("");
movieProgram.forEach((_, programIndex) => { movieProgram.forEach((_: any, programIndex: number) => {
renderScheduleRows(programIndex, 0); renderScheduleRows(programIndex, 0);
}); });
}; };
@@ -658,12 +666,16 @@ document.addEventListener("DOMContentLoaded", () => {
ui.linkSnacks?.addEventListener("click", (event) => { ui.linkSnacks?.addEventListener("click", (event) => {
event.preventDefault(); event.preventDefault();
if (views.snacks) {
showStaticView(views.snacks); showStaticView(views.snacks);
}
}); });
ui.linkAbout?.addEventListener("click", (event) => { ui.linkAbout?.addEventListener("click", (event) => {
event.preventDefault(); event.preventDefault();
if (views.about) {
showStaticView(views.about); showStaticView(views.about);
}
}); });
ui.linkCart?.addEventListener("click", (event) => { ui.linkCart?.addEventListener("click", (event) => {
@@ -693,17 +705,17 @@ document.addEventListener("DOMContentLoaded", () => {
}; };
const bindProgramActions = () => { const bindProgramActions = () => {
views.moviesGrid?.addEventListener("click", (event) => { views.moviesGrid?.addEventListener("click", (event:any) => {
const trigger = event.target.closest(".open-program-btn"); const trigger = event.target.closest(".open-program-btn");
if (!trigger) { if (!trigger) {
return; return;
} }
const programIndex = Number(trigger.dataset.programIndex || 0); const programIndex = Number(trigger.dataset.programIndex) || 0;
showMovieList(programIndex); showMovieList(programIndex);
}); });
ui.movieProgramList?.addEventListener("click", (event) => { ui.movieProgramList?.addEventListener("click", (event:any) => {
const dayButton = event.target.closest(".program-day-tab"); const dayButton = event.target.closest(".program-day-tab");
if (!dayButton) { if (!dayButton) {
return; return;
@@ -713,7 +725,7 @@ document.addEventListener("DOMContentLoaded", () => {
const dayIndex = Number(dayButton.dataset.dayIndex || 0); const dayIndex = Number(dayButton.dataset.dayIndex || 0);
const tabRow = dayButton.closest(".program-day-tabs"); const tabRow = dayButton.closest(".program-day-tabs");
tabRow?.querySelectorAll(".program-day-tab").forEach((tab) => tab.classList.remove("active")); tabRow?.querySelectorAll(".program-day-tab").forEach((tab: { classList: { remove: (arg0: string) => any; }; }) => tab.classList.remove("active"));
dayButton.classList.add("active"); dayButton.classList.add("active");
renderScheduleRows(programIndex, dayIndex); renderScheduleRows(programIndex, dayIndex);
@@ -739,7 +751,7 @@ document.addEventListener("DOMContentLoaded", () => {
openButtons.forEach((button) => { openButtons.forEach((button) => {
button.addEventListener("click", () => { button.addEventListener("click", () => {
const targetId = button.getAttribute("data-home-view-open"); const targetId = button.getAttribute("data-home-view-open") as keyof typeof targetMap;
const target = targetId ? targetMap[targetId] : null; const target = targetId ? targetMap[targetId] : null;
if (target) { if (target) {
showStaticView(target); showStaticView(target);
@@ -792,10 +804,11 @@ document.addEventListener("DOMContentLoaded", () => {
const THEME_KEY = "eagleTheme"; const THEME_KEY = "eagleTheme";
const applyTheme = (theme) => { const applyTheme = (theme: any) => {
const isLight = theme === "light"; const isLight = theme === "light";
document.body.classList.toggle("theme-light", isLight); document.body.classList.toggle("theme-light", isLight);
document.body.classList.toggle("theme-dark", !isLight); document.body.classList.toggle("theme-dark", !isLight);
//@ts-ignore
ui.themeToggle.classList.toggle("is-light", isLight); ui.themeToggle.classList.toggle("is-light", isLight);
localStorage.setItem(THEME_KEY, isLight ? "light" : "dark"); localStorage.setItem(THEME_KEY, isLight ? "light" : "dark");
}; };
@@ -812,14 +825,14 @@ document.addEventListener("DOMContentLoaded", () => {
const bindAccountActions = () => { const bindAccountActions = () => {
const registerModal = document.getElementById("register-modal"); const registerModal = document.getElementById("register-modal");
const forgotModal = document.getElementById("forgot-modal"); const forgotModal = document.getElementById("forgot-modal");
const forgotEmailInput = document.getElementById("forgot-email"); const forgotEmailInput = document.getElementById("forgot-email") as HTMLInputElement;
const resetMessage = document.getElementById("reset-message"); const resetMessage = document.getElementById("reset-message");
const loginError = document.getElementById("login-error"); const loginError = document.getElementById("login-error");
const loginEmailInput = document.getElementById("login-email"); const loginEmailInput = document.getElementById("login-email");
const loginPasswordInput = document.getElementById("login-password"); const loginPasswordInput = document.getElementById("login-password");
const openModal = (modal) => modal?.classList.remove("hidden"); const openModal = (modal: HTMLElement | null) => modal?.classList.remove("hidden");
const closeModal = (modal) => modal?.classList.add("hidden"); const closeModal = (modal: HTMLElement | null) => modal?.classList.add("hidden");
const triggerLogin = () => { const triggerLogin = () => {
loginError?.classList.add("hidden"); loginError?.classList.add("hidden");
if (typeof loginUser === "function") { if (typeof loginUser === "function") {
@@ -855,7 +868,7 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
document.getElementById("btn-forgot-password")?.addEventListener("click", () => { document.getElementById("btn-forgot-password")?.addEventListener("click", () => {
if (forgotEmailInput) { if (forgotEmailInput != null) {
forgotEmailInput.value = ""; forgotEmailInput.value = "";
} }
resetMessage?.classList.add("hidden"); resetMessage?.classList.add("hidden");
@@ -900,10 +913,10 @@ document.addEventListener("DOMContentLoaded", () => {
}; };
const bindGlobalDocumentClicks = () => { const bindGlobalDocumentClicks = () => {
document.addEventListener("click", (event) => { document.addEventListener("click", (event: any) => {
if (event.target.classList.contains("opt-btn")) { if (event.target.classList.contains("opt-btn")) {
const optionGroup = event.target.parentElement; const optionGroup = event.target.parentElement;
optionGroup?.querySelectorAll(".opt-btn").forEach((button) => button.classList.remove("active")); optionGroup?.querySelectorAll(".opt-btn").forEach((button: { classList: { remove: (arg0: string) => any; }; }) => button.classList.remove("active"));
event.target.classList.add("active"); event.target.classList.add("active");
} }
@@ -912,15 +925,17 @@ document.addEventListener("DOMContentLoaded", () => {
const row = deleteBtn.closest(".cart-item-row"); const row = deleteBtn.closest(".cart-item-row");
if (row) { if (row) {
row.classList.add("slide-out-left"); row.classList.add("slide-out-left");
row.querySelectorAll("button").forEach((button) => { row.querySelectorAll("button").forEach((button: { disabled: boolean; }) => {
button.disabled = true; button.disabled = true;
}); });
setTimeout(() => { setTimeout(() => {
removeFromCartByKey(deleteBtn.dataset.key); //@ts-ignore
removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist
}, 380); }, 380);
} else { } else {
removeFromCartByKey(deleteBtn.dataset.key); //@ts-ignore
removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist
} }
return; return;
} }
@@ -949,7 +964,7 @@ document.addEventListener("DOMContentLoaded", () => {
return; return;
} }
const relatedItem = cart.find((item) => { const relatedItem = cart.find((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => {
const infoText = item.category === "movie" const infoText = item.category === "movie"
? `Sitz: ${item.seatId} (${item.hall})` ? `Sitz: ${item.seatId} (${item.hall})`
: item.time; : item.time;
@@ -963,7 +978,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (action === "plus") { if (action === "plus") {
cart.push({ ...relatedItem, id: Date.now() + Math.random() }); cart.push({ ...relatedItem, id: Date.now() + Math.random() });
} else { } else {
const keyList = cart.map((item) => { const keyList = cart.map((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => {
const infoText = item.category === "movie" const infoText = item.category === "movie"
? `Sitz: ${item.seatId} (${item.hall})` ? `Sitz: ${item.seatId} (${item.hall})`
: item.time; : item.time;
@@ -982,7 +997,7 @@ document.addEventListener("DOMContentLoaded", () => {
}; };
const bindSnacksActions = () => { const bindSnacksActions = () => {
ui.snacksView?.addEventListener("click", (event) => { ui.snacksView?.addEventListener("click", (event:any) => {
const sizeChip = event.target.closest(".size-chip"); const sizeChip = event.target.closest(".size-chip");
if (!sizeChip) { if (!sizeChip) {
return; return;
@@ -1026,7 +1041,7 @@ document.addEventListener("DOMContentLoaded", () => {
}, 800); }, 800);
}); });
document.querySelectorAll(".tab-btn").forEach((button) => { document.querySelectorAll(".tab-btn").forEach((button: any) => {
button.addEventListener("click", () => { button.addEventListener("click", () => {
document.querySelectorAll(".tab-btn").forEach((tab) => tab.classList.remove("active")); document.querySelectorAll(".tab-btn").forEach((tab) => tab.classList.remove("active"));
button.classList.add("active"); button.classList.add("active");
@@ -1063,9 +1078,9 @@ document.addEventListener("DOMContentLoaded", () => {
} }
}); });
}; };
// @ts-ignore
window.removeFromCartByKey = function removeFromCartByKey(key) { window.removeFromCartByKey = function removeFromCartByKey(key: string) {
cart = cart.filter((item) => { cart = cart.filter((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => {
const infoText = item.category === "movie" const infoText = item.category === "movie"
? `Sitz: ${item.seatId} (${item.hall})` ? `Sitz: ${item.seatId} (${item.hall})`
: item.time; : item.time;
@@ -1090,3 +1105,8 @@ document.addEventListener("DOMContentLoaded", () => {
updateCartBadge?.(); updateCartBadge?.();
renderCheckout?.(); renderCheckout?.();
}); });
export function emptyCart() {
cart = []
return
}