Routing
React Router v7 (react-router-dom 7.15.1, exact-pinned) with centralized route definitions in frontend/src/App.tsx. Routes are organized into protected (JWT), portal (token-based), and public (auth) groups.
Route Hierarchy
As of 2026-06:
/ (ProtectedRoute + Layout -- requires JWT auth)
├── index → HomeRedirect (first starred sidebar item,
│ fallback "Meine Aufgaben" — sidebarService)
├── /buildings → Buildings list
├── /buildings/:id → Building dashboard (tabs incl. site protocols)
├── /buildings/:buildingId/projects/:projectId
│ └── /scenarios/:scenarioId → Scenario detail
├── /funding-applications → Funding applications list
├── /quotes/* → Quotes (nested routes)
├── /contacts, /companies, /leads → CRM pages (+ /:id detail)
├── /profile → User profile (tabbed)
├── /admin/* → Admin section
│ ├── /settings, /responsibilities (old /users, /team-admin, /departments redirect)
│ ├── /funding-programs/* (versions, measures, loans), /forms, /form-builder,
│ │ /form-fields, /form-submissions, /products, /u-values
│ ├── /hubspot (consolidated), /dashboard
│ ├── /document-templates, /pdf-templates, /engagement-task-templates
├── /internal/* → Internal tools
│ ├── /document-obtaining/* → Document collection suite
│ ├── /workflow (+ /settings/task-types) → Workflow v4 board
│ ├── /billing, /team, /changelog, /help-requests
│ ├── /collection-settings/{email-templates,rejection-reasons,flow-definitions}
│ ├── /portal-preview/:id, /entra-groups (+ /:id)
├── /files-demo
└── /helper/{floor-plan-capture,pdf-export}
/internal/document-review/:collectionId/:fulfillmentId
→ ProtectedRoute WITHOUT Layout (fullscreen review)
/portal (token-based auth from URL or localStorage)
├── /portal, /portal/collections/:id → Portal dashboard / document upload
├── /portal/appointments/:proposalId → Appointment view
└── /portal/forms/:formKey[/:submissionId]
→ ProtectedRoute (JWT!) -- Form Builder v3 portal forms
(public auth)
├── /login, /signup, /check-email, /verify-email
├── /forgot-password, /reset-password
├── /phone-number, /phone-verification
├── /magic-link, /auth/magic-link
└── /auth/callback → OAuth redirect
Legacy paths are kept alive via <Navigate replace /> redirects (e.g. /admin/users → /admin/responsibilities?tab=employees, /admin/hubspot-monitoring → /admin/hubspot).
Route Protection
| Guard | Where | Mechanism |
|---|---|---|
ProtectedRoute | defined inline in frontend/src/App.tsx | Checks isAuthenticated from the Zustand auth store; redirects to /login with a sanitized returnUrl (getSafeReturnUrl prevents the “log in twice” loop, fluffy-doodle#43) |
| Portal auth | components/portal/ | Token from URL or localStorage, no session refresh |
| Public routes | — | No guard, accessible without auth |
Note: /portal/forms/* is the exception in the portal group — it requires JWT auth (ProtectedRoute).
See Authentication for JWT lifecycle, Portal for token-based access.
Code Splitting
All page components are lazily loaded (71 lazy() imports in App.tsx as of 2026-06):
const BuildingDetail = lazy(() => import('./pages/BuildingDetail'));A shared <LoadingFallback /> displays during chunk loading. This keeps the initial bundle small and loads page code on demand.
Admin Route Access
Admin routes are guarded by RBAC Authorization checks. The sidebar (components/sidebar/, with components/topnav/ — redesign landed via the PR#1567 saga, closed 2026-05-06) conditionally renders admin links based on user role; getDefaultRoute() in sidebarService.ts picks the landing route from the user’s first starred sidebar item (fallback: Meine Aufgaben).
Related
- Pages Overview — All routed page components
- Frontend Architecture — App shell and layout structure
- Authentication — JWT auth and ProtectedRoute
- Portal — Token-based portal routing
- RBAC Authorization — Role-based route visibility
- Component Library — Sidebar navigation component