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:
57
frontend/src/components/DesignModal.jsx
Normal file
57
frontend/src/components/DesignModal.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
33
frontend/src/components/WinnerCard.jsx
Normal file
33
frontend/src/components/WinnerCard.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user