diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 4f56f65..9581f76 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -4,9 +4,13 @@ import React, { useEffect, useState } from "react";
import { api } from "./api/client";
import { cycleTag } from "./utils/cycleTag";
import { getChipLS, setChipLS, clearChipLS } from "./utils/chipStorage";
+import { getWinner, setWinner as saveWinnerLS } from "./utils/winnerStorage";
+
import { useHpGlobalStyles } from "./styles/hooks/useHpGlobalStyles";
import { styles } from "./styles/styles";
+import { applyTheme, loadThemeKey, saveThemeKey, DEFAULT_THEME_KEY } from "./styles/themes";
+
import AdminPanel from "./components/AdminPanel";
import LoginPage from "./components/LoginPage";
import TopBar from "./components/TopBar";
@@ -15,6 +19,8 @@ import ChipModal from "./components/ChipModal";
import HelpModal from "./components/HelpModal";
import GamePickerCard from "./components/GamePickerCard";
import SheetSection from "./components/SheetSection";
+import DesignModal from "./components/DesignModal";
+import WinnerCard from "./components/WinnerCard";
export default function App() {
useHpGlobalStyles();
@@ -31,6 +37,9 @@ export default function App() {
const [sheet, setSheet] = useState(null);
const [pulseId, setPulseId] = useState(null);
+ // Winner (per game)
+ const [winnerName, setWinnerName] = useState("");
+
// Modals
const [helpOpen, setHelpOpen] = useState(false);
@@ -45,11 +54,20 @@ export default function App() {
const [pwMsg, setPwMsg] = useState("");
const [pwSaving, setPwSaving] = useState(false);
+ // Theme
+ const [designOpen, setDesignOpen] = useState(false);
+ const [themeKey, setThemeKey] = useState(DEFAULT_THEME_KEY);
+
// ===== Data loaders =====
const load = async () => {
const m = await api("/auth/me");
setMe(m);
+ // Theme pro User laden & anwenden
+ const tk = loadThemeKey(m?.email);
+ setThemeKey(tk);
+ applyTheme(tk);
+
const gs = await api("/games");
setGames(gs);
@@ -62,8 +80,6 @@ export default function App() {
setSheet(sh);
};
- // ===== Effects =====
-
// Dropdown outside click
useEffect(() => {
const onDown = (e) => {
@@ -92,9 +108,9 @@ export default function App() {
if (!gameId) return;
try {
await reloadSheet();
- } catch {
- // ignore
- }
+ } catch {}
+ // Winner pro Game aus LS laden
+ setWinnerName(getWinner(gameId));
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [gameId]);
@@ -114,6 +130,7 @@ export default function App() {
setGames([]);
setGameId(null);
setSheet(null);
+ setWinnerName("");
};
// ===== Password change =====
@@ -153,6 +170,18 @@ export default function App() {
}
};
+ // ===== Theme actions =====
+ const openDesignModal = () => {
+ setDesignOpen(true);
+ setUserMenuOpen(false);
+ };
+
+ const selectTheme = (key) => {
+ setThemeKey(key);
+ applyTheme(key);
+ saveThemeKey(me?.email, key);
+ };
+
// ===== Game actions =====
const newGame = async () => {
const g = await api("/games", {
@@ -164,6 +193,12 @@ export default function App() {
setGameId(g.id);
};
+ // ===== Winner actions =====
+ const saveWinner = () => {
+ if (!gameId) return;
+ saveWinnerLS(gameId, winnerName.trim());
+ };
+
// ===== Sheet actions =====
const cycleStatus = async (entry) => {
let next = 0;
@@ -185,14 +220,12 @@ export default function App() {
const toggleTag = async (entry) => {
const next = cycleTag(entry.note_tag);
- // going to "s" -> open chip modal, don't write backend yet
if (next === "s") {
setChipEntry(entry);
setChipOpen(true);
return;
}
- // s -> — : clear local chip
if (next === null) clearChipLS(gameId, entry.entry_id);
await api(`/games/${gameId}/sheet/${entry.entry_id}`, {
@@ -210,11 +243,9 @@ export default function App() {
setChipOpen(false);
setChipEntry(null);
- // frontend-only save
setChipLS(gameId, entry.entry_id, chip);
try {
- // backend only gets "s"
await api(`/games/${gameId}/sheet/${entry.entry_id}`, {
method: "PATCH",
body: JSON.stringify({ note_tag: "s" }),
@@ -253,7 +284,7 @@ export default function App() {
const chip = getChipLS(gameId, entry.entry_id);
return chip ? `s.${chip}` : "s";
}
- return t; // i oder m
+ return t;
};
// ===== Login page =====
@@ -291,6 +322,7 @@ export default function App() {
userMenuOpen={userMenuOpen}
setUserMenuOpen={setUserMenuOpen}
openPwModal={openPwModal}
+ openDesignModal={openDesignModal}
doLogout={doLogout}
newGame={newGame}
/>
@@ -320,6 +352,9 @@ export default function App() {
))}
+ {/* Sieger ganz unten */}
+