Initial commit

This commit is contained in:
dev committed 2025-11-15 18:40:39 +01:00
commit b82adc93c7
12 files changed
+193

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
+11
View File
@@ -0,0 +1,11 @@
{
"name": "whopays",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.19.2"
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

+48
View File
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="manifest" href="manifest.json" />
<meta name="theme-color" content="#ec4899" />
<title>Wer zahlt?</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body
class="flex flex-col items-center justify-center min-h-screen bg-gradient-to-br from-yellow-50 to-pink-50 text-gray-800"
>
<div class="text-center space-y-8">
<h1 class="text-4xl font-extrabold drop-shadow-sm tracking-tight">
Who pays?
</h1>
<div
id="statusBox"
class="flex flex-col items-center space-y-4 transition-all duration-500"
>
<img
id="personImg"
src="img/a.png"
alt="Person"
class="w-48 h-48 rounded-full shadow-md border-4 border-white object-cover transition-transform duration-500"
/>
<div
id="personName"
class="text-3xl font-semibold text-blue-800 transition-all duration-500"
>
Load ...
</div>
</div>
<button
id="toggleButton"
class="bg-green-500 hover:bg-green-600 text-white text-xl px-6 py-3 rounded-2xl shadow-md transition-all transform hover:scale-105 active:scale-95"
>
Switch 🔁
</button>
</div>
<script src="script.js"></script>
</body>
</html>
+20
View File
@@ -0,0 +1,20 @@
{
"name": "Who pays?",
"short_name": "Whopays",
"start_url": "/",
"display": "standalone",
"background_color": "#fff7f9",
"theme_color": "#ec4899",
"icons": [
{
"src": "img/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "img/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
+48
View File
@@ -0,0 +1,48 @@
const img = document.getElementById("personImg");
const nameEl = document.getElementById("personName");
const button = document.getElementById("toggleButton");
async function loadStatus() {
const res = await fetch("/status");
const data = await res.json();
showStatus(data.current, false);
}
function showStatus(person, animate = true) {
const file = person === "A" ? "img/a.png" : "img/b.png";
const label = person === "A" ? "Honey" : "Kirby";
// Hintergrundbilder für A und B
const bgA = "url('img/bgA.jpg')";
const bgB = "url('img/bgB.jpg')";
// Hintergrundanimation (optional)
document.body.style.transition = "background 0.4s ease";
// Hintergrund setzen
document.body.style.backgroundImage = person === "A" ? bgA : bgB;
document.body.style.backgroundSize = "cover";
document.body.style.backgroundPosition = "center";
if (animate) {
img.classList.add("rotate");
}
setTimeout(() => {
img.src = file;
nameEl.textContent = label;
}, 150);
setTimeout(() => {
img.classList.remove("rotate");
}, 600);
}
button.addEventListener("click", async () => {
const res = await fetch("/toggle", { method: "POST" });
const data = await res.json();
showStatus(data.current);
});
loadStatus();
+18
View File
@@ -0,0 +1,18 @@
@keyframes pop {
0% { transform: scale(0.9); opacity: 0.5; }
100% { transform: scale(1); opacity: 1; }
}
.status-box {
animation: pop 0.3s ease-out;
}
@keyframes rotate {
0% { transform: rotateY(0deg); }
50% { transform: rotateY(90deg); opacity: 0.6; }
100% { transform: rotateY(180deg); opacity: 1; }
}
.rotate {
animation: rotate 0.6s ease-in-out;
transform-style: preserve-3d;
}
+40
View File
@@ -0,0 +1,40 @@
import express from "express";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, "public")));
const stateFile = path.join(__dirname, "state.json");
// Initialzustand laden oder erzeugen
function getState() {
if (!fs.existsSync(stateFile)) {
fs.writeFileSync(stateFile, JSON.stringify({ current: "A" }, null, 2));
}
return JSON.parse(fs.readFileSync(stateFile));
}
function setState(newState) {
fs.writeFileSync(stateFile, JSON.stringify(newState, null, 2));
}
// GET: aktuellen Zustand
app.get("/status", (req, res) => {
res.json(getState());
});
// POST: wechseln
app.post("/toggle", (req, res) => {
const state = getState();
state.current = state.current === "A" ? "B" : "A";
setState(state);
res.json(state);
});
// Start
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Zahl-Schalter läuft auf Port ${PORT}`));
+1
View File
@@ -0,0 +1 @@
{"current": "A"}