Files
BanGUI/frontend/src/App.tsx

112 lines
4.8 KiB
TypeScript

/**
* Application root component.
*
* Wraps the entire application in:
* 1. `FluentProvider` — supplies the Fluent UI theme and design tokens.
* 2. `BrowserRouter` — enables client-side routing via React Router.
* 3. `AuthProvider` — manages session state and exposes `useAuth()`.
*
* Routes:
* - `/setup` — first-run setup wizard (always accessible; redirects to /login if already done)
* - `/login` — master password login (redirects to /setup if not done)
* - `/` — dashboard (protected, inside MainLayout)
* - `/map` — world map (protected)
* - `/jails` — jail list (protected)
* - `/jails/:name` — jail detail (protected)
* - `/config` — configuration editor (protected)
* - `/history` — event history (protected)
* - `/blocklists` — blocklist management (protected)
* All unmatched paths redirect to `/`.
*/
import { lazy, Suspense } from "react";
import { FluentProvider, Spinner } from "@fluentui/react-components";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { darkTheme, lightTheme } from "./theme/customTheme";
import { AuthProvider } from "./providers/AuthProvider";
import { ThemeProvider, useThemeMode } from "./providers/ThemeProvider";
import { TimezoneProvider } from "./providers/TimezoneProvider";
import { RequireAuth } from "./components/RequireAuth";
import { SetupGuard } from "./components/SetupGuard";
import { ErrorBoundary } from "./components/ErrorBoundary";
import { MainLayout } from "./layouts/MainLayout";
const SetupPage = lazy(() => import("./pages/SetupPage").then((m) => ({ default: m.SetupPage })));
const LoginPage = lazy(() => import("./pages/LoginPage").then((m) => ({ default: m.LoginPage })));
const DashboardPage = lazy(() => import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage })));
const MapPage = lazy(() => import("./pages/MapPage").then((m) => ({ default: m.MapPage })));
const JailsPage = lazy(() => import("./pages/JailsPage").then((m) => ({ default: m.JailsPage })));
const JailDetailPage = lazy(() => import("./pages/JailDetailPage").then((m) => ({ default: m.JailDetailPage })));
const ConfigPage = lazy(() => import("./pages/ConfigPage").then((m) => ({ default: m.ConfigPage })));
const HistoryPage = lazy(() => import("./pages/HistoryPage").then((m) => ({ default: m.HistoryPage })));
const BlocklistsPage = lazy(() => import("./pages/BlocklistsPage").then((m) => ({ default: m.BlocklistsPage })));
/**
* Root application component — mounts providers and top-level routes.
*/
function AppContents(): React.JSX.Element {
const { colorMode } = useThemeMode();
const theme = colorMode === "dark" ? darkTheme : lightTheme;
return (
<FluentProvider theme={theme}>
<ErrorBoundary>
<BrowserRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
<Suspense fallback={<Spinner size="large" label="Loading…" />}>
<AuthProvider>
<Routes>
{/* Setup wizard — always accessible; redirects to /login if already done */}
<Route path="/setup" element={<SetupPage />} />
{/* Login — requires setup to be complete */}
<Route
path="/login"
element={
<SetupGuard>
<LoginPage />
</SetupGuard>
}
/>
{/* Protected routes — require setup AND authentication */}
<Route
element={
<SetupGuard>
<RequireAuth>
<TimezoneProvider>
<MainLayout />
</TimezoneProvider>
</RequireAuth>
</SetupGuard>
}
>
<Route index element={<DashboardPage />} />
<Route path="/map" element={<MapPage />} />
<Route path="/jails" element={<JailsPage />} />
<Route path="/jails/:name" element={<JailDetailPage />} />
<Route path="/config" element={<ConfigPage />} />
<Route path="/history" element={<HistoryPage />} />
<Route path="/blocklists" element={<BlocklistsPage />} />
</Route>
{/* Fallback — redirect unknown paths to dashboard */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</AuthProvider>
</Suspense>
</BrowserRouter>
</ErrorBoundary>
</FluentProvider>
);
}
function App(): React.JSX.Element {
return (
<ThemeProvider>
<AppContents />
</ThemeProvider>
);
}
export default App;