/** * Application root component. * * Provider order (see `src/providers/PROVIDER_ORDER.md` for detailed contract): * 1. `ThemeProvider` — OUTERMOST; provides theme context to AppContents * 2. `FluentProvider` — supplies Fluent UI theme and design tokens * 3. `NotificationProvider` — provides notification service to all descendants * 4. `ErrorBoundary` — catches catastrophic errors * 5. `BrowserRouter` — enables client-side routing via React Router * 6. `NavigationCancellationProvider` — manages route-aware request cancellation * 7. `AuthProvider` — manages session state; validates on mount; uses useNavigate() * 8. `TimezoneProvider` — INNERMOST (inside protected routes); fetches timezone after auth * * CRITICAL: Provider order is order-sensitive. See PROVIDER_ORDER.md before refactoring. * * 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 `/`. * * Error Boundaries: * - Top-level ErrorBoundary wraps the entire app shell (rare full-page reload). * - Each page route wrapped in PageErrorBoundary (page fails but nav persists). * - Risky sections within pages wrapped in SectionErrorBoundary (graceful degradation). */ import { lazy, Suspense, useEffect } 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 { NavigationCancellationProvider } from "./providers/NavigationCancellationProvider"; import { NotificationProvider } from "./services/notificationService"; import { RequireAuth } from "./components/RequireAuth"; import { SetupGuard } from "./components/SetupGuard"; import { ErrorBoundary } from "./components/ErrorBoundary"; import { PageErrorBoundary } from "./components/PageErrorBoundary"; import { NotificationContainer } from "./components/NotificationContainer"; import { MainLayout } from "./layouts/MainLayout"; import { injectSkeletonStyles } from "./utils/skeletonStyles"; import { initializeWebVitals } from "./utils/metrics"; 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. * * Provider stack (see PROVIDER_ORDER.md for detailed contract): * - FluentProvider (2) — receives theme from useThemeMode() * - NotificationProvider (3) — provides notification service * - ErrorBoundary (4) — catches catastrophic errors * - BrowserRouter (5) — enables routing * - NavigationCancellationProvider (6) — manages route-aware request cancellation * - AuthProvider (7) — session validation; uses useNavigate() * - TimezoneProvider (8) — inside protected routes only */ function AppContents(): React.JSX.Element { const { colorMode } = useThemeMode(); const theme = colorMode === "dark" ? darkTheme : lightTheme; // Inject skeleton animation styles once at app startup injectSkeletonStyles(); // Initialize web vitals tracking on component mount useEffect(() => { initializeWebVitals(); }, []); return ( // 2. FluentProvider — supplies Fluent UI theme and tokens {/* 3. NotificationProvider — makes notification service available */} {/* 4. ErrorBoundary — catches catastrophic errors that would crash the app */} {/* Notification container must be rendered inside ErrorBoundary */} {/* 5. BrowserRouter — enables routing; required by AuthProvider's useNavigate() */} {/* 6. NavigationCancellationProvider — manages route-aware request cancellation */} }> {/* 7. AuthProvider — validates session on mount; must be inside BrowserRouter */} {/* Setup wizard — always accessible; redirects to /login if already done */} } /> {/* Login — requires setup to be complete */} } /> {/* Protected routes — require setup AND authentication */} {/* 7. TimezoneProvider — INNERMOST; fetches timezone after auth validation */} } > } /> } /> } /> } /> } /> } /> } /> {/* Fallback — redirect unknown paths to dashboard */} } /> ); } function App(): React.JSX.Element { // ThemeProvider (1. OUTERMOST) — provides theme context needed by AppContents return ( ); } export default App;