Add dedicated home page with game list and welcome screen, replacing auto-selection behavior

Implemented HomePage component with centered welcome message, create/join button, and list of user's games with name/code display. Modified App.jsx to show HomePage when no game is selected instead of auto-selecting first game. Added `goHome` handler to reset all game state and return to home screen. Added home button (🏠) to GamePickerCard header next to help button with bilingual tooltip. Removed automatic
This commit is contained in:
2026-08-02 10:58:38 +02:00
parent c9eae136fb
commit ea08016ea5
3 changed files with 57 additions and 4 deletions
+22 -2
View File
@@ -27,6 +27,7 @@ import NewGameModal from "./components/NewGameModal";
import StatsModal from "./components/StatsModal"; import StatsModal from "./components/StatsModal";
import AdminSettingsModal from "./components/AdminSettingsModal"; import AdminSettingsModal from "./components/AdminSettingsModal";
import InvitePage from "./components/InvitePage"; import InvitePage from "./components/InvitePage";
import HomePage from "./components/HomePage";
import { useLanguage } from "./i18n"; import { useLanguage } from "./i18n";
export default function App() { export default function App() {
@@ -134,7 +135,7 @@ export default function App() {
const gs = await api("/games"); const gs = await api("/games");
setGames(gs); setGames(gs);
if (gs[0] && !gameId) setGameId(gs[0].id); // Always open on the start page; the user explicitly chooses a game.
}; };
const reloadSheet = async () => { const reloadSheet = async () => {
@@ -528,7 +529,7 @@ export default function App() {
await api(`/games/${gameId}`, { method: "DELETE" }); await api(`/games/${gameId}`, { method: "DELETE" });
const nextGames = await api("/games"); const nextGames = await api("/games");
setGames(nextGames); setGames(nextGames);
setGameId(nextGames[0]?.id || null); setGameId(null);
setSheet(null); setSheet(null);
setGameMeta(null); setGameMeta(null);
setMembers([]); setMembers([]);
@@ -539,6 +540,20 @@ export default function App() {
showSnack(language === "en" ? "Game cancelled." : "Spiel abgebrochen."); showSnack(language === "en" ? "Game cancelled." : "Spiel abgebrochen.");
}; };
const goHome = () => {
setGameId(null);
setSheet(null);
setGameMeta(null);
setMembers([]);
setGameChips([]);
setWinnerUserId("");
setChipOpen(false);
setChipEntry(null);
setCelebrateOpen(false);
setCelebrateName("");
setStartCelebrateOpen(false);
};
// ===== Winner ===== // ===== Winner =====
const saveWinner = async () => { const saveWinner = async () => {
if (!gameId) return; if (!gameId) return;
@@ -735,6 +750,9 @@ export default function App() {
<AdminSettingsModal open={adminSettingsOpen} onClose={() => setAdminSettingsOpen(false)} /> <AdminSettingsModal open={adminSettingsOpen} onClose={() => setAdminSettingsOpen(false)} />
)} )}
{!gameId ? (
<HomePage games={games} onOpenNewGame={() => setNewGameOpen(true)} onOpenGame={setGameId} />
) : <>
<GamePickerCard <GamePickerCard
games={games} games={games}
gameId={gameId} gameId={gameId}
@@ -750,6 +768,7 @@ export default function App() {
chipCount={gameChips.length} chipCount={gameChips.length}
onStartGame={startGame} onStartGame={startGame}
onCancelGame={cancelGame} onCancelGame={cancelGame}
onGoHome={goHome}
/> />
{gameStarted && ( {gameStarted && (
@@ -760,6 +779,7 @@ export default function App() {
}} }}
/> />
)} )}
</>}
<HelpModal open={helpOpen} onClose={() => setHelpOpen(false)} /> <HelpModal open={helpOpen} onClose={() => setHelpOpen(false)} />
+6 -2
View File
@@ -18,6 +18,7 @@ export default function GamePickerCard({
chipCount = 0, chipCount = 0,
onStartGame, onStartGame,
onCancelGame, onCancelGame,
onGoHome,
}) { }) {
const { language, t } = useLanguage(); const { language, t } = useLanguage();
const cur = games.find((x) => x.id === gameId); const cur = games.find((x) => x.id === gameId);
@@ -102,8 +103,11 @@ export default function GamePickerCard({
))} ))}
</select> </select>
<button onClick={onOpenHelp} style={styles.helpBtn} title={t("help")}> <button onClick={onGoHome} style={styles.helpBtn} title={language === "en" ? "Home" : "Startseite"} aria-label={language === "en" ? "Home" : "Startseite"}>
{t("help")} 🏠
</button>
<button onClick={onOpenHelp} style={styles.helpBtn} title={t("help")} aria-label={t("help")}>
</button> </button>
</div> </div>
+29
View File
@@ -0,0 +1,29 @@
import React from "react";
import { styles } from "../styles/styles";
import { stylesTokens } from "../styles/theme";
import { useLanguage } from "../i18n";
export default function HomePage({ games = [], onOpenNewGame, onOpenGame }) {
const { language, t } = useLanguage();
return (
<main className="hp-home-page" style={{ marginTop: 14, display: "grid", gap: 14 }}>
<div style={styles.card}>
<div style={{ padding: "26px 20px 22px", textAlign: "center" }}>
<div style={{ fontSize: 34, color: stylesTokens.textGold }}></div>
<h1 style={{ margin: "8px 0 0", color: stylesTokens.textGold, fontSize: 24 }}>{language === "en" ? "Welcome to Notizbogen" : "Willkommen beim Notizbogen"}</h1>
<p style={{ margin: "9px auto 0", maxWidth: 430, color: stylesTokens.textDim, lineHeight: 1.5 }}>{language === "en" ? "Create a new investigation or join an existing game to get started." : "Erstelle eine neue Ermittlung oder tritt einem bestehenden Spiel bei, um zu beginnen."}</p>
<button onClick={onOpenNewGame} style={{ ...styles.primaryBtn, marginTop: 18 }}>{language === "en" ? "✦ Create or join game" : "✦ Spiel erstellen oder beitreten"}</button>
</div>
</div>
{games.length > 0 && <div style={styles.card}>
<div style={styles.sectionHeader}>{language === "en" ? "Your games" : "Deine Spiele"}</div>
<div style={{ display: "grid", gap: 8, padding: 12 }}>
{games.map((game) => <button key={game.id} onClick={() => onOpenGame(game.id)} style={{ ...styles.secondaryBtn, display: "flex", justifyContent: "space-between", alignItems: "center", textAlign: "left" }}>
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{game.name}</span><span style={{ color: stylesTokens.textGold, marginLeft: 10 }}>{game.code}</span>
</button>)}
</div>
</div>}
</main>
);
}