Compare commits
6 Commits
10883aa381
...
dev_jannis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e143d8360a | ||
| c805221208 | |||
|
|
40ee49aff5 | ||
|
|
c88242b2be | ||
|
|
425c5d1900 | ||
|
|
e2b4852e0d |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist/
|
||||
14
index.html
14
index.html
@@ -735,8 +735,8 @@
|
||||
<div class="header-sub-info">
|
||||
<p id="modal-info-text">Saal • Zeit</p>
|
||||
<div id="tech-badges" class="tech-badges-container hidden">
|
||||
<img src="img/dolby.png" alt="Dolby" class="tech-badge">
|
||||
<img src="img/dbox.png" alt="D-Box" class="tech-badge">
|
||||
<img src="img/Dolby.png" alt="Dolby" class="tech-badge">
|
||||
<img src="img/dbox.jpg" alt="D-Box" class="tech-badge">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -948,11 +948,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="account.js"></script>
|
||||
<script src="cart.js"></script>
|
||||
<script src="booking.js"></script>
|
||||
<script src="checkout.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script type="module" src="dist/main.js"></script>
|
||||
<script type="module" src="dist/cart.js"></script>
|
||||
<script type="module" src="dist/booking.js"></script>
|
||||
<script type="module" src="dist/checkout.js"></script>
|
||||
<script type="module" src="dist/account.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
3093
package-lock.json
generated
Normal file
3093
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "p1---kino",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch",
|
||||
"serve": "serve .",
|
||||
"run": "npm-run-all --parallel watch serve"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@gitea.starfour.de:2222/Aaron/Kino-Website.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Aaron und Jannis",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"devDependencies": {
|
||||
"npm-run-all": "^4.1.5",
|
||||
"serve": "^14.2.6",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
function readStorageJson(key, fallbackValue) {
|
||||
import type { User } from "./interfaces.js";
|
||||
|
||||
function readStorageJson(key: string, fallbackValue: any) {
|
||||
const raw = localStorage.getItem(key);
|
||||
|
||||
if (!raw || raw === "undefined" || raw === "null") {
|
||||
@@ -13,22 +15,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUser(user) {
|
||||
if (!user || typeof user !== "object") {
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeUser(user: User): User {
|
||||
return {
|
||||
firstName: user.firstName || "",
|
||||
lastName: user.lastName || "",
|
||||
email: user.email || "",
|
||||
password: user.password || "",
|
||||
hashedPassword: user.hashedPassword || "",
|
||||
orders: Array.isArray(user.orders) ? user.orders : [],
|
||||
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
|
||||
};
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
function escapeHtml(value: string) {
|
||||
return String(value || "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
@@ -37,7 +35,7 @@ function escapeHtml(value) {
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function formatEuro(value) {
|
||||
function formatEuro(value: string) {
|
||||
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
||||
}
|
||||
|
||||
@@ -53,15 +51,21 @@ function persistCurrentUser() {
|
||||
}
|
||||
}
|
||||
|
||||
let users = readStorageJson("eagleUsers", []);
|
||||
export let users = readStorageJson("eagleUsers", []);
|
||||
if (!Array.isArray(users)) {
|
||||
users = [];
|
||||
}
|
||||
users = users.map(normalizeUser).filter(Boolean);
|
||||
|
||||
let currentUser = normalizeUser(readStorageJson("currentUser", null));
|
||||
const rawCurrentUser = readStorageJson("currentUser", null);
|
||||
|
||||
export var currentUser: User | null = rawCurrentUser ? normalizeUser(rawCurrentUser) : null;
|
||||
|
||||
if (currentUser && currentUser.email) {
|
||||
const storedMatch = users.find((user) => user.email === currentUser.email);
|
||||
const currentEmail = currentUser.email;
|
||||
const storedMatch = users.find((user: { email: string; }) => {
|
||||
return user.email === currentEmail;
|
||||
});
|
||||
if (storedMatch) {
|
||||
currentUser = storedMatch;
|
||||
} else {
|
||||
@@ -70,11 +74,23 @@ if (currentUser && currentUser.email) {
|
||||
}
|
||||
}
|
||||
|
||||
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 || "";
|
||||
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<HTMLInputElement>("#reg-password")?.value ?? "";
|
||||
|
||||
if (!firstName || !lastName || !email || !password) {
|
||||
alert("Bitte fuelle alle Felder aus.");
|
||||
@@ -86,17 +102,19 @@ function registerUser() {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingUser = users.find((user) => user.email.toLowerCase() === email);
|
||||
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,
|
||||
password,
|
||||
hashedPassword,
|
||||
orders: [],
|
||||
paymentMethods: []
|
||||
};
|
||||
@@ -113,12 +131,13 @@ function registerUser() {
|
||||
openAccountDashboard();
|
||||
}
|
||||
|
||||
function loginUser() {
|
||||
const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase();
|
||||
const password = document.getElementById("login-password")?.value || "";
|
||||
export async function loginUser() {
|
||||
const email = (document.querySelector<HTMLInputElement>("#login-email")?.value.trim() || "").toLowerCase();
|
||||
const password = document.querySelector<HTMLInputElement>("#login-password")?.value || "";
|
||||
const hashedPassword = await hashMessage(password);
|
||||
|
||||
const user = users.find(
|
||||
(entry) => entry.email.toLowerCase() === email && entry.password === password
|
||||
(entry: User) => entry.email.toLowerCase() === email && entry.hashedPassword === hashedPassword
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
@@ -131,7 +150,7 @@ function loginUser() {
|
||||
openAccountDashboard();
|
||||
}
|
||||
|
||||
function openAccountDashboard() {
|
||||
export function openAccountDashboard() {
|
||||
const accountView = document.getElementById("account-view");
|
||||
if (!accountView) {
|
||||
return;
|
||||
@@ -142,7 +161,7 @@ function openAccountDashboard() {
|
||||
return;
|
||||
}
|
||||
|
||||
accountView.innerHTML = `
|
||||
accountView.innerHTML = /*html*/`
|
||||
<div class="account-panel">
|
||||
<div class="account-panel-header">
|
||||
<h2>Mein Konto</h2>
|
||||
@@ -198,7 +217,7 @@ function renderOrders() {
|
||||
const orderHtml = orders
|
||||
.map((order, index) => {
|
||||
const movieItems = Array.isArray(order.items)
|
||||
? order.items.filter((item) => item.category === "movie")
|
||||
? 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";
|
||||
@@ -227,20 +246,20 @@ function renderOrders() {
|
||||
`;
|
||||
|
||||
const detailTarget = document.getElementById("order-ticket-details");
|
||||
const orderButtons = Array.from(target.querySelectorAll(".order-item-btn"));
|
||||
const orderButtons = Array.from(target.querySelectorAll<HTMLButtonElement>(".order-item-btn"));
|
||||
|
||||
const renderOrderTicket = (orderIndex) => {
|
||||
const renderOrderTicket = (orderIndex: number) => {
|
||||
const order = orders[orderIndex];
|
||||
if (!order || !detailTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const movieItems = Array.isArray(order.items)
|
||||
? order.items.filter((item) => item.category === "movie")
|
||||
? 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) => item.seatId).filter(Boolean).join(", ") || "-";
|
||||
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` : "-";
|
||||
@@ -290,7 +309,7 @@ function renderPayments() {
|
||||
return;
|
||||
}
|
||||
|
||||
target.innerHTML = `
|
||||
target.innerHTML = /*html*/`
|
||||
<div class="account-card">
|
||||
<h3>Zahlungsmethoden</h3>
|
||||
<p class="account-payments-note">Platzhalter zum Hinterlegen deiner Logos oder Anbieter-Informationen.</p>
|
||||
@@ -444,7 +463,7 @@ function renderPayments() {
|
||||
}
|
||||
|
||||
function logoutUser() {
|
||||
currentUser = null;
|
||||
persistCurrentUser();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
let currentBookingContext = null;
|
||||
let currentHallLayout = null;
|
||||
import { seatLayouts, occupiedSeatsData, prices, cart } from "./main.js"
|
||||
import { renderCart, saveCart } from "./cart.js";
|
||||
import { renderCheckout } from "./checkout.js";
|
||||
|
||||
function openBooking(movie, hall, time) {
|
||||
const titleEl = document.getElementById("modal-movie-title");
|
||||
const infoEl = document.getElementById("modal-info-text");
|
||||
let currentBookingContext: any = null;
|
||||
let currentHallLayout: any = null;
|
||||
|
||||
if (titleEl) {
|
||||
titleEl.innerText = movie;
|
||||
}
|
||||
export function openBooking(movie: string, hall: string, time: any) {
|
||||
const titleEl = document.getElementById("modal-movie-title");
|
||||
const infoEl = document.getElementById("modal-info-text");
|
||||
|
||||
if (infoEl) {
|
||||
infoEl.innerText = `${hall} • ${time} Uhr`;
|
||||
}
|
||||
if (titleEl) {
|
||||
titleEl.innerText = movie;
|
||||
}
|
||||
|
||||
currentBookingContext = { movie, hall, time };
|
||||
if (infoEl) {
|
||||
infoEl.innerText = `${hall} • ${time} Uhr`;
|
||||
}
|
||||
|
||||
createSeats(hall, time);
|
||||
renderBookingLegend();
|
||||
updateBookingSummary();
|
||||
currentBookingContext = { movie, hall, time };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function buildHallLayout(hallName, baseConfig) {
|
||||
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);
|
||||
@@ -39,7 +43,7 @@ function buildHallLayout(hallName, baseConfig) {
|
||||
const vipRows = rows > 0 ? [rows] : [];
|
||||
|
||||
const dboxMap = new Set();
|
||||
const markDboxRange = (rowNumber, startCol, width) => {
|
||||
const markDboxRange = (rowNumber: number, startCol: number, width: number) => {
|
||||
if (!rowNumber || width <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -52,7 +56,7 @@ function buildHallLayout(hallName, baseConfig) {
|
||||
|
||||
if (isDeluxe) {
|
||||
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;
|
||||
|
||||
const totalDboxSeats = Math.max(4, configuredDboxSeats || 0);
|
||||
@@ -77,7 +81,7 @@ function buildHallLayout(hallName, baseConfig) {
|
||||
markDboxRange(rowNumber, startCol, seatsForRow);
|
||||
});
|
||||
} else if (Array.isArray(baseConfig.dbox)) {
|
||||
baseConfig.dbox.forEach((section) => {
|
||||
baseConfig.dbox.forEach((section: any) => {
|
||||
const rowNumber = Number(section.r || 0);
|
||||
const width = Number(section.w || 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}`)) {
|
||||
return "dbox";
|
||||
}
|
||||
@@ -112,7 +116,7 @@ function getSeatType(layout, rowNumber, colNumber) {
|
||||
return "normal";
|
||||
}
|
||||
|
||||
function createSeatElement({ seatId, seatType, occupiedSeats }) {
|
||||
function createSeatElement({seatId, seatType, occupiedSeats }:any) {
|
||||
const seat = document.createElement("button");
|
||||
seat.type = "button";
|
||||
seat.classList.add("seat", seatType);
|
||||
@@ -136,7 +140,7 @@ function createSeatElement({ seatId, seatType, occupiedSeats }) {
|
||||
return seat;
|
||||
}
|
||||
|
||||
function createSeats(hallName, time) {
|
||||
function createSeats(hallName: string, time: any) {
|
||||
const seatGrid = document.getElementById("seat-grid");
|
||||
if (!seatGrid) {
|
||||
return;
|
||||
@@ -144,7 +148,8 @@ function createSeats(hallName, time) {
|
||||
|
||||
seatGrid.innerHTML = "";
|
||||
|
||||
const baseConfig = seatLayouts[hallName];
|
||||
const arrIndex = hallName as keyof typeof seatLayouts;
|
||||
const baseConfig: any = seatLayouts[arrIndex];
|
||||
if (!baseConfig) {
|
||||
currentHallLayout = null;
|
||||
return;
|
||||
@@ -235,7 +240,7 @@ function renderBookingLegend() {
|
||||
}
|
||||
|
||||
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 summaryItems = document.getElementById("summary-items");
|
||||
const totalEl = document.getElementById("total-price");
|
||||
@@ -245,7 +250,7 @@ function updateBookingSummary() {
|
||||
if (summaryItems) {
|
||||
summaryItems.innerHTML = selectedSeats
|
||||
.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);
|
||||
total += seatPrice;
|
||||
|
||||
@@ -272,12 +277,13 @@ function updateBookingSummary() {
|
||||
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 normalizedTarget = String(movieTitle || "").trim().toLowerCase();
|
||||
|
||||
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) {
|
||||
const imageSrc = card.querySelector("img")?.src;
|
||||
if (imageSrc) {
|
||||
@@ -290,7 +296,7 @@ function findMoviePoster(movieTitle) {
|
||||
}
|
||||
|
||||
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) {
|
||||
alert("Bitte waehle mindestens einen Platz aus.");
|
||||
@@ -304,7 +310,7 @@ function confirmSelectedSeats() {
|
||||
const seatId = seat.dataset.seatId;
|
||||
const seatType = seat.dataset.type || "normal";
|
||||
|
||||
const alreadyInCart = cart.some((item) =>
|
||||
const alreadyInCart = cart.some((item: any) =>
|
||||
item.category === "movie" &&
|
||||
item.title === currentBookingContext.movie &&
|
||||
item.hall === currentBookingContext.hall &&
|
||||
@@ -1,8 +1,10 @@
|
||||
function formatEuro(value) {
|
||||
import { cart } from "./main.js";
|
||||
|
||||
function formatEuro(value: number) {
|
||||
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
function escapeHtml(value: any) {
|
||||
return String(value || "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
@@ -11,14 +13,14 @@ function escapeHtml(value) {
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function buildCartKey(item) {
|
||||
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) {
|
||||
function isDrinkItem(item: { category: string; title: any; hall: any; }) {
|
||||
if (item.category !== "snack") {
|
||||
return false;
|
||||
}
|
||||
@@ -39,7 +41,7 @@ function isDrinkItem(item) {
|
||||
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") {
|
||||
return `
|
||||
<div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div>
|
||||
@@ -65,7 +67,7 @@ function buildItemInfo(item) {
|
||||
function groupCartItems() {
|
||||
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);
|
||||
|
||||
if (!groups.has(key)) {
|
||||
@@ -85,12 +87,12 @@ function groupCartItems() {
|
||||
return Array.from(groups.values());
|
||||
}
|
||||
|
||||
function saveCart() {
|
||||
export function saveCart() {
|
||||
localStorage.setItem("eagleCart", JSON.stringify(cart));
|
||||
updateCartBadge();
|
||||
}
|
||||
|
||||
function updateCartBadge() {
|
||||
export function updateCartBadge() {
|
||||
const cartBadge = document.getElementById("cart-badge");
|
||||
|
||||
if (!cartBadge) {
|
||||
@@ -101,7 +103,7 @@ function updateCartBadge() {
|
||||
cartBadge.classList.toggle("hidden", cart.length === 0);
|
||||
}
|
||||
|
||||
function renderCart() {
|
||||
export function renderCart() {
|
||||
const cartList = document.getElementById("cart-items-list");
|
||||
const totalEl = document.getElementById("cart-total-right");
|
||||
const vatEl = document.getElementById("cart-vat-right");
|
||||
@@ -119,7 +121,7 @@ function renderCart() {
|
||||
|
||||
const groupedItems = groupCartItems();
|
||||
|
||||
const header = `
|
||||
const header = /*html*/`
|
||||
<div class="cart-header-row">
|
||||
<div class="col-amount">MENGE</div>
|
||||
<div class="col-img">VORSCHAU</div>
|
||||
@@ -133,11 +135,11 @@ function renderCart() {
|
||||
const rows = groupedItems
|
||||
.map((group) => {
|
||||
const imageHtml = group.item.img
|
||||
? `<img class="cart-img-small" src="${escapeHtml(group.item.img)}" alt="${escapeHtml(group.item.title)}">`
|
||||
: `<div class="cart-img-fallback">Kein Bild</div>`;
|
||||
? /*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"
|
||||
? `<div class="qty-static" aria-label="Feste Ticketanzahl">${group.quantity}x</div>`
|
||||
: `
|
||||
? /*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>
|
||||
@@ -145,7 +147,7 @@ function renderCart() {
|
||||
</div>
|
||||
`;
|
||||
|
||||
return `
|
||||
return /*html*/`
|
||||
<div class="cart-item-row">
|
||||
<div class="col-amount">
|
||||
${quantityHtml}
|
||||
@@ -172,22 +174,23 @@ function renderCart() {
|
||||
|
||||
saveCart();
|
||||
}
|
||||
|
||||
window.removeItem = function removeItem(id) {
|
||||
cart = cart.filter((item) => item.id !== id);
|
||||
//@ts-ignore
|
||||
window.removeItem = function removeItem(id: any) {
|
||||
var localCart = cart.filter((item: { id: any; }) => item.id !== id);
|
||||
saveCart();
|
||||
renderCart();
|
||||
};
|
||||
|
||||
window.changeQty = function changeQty(title, delta) {
|
||||
//@ts-ignore
|
||||
window.changeQty = function changeQty(title, delta): void {
|
||||
if (delta > 0) {
|
||||
const item = cart.find((entry) => entry.title === title);
|
||||
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) => entry.title)
|
||||
.map((entry: { title: any; }) => entry.title)
|
||||
.lastIndexOf(title);
|
||||
if (index !== -1) {
|
||||
cart.splice(index, 1);
|
||||
@@ -197,3 +200,4 @@ window.changeQty = function changeQty(title, delta) {
|
||||
saveCart();
|
||||
renderCart();
|
||||
};
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
function formatCheckoutEuro(value) {
|
||||
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) {
|
||||
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");
|
||||
@@ -27,7 +31,7 @@ function setCheckoutStep(step) {
|
||||
line2?.classList.toggle("active", step >= 3);
|
||||
}
|
||||
|
||||
function renderCheckout() {
|
||||
export function renderCheckout() {
|
||||
const summaryList = document.getElementById("checkout-summary-list");
|
||||
const totalDisplay = document.getElementById("checkout-total-display");
|
||||
const vatDisplay = document.getElementById("checkout-vat-display");
|
||||
@@ -93,7 +97,7 @@ function generateTicket() {
|
||||
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 = `
|
||||
ticketContainer.innerHTML = /*html*/`
|
||||
<div class="luxury-ticket">
|
||||
<div class="ticket-left">
|
||||
<img src="${mainMovie.img}" class="ticket-poster" alt="${mainMovie.title}">
|
||||
@@ -115,7 +119,7 @@ function generateTicket() {
|
||||
`;
|
||||
}
|
||||
|
||||
function saveOrderForCurrentUser(orderItems, orderTotal) {
|
||||
function saveOrderForCurrentUser(orderItems: any[], orderTotal: any) {
|
||||
if (typeof currentUser === "undefined" || !currentUser) {
|
||||
return;
|
||||
}
|
||||
@@ -131,6 +135,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
|
||||
paymentMethod: selectedPaymentMethod || "-"
|
||||
};
|
||||
|
||||
//@ts-ignore
|
||||
const userIndex = users.findIndex((entry) => entry.email === currentUser.email);
|
||||
if (userIndex === -1) {
|
||||
return;
|
||||
@@ -144,7 +149,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
|
||||
localStorage.setItem("eagleUsers", JSON.stringify(users));
|
||||
}
|
||||
|
||||
function reserveSeatsAfterPayment(orderItems) {
|
||||
function reserveSeatsAfterPayment(orderItems: any[]) {
|
||||
const movieItems = orderItems.filter((item) => item.category === "movie");
|
||||
|
||||
movieItems.forEach((item) => {
|
||||
@@ -166,7 +171,7 @@ function completeCheckout() {
|
||||
saveOrderForCurrentUser(orderItems, orderTotal);
|
||||
reserveSeatsAfterPayment(orderItems);
|
||||
|
||||
cart = [];
|
||||
emptyCart?.()
|
||||
saveCart?.();
|
||||
renderCart?.();
|
||||
}
|
||||
@@ -180,7 +185,7 @@ function bindCheckoutEvents() {
|
||||
|
||||
const nextButton = document.getElementById("btn-next-step-2");
|
||||
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) => {
|
||||
method.addEventListener("click", () => {
|
||||
@@ -189,6 +194,7 @@ function bindCheckoutEvents() {
|
||||
});
|
||||
|
||||
method.classList.add("selected");
|
||||
//@ts-ignore
|
||||
selectedPaymentMethod = method.dataset.method || "";
|
||||
nextButton?.classList.remove("hidden");
|
||||
});
|
||||
8
src/interfaces.ts
Normal file
8
src/interfaces.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
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
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
// Shared app state for legacy script files (account.js, booking.js, cart.js, checkout.js)
|
||||
var prices = { normal: 11.0, imax: 15.0, vip: 12.0, dbox: 16.0 };
|
||||
var seatLayouts = {
|
||||
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";
|
||||
|
||||
// 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 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 }
|
||||
};
|
||||
var cart = JSON.parse(localStorage.getItem("eagleCart")) || [];
|
||||
var occupiedSeatsData = JSON.parse(localStorage.getItem("eagleOccupied")) || {};
|
||||
export var cart = JSON.parse(localStorage.getItem("eagleCart") || '{}');
|
||||
export var occupiedSeatsData = JSON.parse(localStorage.getItem("eagleOccupied") || '{}');
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const views = {
|
||||
@@ -62,8 +67,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
duration: 148,
|
||||
fsk: "6",
|
||||
description: "In Walt Disney Animation Studios’ \"Zoomania 2\" geraten die tierischen Detektive Judy Hopps und Nick Wilde auf die rätselhafte Spur eines geheimnisvollen Reptils, das in Zoomania auftaucht und die Metropole völlig auf den Kopf stellt: Gary De’Snake! ",
|
||||
poster: "img/zoomania-2.jpg",
|
||||
backdrop: "img/zoomania-2.jpg"
|
||||
poster: "img/Zoomania-2.jpg",
|
||||
backdrop: "img/Zoomania-2.jpg"
|
||||
},
|
||||
{
|
||||
title: "Shelter",
|
||||
@@ -326,10 +331,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
["13:00", "16:00", "18:20", "20:40"]
|
||||
];
|
||||
|
||||
let movieProgram = [];
|
||||
let heroItems = [];
|
||||
let movieProgram: any = []; // TODO: Find type
|
||||
let heroItems: any = []; // TODO: find Type
|
||||
let heroIndex = 0;
|
||||
let heroTimer = null;
|
||||
let heroTimer:any = null; // TODO: find type
|
||||
|
||||
const weekdayShort = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
||||
|
||||
@@ -348,7 +353,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
const showMovieList = (programIndexToFocus = null) => {
|
||||
const showMovieList = (programIndexToFocus:number = NaN) => {
|
||||
hideAllViews();
|
||||
views.list?.classList.remove("hidden");
|
||||
|
||||
@@ -365,7 +370,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const showStaticView = (viewElement) => {
|
||||
const showStaticView = (viewElement: HTMLElement) => {
|
||||
if (!viewElement) {
|
||||
return;
|
||||
}
|
||||
@@ -394,20 +399,20 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
ui.bookingModal?.classList.add("hidden");
|
||||
};
|
||||
|
||||
const escapeHtml = (value) => String(value || "")
|
||||
const escapeHtml = (value: string) => String(value || "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
|
||||
const formatDateShort = (dateObj) => {
|
||||
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) => {
|
||||
const buildDayMeta = (offset: number) => {
|
||||
const date = new Date();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
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) => {
|
||||
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 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];
|
||||
return { time, hall };
|
||||
});
|
||||
@@ -468,7 +474,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
heroItems = movieProgram.slice(0, 5);
|
||||
};
|
||||
|
||||
const setHeroSlide = (index) => {
|
||||
const setHeroSlide = (index: number) => {
|
||||
if (!heroItems.length || !ui.heroSlider) {
|
||||
return;
|
||||
}
|
||||
@@ -497,17 +503,18 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
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>
|
||||
`).join("");
|
||||
|
||||
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>
|
||||
`).join("");
|
||||
|
||||
ui.heroDots.addEventListener("click", (event) => {
|
||||
const dot = event.target.closest(".hero-dot");
|
||||
ui.heroDots.addEventListener("click", (event: any) => {
|
||||
const dotTarget = event.target || 0;
|
||||
const dot = dotTarget.closest(".hero-dot");
|
||||
if (!dot) {
|
||||
return;
|
||||
}
|
||||
@@ -538,7 +545,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
ui.nowRunningRow.innerHTML = movieProgram.map((movie, index) => `
|
||||
// TODO: implement movie interface
|
||||
ui.nowRunningRow.innerHTML = movieProgram.map((movie: any, index: number) => /*html*/`
|
||||
<article class="running-poster">
|
||||
<img src="${escapeHtml(movie.poster)}" alt="${escapeHtml(movie.title)}">
|
||||
<div class="running-meta">
|
||||
@@ -550,7 +558,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
`).join("");
|
||||
};
|
||||
|
||||
const renderScheduleRows = (programIndex, dayIndex) => {
|
||||
const renderScheduleRows = (programIndex: number, dayIndex: number) => {
|
||||
const movie = movieProgram[programIndex];
|
||||
if (!movie) {
|
||||
return;
|
||||
@@ -562,7 +570,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
body.innerHTML = day.showings.map((showing) => `
|
||||
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)}">
|
||||
<span>${escapeHtml(day.long)}</span>
|
||||
<span class="hall-pill">${escapeHtml(showing.hall)}</span>
|
||||
@@ -576,15 +584,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
ui.movieProgramList.innerHTML = movieProgram.map((movie, programIndex) => {
|
||||
const dayTabs = movie.schedule.map((day, dayIndex) => `
|
||||
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*/`
|
||||
<button type="button" class="program-day-tab ${dayIndex === 0 ? "active" : ""}" data-program-index="${programIndex}" data-day-index="${dayIndex}">
|
||||
<span>${escapeHtml(day.short)}</span>
|
||||
<small>${escapeHtml(formatDateShort(day.date))}</small>
|
||||
</button>
|
||||
`).join("");
|
||||
|
||||
return `
|
||||
return /*html*/`
|
||||
<article class="detailed-card program-card reveal-on-scroll" data-program-index="${programIndex}">
|
||||
<div class="card-left">
|
||||
<img src="${escapeHtml(movie.poster)}" alt="${escapeHtml(movie.title)}">
|
||||
@@ -610,7 +618,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
`;
|
||||
}).join("");
|
||||
|
||||
movieProgram.forEach((_, programIndex) => {
|
||||
movieProgram.forEach((_: any, programIndex: number) => {
|
||||
renderScheduleRows(programIndex, 0);
|
||||
});
|
||||
};
|
||||
@@ -658,12 +666,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
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) => {
|
||||
@@ -693,17 +705,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
};
|
||||
|
||||
const bindProgramActions = () => {
|
||||
views.moviesGrid?.addEventListener("click", (event) => {
|
||||
views.moviesGrid?.addEventListener("click", (event:any) => {
|
||||
const trigger = event.target.closest(".open-program-btn");
|
||||
if (!trigger) {
|
||||
return;
|
||||
}
|
||||
|
||||
const programIndex = Number(trigger.dataset.programIndex || 0);
|
||||
const programIndex = Number(trigger.dataset.programIndex) || 0;
|
||||
showMovieList(programIndex);
|
||||
});
|
||||
|
||||
ui.movieProgramList?.addEventListener("click", (event) => {
|
||||
ui.movieProgramList?.addEventListener("click", (event:any) => {
|
||||
const dayButton = event.target.closest(".program-day-tab");
|
||||
if (!dayButton) {
|
||||
return;
|
||||
@@ -713,7 +725,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const dayIndex = Number(dayButton.dataset.dayIndex || 0);
|
||||
|
||||
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");
|
||||
|
||||
renderScheduleRows(programIndex, dayIndex);
|
||||
@@ -739,7 +751,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
openButtons.forEach((button) => {
|
||||
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;
|
||||
if (target) {
|
||||
showStaticView(target);
|
||||
@@ -792,10 +804,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
const THEME_KEY = "eagleTheme";
|
||||
|
||||
const applyTheme = (theme) => {
|
||||
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");
|
||||
};
|
||||
@@ -812,14 +825,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const bindAccountActions = () => {
|
||||
const registerModal = document.getElementById("register-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 loginError = document.getElementById("login-error");
|
||||
const loginEmailInput = document.getElementById("login-email");
|
||||
const loginPasswordInput = document.getElementById("login-password");
|
||||
|
||||
const openModal = (modal) => modal?.classList.remove("hidden");
|
||||
const closeModal = (modal) => modal?.classList.add("hidden");
|
||||
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") {
|
||||
@@ -855,7 +868,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
|
||||
document.getElementById("btn-forgot-password")?.addEventListener("click", () => {
|
||||
if (forgotEmailInput) {
|
||||
if (forgotEmailInput != null) {
|
||||
forgotEmailInput.value = "";
|
||||
}
|
||||
resetMessage?.classList.add("hidden");
|
||||
@@ -900,10 +913,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
};
|
||||
|
||||
const bindGlobalDocumentClicks = () => {
|
||||
document.addEventListener("click", (event) => {
|
||||
document.addEventListener("click", (event: any) => {
|
||||
if (event.target.classList.contains("opt-btn")) {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -912,15 +925,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const row = deleteBtn.closest(".cart-item-row");
|
||||
if (row) {
|
||||
row.classList.add("slide-out-left");
|
||||
row.querySelectorAll("button").forEach((button) => {
|
||||
row.querySelectorAll("button").forEach((button: { disabled: boolean; }) => {
|
||||
button.disabled = true;
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
removeFromCartByKey(deleteBtn.dataset.key);
|
||||
//@ts-ignore
|
||||
removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist
|
||||
}, 380);
|
||||
} else {
|
||||
removeFromCartByKey(deleteBtn.dataset.key);
|
||||
//@ts-ignore
|
||||
removeFromCartByKey(deleteBtn.dataset.key); //TODO: removeFromCartByKey doesnt exist
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -949,7 +964,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
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"
|
||||
? `Sitz: ${item.seatId} (${item.hall})`
|
||||
: item.time;
|
||||
@@ -963,7 +978,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
if (action === "plus") {
|
||||
cart.push({ ...relatedItem, id: Date.now() + Math.random() });
|
||||
} 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"
|
||||
? `Sitz: ${item.seatId} (${item.hall})`
|
||||
: item.time;
|
||||
@@ -982,7 +997,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
};
|
||||
|
||||
const bindSnacksActions = () => {
|
||||
ui.snacksView?.addEventListener("click", (event) => {
|
||||
ui.snacksView?.addEventListener("click", (event:any) => {
|
||||
const sizeChip = event.target.closest(".size-chip");
|
||||
if (!sizeChip) {
|
||||
return;
|
||||
@@ -1026,7 +1041,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}, 800);
|
||||
});
|
||||
|
||||
document.querySelectorAll(".tab-btn").forEach((button) => {
|
||||
document.querySelectorAll(".tab-btn").forEach((button: any) => {
|
||||
button.addEventListener("click", () => {
|
||||
document.querySelectorAll(".tab-btn").forEach((tab) => tab.classList.remove("active"));
|
||||
button.classList.add("active");
|
||||
@@ -1063,9 +1078,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.removeFromCartByKey = function removeFromCartByKey(key) {
|
||||
cart = cart.filter((item) => {
|
||||
// @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;
|
||||
@@ -1090,3 +1105,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
updateCartBadge?.();
|
||||
renderCheckout?.();
|
||||
});
|
||||
|
||||
export function emptyCart() {
|
||||
cart = []
|
||||
return
|
||||
}
|
||||
@@ -2902,15 +2902,15 @@ body {
|
||||
}
|
||||
|
||||
.inline-halls .inline-media {
|
||||
background-image: linear-gradient(120deg, rgba(0, 113, 227, 0.3), rgba(7, 10, 16, 0.55)), url('img/placeholder-hall.jpg');
|
||||
background-image: linear-gradient(120deg, rgba(0, 113, 227, 0.3), rgba(7, 10, 16, 0.55)), url('img/shelter.jpg');
|
||||
}
|
||||
|
||||
.inline-dbox .inline-media {
|
||||
background-image: linear-gradient(120deg, rgba(255, 176, 0, 0.2), rgba(8, 12, 18, 0.62)), url('img/placeholder-dbox.jpg');
|
||||
background-image: linear-gradient(120deg, rgba(255, 176, 0, 0.2), rgba(8, 12, 18, 0.62)), url('img/dbox.jpg');
|
||||
}
|
||||
|
||||
.inline-collectors .inline-media {
|
||||
background-image: linear-gradient(120deg, rgba(185, 124, 255, 0.15), rgba(8, 12, 18, 0.62)), url('img/placeholder-collector.jpg');
|
||||
background-image: linear-gradient(120deg, rgba(185, 124, 255, 0.15), rgba(8, 12, 18, 0.62)), url('img/popcorn.jpg');
|
||||
}
|
||||
|
||||
.inline-content {
|
||||
|
||||
45
tsconfig.json
Normal file
45
tsconfig.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "ES2020",
|
||||
"target": "ES2021",
|
||||
"types": [],
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
// "types": ["node"],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"include": ["src/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user