Routing

React Router v7 with centralized route definitions in frontend/src/App.tsx. Routes are organized into protected (JWT), portal (token-based), and public (auth) groups.

Route Hierarchy

/ (ProtectedRoute -- requires JWT auth)
├── /buildings                          → Buildings list
├── /buildings/:id                      → Building detail
├── /buildings/:buildingId/projects/:projectId
│   └── /scenarios/:scenarioId          → Scenario detail
├── /funding-applications               → Funding applications list
├── /quotes                             → Quotes list
├── /contacts, /companies, /leads       → CRM pages
├── /profile                            → User profile (tabbed)
├── /admin/*                            → Admin section
│   ├── /users, /team-departments
│   ├── /funding-programs, /forms, /products
│   ├── /hubspot, /dashboard
│   └── /document-templates, /pdf-templates
├── /internal/*                         → Internal tools
│   ├── /doc-obtaining/*                → Document collection
│   ├── /workflows, /team, /changelog
│   └── /email-templates, /rejection-reasons
└── /helper/*                           → Dev utilities

/portal (token-based auth, no session refresh)
├── /portal/collections/:id             → Document upload
├── /portal/forms/:formKey              → Form submission
└── /portal/appointments/:proposalId    → Appointment view

/auth (public)
├── /login, /signup
├── /forgot-password, /reset-password
├── /magic-link, /magic-link/verify
└── /auth/callback                      → OAuth redirect

Route Protection

GuardFileMechanism
ProtectedRoutecomponents/ProtectedRoute.tsxJWT validation, redirects to /login with returnUrl
Portal authcomponents/portal/PortalLayout.tsxToken param validation, no session refresh
Public routesNo guard, accessible without auth

See Authentication for JWT lifecycle, Portal for token-based access.

Code Splitting

All page components are lazily loaded:

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 conditionally renders admin links based on user role.