refactor(App): migrate to Data Router for improved routing and unsaved changes handling
All checks were successful
Deploy Development / deploy (push) Successful in 41s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 56s

- Replaced `BrowserRouter` and `Routes` with `createBrowserRouter` and `RouterProvider` to support unsaved changes blocking.
- Restructured route definitions for better organization and clarity, maintaining existing functionality.
- Added comments to clarify the necessity of the Data Router for handling unsaved changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-05-13 17:19:59 +02:00
parent 49adb395dd
commit d50bed428b

View File

@ -1,8 +1,7 @@
import React from 'react' import React from 'react'
import { import {
BrowserRouter as Router, RouterProvider,
Routes, createBrowserRouter,
Route,
Navigate, Navigate,
NavLink, NavLink,
useLocation, useLocation,
@ -163,115 +162,115 @@ function PublicRoute({ children }) {
return !isAuthenticated ? children : <Navigate to="/" replace /> return !isAuthenticated ? children : <Navigate to="/" replace />
} }
function AppRoutes() { /**
return ( * Data Router erforderlich für `useBlocker` (ungespeicherte Änderungen).
<Routes> * Klassisches `BrowserRouter` stellt keinen DataRouterContext bereit; ohne Migration
<Route path="/verify" element={<VerifyPage />} /> * werfen Seiten mit `useUnsavedChangesBlocker` beim Rendern eine Invariante.
*/
<Route const appRouter = createBrowserRouter([
path="/login" { path: '/verify', element: <VerifyPage /> },
element={ {
<PublicRoute> path: '/login',
<LoginPage /> element: (
</PublicRoute> <PublicRoute>
} <LoginPage />
/> </PublicRoute>
),
{/* P-01: Öffentliche Rechtstextseiten — kein Auth erforderlich */} },
<Route path="/impressum" element={<LegalPage type="impressum" />} /> { path: '/impressum', element: <LegalPage type="impressum" /> },
<Route path="/datenschutz" element={<LegalPage type="datenschutz" />} /> { path: '/datenschutz', element: <LegalPage type="datenschutz" /> },
<Route path="/nutzungsbedingungen" element={<LegalPage type="nutzungsbedingungen" />} /> { path: '/nutzungsbedingungen', element: <LegalPage type="nutzungsbedingungen" /> },
<Route path="/medienrichtlinie" element={<LegalPage type="medienrichtlinie" />} /> { path: '/medienrichtlinie', element: <LegalPage type="medienrichtlinie" /> },
{
<Route element={<ProtectedLayout />}> element: <ProtectedLayout />,
<Route index element={<Dashboard />} /> children: [
<Route path="profile" element={<Navigate to="/settings" replace />} /> { index: true, element: <Dashboard /> },
<Route path="settings" element={<AccountSettingsPage />} /> { path: 'profile', element: <Navigate to="/settings" replace /> },
<Route path="settings/system" element={<SettingsSystemInfoPage />} /> { path: 'settings', element: <AccountSettingsPage /> },
<Route path="settings/legal" element={<SettingsLegalPage />} /> { path: 'settings/system', element: <SettingsSystemInfoPage /> },
<Route path="media" element={<MediaLibraryPage />} /> { path: 'settings/legal', element: <SettingsLegalPage /> },
<Route path="exercises"> { path: 'media', element: <MediaLibraryPage /> },
<Route index element={<ExercisesListPage />} /> {
<Route path="new" element={<ExerciseFormPage />} /> path: 'exercises',
<Route path=":id/edit" element={<ExerciseFormPage />} /> children: [
<Route path=":id" element={<ExerciseDetailPage />} /> { index: true, element: <ExercisesListPage /> },
</Route> { path: 'new', element: <ExerciseFormPage /> },
<Route path="clubs" element={<ClubsPage />} /> { path: ':id/edit', element: <ExerciseFormPage /> },
<Route path="inbox" element={<InboxPage />} /> { path: ':id', element: <ExerciseDetailPage /> },
<Route path="skills" element={<SkillsPage />} /> ],
<Route path="planning/framework-programs/new" element={<TrainingFrameworkProgramEditPage />} /> },
<Route path="planning/framework-programs/:id" element={<TrainingFrameworkProgramEditPage />} /> { path: 'clubs', element: <ClubsPage /> },
<Route path="planning/framework-programs" element={<TrainingFrameworkProgramsListPage />} /> { path: 'inbox', element: <InboxPage /> },
<Route path="planning/training-modules/new" element={<TrainingModuleEditPage />} /> { path: 'skills', element: <SkillsPage /> },
<Route path="planning/training-modules/:id" element={<TrainingModuleEditPage />} /> { path: 'planning/framework-programs/new', element: <TrainingFrameworkProgramEditPage /> },
<Route path="planning/training-modules" element={<TrainingModulesListPage />} /> { path: 'planning/framework-programs/:id', element: <TrainingFrameworkProgramEditPage /> },
<Route path="planning/run/:unitId/coach" element={<TrainingCoachPage />} /> { path: 'planning/framework-programs', element: <TrainingFrameworkProgramsListPage /> },
<Route path="planning/run/:unitId" element={<TrainingUnitRunPage />} /> { path: 'planning/training-modules/new', element: <TrainingModuleEditPage /> },
<Route path="planning" element={<TrainingPlanningPage />} /> { path: 'planning/training-modules/:id', element: <TrainingModuleEditPage /> },
<Route path="admin" element={<AdminHomeRedirect />} /> { path: 'planning/training-modules', element: <TrainingModulesListPage /> },
<Route { path: 'planning/run/:unitId/coach', element: <TrainingCoachPage /> },
path="admin/users" { path: 'planning/run/:unitId', element: <TrainingUnitRunPage /> },
element={ { path: 'planning', element: <TrainingPlanningPage /> },
<PlatformAdminRoute> { path: 'admin', element: <AdminHomeRedirect /> },
<AdminUsersPage /> {
</PlatformAdminRoute> path: 'admin/users',
} element: (
/> <PlatformAdminRoute>
<Route <AdminUsersPage />
path="admin/hierarchy" </PlatformAdminRoute>
element={ ),
<PlatformAdminRoute> },
<AdminHierarchyPage /> {
</PlatformAdminRoute> path: 'admin/hierarchy',
} element: (
/> <PlatformAdminRoute>
<Route <AdminHierarchyPage />
path="admin/maturity-models" </PlatformAdminRoute>
element={ ),
<PlatformAdminRoute> },
<AdminMaturityModelsPage /> {
</PlatformAdminRoute> path: 'admin/maturity-models',
} element: (
/> <PlatformAdminRoute>
<Route <AdminMaturityModelsPage />
path="admin/catalogs" </PlatformAdminRoute>
element={ ),
<PlatformAdminRoute> },
<AdminCatalogsPage /> {
</PlatformAdminRoute> path: 'admin/catalogs',
} element: (
/> <PlatformAdminRoute>
<Route <AdminCatalogsPage />
path="admin/mediawiki-import" </PlatformAdminRoute>
element={ ),
<PlatformAdminRoute> },
<MediaWikiImportPage /> {
</PlatformAdminRoute> path: 'admin/mediawiki-import',
} element: (
/> <PlatformAdminRoute>
<Route <MediaWikiImportPage />
path="admin/legal-documents" </PlatformAdminRoute>
element={ ),
<PlatformAdminRoute> },
<AdminLegalDocumentsPage /> {
</PlatformAdminRoute> path: 'admin/legal-documents',
} element: (
/> <PlatformAdminRoute>
<Route path="trainer-contexts" element={<TrainerContextsPage />} /> <AdminLegalDocumentsPage />
</Route> </PlatformAdminRoute>
),
<Route path="*" element={<Navigate to="/" replace />} /> },
</Routes> { path: 'trainer-contexts', element: <TrainerContextsPage /> },
) ],
} },
{ path: '*', element: <Navigate to="/" replace /> },
])
function App() { function App() {
return ( return (
<AuthProvider> <AuthProvider>
<ToastProvider> <ToastProvider>
<Router> <RouterProvider router={appRouter} />
<AppRoutes />
</Router>
</ToastProvider> </ToastProvider>
</AuthProvider> </AuthProvider>
) )