Compare commits
10 Commits
dev_aaron
...
7a07dd8c70
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a07dd8c70 | ||
|
|
d6905da5c5 | ||
|
|
e72f8a4f1f | ||
|
|
23d5a75891 | ||
|
|
e143d8360a | ||
| c805221208 | |||
|
|
40ee49aff5 | ||
|
|
c88242b2be | ||
|
|
425c5d1900 | ||
|
|
e2b4852e0d |
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
TMDB_API_TOKEN=yourapitoken
|
||||||
|
TMDB_API_KEY=yourapikey
|
||||||
4
.gitattributes
vendored
@@ -1,2 +1,2 @@
|
|||||||
img/ filter=lfs diff=lfs merge=lfs -text
|
public/img/* filter=lfs diff=lfs merge=lfs -text
|
||||||
img/** filter=lfs diff=lfs merge=lfs -text
|
|
||||||
|
|||||||
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# jetbrains setting folder
|
||||||
|
.idea/
|
||||||
|
.claude/
|
||||||
4
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["astro-build.astro-vscode"],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
||||||
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
43
README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Astro Starter Kit: Minimal
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
├── src/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||||
22
astro.config.mjs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { defineConfig, envField } from 'astro/config';
|
||||||
|
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [react({
|
||||||
|
include: ['**/react/*']
|
||||||
|
})],
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
schema: {
|
||||||
|
TMDB_API_TOKEN: envField.string({ context: 'client', access: 'public', default: 'https://api.example.com' }),
|
||||||
|
SETTINGS_TOKEN: envField.string({ context: 'server', access: 'secret' }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
14
index.html
@@ -735,8 +735,8 @@
|
|||||||
<div class="header-sub-info">
|
<div class="header-sub-info">
|
||||||
<p id="modal-info-text">Saal • Zeit</p>
|
<p id="modal-info-text">Saal • Zeit</p>
|
||||||
<div id="tech-badges" class="tech-badges-container hidden">
|
<div id="tech-badges" class="tech-badges-container hidden">
|
||||||
<img src="img/dolby.png" alt="Dolby" class="tech-badge">
|
<img src="img/Dolby.png" alt="Dolby" class="tech-badge">
|
||||||
<img src="img/dbox.png" alt="D-Box" class="tech-badge">
|
<img src="img/dbox.jpg" alt="D-Box" class="tech-badge">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -948,11 +948,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="account.js"></script>
|
<script type="module" src="dist/main.js"></script>
|
||||||
<script src="cart.js"></script>
|
<script type="module" src="dist/cart.js"></script>
|
||||||
<script src="booking.js"></script>
|
<script type="module" src="dist/booking.js"></script>
|
||||||
<script src="checkout.js"></script>
|
<script type="module" src="dist/checkout.js"></script>
|
||||||
<script src="main.js"></script>
|
<script type="module" src="dist/account.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
6442
package-lock.json
generated
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "kino-astro",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/react": "^5.0.4",
|
||||||
|
"@tailwindcss/vite": "^4.2.4",
|
||||||
|
"@types/react": "^19.2.14",
|
||||||
|
"@types/react-dom": "^19.2.3",
|
||||||
|
"astro": "^6.1.10",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"react": "^19.2.5",
|
||||||
|
"react-dom": "^19.2.5",
|
||||||
|
"tailwindcss": "^4.2.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^25.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 655 B |
9
public/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 749 B |
BIN
public/img/Apfelschorle.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/img/Dolby.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/img/Schorle.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
public/img/Zoomania-2.jpg
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
public/img/applepay.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
public/img/astronautpopcorn.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/img/bladerunner2049.jpg
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
public/img/cola-light.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
public/img/cola-zero.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/img/cola.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/img/dbox.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/img/derAustronaut.jpg
Normal file
|
After Width: | Height: | Size: 878 KiB |
BIN
public/img/fallguy.jpg
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
public/img/fanta.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
public/img/fuze-tea.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/img/gangstergang.jpg
Normal file
|
After Width: | Height: | Size: 362 KiB |
BIN
public/img/gangstergang2.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
public/img/glennkill.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
BIN
public/img/goat.jpg
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
public/img/googlepay.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/img/haribo.png
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
public/img/homefront.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/img/hoppers.jpg
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
public/img/hopperskidsmenu.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
public/img/hopperspopcornmetall.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/img/hopperspopcornwood.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/img/klarna.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
public/img/käsedip.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
public/img/mandalorian.jpeg
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/img/mariogalaxy.jpg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/img/mariokidsmenu.png
Normal file
|
After Width: | Height: | Size: 307 KiB |
BIN
public/img/marioyoshipopcorn.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
public/img/mastercard.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
public/img/masteruniverse.jpg
Normal file
|
After Width: | Height: | Size: 910 KiB |
BIN
public/img/meg.JPG
Normal file
|
After Width: | Height: | Size: 586 KiB |
BIN
public/img/meg2.jpg
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/img/menu-big.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
public/img/minionsmonsters.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/img/mms.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/img/monsterag.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
BIN
public/img/monsteruni.jpg
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
public/img/mutiny.jpg
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
public/img/nachokombiklein.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
public/img/nachos.jpg
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
public/img/nachosnormal.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
public/img/paypal.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
public/img/popcorn-big.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/img/popcorn-klein.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
public/img/popcorn-mittel.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/img/popcorn.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/img/riegel.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
public/img/salsadip.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/img/screamdoorpopcorn.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/img/screamvii.jpg
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
public/img/shelter.jpg
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
public/img/solomio.png
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
public/img/sourdip.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
public/img/spezi.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/img/spidermannewday.jpg
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
public/img/sprite.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/img/super-mario-galaxy-banner.jpg
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
public/img/toystory1.jpg
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/img/toystory2.jpg
Normal file
|
After Width: | Height: | Size: 643 KiB |
BIN
public/img/toystory3.jpg
Normal file
|
After Width: | Height: | Size: 481 KiB |
BIN
public/img/toystory4.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
public/img/toystory5.png
Normal file
|
After Width: | Height: | Size: 530 KiB |
BIN
public/img/visa.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/img/wasser.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
public/img/zoomania-popcorn.jpg
Normal file
|
After Width: | Height: | Size: 544 KiB |
BIN
public/img/zoomaniakidsmenu.jpg
Normal file
|
After Width: | Height: | Size: 524 KiB |
@@ -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);
|
const raw = localStorage.getItem(key);
|
||||||
|
|
||||||
if (!raw || raw === "undefined" || raw === "null") {
|
if (!raw || raw === "undefined" || raw === "null") {
|
||||||
@@ -13,22 +15,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeUser(user) {
|
function normalizeUser(user: User): User {
|
||||||
if (!user || typeof user !== "object") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
firstName: user.firstName || "",
|
firstName: user.firstName || "",
|
||||||
lastName: user.lastName || "",
|
lastName: user.lastName || "",
|
||||||
email: user.email || "",
|
email: user.email || "",
|
||||||
password: user.password || "",
|
hashedPassword: user.hashedPassword || "",
|
||||||
orders: Array.isArray(user.orders) ? user.orders : [],
|
orders: Array.isArray(user.orders) ? user.orders : [],
|
||||||
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
|
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value: string) {
|
||||||
return String(value || "")
|
return String(value || "")
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
@@ -37,7 +35,7 @@ function escapeHtml(value) {
|
|||||||
.replaceAll("'", "'");
|
.replaceAll("'", "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEuro(value) {
|
function formatEuro(value: string) {
|
||||||
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
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)) {
|
if (!Array.isArray(users)) {
|
||||||
users = [];
|
users = [];
|
||||||
}
|
}
|
||||||
users = users.map(normalizeUser).filter(Boolean);
|
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) {
|
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) {
|
if (storedMatch) {
|
||||||
currentUser = storedMatch;
|
currentUser = storedMatch;
|
||||||
} else {
|
} else {
|
||||||
@@ -70,11 +74,23 @@ if (currentUser && currentUser.email) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerUser() {
|
async function hashMessage(message: string) {
|
||||||
const firstName = document.getElementById("reg-firstname")?.value.trim() || "";
|
const msgBuffer = new TextEncoder().encode(message); // Encode as UTF-8
|
||||||
const lastName = document.getElementById("reg-lastname")?.value.trim() || "";
|
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); // Hash
|
||||||
const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase();
|
const hashArray = Array.from(new Uint8Array(hashBuffer)); // Convert to bytes
|
||||||
const password = document.getElementById("reg-password")?.value || "";
|
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) {
|
if (!firstName || !lastName || !email || !password) {
|
||||||
alert("Bitte fuelle alle Felder aus.");
|
alert("Bitte fuelle alle Felder aus.");
|
||||||
@@ -86,17 +102,19 @@ function registerUser() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingUser = users.find((user) => user.email.toLowerCase() === email);
|
const existingUser = users.find((user: User) => user.email.toLowerCase() === email);
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
alert("E-Mail bereits registriert");
|
alert("E-Mail bereits registriert");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hashedPassword = await hashMessage(password);
|
||||||
|
|
||||||
const newUser = {
|
const newUser = {
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
email,
|
email,
|
||||||
password,
|
hashedPassword,
|
||||||
orders: [],
|
orders: [],
|
||||||
paymentMethods: []
|
paymentMethods: []
|
||||||
};
|
};
|
||||||
@@ -113,12 +131,13 @@ function registerUser() {
|
|||||||
openAccountDashboard();
|
openAccountDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
function loginUser() {
|
export async function loginUser() {
|
||||||
const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase();
|
const email = (document.querySelector<HTMLInputElement>("#login-email")?.value.trim() || "").toLowerCase();
|
||||||
const password = document.getElementById("login-password")?.value || "";
|
const password = document.querySelector<HTMLInputElement>("#login-password")?.value || "";
|
||||||
|
const hashedPassword = await hashMessage(password);
|
||||||
|
|
||||||
const user = users.find(
|
const user = users.find(
|
||||||
(entry) => entry.email.toLowerCase() === email && entry.password === password
|
(entry: User) => entry.email.toLowerCase() === email && entry.hashedPassword === hashedPassword
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -131,7 +150,7 @@ function loginUser() {
|
|||||||
openAccountDashboard();
|
openAccountDashboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openAccountDashboard() {
|
export function openAccountDashboard() {
|
||||||
const accountView = document.getElementById("account-view");
|
const accountView = document.getElementById("account-view");
|
||||||
if (!accountView) {
|
if (!accountView) {
|
||||||
return;
|
return;
|
||||||
@@ -142,7 +161,7 @@ function openAccountDashboard() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
accountView.innerHTML = `
|
accountView.innerHTML = /*html*/`
|
||||||
<div class="account-panel">
|
<div class="account-panel">
|
||||||
<div class="account-panel-header">
|
<div class="account-panel-header">
|
||||||
<h2>Mein Konto</h2>
|
<h2>Mein Konto</h2>
|
||||||
@@ -198,7 +217,7 @@ function renderOrders() {
|
|||||||
const orderHtml = orders
|
const orderHtml = orders
|
||||||
.map((order, index) => {
|
.map((order, index) => {
|
||||||
const movieItems = Array.isArray(order.items)
|
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 previewItem = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null);
|
||||||
const previewTitle = previewItem?.title || "Bestellung";
|
const previewTitle = previewItem?.title || "Bestellung";
|
||||||
@@ -227,20 +246,20 @@ function renderOrders() {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const detailTarget = document.getElementById("order-ticket-details");
|
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];
|
const order = orders[orderIndex];
|
||||||
if (!order || !detailTarget) {
|
if (!order || !detailTarget) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const movieItems = Array.isArray(order.items)
|
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 primaryMovie = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null);
|
||||||
const poster = primaryMovie?.img || "";
|
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 ticketCount = movieItems.length || (Array.isArray(order.items) ? order.items.length : 0);
|
||||||
const hall = primaryMovie?.hall || "-";
|
const hall = primaryMovie?.hall || "-";
|
||||||
const time = primaryMovie?.time ? `${primaryMovie.time} Uhr` : "-";
|
const time = primaryMovie?.time ? `${primaryMovie.time} Uhr` : "-";
|
||||||
@@ -290,7 +309,7 @@ function renderPayments() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.innerHTML = `
|
target.innerHTML = /*html*/`
|
||||||
<div class="account-card">
|
<div class="account-card">
|
||||||
<h3>Zahlungsmethoden</h3>
|
<h3>Zahlungsmethoden</h3>
|
||||||
<p class="account-payments-note">Platzhalter zum Hinterlegen deiner Logos oder Anbieter-Informationen.</p>
|
<p class="account-payments-note">Platzhalter zum Hinterlegen deiner Logos oder Anbieter-Informationen.</p>
|
||||||
@@ -444,7 +463,7 @@ function renderPayments() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logoutUser() {
|
function logoutUser() {
|
||||||
currentUser = null;
|
|
||||||
persistCurrentUser();
|
persistCurrentUser();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,32 +1,36 @@
|
|||||||
let currentBookingContext = null;
|
import { seatLayouts, occupiedSeatsData, prices, cart } from "./main.js"
|
||||||
let currentHallLayout = null;
|
import { renderCart, saveCart } from "./cart.js";
|
||||||
|
import { renderCheckout } from "./checkout.js";
|
||||||
|
|
||||||
function openBooking(movie, hall, time) {
|
let currentBookingContext: any = null;
|
||||||
const titleEl = document.getElementById("modal-movie-title");
|
let currentHallLayout: any = null;
|
||||||
const infoEl = document.getElementById("modal-info-text");
|
|
||||||
|
|
||||||
if (titleEl) {
|
export function openBooking(movie: string, hall: string, time: any) {
|
||||||
titleEl.innerText = movie;
|
const titleEl = document.getElementById("modal-movie-title");
|
||||||
}
|
const infoEl = document.getElementById("modal-info-text");
|
||||||
|
|
||||||
if (infoEl) {
|
if (titleEl) {
|
||||||
infoEl.innerText = `${hall} • ${time} Uhr`;
|
titleEl.innerText = movie;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBookingContext = { movie, hall, time };
|
if (infoEl) {
|
||||||
|
infoEl.innerText = `${hall} • ${time} Uhr`;
|
||||||
|
}
|
||||||
|
|
||||||
createSeats(hall, time);
|
currentBookingContext = { movie, hall, time };
|
||||||
renderBookingLegend();
|
|
||||||
updateBookingSummary();
|
|
||||||
|
|
||||||
document.getElementById("booking-modal")?.classList.remove("hidden");
|
createSeats(hall, time);
|
||||||
}
|
renderBookingLegend();
|
||||||
|
updateBookingSummary();
|
||||||
|
|
||||||
function getRowLabel(rowIndex) {
|
document.getElementById("booking-modal")?.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowLabel(rowIndex: number) {
|
||||||
return String(rowIndex + 1);
|
return String(rowIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildHallLayout(hallName, baseConfig) {
|
function buildHallLayout(hallName: string, baseConfig:any) {
|
||||||
const rows = Number(baseConfig.rows || 0);
|
const rows = Number(baseConfig.rows || 0);
|
||||||
const totalCols = Number(baseConfig.left || 0) + Number(baseConfig.right || 0);
|
const totalCols = Number(baseConfig.left || 0) + Number(baseConfig.right || 0);
|
||||||
const isDeluxe = /deluxe/i.test(hallName);
|
const isDeluxe = /deluxe/i.test(hallName);
|
||||||
@@ -39,7 +43,7 @@ function buildHallLayout(hallName, baseConfig) {
|
|||||||
const vipRows = rows > 0 ? [rows] : [];
|
const vipRows = rows > 0 ? [rows] : [];
|
||||||
|
|
||||||
const dboxMap = new Set();
|
const dboxMap = new Set();
|
||||||
const markDboxRange = (rowNumber, startCol, width) => {
|
const markDboxRange = (rowNumber: number, startCol: number, width: number) => {
|
||||||
if (!rowNumber || width <= 0) {
|
if (!rowNumber || width <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -52,7 +56,7 @@ function buildHallLayout(hallName, baseConfig) {
|
|||||||
|
|
||||||
if (isDeluxe) {
|
if (isDeluxe) {
|
||||||
const configuredDboxSeats = Array.isArray(baseConfig.dbox)
|
const configuredDboxSeats = Array.isArray(baseConfig.dbox)
|
||||||
? baseConfig.dbox.reduce((sum, section) => sum + Number(section.w || 0), 0)
|
? baseConfig.dbox.reduce((sum: number, section: any) => sum + Number(section.w || 0), 0)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const totalDboxSeats = Math.max(4, configuredDboxSeats || 0);
|
const totalDboxSeats = Math.max(4, configuredDboxSeats || 0);
|
||||||
@@ -77,7 +81,7 @@ function buildHallLayout(hallName, baseConfig) {
|
|||||||
markDboxRange(rowNumber, startCol, seatsForRow);
|
markDboxRange(rowNumber, startCol, seatsForRow);
|
||||||
});
|
});
|
||||||
} else if (Array.isArray(baseConfig.dbox)) {
|
} else if (Array.isArray(baseConfig.dbox)) {
|
||||||
baseConfig.dbox.forEach((section) => {
|
baseConfig.dbox.forEach((section: any) => {
|
||||||
const rowNumber = Number(section.r || 0);
|
const rowNumber = Number(section.r || 0);
|
||||||
const width = Number(section.w || 0);
|
const width = Number(section.w || 0);
|
||||||
const startCol = Number(section.c || 0);
|
const startCol = Number(section.c || 0);
|
||||||
@@ -96,7 +100,7 @@ function buildHallLayout(hallName, baseConfig) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSeatType(layout, rowNumber, colNumber) {
|
function getSeatType(layout: any, rowNumber: number, colNumber: number) {
|
||||||
if (layout.dboxMap.has(`${rowNumber}-${colNumber}`)) {
|
if (layout.dboxMap.has(`${rowNumber}-${colNumber}`)) {
|
||||||
return "dbox";
|
return "dbox";
|
||||||
}
|
}
|
||||||
@@ -112,7 +116,7 @@ function getSeatType(layout, rowNumber, colNumber) {
|
|||||||
return "normal";
|
return "normal";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSeatElement({ seatId, seatType, occupiedSeats }) {
|
function createSeatElement({seatId, seatType, occupiedSeats }:any) {
|
||||||
const seat = document.createElement("button");
|
const seat = document.createElement("button");
|
||||||
seat.type = "button";
|
seat.type = "button";
|
||||||
seat.classList.add("seat", seatType);
|
seat.classList.add("seat", seatType);
|
||||||
@@ -136,7 +140,7 @@ function createSeatElement({ seatId, seatType, occupiedSeats }) {
|
|||||||
return seat;
|
return seat;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSeats(hallName, time) {
|
function createSeats(hallName: string, time: any) {
|
||||||
const seatGrid = document.getElementById("seat-grid");
|
const seatGrid = document.getElementById("seat-grid");
|
||||||
if (!seatGrid) {
|
if (!seatGrid) {
|
||||||
return;
|
return;
|
||||||
@@ -144,7 +148,8 @@ function createSeats(hallName, time) {
|
|||||||
|
|
||||||
seatGrid.innerHTML = "";
|
seatGrid.innerHTML = "";
|
||||||
|
|
||||||
const baseConfig = seatLayouts[hallName];
|
const arrIndex = hallName as keyof typeof seatLayouts;
|
||||||
|
const baseConfig: any = seatLayouts[arrIndex];
|
||||||
if (!baseConfig) {
|
if (!baseConfig) {
|
||||||
currentHallLayout = null;
|
currentHallLayout = null;
|
||||||
return;
|
return;
|
||||||
@@ -235,7 +240,7 @@ function renderBookingLegend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateBookingSummary() {
|
function updateBookingSummary() {
|
||||||
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected"));
|
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")) as HTMLElement[];;
|
||||||
const summaryPanel = document.getElementById("booking-summary");
|
const summaryPanel = document.getElementById("booking-summary");
|
||||||
const summaryItems = document.getElementById("summary-items");
|
const summaryItems = document.getElementById("summary-items");
|
||||||
const totalEl = document.getElementById("total-price");
|
const totalEl = document.getElementById("total-price");
|
||||||
@@ -245,7 +250,7 @@ function updateBookingSummary() {
|
|||||||
if (summaryItems) {
|
if (summaryItems) {
|
||||||
summaryItems.innerHTML = selectedSeats
|
summaryItems.innerHTML = selectedSeats
|
||||||
.map((seat) => {
|
.map((seat) => {
|
||||||
const type = seat.dataset.type || "normal";
|
const type = (seat.dataset.type || "normal") as keyof typeof prices;
|
||||||
const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11);
|
const seatPrice = Number(prices?.[type] ?? prices?.normal ?? 11);
|
||||||
total += seatPrice;
|
total += seatPrice;
|
||||||
|
|
||||||
@@ -272,12 +277,13 @@ function updateBookingSummary() {
|
|||||||
summaryPanel?.classList.toggle("hidden", selectedSeats.length === 0);
|
summaryPanel?.classList.toggle("hidden", selectedSeats.length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMoviePoster(movieTitle) {
|
function findMoviePoster(movieTitle: string) {
|
||||||
const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card"));
|
const cards = Array.from(document.querySelectorAll(".movie-card, .detailed-card"));
|
||||||
const normalizedTarget = String(movieTitle || "").trim().toLowerCase();
|
const normalizedTarget = String(movieTitle || "").trim().toLowerCase();
|
||||||
|
|
||||||
for (const card of cards) {
|
for (const card of cards) {
|
||||||
const title = card.querySelector("h2, h3")?.innerText?.trim().toLowerCase();
|
const currentCard = card.querySelector("h2, h3") as HTMLElement;
|
||||||
|
const title = currentCard.innerText?.trim().toLowerCase();
|
||||||
if (title === normalizedTarget) {
|
if (title === normalizedTarget) {
|
||||||
const imageSrc = card.querySelector("img")?.src;
|
const imageSrc = card.querySelector("img")?.src;
|
||||||
if (imageSrc) {
|
if (imageSrc) {
|
||||||
@@ -290,7 +296,7 @@ function findMoviePoster(movieTitle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirmSelectedSeats() {
|
function confirmSelectedSeats() {
|
||||||
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected"));
|
const selectedSeats = Array.from(document.querySelectorAll("#seat-grid .seat.selected")) as HTMLElement[];
|
||||||
|
|
||||||
if (!currentBookingContext || selectedSeats.length === 0) {
|
if (!currentBookingContext || selectedSeats.length === 0) {
|
||||||
alert("Bitte waehle mindestens einen Platz aus.");
|
alert("Bitte waehle mindestens einen Platz aus.");
|
||||||
@@ -304,7 +310,7 @@ function confirmSelectedSeats() {
|
|||||||
const seatId = seat.dataset.seatId;
|
const seatId = seat.dataset.seatId;
|
||||||
const seatType = seat.dataset.type || "normal";
|
const seatType = seat.dataset.type || "normal";
|
||||||
|
|
||||||
const alreadyInCart = cart.some((item) =>
|
const alreadyInCart = cart.some((item: any) =>
|
||||||
item.category === "movie" &&
|
item.category === "movie" &&
|
||||||
item.title === currentBookingContext.movie &&
|
item.title === currentBookingContext.movie &&
|
||||||
item.hall === currentBookingContext.hall &&
|
item.hall === currentBookingContext.hall &&
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
function formatEuro(value) {
|
import { cart } from "./main.js";
|
||||||
|
|
||||||
|
function formatEuro(value: number) {
|
||||||
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value: any) {
|
||||||
return String(value || "")
|
return String(value || "")
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
.replaceAll("<", "<")
|
.replaceAll("<", "<")
|
||||||
@@ -11,14 +13,14 @@ function escapeHtml(value) {
|
|||||||
.replaceAll("'", "'");
|
.replaceAll("'", "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCartKey(item) {
|
function buildCartKey(item: { category: string; seatId: any; hall: any; time: any; title: any; }) {
|
||||||
const infoText = item.category === "movie"
|
const infoText = item.category === "movie"
|
||||||
? `Sitz: ${item.seatId} (${item.hall})`
|
? `Sitz: ${item.seatId} (${item.hall})`
|
||||||
: item.time;
|
: item.time;
|
||||||
return `${item.title}-${item.hall}-${infoText}`;
|
return `${item.title}-${item.hall}-${infoText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDrinkItem(item) {
|
function isDrinkItem(item: { category: string; title: any; hall: any; }) {
|
||||||
if (item.category !== "snack") {
|
if (item.category !== "snack") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -39,7 +41,7 @@ function isDrinkItem(item) {
|
|||||||
return drinkKeywords.some((word) => title.includes(word)) || size.includes("l");
|
return drinkKeywords.some((word) => title.includes(word)) || size.includes("l");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildItemInfo(item) {
|
function buildItemInfo(item: { category: any; seatId?: any; hall: any; time?: any; title: any; }) {
|
||||||
if (item.category === "movie") {
|
if (item.category === "movie") {
|
||||||
return `
|
return `
|
||||||
<div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div>
|
<div>Sitzplatz: ${escapeHtml(item.seatId || "-")}</div>
|
||||||
@@ -65,7 +67,7 @@ function buildItemInfo(item) {
|
|||||||
function groupCartItems() {
|
function groupCartItems() {
|
||||||
const groups = new Map();
|
const groups = new Map();
|
||||||
|
|
||||||
cart.forEach((item) => {
|
cart.forEach((item: { price?: any; category: string; seatId: any; hall: any; time: any; title: any; }) => {
|
||||||
const key = buildCartKey(item);
|
const key = buildCartKey(item);
|
||||||
|
|
||||||
if (!groups.has(key)) {
|
if (!groups.has(key)) {
|
||||||
@@ -85,12 +87,12 @@ function groupCartItems() {
|
|||||||
return Array.from(groups.values());
|
return Array.from(groups.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveCart() {
|
export function saveCart() {
|
||||||
localStorage.setItem("eagleCart", JSON.stringify(cart));
|
localStorage.setItem("eagleCart", JSON.stringify(cart));
|
||||||
updateCartBadge();
|
updateCartBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCartBadge() {
|
export function updateCartBadge() {
|
||||||
const cartBadge = document.getElementById("cart-badge");
|
const cartBadge = document.getElementById("cart-badge");
|
||||||
|
|
||||||
if (!cartBadge) {
|
if (!cartBadge) {
|
||||||
@@ -101,7 +103,7 @@ function updateCartBadge() {
|
|||||||
cartBadge.classList.toggle("hidden", cart.length === 0);
|
cartBadge.classList.toggle("hidden", cart.length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCart() {
|
export function renderCart() {
|
||||||
const cartList = document.getElementById("cart-items-list");
|
const cartList = document.getElementById("cart-items-list");
|
||||||
const totalEl = document.getElementById("cart-total-right");
|
const totalEl = document.getElementById("cart-total-right");
|
||||||
const vatEl = document.getElementById("cart-vat-right");
|
const vatEl = document.getElementById("cart-vat-right");
|
||||||
@@ -119,7 +121,7 @@ function renderCart() {
|
|||||||
|
|
||||||
const groupedItems = groupCartItems();
|
const groupedItems = groupCartItems();
|
||||||
|
|
||||||
const header = `
|
const header = /*html*/`
|
||||||
<div class="cart-header-row">
|
<div class="cart-header-row">
|
||||||
<div class="col-amount">MENGE</div>
|
<div class="col-amount">MENGE</div>
|
||||||
<div class="col-img">VORSCHAU</div>
|
<div class="col-img">VORSCHAU</div>
|
||||||
@@ -133,11 +135,11 @@ function renderCart() {
|
|||||||
const rows = groupedItems
|
const rows = groupedItems
|
||||||
.map((group) => {
|
.map((group) => {
|
||||||
const imageHtml = group.item.img
|
const imageHtml = group.item.img
|
||||||
? `<img class="cart-img-small" src="${escapeHtml(group.item.img)}" alt="${escapeHtml(group.item.title)}">`
|
? /*html*/`<img class="cart-img-small" src="${escapeHtml(group.item.img)}" alt="${escapeHtml(group.item.title)}">`
|
||||||
: `<div class="cart-img-fallback">Kein Bild</div>`;
|
: /*html*/`<div class="cart-img-fallback">Kein Bild</div>`;
|
||||||
const quantityHtml = group.item.category === "movie"
|
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">
|
<div class="qty-stepper">
|
||||||
<button class="btn-qty" data-action="minus" data-key="${escapeHtml(group.key)}">-</button>
|
<button class="btn-qty" data-action="minus" data-key="${escapeHtml(group.key)}">-</button>
|
||||||
<span>${group.quantity}</span>
|
<span>${group.quantity}</span>
|
||||||
@@ -145,7 +147,7 @@ function renderCart() {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return `
|
return /*html*/`
|
||||||
<div class="cart-item-row">
|
<div class="cart-item-row">
|
||||||
<div class="col-amount">
|
<div class="col-amount">
|
||||||
${quantityHtml}
|
${quantityHtml}
|
||||||
@@ -172,22 +174,23 @@ function renderCart() {
|
|||||||
|
|
||||||
saveCart();
|
saveCart();
|
||||||
}
|
}
|
||||||
|
//@ts-ignore
|
||||||
window.removeItem = function removeItem(id) {
|
window.removeItem = function removeItem(id: any) {
|
||||||
cart = cart.filter((item) => item.id !== id);
|
var localCart = cart.filter((item: { id: any; }) => item.id !== id);
|
||||||
saveCart();
|
saveCart();
|
||||||
renderCart();
|
renderCart();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.changeQty = function changeQty(title, delta) {
|
//@ts-ignore
|
||||||
|
window.changeQty = function changeQty(title, delta): void {
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
const item = cart.find((entry) => entry.title === title);
|
const item = cart.find((entry: { title: any; }) => entry.title === title);
|
||||||
if (item) {
|
if (item) {
|
||||||
cart.push({ ...item, id: Date.now() + Math.random() });
|
cart.push({ ...item, id: Date.now() + Math.random() });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const index = cart
|
const index = cart
|
||||||
.map((entry) => entry.title)
|
.map((entry: { title: any; }) => entry.title)
|
||||||
.lastIndexOf(title);
|
.lastIndexOf(title);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
cart.splice(index, 1);
|
cart.splice(index, 1);
|
||||||
@@ -197,3 +200,4 @@ window.changeQty = function changeQty(title, delta) {
|
|||||||
saveCart();
|
saveCart();
|
||||||
renderCart();
|
renderCart();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -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`;
|
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedPaymentMethod = "";
|
let selectedPaymentMethod = "";
|
||||||
let checkoutEventsBound = false;
|
let checkoutEventsBound = false;
|
||||||
|
|
||||||
function setCheckoutStep(step) {
|
function setCheckoutStep(step: number) {
|
||||||
const step1 = document.getElementById("checkout-step-1");
|
const step1 = document.getElementById("checkout-step-1");
|
||||||
const step2 = document.getElementById("checkout-step-2");
|
const step2 = document.getElementById("checkout-step-2");
|
||||||
const step3 = document.getElementById("checkout-step-3");
|
const step3 = document.getElementById("checkout-step-3");
|
||||||
@@ -27,7 +31,7 @@ function setCheckoutStep(step) {
|
|||||||
line2?.classList.toggle("active", step >= 3);
|
line2?.classList.toggle("active", step >= 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCheckout() {
|
export function renderCheckout() {
|
||||||
const summaryList = document.getElementById("checkout-summary-list");
|
const summaryList = document.getElementById("checkout-summary-list");
|
||||||
const totalDisplay = document.getElementById("checkout-total-display");
|
const totalDisplay = document.getElementById("checkout-total-display");
|
||||||
const vatDisplay = document.getElementById("checkout-vat-display");
|
const vatDisplay = document.getElementById("checkout-vat-display");
|
||||||
@@ -93,7 +97,7 @@ function generateTicket() {
|
|||||||
const qrData = encodeURIComponent(`EAGLE-IMAX|${mainMovie.title}|${mainMovie.hall}|${matchingMovieSeats}`);
|
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`;
|
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="luxury-ticket">
|
||||||
<div class="ticket-left">
|
<div class="ticket-left">
|
||||||
<img src="${mainMovie.img}" class="ticket-poster" alt="${mainMovie.title}">
|
<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) {
|
if (typeof currentUser === "undefined" || !currentUser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -131,6 +135,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
|
|||||||
paymentMethod: selectedPaymentMethod || "-"
|
paymentMethod: selectedPaymentMethod || "-"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
const userIndex = users.findIndex((entry) => entry.email === currentUser.email);
|
const userIndex = users.findIndex((entry) => entry.email === currentUser.email);
|
||||||
if (userIndex === -1) {
|
if (userIndex === -1) {
|
||||||
return;
|
return;
|
||||||
@@ -144,7 +149,7 @@ function saveOrderForCurrentUser(orderItems, orderTotal) {
|
|||||||
localStorage.setItem("eagleUsers", JSON.stringify(users));
|
localStorage.setItem("eagleUsers", JSON.stringify(users));
|
||||||
}
|
}
|
||||||
|
|
||||||
function reserveSeatsAfterPayment(orderItems) {
|
function reserveSeatsAfterPayment(orderItems: any[]) {
|
||||||
const movieItems = orderItems.filter((item) => item.category === "movie");
|
const movieItems = orderItems.filter((item) => item.category === "movie");
|
||||||
|
|
||||||
movieItems.forEach((item) => {
|
movieItems.forEach((item) => {
|
||||||
@@ -166,7 +171,7 @@ function completeCheckout() {
|
|||||||
saveOrderForCurrentUser(orderItems, orderTotal);
|
saveOrderForCurrentUser(orderItems, orderTotal);
|
||||||
reserveSeatsAfterPayment(orderItems);
|
reserveSeatsAfterPayment(orderItems);
|
||||||
|
|
||||||
cart = [];
|
emptyCart?.()
|
||||||
saveCart?.();
|
saveCart?.();
|
||||||
renderCart?.();
|
renderCart?.();
|
||||||
}
|
}
|
||||||
@@ -180,7 +185,7 @@ function bindCheckoutEvents() {
|
|||||||
|
|
||||||
const nextButton = document.getElementById("btn-next-step-2");
|
const nextButton = document.getElementById("btn-next-step-2");
|
||||||
const backButton = document.getElementById("btn-back-to-step1");
|
const backButton = document.getElementById("btn-back-to-step1");
|
||||||
const payNowButton = document.getElementById("btn-pay-now");
|
const payNowButton = document.getElementById("btn-pay-now") as HTMLButtonElement;
|
||||||
|
|
||||||
document.querySelectorAll(".payment-method").forEach((method) => {
|
document.querySelectorAll(".payment-method").forEach((method) => {
|
||||||
method.addEventListener("click", () => {
|
method.addEventListener("click", () => {
|
||||||
@@ -189,6 +194,7 @@ function bindCheckoutEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
method.classList.add("selected");
|
method.classList.add("selected");
|
||||||
|
//@ts-ignore
|
||||||
selectedPaymentMethod = method.dataset.method || "";
|
selectedPaymentMethod = method.dataset.method || "";
|
||||||
nextButton?.classList.remove("hidden");
|
nextButton?.classList.remove("hidden");
|
||||||
});
|
});
|
||||||
58
src/components/AboutView.astro
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<section id="about-view" class="hidden info-view">
|
||||||
|
<div class="container info-view-shell">
|
||||||
|
<div class="about-hero-block">
|
||||||
|
<div class="about-hero-content">
|
||||||
|
<h1>Über uns</h1>
|
||||||
|
<p class="about-intro">
|
||||||
|
EAGLE's IMAX vereint modernes Design, starke Technik und echtes Kinofeeling.
|
||||||
|
Unser Anspruch: Jeder Besuch soll wie ein kleines Event wirken. Von entspannten Abenden
|
||||||
|
bis zu großen Blockbuster-Premieren liefern wir Bild, Sound und Atmosphäre auf Top-Niveau.
|
||||||
|
</p>
|
||||||
|
<div class="about-pill-row">
|
||||||
|
<span>4 Säle</span>
|
||||||
|
<span>IMAX Experience</span>
|
||||||
|
<span>D-BOX Motion Seats</span>
|
||||||
|
<span>Premium Snacks</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="about-hero-media" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-stats-grid">
|
||||||
|
<article>
|
||||||
|
<h3>600+</h3>
|
||||||
|
<p>Sitzplätze insgesamt</p>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<h3>4K / Laser</h3>
|
||||||
|
<p>Hochauflösende Projektion</p>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<h3>Dolby Atmos</h3>
|
||||||
|
<p>Raumklang in ausgewählten Sälen</p>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
<h3>D-BOX</h3>
|
||||||
|
<p>Bewegung synchron zum Film</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about-cards">
|
||||||
|
<article class="about-card about-card-halls">
|
||||||
|
<h3>Säle</h3>
|
||||||
|
<p>Vom klassischen Kinoraum bis zum IMAX-Erlebnis: Jeder Saal ist individuell abgestimmt auf Genre, Publikum und Stimmung.</p>
|
||||||
|
<button type="button" class="story-more-btn" data-home-view-open="halls-view">Mehr erfahren</button>
|
||||||
|
</article>
|
||||||
|
<article class="about-card about-card-dbox">
|
||||||
|
<h3>D-BOX Plätze</h3>
|
||||||
|
<p>Synchronisierte Sitzbewegungen machen Action und Effekte physisch spürbar und verstärken die Immersion im Film.</p>
|
||||||
|
<button type="button" class="story-more-btn" data-home-view-open="dbox-view">Mehr erfahren</button>
|
||||||
|
</article>
|
||||||
|
<article class="about-card about-card-tech">
|
||||||
|
<h3>Technik</h3>
|
||||||
|
<p>Leinwandgrößen, Projektoren, Soundsysteme und Kapazitäten in einem separaten Technikfenster zusammengefasst.</p>
|
||||||
|
<button id="btn-open-tech-modal" type="button" class="story-more-btn" data-about-modal-open="about-tech-modal">Mehr erfahren</button>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
45
src/components/AccountView.astro
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<div id="account-view" class="hidden">
|
||||||
|
<div class="account-login-box">
|
||||||
|
<h2>Mein Konto</h2>
|
||||||
|
|
||||||
|
<input type="email" id="login-email" placeholder="E-Mail">
|
||||||
|
<input type="password" id="login-password" placeholder="Passwort">
|
||||||
|
<button id="btn-forgot-password" type="button">Passwort vergessen?</button>
|
||||||
|
|
||||||
|
<div id="login-error" class="hidden">
|
||||||
|
Falsche E-Mail oder Passwort
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="btn-login-account" type="button">Anmelden</button>
|
||||||
|
<p id="account-register-hint">Noch kein Konto?</p>
|
||||||
|
<button id="btn-open-register" type="button">Registrieren</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="register-modal" class="modal hidden">
|
||||||
|
<div class="modal-content account-auth-modal">
|
||||||
|
<button id="btn-close-register" class="modal-close-btn" type="button" aria-label="Registrierung schliessen">×</button>
|
||||||
|
<h2>Registrieren</h2>
|
||||||
|
<p class="auth-modal-subtitle">Erstelle dein Konto für schnellere Buchungen.</p>
|
||||||
|
<input type="text" id="reg-firstname" placeholder="Vorname">
|
||||||
|
<input type="text" id="reg-lastname" placeholder="Nachname">
|
||||||
|
<input type="email" id="reg-email" placeholder="E-Mail">
|
||||||
|
<input type="password" id="reg-password" placeholder="Passwort">
|
||||||
|
<button id="btn-register-save" class="auth-submit-btn" type="button">Konto erstellen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="forgot-modal" class="modal hidden">
|
||||||
|
<div class="modal-content account-auth-modal">
|
||||||
|
<button id="btn-close-forgot" class="modal-close-btn" type="button" aria-label="Passwort-Dialog schliessen">×</button>
|
||||||
|
<h2>Passwort vergessen</h2>
|
||||||
|
<p class="auth-modal-subtitle">Gib deine E-Mail ein und wir senden dir einen Reset-Code.</p>
|
||||||
|
|
||||||
|
<input type="email" id="forgot-email" placeholder="E-Mail">
|
||||||
|
<button id="btn-send-reset" class="auth-submit-btn" type="button">Code senden</button>
|
||||||
|
|
||||||
|
<div id="reset-message" class="hidden">
|
||||||
|
Ein Code wurde simuliert versendet.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
43
src/components/BookingModal.astro
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<div id="booking-modal" class="modal hidden">
|
||||||
|
<div class="modal-content modal-large">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div class="header-text-container">
|
||||||
|
<h2 id="modal-movie-title">Film Titel</h2>
|
||||||
|
<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.jpg" alt="D-Box" class="tech-badge">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="close-btn">×</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="screen-container">
|
||||||
|
<div class="screen"></div>
|
||||||
|
<p class="screen-text">LEINWAND</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="booking-layout">
|
||||||
|
<div class="seat-map-container">
|
||||||
|
<div id="seat-grid" class="seat-grid-custom"></div>
|
||||||
|
</div>
|
||||||
|
<div id="booking-summary" class="summary-panel hidden">
|
||||||
|
<h3>Deine Auswahl</h3>
|
||||||
|
<div id="summary-items"></div>
|
||||||
|
<div class="summary-total">
|
||||||
|
<div class="divider"></div>
|
||||||
|
<div class="total-row">
|
||||||
|
<span>Gesamtbetrag:</span>
|
||||||
|
<span id="total-price">0,00€</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="legend" id="dynamic-legend"></div>
|
||||||
|
|
||||||
|
<button id="btn-confirm-seats" class="btn-primary" style="margin-top:20px">Plätze bestätigen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
37
src/components/CartView.astro
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<section id="cart-view" class="cart-section hidden">
|
||||||
|
<div class="container" style="padding: 120px 8% 50px 8%;">
|
||||||
|
<h1 class="list-title">Dein Warenkorb</h1>
|
||||||
|
|
||||||
|
<div class="cart-container">
|
||||||
|
<div class="cart-left">
|
||||||
|
<div id="cart-items-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cart-right">
|
||||||
|
<div class="summary-box-black">
|
||||||
|
<h2>Zusammenfassung</h2>
|
||||||
|
<div class="summary-row-large">
|
||||||
|
<span>Gesamtsumme:</span>
|
||||||
|
<span id="cart-total-right">0,00€</span>
|
||||||
|
</div>
|
||||||
|
<p id="cart-vat-right" style="text-align:right; color:#86868b; font-size:0.8rem; margin-top:5px;">
|
||||||
|
inkl. 19% MwSt: 0,00€
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button id="btn-checkout-final" class="blue-button">Jetzt kostenpflichtig bestellen</button>
|
||||||
|
|
||||||
|
<div class="payment-methods" style="margin-top: 25px; text-align: center;">
|
||||||
|
<p style="font-size: 0.75rem; color: #86868b; margin-bottom: 12px;">Sicher bezahlen mit</p>
|
||||||
|
<div style="display: flex; justify-content: center; gap: 15px; opacity: 0.6;">
|
||||||
|
<img src="/img/paypal.png" alt="PayPal" style="height: 20px;">
|
||||||
|
<img src="/img/visa.png" alt="Visa" style="height: 20px;">
|
||||||
|
<img src="/img/mastercard.png" alt="Mastercard" style="height: 20px;">
|
||||||
|
<img src="/img/applepay.png" alt="Apple Pay" style="height: 20px;">
|
||||||
|
<img src="/img/googlepay.png" alt="Google Pay" style="height: 20px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
52
src/components/CheckoutView.astro
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<section id="checkout-view" class="hidden" style="padding: 40px 20px;">
|
||||||
|
<div class="checkout-container">
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div class="step active" id="step-1-indicator">1</div>
|
||||||
|
<div class="line" id="line-1"></div>
|
||||||
|
<div class="step" id="step-2-indicator">2</div>
|
||||||
|
<div class="line" id="line-2"></div>
|
||||||
|
<div class="step" id="step-3-indicator">3</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="checkout-step-1" class="checkout-step">
|
||||||
|
<h2 style="text-align: center; margin-bottom: 20px;">Zahlungsmethode wählen</h2>
|
||||||
|
<div class="payment-grid">
|
||||||
|
<div class="payment-method" data-method="Apple Pay">
|
||||||
|
<img src="/img/applepay.png" alt="Apple Pay" style="height: 20px;">
|
||||||
|
<span>Apple Pay</span>
|
||||||
|
</div>
|
||||||
|
<div class="payment-method" data-method="PayPal">
|
||||||
|
<img src="/img/paypal.png" alt="PayPal" style="height: 20px;">
|
||||||
|
<span>PayPal</span>
|
||||||
|
</div>
|
||||||
|
<div class="payment-method" data-method="Google Pay">
|
||||||
|
<img src="/img/googlepay.png" alt="Google Pay" style="height: 20px;">
|
||||||
|
<span>Google Pay</span>
|
||||||
|
</div>
|
||||||
|
<div class="payment-method" data-method="Visa">
|
||||||
|
<img src="/img/visa.png" alt="Visa" style="height: 20px;">
|
||||||
|
<span>Visa</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="btn-next-step-2" class="hidden" style="margin-top: 25px; width: 100%; padding: 15px; background: #0071e3; color: white; border: none; border-radius: 8px; font-weight: bold; cursor: pointer;">Weiter zur Übersicht</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="checkout-step-2" class="checkout-step hidden">
|
||||||
|
<div style="position: relative;">
|
||||||
|
<button id="btn-back-to-step1" style="position: absolute; top: -15px; left: 0; background: none; border: none; color: white; font-size: 1.5rem; cursor: pointer; opacity: 0.6; transition: 0.3s;">←</button>
|
||||||
|
<h2 style="text-align: center; margin-bottom: 25px;">Persönliche Daten</h2>
|
||||||
|
</div>
|
||||||
|
<h2 style="text-align: center; margin-bottom: 20px;">Bestellübersicht</h2>
|
||||||
|
<div id="checkout-summary-list" style="background: #222; padding: 15px; border-radius: 8px;"></div>
|
||||||
|
<h3 id="checkout-total-display" style="text-align: right; margin-top: 15px;"></h3>
|
||||||
|
<div id="checkout-vat-display" style="text-align: right; color: #86868b; font-size: 0.8rem;"></div>
|
||||||
|
<button id="btn-pay-now" style="margin-top: 25px; width: 100%; padding: 15px; background: #4caf50; color: white; border: none; border-radius: 8px; font-weight: bold; cursor: pointer;">Jetzt Bezahlen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="checkout-step-3" class="checkout-step hidden">
|
||||||
|
<h2 style="color: #4caf50; text-align: center;">Kauf erfolgreich!</h2>
|
||||||
|
<div id="ticket-container" style="margin-top: 30px;"></div>
|
||||||
|
<button id="btn-back-home" style="margin-top: 30px; width: 100%; padding: 15px; background: #333; color: white; border: none; border-radius: 8px; cursor: pointer;">Zurück zur Startseite</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
33
src/components/CollectorsView.astro
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<section id="collectors-view" class="hidden info-view">
|
||||||
|
<div class="container info-view-shell">
|
||||||
|
<button class="subpage-back-btn" data-go-home type="button">← Zur Startseite</button>
|
||||||
|
<h1>Collectors Popcorn Specials</h1>
|
||||||
|
<p class="info-view-subtitle">Filmbezogene Specials in einer links/rechts versetzten Darstellung.</p>
|
||||||
|
<div class="collector-zigzag">
|
||||||
|
<article class="collector-entry left">
|
||||||
|
<div class="collector-film-logo">Filmlogo</div>
|
||||||
|
<div class="collector-entry-content">
|
||||||
|
<h4>Special Becher 01</h4>
|
||||||
|
<p>Kurzbeschreibung des Artikels und Bezug zum Film.</p>
|
||||||
|
<input type="text" placeholder="Bildpfad (z.B. img/becher1.jpg)">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="collector-entry right">
|
||||||
|
<div class="collector-film-logo">Filmlogo</div>
|
||||||
|
<div class="collector-entry-content">
|
||||||
|
<h4>Special Eimer 02</h4>
|
||||||
|
<p>Weitere Edition mit eigenem Motiv und Text.</p>
|
||||||
|
<input type="text" placeholder="Bildpfad (z.B. img/eimer2.jpg)">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article class="collector-entry left">
|
||||||
|
<div class="collector-film-logo">Filmlogo</div>
|
||||||
|
<div class="collector-entry-content">
|
||||||
|
<h4>Special Box 03</h4>
|
||||||
|
<p>Noch ein Eintrag fuer limitierte Collectors.</p>
|
||||||
|
<input type="text" placeholder="Bildpfad (z.B. img/collector3.jpg)">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
31
src/components/DboxView.astro
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<section id="dbox-view" class="hidden info-view">
|
||||||
|
<div class="container info-view-shell">
|
||||||
|
<button class="subpage-back-btn" data-go-home type="button">← Zur Startseite</button>
|
||||||
|
<h1>D-BOX & Technik</h1>
|
||||||
|
<p class="info-view-subtitle">Inspiriert von Technikseiten moderner Kinos, aber in deinem Design.</p>
|
||||||
|
<section class="dbox-modal-section">
|
||||||
|
<h3>Wie D-BOX funktioniert</h3>
|
||||||
|
<p>D-BOX Sitze reagieren synchron zum Film. Die Bewegungsintensität ist individuell steuerbar und kann bei Bedarf reduziert oder deaktiviert werden.</p>
|
||||||
|
</section>
|
||||||
|
<section class="dbox-modal-section">
|
||||||
|
<h3>Technik im Saal</h3>
|
||||||
|
<p>Ergänze hier deine Informationen zu Projektion, Soundsystem, Sitzkomfort und den Sälen mit D-BOX-Unterstützung.</p>
|
||||||
|
<div class="dbox-image-grid">
|
||||||
|
<div class="dbox-image-slot">Bildplatz Technik 1</div>
|
||||||
|
<div class="dbox-image-slot">Bildplatz Technik 2</div>
|
||||||
|
<div class="dbox-image-slot">Bildplatz Technik 3</div>
|
||||||
|
<div class="dbox-image-slot">Bildplatz Technik 4</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="dbox-now-highlight modal-highlight">
|
||||||
|
<h4>Filme in D-BOX</h4>
|
||||||
|
<div class="dbox-mini-cards">
|
||||||
|
<div>Zoomania 2</div>
|
||||||
|
<div>Der Austronaut</div>
|
||||||
|
<div>Spider Man</div>
|
||||||
|
<div>Scream VII</div>
|
||||||
|
<div>Gangster Gang 2</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
29
src/components/HallsView.astro
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<section id="halls-view" class="hidden info-view">
|
||||||
|
<div class="container info-view-shell">
|
||||||
|
<button class="subpage-back-btn" data-go-home type="button">← Zur Startseite</button>
|
||||||
|
<h1>Unsere Säle</h1>
|
||||||
|
<p class="info-view-subtitle">Pflegbare Infoseite zu allen Sälen inkl. Bild- und Textbereichen.</p>
|
||||||
|
<div class="hall-modal-grid full-page-grid">
|
||||||
|
<article class="hall-modal-item">
|
||||||
|
<h4>Kino 1</h4>
|
||||||
|
<textarea placeholder="Infos zu Kino 1 eintragen..."></textarea>
|
||||||
|
<input type="text" placeholder="Bildpfad fuer den Hintergrund (z.B. img/saal1.jpg)">
|
||||||
|
</article>
|
||||||
|
<article class="hall-modal-item">
|
||||||
|
<h4>Kino 2</h4>
|
||||||
|
<textarea placeholder="Infos zu Kino 2 eintragen..."></textarea>
|
||||||
|
<input type="text" placeholder="Bildpfad fuer den Hintergrund (z.B. img/saal2.jpg)">
|
||||||
|
</article>
|
||||||
|
<article class="hall-modal-item">
|
||||||
|
<h4>Deluxe 1</h4>
|
||||||
|
<textarea placeholder="Infos zu Deluxe 1 eintragen..."></textarea>
|
||||||
|
<input type="text" placeholder="Bildpfad fuer den Hintergrund (z.B. img/deluxe.jpg)">
|
||||||
|
</article>
|
||||||
|
<article class="hall-modal-item">
|
||||||
|
<h4>IMAX</h4>
|
||||||
|
<textarea placeholder="Infos zu IMAX eintragen..."></textarea>
|
||||||
|
<input type="text" placeholder="Bildpfad fuer den Hintergrund (z.B. img/imax.jpg)">
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
10
src/components/Hero.astro
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<header class="hero">
|
||||||
|
<div id="hero-slider" class="hero-slider"></div>
|
||||||
|
<div class="hero-overlay">
|
||||||
|
<span class="badge">Neu im Kino</span>
|
||||||
|
<h1 id="hero-title">Jetzt im Kino</h1>
|
||||||
|
<p id="hero-text">Erlebe die neuesten Highlights auf der großen Leinwand.</p>
|
||||||
|
<button class="btn-primary" id="hero-booking-btn">Jetzt Tickets kaufen</button>
|
||||||
|
<div id="hero-dots" class="hero-dots"></div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
58
src/components/HomeSection.astro
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<section id="movies-grid-section" class="movie-section cinema-home">
|
||||||
|
<div class="section-header reveal-on-scroll">
|
||||||
|
<h2>Dein Kinoabend beginnt hier</h2>
|
||||||
|
<p>Premieren, Lieblingsfilme und flexible Spielzeiten in allen Sälen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="home-poster-band reveal-on-scroll">
|
||||||
|
<div class="home-band-header">
|
||||||
|
<h3>Jetzt läuft</h3>
|
||||||
|
<span>Heute im Fokus</span>
|
||||||
|
</div>
|
||||||
|
<div id="now-running-row" class="now-running-row"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="home-inline-showcase reveal-on-scroll">
|
||||||
|
<article class="inline-section inline-halls">
|
||||||
|
<div class="inline-media"></div>
|
||||||
|
<div class="inline-content">
|
||||||
|
<h3>Unsere Säle im Überblick</h3>
|
||||||
|
<p>Jeder Saal hat ein eigenes Profil: von klassischem Kinofeeling bis High-End-Erlebnis mit Premiumtechnik und mehr Komfort.</p>
|
||||||
|
<div class="inline-tags">
|
||||||
|
<span>IMAX</span>
|
||||||
|
<span>Deluxe 1</span>
|
||||||
|
<span>Kino 1</span>
|
||||||
|
<span>Kino 2</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="story-more-btn" data-home-view-open="halls-view">Mehr erfahren</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="inline-section inline-dbox">
|
||||||
|
<div class="inline-media"></div>
|
||||||
|
<div class="inline-content">
|
||||||
|
<h3>D-BOX & Technik</h3>
|
||||||
|
<p>Spüre Bewegungen synchron zum Film und kombiniere das Erlebnis mit modernem Raumklang und Premium-Bestuhlung.</p>
|
||||||
|
<div class="dbox-now-highlight">
|
||||||
|
<h4>Aktuell in D-BOX</h4>
|
||||||
|
<div class="dbox-mini-cards">
|
||||||
|
<div>Zoomania 2</div>
|
||||||
|
<div>Shelter</div>
|
||||||
|
<div>Hoppers</div>
|
||||||
|
<div>Spider Man</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="story-more-btn" data-home-view-open="dbox-view">Mehr erfahren</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="inline-section inline-collectors">
|
||||||
|
<div class="inline-media"></div>
|
||||||
|
<div class="inline-content">
|
||||||
|
<h3>Collectors Popcorn Specials</h3>
|
||||||
|
<p>Präsentiere Sonderbecher und Eimer filmbezogen mit Bild, Logo und kurzem Text in einer lebendigen Timeline.</p>
|
||||||
|
<button type="button" class="story-more-btn" data-home-view-open="collectors-view">Mehr erfahren</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
19
src/components/MovieListView.astro
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
const api_token = import.meta.env.TMDB_API_TOKEN;
|
||||||
|
|
||||||
|
var api = await fetch("https://api.themoviedb.org/3/movie/11")
|
||||||
|
var request = await api.json
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="movie-list-view" class="hidden">
|
||||||
|
<div class="container movie-list-shell">
|
||||||
|
<h1 class="list-title">Aktuelle Filme & Spielzeiten</h1>
|
||||||
|
<p class="list-subtitle">Alle Filme mit 7 Tagen Spielplan. Erste Vorstellung täglich ab 13:00 Uhr.</p>
|
||||||
|
<div>{ api_token }</div>
|
||||||
|
|
||||||
|
<!-- Movie List. -->
|
||||||
|
<div id="movie-program-list" class="movie-program-list"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
22
src/components/Navbar.astro
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<nav class="navbar">
|
||||||
|
<div class="logo" id="logo-home" style="cursor:pointer">EAGLE's IMAX</div>
|
||||||
|
<ul class="nav-links">
|
||||||
|
<li><a href="#" id="link-filme">Aktuelle Filme</a></li>
|
||||||
|
<li><a href="#" id="link-snacks">Snacks & Getränke</a></li>
|
||||||
|
<li><a href="#" id="link-about">Über uns</a></li>
|
||||||
|
<li><a href="#" id="link-account">Mein Konto</a></li>
|
||||||
|
<li>
|
||||||
|
<a href="#" id="link-cart" style="position: relative; display: flex; align-items: center; gap: 5px;">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"></circle><circle cx="20" cy="21" r="1"></circle><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path></svg>
|
||||||
|
Warenkorb
|
||||||
|
<span id="cart-badge" class="badge">0</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="theme-toggle-item">
|
||||||
|
<button id="theme-toggle" class="theme-toggle-btn" type="button" aria-label="Theme wechseln">
|
||||||
|
<span class="theme-icon theme-icon-sun">☀</span>
|
||||||
|
<span class="theme-icon theme-icon-moon">☾</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
10
src/components/SnackPrompt.astro
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<div id="snack-prompt-overlay" class="snack-overlay hidden">
|
||||||
|
<div class="snack-prompt-box">
|
||||||
|
<h2>Popcorn gefällig? 🍿</h2>
|
||||||
|
<p>Möchtest du noch Snacks oder Getränke hinzufügen?</p>
|
||||||
|
<div class="prompt-buttons">
|
||||||
|
<button id="btn-yes-snacks" class="btn-primary">Ja, Snacks wählen</button>
|
||||||
|
<button id="btn-no-cart" class="btn-secondary">Weiter zum Warenkorb</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
449
src/components/SnacksView.astro
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
<section id="snacks-view" class="hidden">
|
||||||
|
<div class="container" style="padding: 120px 8% 50px 8%;">
|
||||||
|
<h1 class="list-title">Snacks & Getränke</h1>
|
||||||
|
|
||||||
|
<div class="category-tabs">
|
||||||
|
<button class="tab-btn active" data-target="cat-getraenke">Getränke</button>
|
||||||
|
<button class="tab-btn" data-target="cat-popcorn">Popcorn</button>
|
||||||
|
<button class="tab-btn" data-target="cat-nachos">Nachos</button>
|
||||||
|
<button class="tab-btn" data-target="cat-snacks">Snacks</button>
|
||||||
|
<button class="tab-btn" data-target="cat-kombi">Kombi</button>
|
||||||
|
<button class="tab-btn" data-target="cat-eis">Eis</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-getraenke" class="snack-category active">
|
||||||
|
<div class="snack-grid">
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/cola.png" alt="Cola"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Coca Cola</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/cola-zero.png" alt="Cola Zero"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Coca Cola Zero</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/cola-light.png" alt="Cola Light"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Coca Cola Light</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/sprite.png" alt="Sprite"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Sprite</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/fanta.png" alt="Fanta"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Fanta</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/spezi.png" alt="Spezi"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Mezzo Mix</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>4,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>5,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/wasser.png" alt="Wasser"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Wasser</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,00€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,00€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>4,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/Apfelschorle.png" alt="Apfelschorle"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Apfelschorle</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,00€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,00€</span></button>
|
||||||
|
<button class="size-chip">0,75L <span>3,50€</span></button>
|
||||||
|
<button class="size-chip">1L <span>4,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/fuze-tea.png" alt="Fuze Tea"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Fuze Tea</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">0,33L <span>2,50€</span></button>
|
||||||
|
<button class="size-chip">0,50L <span>3,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-popcorn" class="snack-category hidden">
|
||||||
|
<div class="snack-grid">
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/popcorn-klein.png" alt="Popcorn klein"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Popcorn klein</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">3,50€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/popcorn-mittel.png" alt="Popcorn mittel"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Popcorn Mittel</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">4,50€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/popcorn-big.png" alt="Popcorn groß"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Popcorn Groß</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">6,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/zoomania-popcorn.jpg" alt="Popcorn limited - Zoomania 2"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Metallbecher - Zoomania 2</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">12,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/screamdoorpopcorn.jpg" alt="Popcorn limited - Scream VII"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Sammelbecher - Scream VII</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">29,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/astronautpopcorn.jpg" alt="Popcorn limited - Der Austronaut"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Sammelbecher - Der Austronaut</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">34,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/hopperspopcornmetall.jpg" alt="Popcorn limited - Hoppers"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Metallbecher - Hoppers</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">12,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/hopperspopcornwood.png" alt="Popcorn limited - Hoppers Wood"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Sammelbecher - Hoppers</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">21,00€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/marioyoshipopcorn.png" alt="Popcorn limited - Yoshi"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Limitierter Sammelbecher - Yoshi Becher</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Süß</button>
|
||||||
|
<button class="opt-btn">Salzig</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">35,90€</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-nachos" class="snack-category hidden">
|
||||||
|
<div class="snack-grid">
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/nachosnormal.png" alt="Nachos"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Nachos Klein</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Käse-Dip</button>
|
||||||
|
<button class="opt-btn">Salsa-Dip</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Klein <span>5,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/nachosnormal.png" alt="Nachos"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Nachos Normal</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Käse-Dip</button>
|
||||||
|
<button class="opt-btn">Salsa-Dip</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Mittel <span>6,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/nachos.jpg" alt="Nachos groß"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Nachos Groß</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Käse-Dip</button>
|
||||||
|
<button class="opt-btn">Sourcreme-Dip</button>
|
||||||
|
<button class="opt-btn">Salsa-Dip</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Groß <span>8,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h2 class="list-title">Dips</h2>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/käsedip.png" alt="Käse-Dip"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Käse-Dip (warm)</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Normal</button>
|
||||||
|
<button class="opt-btn">Scharf</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Schale <span>2,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/sourdip.png" alt="Sourcreme-Dip"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Sourcreme-Dip</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Normal</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Schale <span>2,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/salsadip.png" alt="Salsa-Dip"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Salsa-Dip</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Normal</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Schale <span>2,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h2 class="list-title">Nacho Kombi-Menüs</h2>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/nachokombiklein.png" alt="Nacho Kombi Klein"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Nacho Menü Klein – Nachos klein + 1 Dip + 1 0,33L Getränk</h3>
|
||||||
|
<div class="option-group">
|
||||||
|
<button class="opt-btn active">Klein</button>
|
||||||
|
</div>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Kombi <span>6,90€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-snacks" class="snack-category hidden">
|
||||||
|
<div class="snack-grid">
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/mms.png" alt="M&Ms"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>M&M's (verschiedene Sorten)</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Packung <span>4,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/haribo.png" alt="Haribo"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Haribo Goldbären (verschiedene Sorten)</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Tüte <span>3,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card">
|
||||||
|
<div class="snack-img"><img src="/img/riegel.png" alt="Riegel"></div>
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Schokoriegel (verschiedene Sorten)</h3>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Tüte <span>2,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-kombi" class="snack-category hidden">
|
||||||
|
<div class="snack-grid">
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Kleines Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">0,33L Getränk + Popcorn Klein</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>5,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<h3>Mittleres Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">0,5L Getränk + Popcorn Mittel</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>6,50€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<span class="badge">Bestseller</span>
|
||||||
|
<h3>Großes Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">1L Getränk + Popcorn Groß</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>8,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<div class="snack-img"><img src="/img/hopperskidsmenu.jpg" alt="Hoppers Kids Menu"></div>
|
||||||
|
<span class="badge">SPECIAL</span>
|
||||||
|
<h3>Limitiertes Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">0,5L Getränk im HOPPERS Becher + HOPPERS Popcorn Schale + HOPPERS Figur</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>10,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<div class="snack-img"><img src="/img/mariokidsmenu.png" alt="Mario Kids Menu"></div>
|
||||||
|
<span class="badge">SPECIAL</span>
|
||||||
|
<h3>Limitiertes Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">0,5L Getränk im MARIO GALAXY Becher + MARIO GALAXY Popcorn Schale</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>10,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="snack-card highlight">
|
||||||
|
<div class="snack-info">
|
||||||
|
<div class="snack-img"><img src="/img/zoomaniakidsmenu.jpg" alt="Zoomania Kids Menu"></div>
|
||||||
|
<span class="badge">SPECIAL</span>
|
||||||
|
<h3>Limitiertes Menü</h3>
|
||||||
|
<p style="font-size: 0.8rem; color: #86868b; margin-bottom: 10px;">0,5L Getränk im ZOOMANIA Becher + ZOOMANIA Popcorn Schale + Figur zum aussuchen</p>
|
||||||
|
<div class="size-selector">
|
||||||
|
<button class="size-chip">Menü-Preis <span>10,00€</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cat-eis" class="snack-category hidden">
|
||||||
|
<div class="coming-soon-banner">
|
||||||
|
<h2>Eiscreme & Shakes</h2>
|
||||||
|
<p>Coming Soon...</p>
|
||||||
|
<span>Wir bereiten etwas ganz Besonderes für dich vor! Coming this summer!</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||