diff --git a/src/components/Movie.astro b/src/components/Movie.astro deleted file mode 100644 index bfcea85..0000000 --- a/src/components/Movie.astro +++ /dev/null @@ -1,14 +0,0 @@ ---- -import { getTopMovies } from "../scripts/fetchMovies"; -import type { MovieCatalog } from "../scripts/interfaces"; - -const movieProgram = await getTopMovies(); ---- - -
- { - movieProgram.map((movie: MovieCatalog, programIndex: any) => { - {
{movie.genre}
} - }) - } -
diff --git a/src/scripts/account.ts b/src/scripts/account.ts deleted file mode 100644 index 4f39041..0000000 --- a/src/scripts/account.ts +++ /dev/null @@ -1,474 +0,0 @@ -import type { User } from "./interfaces.js"; - -function readStorageJson(key: string, fallbackValue: any) { - 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: User): User { - 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: string) { - return String(value || "") - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); -} - -function formatEuro(value: string) { - 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"); - } -} - -export let users = readStorageJson("eagleUsers", []); -if (!Array.isArray(users)) { - users = []; -} -users = users.map(normalizeUser).filter(Boolean); - -const rawCurrentUser = readStorageJson("currentUser", null); - -export var currentUser: User | null = rawCurrentUser ? normalizeUser(rawCurrentUser) : null; - -if (currentUser && currentUser.email) { - const currentEmail = currentUser.email; - const storedMatch = users.find((user: { email: string; }) => { - return user.email === currentEmail; - }); - if (storedMatch) { - currentUser = storedMatch; - } else { - users.push(currentUser); - persistUsers(); - } -} - -async function hashMessage(message: string) { - 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 -} - -function getInputValue(id: string): string { - const el = document.getElementById(id) as HTMLInputElement | null; - return el?.value.trim() ?? ""; -} - -export async function registerUser() { -const firstName = getInputValue("reg-firstname"); -const lastName = getInputValue("reg-lastname"); -const email = getInputValue("reg-email").toLowerCase(); -const password = document.querySelector("#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) => 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(); -} - -export async function loginUser() { - const email = (document.querySelector("#login-email")?.value.trim() || "").toLowerCase(); - const password = document.querySelector("#login-password")?.value || ""; - const hashedPassword = await hashMessage(password); - - const user = users.find( - (entry: User) => entry.email.toLowerCase() === email && entry.hashedPassword === hashedPassword - ); - - if (!user) { - document.getElementById("login-error")?.classList.remove("hidden"); - return; - } - - currentUser = user; - persistCurrentUser(); - openAccountDashboard(); -} - -export function openAccountDashboard() { - const accountView = document.getElementById("account-view"); - if (!accountView) { - return; - } - - if (!currentUser) { - accountView.innerHTML = ""; - return; - } - - accountView.innerHTML = /*html*/` - - `; - - renderPersonalInfo(); -} - -function renderPersonalInfo() { - const target = document.getElementById("account-tab-content"); - if (!target || !currentUser) { - return; - } - - target.innerHTML = ` - - `; -} - -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 = ` - - `; - return; - } - - const orderHtml = orders - .map((order, index) => { - const movieItems = Array.isArray(order.items) - ? order.items.filter((item: any) => 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 = ` - - `; - - const detailTarget = document.getElementById("order-ticket-details"); - const orderButtons = Array.from(target.querySelectorAll(".order-item-btn")); - - const renderOrderTicket = (orderIndex: number) => { - const order = orders[orderIndex]; - if (!order || !detailTarget) { - return; - } - - const movieItems = Array.isArray(order.items) - ? order.items.filter((item: any) => item.category === "movie") - : []; - const primaryMovie = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null); - const poster = primaryMovie?.img || ""; - const seats = movieItems.map((item: any) => 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 = /*html*/` - - - - - - - - - - `; - - 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() { - persistCurrentUser(); - window.location.reload(); -} - -(window as any).logoutUser = logoutUser; -(window as any).renderPersonalInfo = renderPersonalInfo; -(window as any).renderOrders = renderOrders; -(window as any).renderPayments = renderPayments; - diff --git a/src/scripts/bigConsts.ts b/src/scripts/bigConsts.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/scripts/booking.ts b/src/scripts/booking.ts deleted file mode 100644 index 9c12878..0000000 --- a/src/scripts/booking.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { seatLayouts, occupiedSeatsData, prices, cart } from "./main.js" -import { renderCart, saveCart } from "./cart.js"; -import { renderCheckout } from "./checkout.js"; - -let currentBookingContext: any = null; -let currentHallLayout: any = null; - -export function openBooking(movie: string, hall: string, time: any) { - 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: number) { - return String(rowIndex + 1); -} - -function buildHallLayout(hallName: string, baseConfig:any) { - 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: number, startCol: number, width: number) => { - 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: number, section: any) => 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: any) => { - 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: any, rowNumber: number, colNumber: number) { - 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 }:any) { - 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: string, time: any) { - const seatGrid = document.getElementById("seat-grid"); - if (!seatGrid) { - return; - } - - seatGrid.innerHTML = ""; - - const arrIndex = hallName as keyof typeof seatLayouts; - const baseConfig: any = seatLayouts[arrIndex]; - 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) => ` -
- - ${item.label} -
- `) - .join(""); -} - -function updateBookingSummary() { - const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")) as HTMLElement[];; - 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") as keyof typeof prices; - const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11); - total += seatPrice; - - return ` -
- ${seat.dataset.seatId} - ${seatPrice.toFixed(2).replace(".", ",")} EUR -
- `; - }) - .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: string) { - const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card")); - const normalizedTarget = String(movieTitle || "").trim().toLowerCase(); - - for (const card of cards) { - const currentCard = card.querySelector("h2, h3") as HTMLElement; - const title = currentCard.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")) as HTMLElement[]; - - 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: any) => - 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); -}); diff --git a/src/scripts/cart.ts b/src/scripts/cart.ts deleted file mode 100644 index a81c934..0000000 --- a/src/scripts/cart.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { cart } from "./main.js"; - -function formatEuro(value: number) { - return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`; -} - -function escapeHtml(value: any) { - return String(value || "") - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); -} - -function buildCartKey(item: { category: string; seatId: any; hall: any; time: any; title: any; }) { - const infoText = item.category === "movie" - ? `Sitz: ${item.seatId} (${item.hall})` - : item.time; - return `${item.title}-${item.hall}-${infoText}`; -} - -function isDrinkItem(item: { category: string; title: any; hall: any; }) { - 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: { category: any; seatId?: any; hall: any; time?: any; title: any; }) { - if (item.category === "movie") { - return ` -
Sitzplatz: ${escapeHtml(item.seatId || "-")}
-
Saal: ${escapeHtml(item.hall || "-")}
-
Uhrzeit: ${escapeHtml(item.time || "-")} Uhr
- `; - } - - if (isDrinkItem(item)) { - return ` -
Variante: ${escapeHtml(item.time || "-")}
-
Groesse: ${escapeHtml(item.hall || "-")}
- `; - } - - return ` -
Kategorie: Snack
-
Variante: ${escapeHtml(item.time || "-")}
-
Groesse: ${escapeHtml(item.hall || "-")}
- `; -} - -function groupCartItems() { - const groups = new Map(); - - cart.forEach((item: { price?: any; category: string; seatId: any; hall: any; time: any; title: any; }) => { - 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()); -} - -export function saveCart() { - localStorage.setItem("eagleCart", JSON.stringify(cart)); - updateCartBadge(); -} - -export function updateCartBadge() { - const cartBadge = document.getElementById("cart-badge"); - - if (!cartBadge) { - return; - } - - cartBadge.innerText = cart.length; - cartBadge.classList.toggle("hidden", cart.length === 0); -} - -export 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 = '

Dein Warenkorb ist leer.

'; - totalEl.innerText = formatEuro(0); - vatEl.innerText = `inkl. 19% MwSt: ${formatEuro(0)}`; - return; - } - - const groupedItems = groupCartItems(); - - const header = /*html*/` -
-
MENGE
-
VORSCHAU
-
NAME
-
INFO
-
PREIS
-
AKTION
-
- `; - - const rows = groupedItems - .map((group) => { - const imageHtml = group.item.img - ? /*html*/`${escapeHtml(group.item.title)}` - : /*html*/`
Kein Bild
`; - const quantityHtml = group.item.category === "movie" - ? /*html*/`
${group.quantity}x
` - : /*html*/` -
- - ${group.quantity} - -
- `; - - return /*html*/` -
-
- ${quantityHtml} -
-
${imageHtml}
-
${escapeHtml(group.item.title)}
-
${buildItemInfo(group.item)}
-
${formatEuro(group.total)}
-
- -
-
- `; - }) - .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(); -} -//@ts-ignore -window.removeItem = function removeItem(id: any) { - var localCart = cart.filter((item: { id: any; }) => item.id !== id); - saveCart(); - renderCart(); -}; - -//@ts-ignore -window.changeQty = function changeQty(title, delta): void { - if (delta > 0) { - const item = cart.find((entry: { title: any; }) => entry.title === title); - if (item) { - cart.push({ ...item, id: Date.now() + Math.random() }); - } - } else { - const index = cart - .map((entry: { title: any; }) => entry.title) - .lastIndexOf(title); - if (index !== -1) { - cart.splice(index, 1); - } - } - - saveCart(); - renderCart(); -}; - diff --git a/src/scripts/checkout.ts b/src/scripts/checkout.ts deleted file mode 100644 index 2c7c58b..0000000 --- a/src/scripts/checkout.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { currentUser, users } from "./account.js"; -import { renderCart, saveCart } from "./cart.js"; -import { cart, emptyCart, occupiedSeatsData } from "./main.js"; - -function formatCheckoutEuro(value: number) { - return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`; -} - -let selectedPaymentMethod = ""; -let checkoutEventsBound = false; - -function setCheckoutStep(step: number) { - 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); -} - -export 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 = `${item.title} (${infoText})${formatCheckoutEuro(item.price)}`; - 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 = "

Danke fuer deinen Einkauf!

"; - 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*/` -
-
- ${mainMovie.title} -
-
-
EAGLE'S IMAX PREMIUM
-

${mainMovie.title}

-
-

SAAL ${mainMovie.hall}

-

ZEIT ${mainMovie.time} Uhr

-

SITZE ${matchingMovieSeats || "-"}

-
- -
-
- `; -} - -function saveOrderForCurrentUser(orderItems: any[], orderTotal: any) { - 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 || "-" - }; - - //@ts-ignore - 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: any[]) { - 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); - - emptyCart?.() - 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") as HTMLButtonElement; - - document.querySelectorAll(".payment-method").forEach((method) => { - method.addEventListener("click", () => { - document.querySelectorAll(".payment-method").forEach((entry) => { - entry.classList.remove("selected"); - }); - - method.classList.add("selected"); - //@ts-ignore - 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); diff --git a/src/scripts/fetchMovies.ts b/src/scripts/fetchMovies.ts deleted file mode 100644 index c4866ab..0000000 --- a/src/scripts/fetchMovies.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { MovieCatalog, ITMDBResponse } from "./interfaces"; - -export async function getTopMovies(): Promise { - const API_KEY = import.meta.env.TMDB_API_KEY; - const IMAGE_BASE_URL = 'https://image.tmdb.org/t/p/w500' - - const response = await fetch(`https://api.themoviedb.org/3/discover/movie?api_key=${API_KEY}`); - const data: ITMDBResponse = await response.json(); - - return data.results.slice(0, 5).map((movie) => ({ - id: movie.id, - title: movie.title, - poster: `${IMAGE_BASE_URL}${movie.poster_path}`, - rating: movie.vote_average, - year: movie.release_date.split('-')[0], // Extracts just the year - genre: movie.genre, - duration: movie.duration, - fsk: movie.age_reccomendation, - description: movie.overview, - backdrop: movie.backdrop_path, - })); -} \ No newline at end of file diff --git a/src/scripts/interfaces.ts b/src/scripts/interfaces.ts deleted file mode 100644 index ebd1f10..0000000 --- a/src/scripts/interfaces.ts +++ /dev/null @@ -1,44 +0,0 @@ -export interface User { - firstName: string; - lastName: string; - email: string; - hashedPassword: string; - orders: any[]; // TODO: figure out proper array type of orders. Probably smartest do create an Order interface which this would be an array of - paymentMethods: any[]; // TODO: figure out proper array type of paymentMethods. create paymentMethod interface and make this an array of it -} - -export interface MovieCatalog { - id: number; - title: string; - genre: string; - duration: number; - fsk: string; - description: string; - poster: string; - backdrop: string; - rating: number; - year: string; -} - -// The shape of a single movie object from TMDb -interface ITMDBMovie { - id: number; - title: string; - poster_path: string; - release_date: string; - vote_average: number; - overview: string; - genre: string; - duration: number; - age_reccomendation: string; - backdrop_path: string; - // ... add other fields as needed -} - -// The shape of the API response -export interface ITMDBResponse { - page: number; - results: ITMDBMovie[]; - total_pages: number; - total_results: number; -} \ No newline at end of file diff --git a/src/scripts/main.ts b/src/scripts/main.ts deleted file mode 100644 index 0ee01c2..0000000 --- a/src/scripts/main.ts +++ /dev/null @@ -1,865 +0,0 @@ -import { currentUser, loginUser, openAccountDashboard, registerUser } from "./account.js"; -import { openBooking } from "./booking.js"; -import { renderCart, saveCart, updateCartBadge } from "./cart.js"; -import { renderCheckout } from "./checkout.js"; -import type { MovieCatalog as MovieCatalogInterface } from "./interfaces.js"; - -export const movieCatalog: MovieCatalogInterface[] = [ - { - id: 1, - title: "oh hell nah", - genre: "n", - duration: 3, - fsk: "jfd", - description: "jsss", - poster: "g", - backdrop: "f", - rating: 1, - year: "d", - } - ] - -// Shared app state for legacy script files (account.js, booking.js, cart.js, checkout.js) -export const prices: Record = { 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 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 }] }, - IMAX: { rows: 15, left: 10, right: 10, vipRows: [], dbox: [], isImax: true } -}; -export var cart: any[] = JSON.parse(localStorage.getItem("eagleCart") || '[]'); -export var occupiedSeatsData = JSON.parse(localStorage.getItem("eagleOccupied") || '{}'); - -document.addEventListener("DOMContentLoaded", () => { - const views = { - hero: document.querySelector(".hero"), - moviesGrid: document.getElementById("movies-grid-section"), - list: document.getElementById("movie-list-view"), - halls: document.getElementById("halls-view"), - dbox: document.getElementById("dbox-view"), - collectors: document.getElementById("collectors-view"), - about: document.getElementById("about-view"), - snacks: document.getElementById("snacks-view"), - cart: document.getElementById("cart-view"), - checkout: document.getElementById("checkout-view"), - account: document.getElementById("account-view") - }; - - const ui = { - logo: document.getElementById("logo-home"), - linkFilme: document.getElementById("link-filme"), - linkSnacks: document.getElementById("link-snacks"), - linkAbout: document.getElementById("link-about"), - linkCart: document.getElementById("link-cart"), - linkAccount: document.getElementById("link-account"), - themeToggle: document.getElementById("theme-toggle"), - heroBookingBtn: document.getElementById("hero-booking-btn"), - heroSlider: document.getElementById("hero-slider"), - heroDots: document.getElementById("hero-dots"), - heroTitle: document.getElementById("hero-title"), - heroText: document.getElementById("hero-text"), - nowRunningRow: document.getElementById("now-running-row"), - movieProgramList: document.getElementById("movie-program-list"), - checkoutBtn: document.getElementById("btn-checkout-final"), - backHomeBtn: document.getElementById("btn-back-home"), - snacksView: document.getElementById("snacks-view"), - snackOverlay: document.getElementById("snack-prompt-overlay"), - btnYesSnacks: document.getElementById("btn-yes-snacks"), - btnNoCart: document.getElementById("btn-no-cart"), - bookingModal: document.getElementById("booking-modal"), - closeBookingModalBtn: document.querySelector(".close-btn") - }; - - const checkoutSteps = { - one: document.getElementById("checkout-step-1"), - two: document.getElementById("checkout-step-2"), - three: document.getElementById("checkout-step-3") - }; - - const hallRotation = ["IMAX", "Deluxe 1", "Kino 1", "Kino 2"]; - const timePatterns = [ - ["13:00", "15:20", "17:40", "20:00", "22:20"], - ["13:00", "14:50", "17:10", "19:30", "21:50"], - ["13:00", "15:10", "17:30", "19:50", "22:10"], - ["13:00", "16:00", "18:20", "20:40"] - ]; - - let movieProgram: any = []; // TODO: Find type - let heroItems: any = []; // TODO: find Type - let heroIndex = 0; - let heroTimer: any = null; // TODO: find type - - const weekdayShort = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]; - - const hideAllViews = () => { - Object.values(views).forEach((view) => view?.classList.add("hidden")); - document.getElementById("about-tech-modal")?.classList.add("hidden"); - document.body.style.overflow = "auto"; - }; - - const showHome = () => { - hideAllViews(); - views.hero?.classList.remove("hidden"); - views.moviesGrid?.classList.remove("hidden"); - document.getElementById("about-tech-modal")?.classList.add("hidden"); - document.body.style.overflow = "auto"; - window.scrollTo({ top: 0, behavior: "smooth" }); - }; - - const showMovieList = (programIndexToFocus: number = NaN) => { - hideAllViews(); - views.list?.classList.remove("hidden"); - - if (programIndexToFocus === null) { - window.scrollTo({ top: 0, behavior: "smooth" }); - return; - } - - const target = views.list?.querySelector(`[data-program-index="${programIndexToFocus}"]`); - if (target) { - target.scrollIntoView({ behavior: "smooth", block: "start" }); - target.classList.add("flash-focus"); - setTimeout(() => target.classList.remove("flash-focus"), 1200); - } - }; - - const showStaticView = (viewElement: HTMLElement) => { - if (!viewElement) { - return; - } - - hideAllViews(); - viewElement.classList.remove("hidden"); - window.scrollTo({ top: 0, behavior: "smooth" }); - }; - - const showCheckoutStart = () => { - if (!cart.length) { - alert("Dein Warenkorb ist leer."); - return; - } - - hideAllViews(); - views.checkout?.classList.remove("hidden"); - checkoutSteps.one?.classList.remove("hidden"); - checkoutSteps.two?.classList.add("hidden"); - checkoutSteps.three?.classList.add("hidden"); - renderCheckout?.(); - window.scrollTo(0, 0); - }; - - const closeBookingModal = () => { - ui.bookingModal?.classList.add("hidden"); - }; - - const escapeHtml = (value: string) => String(value || "") - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); - - const formatDateShort = (dateObj: any) => { - const day = String(dateObj.getDate()).padStart(2, "0"); - const month = String(dateObj.getMonth() + 1).padStart(2, "0"); - return `${day}.${month}.`; - }; - - const buildDayMeta = (offset: number) => { - const date = new Date(); - date.setHours(0, 0, 0, 0); - date.setDate(date.getDate() + offset); - - const weekday = weekdayShort[date.getDay()]; - const formattedDate = formatDateShort(date); - - if (offset === 0) { - return { - offset, - date, - short: "Heute", - long: `Heute, ${formattedDate}` - }; - } - - if (offset === 1) { - return { - offset, - date, - short: "Morgen", - long: `Morgen, ${formattedDate}` - }; - } - - return { - offset, - date, - short: weekday, - long: `${weekday}, ${formattedDate}` - }; - }; - - const buildScheduleForMovie = (movieIndex: number) => { - return Array.from({ length: 7 }, (_, dayOffset) => { - const dayMeta = buildDayMeta(dayOffset); - const pattern = timePatterns[(movieIndex + dayOffset) % timePatterns.length] || "Error reading"; - const desiredCount = 4 + ((movieIndex + dayOffset) % 2); - const showCount = Math.min(pattern.length, desiredCount); - - //@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]; - return { time, hall }; - }); - - return { - ...dayMeta, - showings - }; - }); - }; - - const buildMovieProgram = () => { - movieProgram = movieCatalog.map((movie: any, movieIndex: number) => ({ - ...movie, - schedule: buildScheduleForMovie(movieIndex) - })); - heroItems = movieProgram.slice(0, 5); - }; - - const setHeroSlide = (index: number) => { - if (!heroItems.length || !ui.heroSlider) { - return; - } - - heroIndex = (index + heroItems.length) % heroItems.length; - - ui.heroSlider.querySelectorAll(".hero-slide").forEach((slide, slideIndex) => { - slide.classList.toggle("active", slideIndex === heroIndex); - }); - - ui.heroDots?.querySelectorAll(".hero-dot").forEach((dot, dotIndex) => { - dot.classList.toggle("active", dotIndex === heroIndex); - }); - - const activeMovie = heroItems[heroIndex]; - if (ui.heroTitle) { - ui.heroTitle.textContent = activeMovie.title; - } - if (ui.heroText) { - ui.heroText.textContent = `${activeMovie.genre} • ${activeMovie.duration} Min. • Heute erste Vorstellung um 13:00 Uhr.`; - } - }; - - const renderHero = () => { - if (!ui.heroSlider || !heroItems.length) { - return; - } - - ui.heroSlider.innerHTML = heroItems.map((movie: any, index: number) => ` -
- `).join(""); - - if (ui.heroDots) { - ui.heroDots.innerHTML = heroItems.map((_: any, index: number) => ` - - `).join(""); - - ui.heroDots.addEventListener("click", (event: any) => { - const dotTarget = event.target || 0; - const dot = dotTarget.closest(".hero-dot"); - if (!dot) { - return; - } - - const nextIndex = Number(dot.dataset.heroIndex || 0); - setHeroSlide(nextIndex); - - if (heroTimer) { - clearInterval(heroTimer); - heroTimer = setInterval(() => setHeroSlide(heroIndex + 1), 6500); - } - }); - } - - setHeroSlide(0); - - if (heroTimer) { - clearInterval(heroTimer); - } - - heroTimer = setInterval(() => { - setHeroSlide(heroIndex + 1); - }, 6500); - }; - - const renderNowRunningRow = () => { - if (!ui.nowRunningRow) { - return; - } - - // TODO: implement movie interface - ui.nowRunningRow.innerHTML = movieProgram.map((movie: any, index: number) => /*html*/` -
- ${escapeHtml(movie.title)} -
-

${escapeHtml(movie.title)}

-

${escapeHtml(movie.genre)}

- -
-
- `).join(""); - }; - - const renderScheduleRows = (programIndex: number, dayIndex: number) => { - const movie = movieProgram[programIndex]; - if (!movie) { - return; - } - - const day = movie.schedule[dayIndex]; - const body = document.getElementById(`schedule-body-${programIndex}`); - if (!body || !day) { - return; - } - - body.innerHTML = day.showings.map((showing: { hall: string; time: string; }) => /*html*/` - - `).join(""); - }; - - const renderMovieProgramList = () => { - if (!ui.movieProgramList) { - return; - } - - 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*/` - - `).join(""); - - return /*html*/` -
-
- ${escapeHtml(movie.title)} - ${escapeHtml(movie.fsk)} -
-
-
-

${escapeHtml(movie.title)}

- ${movie.duration} Min. | ${escapeHtml(movie.genre)} | FSK: ${escapeHtml(movie.fsk)} -
-

${escapeHtml(movie.description)}

- -
${dayTabs}
- -
-
- TagKinosaalUhrzeit -
-
-
-
-
- `; - }).join(""); - - movieProgram.forEach((_: any, programIndex: number) => { - renderScheduleRows(programIndex, 0); - }); - }; - - const initRevealAnimations = () => { - const revealElements = Array.from(document.querySelectorAll(".reveal-on-scroll")); - if (!revealElements.length) { - return; - } - - if (!("IntersectionObserver" in window)) { - revealElements.forEach((element) => element.classList.add("is-visible")); - return; - } - - const observer = new IntersectionObserver((entries, obs) => { - entries.forEach((entry) => { - if (!entry.isIntersecting) { - return; - } - - entry.target.classList.add("is-visible"); - obs.unobserve(entry.target); - }); - }, { threshold: 0.2 }); - - revealElements.forEach((element) => observer.observe(element)); - }; - - const renderMovieExperience = () => { - buildMovieProgram(); - renderHero(); - renderNowRunningRow(); - renderMovieProgramList(); - initRevealAnimations(); - }; - - const bindNavigation = () => { - ui.logo?.addEventListener("click", showHome); - - ui.linkFilme?.addEventListener("click", (event) => { - event.preventDefault(); - showMovieList(); - }); - - ui.linkSnacks?.addEventListener("click", (event) => { - event.preventDefault(); - if (views.snacks) { - showStaticView(views.snacks); - } - }); - - ui.linkAbout?.addEventListener("click", (event) => { - event.preventDefault(); - if (views.about) { - showStaticView(views.about); - } - }); - - ui.linkCart?.addEventListener("click", (event) => { - event.preventDefault(); - hideAllViews(); - views.cart?.classList.remove("hidden"); - renderCart?.(); - }); - - ui.linkAccount?.addEventListener("click", (event) => { - event.preventDefault(); - hideAllViews(); - views.account?.classList.remove("hidden"); - - const isUserLoggedIn = typeof currentUser !== "undefined" && currentUser; - if (isUserLoggedIn && typeof openAccountDashboard === "function") { - openAccountDashboard(); - } - }); - - ui.heroBookingBtn?.addEventListener("click", () => { - showMovieList(); - }); - - ui.checkoutBtn?.addEventListener("click", showCheckoutStart); - ui.backHomeBtn?.addEventListener("click", showHome); - }; - - const bindProgramActions = () => { - views.moviesGrid?.addEventListener("click", (event: any) => { - const trigger = event.target.closest(".open-program-btn"); - if (!trigger) { - return; - } - - const programIndex = Number(trigger.dataset.programIndex) || 0; - showMovieList(programIndex); - }); - - ui.movieProgramList?.addEventListener("click", (event: any) => { - const dayButton = event.target.closest(".program-day-tab"); - if (!dayButton) { - return; - } - - const programIndex = Number(dayButton.dataset.programIndex || 0); - const dayIndex = Number(dayButton.dataset.dayIndex || 0); - - const tabRow = dayButton.closest(".program-day-tabs"); - tabRow?.querySelectorAll(".program-day-tab").forEach((tab: { classList: { remove: (arg0: string) => any; }; }) => tab.classList.remove("active")); - dayButton.classList.add("active"); - - renderScheduleRows(programIndex, dayIndex); - }); - }; - - const bindHomeInfoNavigation = () => { - const openButtons = Array.from(document.querySelectorAll("[data-home-view-open]")); - const backButtons = Array.from(document.querySelectorAll("[data-go-home]")); - const aboutOpenButtons = Array.from(document.querySelectorAll("[data-about-modal-open]")); - const aboutCloseButtons = Array.from(document.querySelectorAll("[data-about-modal-close]")); - const aboutModal = document.getElementById("about-tech-modal"); - - if (!openButtons.length) { - return; - } - - const targetMap = { - "halls-view": views.halls, - "dbox-view": views.dbox, - "collectors-view": views.collectors - }; - - openButtons.forEach((button) => { - button.addEventListener("click", () => { - const targetId = button.getAttribute("data-home-view-open") as keyof typeof targetMap; - const target = targetId ? targetMap[targetId] : null; - if (target) { - showStaticView(target); - } - }); - }); - - backButtons.forEach((button) => { - button.addEventListener("click", () => { - showHome(); - }); - }); - - aboutOpenButtons.forEach((button) => { - button.addEventListener("click", () => { - const targetId = button.getAttribute("data-about-modal-open"); - if (targetId === "about-tech-modal" && aboutModal) { - aboutModal.classList.remove("hidden"); - document.body.style.overflow = "hidden"; - } - }); - }); - - aboutCloseButtons.forEach((button) => { - button.addEventListener("click", () => { - aboutModal?.classList.add("hidden"); - document.body.style.overflow = "auto"; - }); - }); - - aboutModal?.addEventListener("click", (event) => { - if (event.target === aboutModal) { - aboutModal.classList.add("hidden"); - document.body.style.overflow = "auto"; - } - }); - - document.addEventListener("keydown", (event) => { - if (event.key === "Escape" && aboutModal && !aboutModal.classList.contains("hidden")) { - aboutModal.classList.add("hidden"); - document.body.style.overflow = "auto"; - } - }); - }; - - const initThemeToggle = () => { - if (!ui.themeToggle) { - return; - } - - const THEME_KEY = "eagleTheme"; - - const applyTheme = (theme: any) => { - const isLight = theme === "light"; - document.body.classList.toggle("theme-light", isLight); - document.body.classList.toggle("theme-dark", !isLight); - //@ts-ignore - ui.themeToggle.classList.toggle("is-light", isLight); - localStorage.setItem(THEME_KEY, isLight ? "light" : "dark"); - }; - - const storedTheme = localStorage.getItem(THEME_KEY); - applyTheme(storedTheme === "light" ? "light" : "dark"); - - ui.themeToggle.addEventListener("click", () => { - const nextTheme = document.body.classList.contains("theme-light") ? "dark" : "light"; - applyTheme(nextTheme); - }); - }; - - const bindAccountActions = () => { - const registerModal = document.getElementById("register-modal"); - const forgotModal = document.getElementById("forgot-modal"); - const forgotEmailInput = document.getElementById("forgot-email") as HTMLInputElement; - const resetMessage = document.getElementById("reset-message"); - const loginError = document.getElementById("login-error"); - const loginEmailInput = document.getElementById("login-email"); - const loginPasswordInput = document.getElementById("login-password"); - - const openModal = (modal: HTMLElement | null) => modal?.classList.remove("hidden"); - const closeModal = (modal: HTMLElement | null) => modal?.classList.add("hidden"); - const triggerLogin = () => { - loginError?.classList.add("hidden"); - if (typeof loginUser === "function") { - loginUser(); - } - }; - - document.getElementById("btn-open-register")?.addEventListener("click", () => { - openModal(registerModal); - }); - - document.getElementById("btn-close-register")?.addEventListener("click", () => { - closeModal(registerModal); - }); - - document.getElementById("btn-register-save")?.addEventListener("click", () => { - if (typeof registerUser === "function") { - registerUser(); - } - }); - - document.getElementById("btn-login-account")?.addEventListener("click", triggerLogin); - - [loginEmailInput, loginPasswordInput].forEach((input) => { - input?.addEventListener("keydown", (event) => { - if (event.key !== "Enter") { - return; - } - - event.preventDefault(); - triggerLogin(); - }); - }); - - document.getElementById("btn-forgot-password")?.addEventListener("click", () => { - if (forgotEmailInput != null) { - forgotEmailInput.value = ""; - } - resetMessage?.classList.add("hidden"); - openModal(forgotModal); - }); - - document.getElementById("btn-close-forgot")?.addEventListener("click", () => { - closeModal(forgotModal); - }); - - document.getElementById("btn-send-reset")?.addEventListener("click", () => { - const email = forgotEmailInput?.value.trim() || ""; - if (!email || !email.includes("@")) { - alert("Bitte gib eine gueltige E-Mail-Adresse ein."); - return; - } - - if (resetMessage) { - resetMessage.textContent = "Wenn ein Konto existiert, wurde ein Reset-Code simuliert versendet."; - resetMessage.classList.remove("hidden"); - } - }); - - registerModal?.addEventListener("click", (event) => { - if (event.target === registerModal) { - closeModal(registerModal); - } - }); - - forgotModal?.addEventListener("click", (event) => { - if (event.target === forgotModal) { - closeModal(forgotModal); - } - }); - - document.addEventListener("keydown", (event) => { - if (event.key === "Escape") { - closeModal(registerModal); - closeModal(forgotModal); - } - }); - }; - - const bindGlobalDocumentClicks = () => { - document.addEventListener("click", (event: any) => { - if (event.target.classList.contains("opt-btn")) { - const optionGroup = event.target.parentElement; - optionGroup?.querySelectorAll(".opt-btn").forEach((button: { classList: { remove: (arg0: string) => any; }; }) => button.classList.remove("active")); - event.target.classList.add("active"); - } - - const deleteBtn = event.target.closest(".btn-delete-item"); - if (deleteBtn?.dataset.key) { - const row = deleteBtn.closest(".cart-item-row"); - if (row) { - row.classList.add("slide-out-left"); - row.querySelectorAll("button").forEach((button: { disabled: boolean; }) => { - button.disabled = true; - }); - - setTimeout(() => { - //@ts-ignore - removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist - }, 380); - } else { - //@ts-ignore - removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist - } - return; - } - - const chip = event.target.closest(".time-chip"); - if (chip) { - const movieFromData = chip.getAttribute("data-movie"); - const movieCard = chip.closest(".movie-card, .detailed-card, .program-card"); - const movie = movieFromData || movieCard?.querySelector("h2, h3")?.innerText || "Film"; - const hall = chip.getAttribute("data-hall"); - const time = chip.getAttribute("data-time"); - - if (hall && time && typeof openBooking === "function") { - openBooking(movie, hall, time); - } - } - - const qtyBtn = event.target.closest(".btn-qty"); - if (!qtyBtn) { - return; - } - - const action = qtyBtn.dataset.action; - const key = qtyBtn.dataset.key; - if (!action || !key) { - return; - } - - const relatedItem = cart.find((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => { - const infoText = item.category === "movie" - ? `Sitz: ${item.seatId} (${item.hall})` - : item.time; - return `${item.title}-${item.hall}-${infoText}` === key; - }); - - if (!relatedItem || relatedItem.category === "movie") { - return; - } - - if (action === "plus") { - cart.push({ ...relatedItem, id: Date.now() + Math.random() }); - } else { - const keyList = cart.map((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => { - const infoText = item.category === "movie" - ? `Sitz: ${item.seatId} (${item.hall})` - : item.time; - return `${item.title}-${item.hall}-${infoText}`; - }); - const lastMatch = keyList.lastIndexOf(key); - - if (lastMatch !== -1) { - cart.splice(lastMatch, 1); - } - } - - saveCart?.(); - renderCart?.(); - }); - }; - - const bindSnacksActions = () => { - ui.snacksView?.addEventListener("click", (event: any) => { - const sizeChip = event.target.closest(".size-chip"); - if (!sizeChip) { - return; - } - - const snackCard = sizeChip.closest(".snack-card"); - if (!snackCard) { - return; - } - - const snackTitle = snackCard.querySelector("h3, h2")?.innerText || "Snack"; - const snackImg = snackCard.querySelector("img")?.src || ""; - const priceSpan = sizeChip.querySelector("span"); - const rawPriceText = (priceSpan ? priceSpan.innerText : sizeChip.innerText) - .replace("EUR", "") - .replace("€", "") - .replace(",", ".") - .trim(); - const priceVal = parseFloat(rawPriceText) || 0; - const sizeVal = sizeChip.innerText.replace(priceSpan?.innerText || "", "").trim() || "Standard"; - const activeOption = snackCard.querySelector(".opt-btn.active"); - const variantVal = activeOption ? activeOption.innerText : "Normal"; - - cart.push({ - id: Date.now() + Math.random(), - category: "snack", - title: snackTitle, - hall: sizeVal, - time: variantVal, - type: "SNACK", - price: priceVal, - img: snackImg - }); - - saveCart?.(); - - const originalHtml = sizeChip.innerHTML; - sizeChip.innerHTML = "Hinzugefügt!"; - setTimeout(() => { - sizeChip.innerHTML = originalHtml; - }, 800); - }); - - document.querySelectorAll(".tab-btn").forEach((button: any) => { - button.addEventListener("click", () => { - document.querySelectorAll(".tab-btn").forEach((tab) => tab.classList.remove("active")); - button.classList.add("active"); - - document.querySelectorAll(".snack-category").forEach((category) => category.classList.add("hidden")); - document.getElementById(button.dataset.target)?.classList.remove("hidden"); - }); - }); - }; - - const bindOverlayActions = () => { - ui.btnYesSnacks?.addEventListener("click", () => { - ui.snackOverlay?.classList.add("hidden"); - hideAllViews(); - views.snacks?.classList.remove("hidden"); - document.body.style.overflow = "auto"; - }); - - ui.btnNoCart?.addEventListener("click", () => { - ui.snackOverlay?.classList.add("hidden"); - hideAllViews(); - views.cart?.classList.remove("hidden"); - renderCart?.(); - document.body.style.overflow = "auto"; - }); - }; - - const bindBookingModalClose = () => { - ui.closeBookingModalBtn?.addEventListener("click", closeBookingModal); - - ui.bookingModal?.addEventListener("click", (event) => { - if (event.target === ui.bookingModal) { - closeBookingModal(); - } - }); - }; - // @ts-ignore - window.removeFromCartByKey = function removeFromCartByKey(key: string) { - cart = cart.filter((item: { category: string; seatId: any; hall: any; time: any; title: any; }) => { - const infoText = item.category === "movie" - ? `Sitz: ${item.seatId} (${item.hall})` - : item.time; - return `${item.title}-${item.hall}-${infoText}` !== key; - }); - - saveCart?.(); - renderCart?.(); - }; - - renderMovieExperience(); - initThemeToggle(); - bindNavigation(); - bindProgramActions(); - bindHomeInfoNavigation(); - bindAccountActions(); - bindGlobalDocumentClicks(); - bindSnacksActions(); - bindOverlayActions(); - bindBookingModalClose(); - - updateCartBadge?.(); - renderCheckout?.(); -}); - -export function emptyCart() { - cart = [] - return -} \ No newline at end of file