Files
game-marathon/frontend/src/App.tsx
2025-12-20 02:01:51 +07:00

226 lines
5.9 KiB
TypeScript

import { useEffect } from 'react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { useAuthStore } from '@/store/auth'
import { ToastContainer, ConfirmModal } from '@/components/ui'
import { BannedScreen } from '@/components/BannedScreen'
// Layout
import { Layout } from '@/components/layout/Layout'
// Pages
import { HomePage } from '@/pages/HomePage'
import { LoginPage } from '@/pages/LoginPage'
import { RegisterPage } from '@/pages/RegisterPage'
import { MarathonsPage } from '@/pages/MarathonsPage'
import { CreateMarathonPage } from '@/pages/CreateMarathonPage'
import { MarathonPage } from '@/pages/MarathonPage'
import { LobbyPage } from '@/pages/LobbyPage'
import { PlayPage } from '@/pages/PlayPage'
import { LeaderboardPage } from '@/pages/LeaderboardPage'
import { InvitePage } from '@/pages/InvitePage'
import { AssignmentDetailPage } from '@/pages/AssignmentDetailPage'
import { ProfilePage } from '@/pages/ProfilePage'
import { UserProfilePage } from '@/pages/UserProfilePage'
import { StaticContentPage } from '@/pages/StaticContentPage'
import { NotFoundPage } from '@/pages/NotFoundPage'
import { TeapotPage } from '@/pages/TeapotPage'
import { ServerErrorPage } from '@/pages/ServerErrorPage'
// Admin Pages
import {
AdminLayout,
AdminDashboardPage,
AdminUsersPage,
AdminMarathonsPage,
AdminLogsPage,
AdminBroadcastPage,
AdminContentPage,
} from '@/pages/admin'
// Protected route wrapper
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
if (!isAuthenticated) {
return <Navigate to="/login" replace />
}
return <>{children}</>
}
// Public route wrapper (redirect if authenticated)
function PublicRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
if (isAuthenticated) {
return <Navigate to="/marathons" replace />
}
return <>{children}</>
}
function App() {
const banInfo = useAuthStore((state) => state.banInfo)
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
const syncUser = useAuthStore((state) => state.syncUser)
// Sync user data with server on app load
useEffect(() => {
syncUser()
}, [syncUser])
// Show banned screen if user is authenticated and banned
if (isAuthenticated && banInfo) {
return (
<>
<ToastContainer />
<BannedScreen banInfo={banInfo} />
</>
)
}
return (
<>
<ToastContainer />
<ConfirmModal />
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<HomePage />} />
{/* Public invite page */}
<Route path="invite/:code" element={<InvitePage />} />
{/* Public static content pages */}
<Route path="terms" element={<StaticContentPage />} />
<Route path="privacy" element={<StaticContentPage />} />
<Route path="page/:key" element={<StaticContentPage />} />
<Route
path="login"
element={
<PublicRoute>
<LoginPage />
</PublicRoute>
}
/>
<Route
path="register"
element={
<PublicRoute>
<RegisterPage />
</PublicRoute>
}
/>
<Route
path="marathons"
element={
<ProtectedRoute>
<MarathonsPage />
</ProtectedRoute>
}
/>
<Route
path="marathons/create"
element={
<ProtectedRoute>
<CreateMarathonPage />
</ProtectedRoute>
}
/>
<Route
path="marathons/:id"
element={
<ProtectedRoute>
<MarathonPage />
</ProtectedRoute>
}
/>
<Route
path="marathons/:id/lobby"
element={
<ProtectedRoute>
<LobbyPage />
</ProtectedRoute>
}
/>
<Route
path="marathons/:id/play"
element={
<ProtectedRoute>
<PlayPage />
</ProtectedRoute>
}
/>
<Route
path="marathons/:id/leaderboard"
element={
<ProtectedRoute>
<LeaderboardPage />
</ProtectedRoute>
}
/>
<Route
path="assignments/:id"
element={
<ProtectedRoute>
<AssignmentDetailPage />
</ProtectedRoute>
}
/>
{/* Profile routes */}
<Route
path="profile"
element={
<ProtectedRoute>
<ProfilePage />
</ProtectedRoute>
}
/>
<Route path="users/:id" element={<UserProfilePage />} />
{/* Easter egg - 418 I'm a teapot */}
<Route path="418" element={<TeapotPage />} />
<Route path="teapot" element={<TeapotPage />} />
<Route path="tea" element={<TeapotPage />} />
{/* Server error page */}
<Route path="500" element={<ServerErrorPage />} />
<Route path="error" element={<ServerErrorPage />} />
{/* Admin routes */}
<Route
path="admin"
element={
<ProtectedRoute>
<AdminLayout />
</ProtectedRoute>
}
>
<Route index element={<AdminDashboardPage />} />
<Route path="users" element={<AdminUsersPage />} />
<Route path="marathons" element={<AdminMarathonsPage />} />
<Route path="logs" element={<AdminLogsPage />} />
<Route path="broadcast" element={<AdminBroadcastPage />} />
<Route path="content" element={<AdminContentPage />} />
</Route>
{/* 404 - must be last */}
<Route path="*" element={<NotFoundPage />} />
</Route>
</Routes>
</>
)
}
export default App