diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-13 22:40:45 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-13 23:07:26 +0900 |
| commit | e239fe743fc66a8712cf9886d3dfed3cc41fce36 (patch) | |
| tree | e3452fb13dce114cea0e8371dbb049118aa1229e /frontend/app/pages/DashboardPage.tsx | |
| parent | 482c3a52a0fcc5870a7db4a190475caf61b211a3 (diff) | |
| download | phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.tar.gz phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.tar.zst phperkaigi-2026-albatross-e239fe743fc66a8712cf9886d3dfed3cc41fce36.zip | |
refactor(frontend): replace React Router BFF with Wouter SPA
Remove React Router 7 SSR/BFF architecture (server-side loaders,
actions, sessions, remix-auth) and replace with a client-side SPA
using Wouter for routing and cookie-based JWT auth.
- Replace reactRouter() Vite plugin with @vitejs/plugin-react
- Add index.html + app/main.tsx as SPA entry points
- Add Wouter routing with auth guards (ProtectedRoute/PublicOnlyRoute)
- Add client-side auth (app/auth.ts) and useAuth hook
- Migrate all route files to app/pages/ with client-side data fetching
- Update NavigateLink and GolfPlayAppGaming to use Wouter Link
- Remove .server/, routes/, root.tsx, react-router.config.ts
- Clean up tsconfig.json (remove .react-router references)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/app/pages/DashboardPage.tsx')
| -rw-r--r-- | frontend/app/pages/DashboardPage.tsx | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/frontend/app/pages/DashboardPage.tsx b/frontend/app/pages/DashboardPage.tsx new file mode 100644 index 0000000..c81014d --- /dev/null +++ b/frontend/app/pages/DashboardPage.tsx @@ -0,0 +1,108 @@ +import { useEffect, useState } from "react"; +import { useLocation } from "wouter"; +import { createApiClient } from "../api/client"; +import type { components } from "../api/schema"; +import { getToken } from "../auth"; +import BorderedContainerWithCaption from "../components/BorderedContainerWithCaption"; +import NavigateLink from "../components/NavigateLink"; +import UserIcon from "../components/UserIcon"; +import { APP_NAME, BASE_PATH } from "../config"; +import { useAuth } from "../hooks/useAuth"; +import { usePageTitle } from "../hooks/usePageTitle"; + +type Game = components["schemas"]["Game"]; + +export default function DashboardPage() { + usePageTitle(`Dashboard | ${APP_NAME}`); + + const { user, logout } = useAuth(); + const [, navigate] = useLocation(); + + const [games, setGames] = useState<Game[]>([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const token = getToken(); + if (!token) return; + const apiClient = createApiClient(token); + apiClient + .getGames() + .then(({ games }) => setGames(games)) + .finally(() => setLoading(false)); + }, []); + + function handleLogout() { + logout(); + navigate("/"); + } + + if (loading) { + 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"> + {user?.icon_path && ( + <UserIcon + iconPath={user.icon_path} + displayName={user.display_name} + className="w-24 h-24" + /> + )} + <h1 className="text-3xl font-bold text-gray-800">{user?.display_name}</h1> + <BorderedContainerWithCaption caption="試合一覧"> + <div className="px-4"> + {games.length === 0 ? ( + <p>エントリーできる試合はありません</p> + ) : ( + <ul className="divide-y divide-gray-300"> + {games.map((game) => ( + <li + key={game.game_id} + className="flex justify-between items-center py-2 gap-4" + > + <div> + <span className="font-medium text-gray-800"> + {game.display_name} + </span> + </div> + <div className="flex gap-2"> + <NavigateLink to={`/golf/${game.game_id}/play`}> + 対戦 + </NavigateLink> + <NavigateLink to={`/golf/${game.game_id}/watch`}> + 観戦 + </NavigateLink> + </div> + </li> + ))} + </ul> + )} + </div> + </BorderedContainerWithCaption> + <button + type="button" + onClick={handleLogout} + className="px-4 py-2 bg-red-500 text-white rounded-sm transition duration-300 hover:bg-red-700 focus:ring-3 focus:ring-red-400 focus:outline-hidden" + > + ログアウト + </button> + {user?.is_admin && ( + <a + href={ + import.meta.env.DEV + ? `http://localhost:8004${BASE_PATH}admin/dashboard` + : `${BASE_PATH}admin/dashboard` + } + className="text-lg text-white bg-sky-600 px-4 py-2 rounded-sm transition duration-300 hover:bg-sky-500 focus:ring-3 focus:ring-sky-400 focus:outline-hidden" + > + Admin Dashboard + </a> + )} + </div> + ); +} |
