353 lines
11 KiB
JavaScript
353 lines
11 KiB
JavaScript
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);
|
|
});
|