Skip to main content

Routing and App Shell

Introduction

This document describes the current routing model, private route guard, dashboard layout, sidebar behavior, and route extension guidance.


Router Setup

Routing is rendered in src/app/App.tsx, while route definitions and dashboard navigation metadata live in src/app/routes.tsx.

The app tree is:

AlertProvider
`-- BrowserRouter
|-- Routes
| |-- / LoginPage
| |-- /register OrganizationRegistrationPage
| |-- /user-form AdminUserRegistrationPage
| `-- PrivateRoutes
| `-- /dashboard AppShell
| |-- index SensorDashboardPage
| |-- all-sensor SensorListPage
| |-- sensor/:sensorId SensorDetailPage
| |-- organization OrganizationDetailPage
| |-- profile ProfilePage
| |-- user UserListPage
| |-- role RoleListPage
| `-- analytics AnalyticsDashboardPage
|-- Toaster
`-- ConfirmDialog

Route Metadata

src/app/routes.tsx exports:

ExportPurpose
publicRoutesPublic route definitions used by App.tsx
dashboardRoutesDashboard route definitions, route titles, nav metadata, and layout variant
dashboardNavigationItemsSidebar items derived from dashboard routes
getDashboardRouteForPath()Active route lookup used by the app shell

Dashboard routes can opt into layoutVariant: 'workspace'. Workspace routes, currently analytics, get full-height controlled overflow and no footer.


Public Routes

PathComponentPurpose
/LoginPageUser login
/registerOrganizationRegistrationPageOrganization registration
/user-formAdminUserRegistrationPageAdmin/user creation after organization registration

/register and /user-form are public because they are part of onboarding before a session exists.


Private Routes

Private routes are wrapped by PrivateRoutes in src/app/PrivateRoutes.tsx.

const PrivateRoutes: React.FC = () => {
return auth.isAuthenticated() ? <Outlet /> : <Navigate to={ROUTES.LOGIN} replace />;
};

The guard only checks for the access token cookie through auth.isAuthenticated(). It does not validate token expiry or permissions before rendering the route. API calls still handle 401 responses and redirect back to /.


Dashboard Layout

/dashboard renders AppShell from src/core/layout/AppShell.tsx, which owns:

  • Desktop sidebar with collapse toggle.
  • Mobile drawer using Sheet.
  • Current route highlighting.
  • Route title lookup from dashboardRoutes.
  • User dropdown with profile, organization, language, and logout actions.
  • Standard vs workspace content frame rules.
  • Nested route rendering through Outlet.

The dashboard home page is the index route:

/dashboard -> SensorDashboardPage

The active sidebar menu is derived from dashboardNavigationItems in src/app/routes.tsx:

LabelPathIcon
Dashboard/dashboardLayoutDashboard
Sensor/dashboard/all-sensorRadio
Security Analytics/dashboard/analyticsActivity
Role/dashboard/roleShield
User/dashboard/userUsers

The user dropdown exposes additional routes:

LabelPath
Profile/dashboard/profile
Organization/dashboard/organization

Page Layout Rules

Route-level dashboard pages should use RoutePage from src/core/page/RoutePage.tsx.

Management pages with table/list workflows should use DataTableCard and domain-specific table partials, for example:

  • modules/sensors/partials/SensorListTable.tsx
  • modules/users/partials/UserListTable.tsx
  • modules/roles/partials/RoleListTable.tsx

Feature-specific visual sections belong in modules/<domain>/partials. A sub-domain folder is used only for larger flows with their own state and UI surface, such as modules/analytics/chat.


Adding a New Dashboard Route

To add a new private dashboard route:

  1. Create the route-level component under src/modules/<domain>/pages.
  2. Add reusable domain sections under src/modules/<domain>/partials when needed.
  3. Add modal flows under src/modules/<domain>/dialogs when needed.
  4. Import the page in src/app/routes.tsx.
  5. Add a dashboardRoutes entry with titleKey, fullPath, and optional sidebar metadata.
  6. Add any reusable path constant in src/lib/constants.ts.
  7. Add translation keys if the menu label should be localized.
  8. Update this documentation if the route becomes part of the stable product surface.