diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-20 23:44:07 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-20 23:44:07 +0900 |
| commit | 00354d392a0bcddaac71fee7b6aae721e5747f59 (patch) | |
| tree | ee9a6108fd93363ea35253f84d7a7b646499dfec /frontend/app/pages | |
| parent | 8e73d12a703e90ad908962143951178c13d0d6fe (diff) | |
| download | phperkaigi-2026-albatross-00354d392a0bcddaac71fee7b6aae721e5747f59.tar.gz phperkaigi-2026-albatross-00354d392a0bcddaac71fee7b6aae721e5747f59.tar.zst phperkaigi-2026-albatross-00354d392a0bcddaac71fee7b6aae721e5747f59.zip | |
feat(frontend): add problem preview page for pre-game viewing
Allow participants to view problem descriptions and sample code
before a game starts. Dashboard shows a preview link only for
games that haven't started yet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/app/pages')
| -rw-r--r-- | frontend/app/pages/DashboardPage.tsx | 5 | ||||
| -rw-r--r-- | frontend/app/pages/GolfProblemPreviewPage.tsx | 60 |
2 files changed, 65 insertions, 0 deletions
diff --git a/frontend/app/pages/DashboardPage.tsx b/frontend/app/pages/DashboardPage.tsx index 3191f1b..00db3f0 100644 --- a/frontend/app/pages/DashboardPage.tsx +++ b/frontend/app/pages/DashboardPage.tsx @@ -68,6 +68,11 @@ export default function DashboardPage() { </span> </div> <div className="flex gap-2"> + {game.started_at == null && ( + <NavigateLink to={`/golf/${game.game_id}/preview`}> + 問題を見る + </NavigateLink> + )} <NavigateLink to={`/golf/${game.game_id}/play`}> 対戦 </NavigateLink> diff --git a/frontend/app/pages/GolfProblemPreviewPage.tsx b/frontend/app/pages/GolfProblemPreviewPage.tsx new file mode 100644 index 0000000..01ac5b2 --- /dev/null +++ b/frontend/app/pages/GolfProblemPreviewPage.tsx @@ -0,0 +1,60 @@ +import { useEffect, useState } from "react"; +import { useLocation } from "wouter"; +import { createApiClient } from "../api/client"; +import type { components } from "../api/schema"; +import ProblemColumnContent from "../components/Gaming/ProblemColumnContent"; +import NavigateLink from "../components/NavigateLink"; +import { APP_NAME } from "../config"; +import { usePageTitle } from "../hooks/usePageTitle"; + +type Game = components["schemas"]["Game"]; + +export default function GolfProblemPreviewPage({ gameId }: { gameId: string }) { + const [, navigate] = useLocation(); + const [game, setGame] = useState<Game | null>(null); + const [loading, setLoading] = useState(true); + + const gameIdNum = Number(gameId); + + usePageTitle( + game + ? `${game.display_name} - 問題プレビュー | ${APP_NAME}` + : `問題プレビュー | ${APP_NAME}`, + ); + + useEffect(() => { + const apiClient = createApiClient(); + apiClient + .getGame(gameIdNum) + .then(({ game }) => setGame(game)) + .catch(() => navigate("/dashboard")) + .finally(() => setLoading(false)); + }, [gameIdNum, navigate]); + + if (loading || !game) { + return ( + <div className="min-h-screen bg-gray-100 flex items-center justify-center"> + <p className="text-gray-500">Loading...</p> + </div> + ); + } + + return ( + <div className="p-6 bg-gray-100 min-h-screen flex flex-col items-center gap-4"> + <h1 className="text-3xl font-bold text-gray-800">{game.display_name}</h1> + <div className="w-full max-w-3xl flex flex-col gap-4"> + <ProblemColumnContent + description={game.problem.description} + language={game.problem.language} + sampleCode={game.problem.sample_code} + /> + </div> + {game.started_at != null && ( + <NavigateLink to={`/golf/${game.game_id}/play`}> + 対戦ページへ + </NavigateLink> + )} + <NavigateLink to="/dashboard">ダッシュボードへ戻る</NavigateLink> + </div> + ); +} |
