Add theme customization and winner management features

Introduced a theme selection feature, allowing users to customize the application's appearance, with themes stored per user. Added functionality to manage and store the game's winner locally. These changes improve user experience and personalization.
This commit is contained in:
2026-02-06 09:15:51 +01:00
parent 1db91c6c88
commit a08b74ff7a
8 changed files with 297 additions and 58 deletions

View File

@@ -0,0 +1,57 @@
import React from "react";
import { styles } from "../styles/styles";
import { stylesTokens } from "../styles/theme";
import { THEMES } from "../styles/themes";
export default function DesignModal({ open, onClose, themeKey, onSelect }) {
if (!open) return null;
const themeEntries = Object.entries(THEMES);
return (
<div style={styles.modalOverlay} onMouseDown={onClose}>
<div style={styles.modalCard} onMouseDown={(e) => e.stopPropagation()}>
<div style={styles.modalHeader}>
<div style={{ fontWeight: 1000, color: stylesTokens.textGold }}>Design ändern</div>
<button onClick={onClose} style={styles.modalCloseBtn} aria-label="Schließen">
</button>
</div>
<div style={{ marginTop: 12, color: stylesTokens.textMain, opacity: 0.92 }}>
Wähle dein Theme:
</div>
<div style={{ marginTop: 12, display: "grid", gap: 10 }}>
{themeEntries.map(([key, t]) => {
const active = key === themeKey;
return (
<button
key={key}
onClick={() => onSelect(key)}
style={{
...styles.secondaryBtn,
textAlign: "left",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
border: active
? `1px solid rgba(233,216,166,0.40)`
: `1px solid rgba(233,216,166,0.18)`,
}}
>
<span style={{ fontWeight: 1000 }}>{t.label}</span>
<span style={{ opacity: 0.75 }}>{active ? "✓ aktiv" : ""}</span>
</button>
);
})}
</div>
<div style={{ marginTop: 12, fontSize: 12, opacity: 0.75, color: stylesTokens.textDim }}>
Hinweis: Das Design wird pro User gespeichert (Email).
</div>
</div>
</div>
);
}

View File

@@ -7,44 +7,20 @@ export default function TopBar({
userMenuOpen,
setUserMenuOpen,
openPwModal,
openDesignModal,
doLogout,
newGame,
}) {
return (
<div style={styles.topBar}>
{/* LINKS: nur Rolle */}
<div>
<div style={{ fontWeight: 900, color: stylesTokens.textGold }}>
Notizbogen
</div>
<div
style={{
fontSize: 12,
opacity: 0.8,
color: stylesTokens.textDim,
}}
>
{me.email}
</div>
<div style={{ fontWeight: 900, color: stylesTokens.textGold }}>Notizbogen</div>
<div style={{ fontSize: 12, opacity: 0.8, color: stylesTokens.textDim }}>{me.email}</div>
</div>
{/* RECHTS: Account + Neues Spiel */}
<div
style={{
display: "flex",
gap: 8,
alignItems: "center",
flexWrap: "nowrap",
}}
data-user-menu
>
{/* Account Dropdown */}
<div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "nowrap" }} data-user-menu>
<div style={{ position: "relative" }}>
<button
onClick={() => setUserMenuOpen((v) => !v)}
style={styles.userBtn}
title="User Menü"
>
<button onClick={() => setUserMenuOpen((v) => !v)} style={styles.userBtn} title="User Menü">
<span style={{ fontSize: 16 }}>👤</span>
<span>User</span>
<span style={{ opacity: 0.7 }}></span>
@@ -52,7 +28,6 @@ export default function TopBar({
{userMenuOpen && (
<div style={styles.userDropdown}>
{/* Email Info */}
<div
style={{
padding: "10px 12px",
@@ -65,11 +40,14 @@ export default function TopBar({
{me.email}
</div>
{/* Actions */}
<button onClick={openPwModal} style={styles.userDropdownItem}>
Passwort setzen
</button>
<button onClick={openDesignModal} style={styles.userDropdownItem}>
Design ändern
</button>
<div style={styles.userDropdownDivider} />
<button
@@ -77,10 +55,7 @@ export default function TopBar({
setUserMenuOpen(false);
doLogout();
}}
style={{
...styles.userDropdownItem,
color: "#ffb3b3",
}}
style={{ ...styles.userDropdownItem, color: "#ffb3b3" }}
>
Logout
</button>
@@ -88,7 +63,6 @@ export default function TopBar({
)}
</div>
{/* Neues Spiel Button */}
<button onClick={newGame} style={styles.primaryBtn}>
New Game
</button>

View File

@@ -0,0 +1,33 @@
import React from "react";
import { styles } from "../styles/styles";
import { stylesTokens } from "../styles/theme";
export default function WinnerCard({ value, setValue, onSave }) {
return (
<div style={{ marginTop: 14 }}>
<div style={styles.card}>
<div style={styles.sectionHeader}>Sieger</div>
<div style={styles.cardBody}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Name des Siegers"
style={{ ...styles.input, flex: 1 }}
onKeyDown={(e) => {
if (e.key === "Enter") onSave();
}}
/>
<button onClick={onSave} style={styles.primaryBtn} title="Speichern">
Speichern
</button>
</div>
<div style={{ padding: "0 12px 12px", fontSize: 12, color: stylesTokens.textDim }}>
Wird pro Spiel lokal gespeichert.
</div>
</div>
</div>
);
}