The Dashboard is an Angular 16 NgModule app (no standalone components). Its entire route tree lives in one root routing module, src/app/app-routing.module.ts, registered via RouterModule.forRoot(...). There are exactly six top-level routes: one redirect, two lazy AuthGuard-protected feature modules (home, billing), and three eager auth pages (login, register, reset-password). Lazy children add their own forChild sub-routes.
This page covers the route table, lazy loading, and preloading only. The guard/interceptor/token mechanics are on

Auth: Guard, Interceptor & AuthService

the authentication page
, and how the shell hosts <router-outlet> is on

App Shell, Bootstrap & Structure

the overview page
.

The route table

src/app/app-routing.module.ts
const routerOptions: ExtraOptions = {
  anchorScrolling: 'enabled',
  preloadingStrategy: PreloadAllModules,
};

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
    canActivate: [AuthGuard],
  },
  // TODO: Re-enable Isaac Sim page when ready
  // { path: 'isaac-sim', component: IsaacSimPageComponent, canActivate: [AuthGuard] },
  {
    path: 'billing',
    loadChildren: () => import('./home/billing-page/billing.module').then((m) => m.BillingModule),
    canActivate: [AuthGuard],
  },
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'reset-password', component: ResetPasswordComponent },
];
PathKindComponent / ModuleGuardNotes
''Redirect (pathMatch: 'full')/homenoneEmpty URL bounces to the dashboard; the guard on /home then gates it.
homeLazy (loadChildren)HomeModuleHomeComponentAuthGuardThe entire authenticated UI (map, fleet, missions, video).
billingLazy (loadChildren)BillingModuleBillingPageComponentAuthGuardHas '' / success / cancel children (see below).
loginEager (component)LoginComponentnoneReads ?register=true to toggle the register hint.
registerEager (component)RegisterComponentnoneRequests a verification email, then routes to /login.
reset-passwordEager (component)ResetPasswordComponentnoneReads ?token and ?verify=true query params.
isaac-simCommented out(IsaacSimPageComponent)(AuthGuard)Route is disabled in source; see the gotcha below.
The three eager components are declared in the root AppModule (src/app/app.module.ts) so they load in the initial bundle — a user hitting /login cold must not wait on a lazy chunk. Note that RegisterComponent physically lives under src/app/home/register/ but is declared in AppModule and routed eagerly — a location/ownership mismatch worth preserving awareness of.

Lazy modules and child routes

Both home and billing use loadChildren so they are compiled into separate code-split chunks. Each defines its own forChild routing module:
src/app/home/home-routing.module.ts
const routes: Routes = [{ path: '', component: HomeComponent }];
A single empty child route renders the monolithic HomeComponent — the composition root of the authenticated UI. HomeModule also declares the two navigation surfaces (MenuMainComponent, MenuTopLeftComponent) and ~50 dialog/child components, so those live only inside the home chunk and cannot be reused on /login or /billing without re-declaration.

PreloadAllModules — “lazy” but eagerly downloaded

The router is configured with preloadingStrategy: PreloadAllModules. This is the single most important behavioral fact on this page:
The home and billing chunks are code-split but not deferred. PreloadAllModules downloads every lazy chunk in the background immediately after the app bootstraps. Do not assume a module is loaded on-demand just because it uses loadChildren — by the time a user navigates, the chunk is already in memory. If you add a lazy module expecting deferred loading (e.g. to shrink first paint), that expectation will not hold with this strategy.
Lazy loading here buys build-time code splitting (smaller individual chunks, better caching granularity, and enforced module boundaries), not runtime deferral. anchorScrolling: 'enabled' is the only other router option set.

Route guards

home and billing both carry canActivate: [AuthGuard]. The guard (src/app/auth/auth.guard.ts) awaits AuthService.isAuthenticated() and, on failure, calls router.navigate(['/login']):
src/app/auth/auth.guard.ts:14
async canActivate(_route, _state): Promise<boolean> {
  try {
    const isAuthenticated = await this.authService.isAuthenticated();
    if (isAuthenticated) return true;
    this.router.navigate(['/login']);
    return false;
  } catch (error) {
    console.error('Error checking authentication:', error);
    return false; // NOTE: no navigation on the error path
  }
}
Double redirect to /login. AuthService.isAuthenticated() also navigates to /login itself when the token is missing/invalid (src/app/auth/auth.service.ts), and then the guard navigates again. Both fire on an unauthenticated hit to a protected route. The catch branch above, however, returns false without navigating — so a thrown error blocks the route but leaves the user on a blank outlet. Full token mechanics are on

Auth: Guard, Interceptor & AuthService

the authentication page
.

Gotcha: there is no 404 route

There is no wildcard ** route in the table. An unknown URL matches nothing and the router renders an empty <router-outlet> — a blank page with no error, no redirect, and no “not found” UI.
If you are debugging a blank page on an unexpected URL, this is almost certainly the cause: the path simply didn’t match any route. A refactor that touches routing must either preserve this behavior consciously or fix it by appending a catch-all, e.g.:
// add as the LAST entry in routes[]
{ path: '**', redirectTo: '/home' }   // or a dedicated NotFoundComponent
Place it last — Angular matches top-to-bottom and ** swallows everything after it.

Gotcha: Isaac Sim is a half-wired dead route

The /isaac-sim route is commented out in app-routing.module.ts (both the import and the route entry), yet MenuMainComponent still calls this.router.navigate(['/isaac-sim']) (src/app/menu-main/menu-main.component.ts:168), gated behind the environment.enableIsaacSim feature flag. With the route disabled, that navigation matches nothing and — per the missing-404 behavior above — lands on a blank outlet. IsaacSimPageComponent itself is still declared in HomeModule. To actually enable the page, uncomment both the import and the route (it already carries canActivate: [AuthGuard]).

Adding or changing a route

1

Decide eager vs lazy

Auth/entry pages that must render before the app is authenticated go in AppModule and are wired as { path, component } (eager). Anything else should be its own NgModule loaded with loadChildren. Remember PreloadAllModules means “lazy” chunks still download up front.
2

Add the top-level entry

Edit the routes array in src/app/app-routing.module.ts. Add canActivate: [AuthGuard] if the page requires login (like home and billing).
3

Add child routes for a lazy module

Create a *-routing.module.ts using RouterModule.forChild(...) and import it into the feature module (mirror home-routing.module.ts / billing-routing.module.ts). Use static route data to parameterize a shared component, as billing does for success/cancel.
4

Wire any navigation

In-app navigation is driven by router.navigate([...]) calls (e.g. MenuMainComponent/billing, LoginComponent/home), not routerLink in most cases. Add the corresponding call where the UI triggers it. See

App Shell, Bootstrap & Structure

the overview
for the menu surfaces.
5

Keep a catch-all in mind

If your change is the first to introduce a ** route, put it last. Otherwise, be aware unknown paths still blank out.

App Shell, Bootstrap & Structure

main.ts → AppModule → AppComponent bootstrap, global chrome, and where <router-outlet> sits.

Auth: Guard, Interceptor & AuthService

AuthGuard, the Bearer interceptor, token refresh, and the double-redirect detail.

Environments, Build & CI

angular.json build configs, environment.enableIsaacSim, and the service-worker flag.

Drone Fleet Management

What the lazy /home route actually renders once past the guard.
Service worker caveat (build/PWA, not routing). angular.json sets serviceWorker: true for production/aws-dev/e2e builds with ngsw-config.json, but AppModule does not import ServiceWorkerModule.register('ngsw-worker.js'). If you rely on SW-cached routes surviving offline navigation, verify the worker is actually registered. Details on

Environments, Build & CI

the build page
.