diff --git a/openspec/changes/migrate-translatr-root-standalone/.openspec.yaml b/openspec/changes/migrate-translatr-root-standalone/.openspec.yaml
new file mode 100644
index 00000000..4ade2b29
--- /dev/null
+++ b/openspec/changes/migrate-translatr-root-standalone/.openspec.yaml
@@ -0,0 +1,3 @@
+schema: spec-driven
+created: 2026-09-19
+skip_specs: true
diff --git a/openspec/changes/migrate-translatr-root-standalone/design.md b/openspec/changes/migrate-translatr-root-standalone/design.md
new file mode 100644
index 00000000..a90d6cd2
--- /dev/null
+++ b/openspec/changes/migrate-translatr-root-standalone/design.md
@@ -0,0 +1,51 @@
+# Design
+
+## Context
+
+This is the fourth and final layer of the `apps/translatr` standalone migration (after `libs/translatr-components`, `apps/translatr/src/app/modules/shared`, `apps/translatr/src/app/modules/pages`). Everything under `apps/translatr/src/app/modules/pages/**` now routes through `*RoutingModule`s with no declarations `NgModule`s left. The only `standalone: false` components remaining in `apps/translatr` are `AppComponent` and `SidenavComponent`, plus `AppModule`, `SidenavModule`, and `SidenavTestingModule`.
+
+## Decisions
+
+### Use native NgRx/Router provider functions where they don't break `forFeature` elsewhere in the app
+
+NgRx 22 and Angular 22 (both in this workspace) ship first-class standalone provider functions: `provideStore`, `provideEffects`, `provideRouterStore`, `provideStoreDevtools`, `provideRouter`, `provideAnimations`, `provideHttpClient`. These are the officially recommended replacement for `StoreModule.forRoot`/`EffectsModule.forRoot`/`StoreRouterConnectingModule.forRoot`/`StoreDevtoolsModule.instrument`/`RouterModule.forRoot`/`BrowserAnimationsModule`/`HttpClientModule` respectively.
+
+**Correction found during implementation**: `provideStore()`/`provideEffects()` are *not* a safe drop-in for `StoreModule.forRoot()`/`EffectsModule.forRoot()` in this app, because every lazy page module still registers its feature state via the NgModule form (`StoreModule.forFeature()`/`EffectsModule.forFeature()`, unchanged and out of scope for this change). `StoreFeatureModule`/`EffectsFeatureModule`'s factories inject the `StoreRootModule`/`EffectsRootModule` marker classes, which only the NgModule `forRoot()` calls provide - `provideStore()`/`provideEffects()` don't provide them, so every lazy route threw `NG0201` at runtime (a failure the unit test suite, which mocks facades and never exercises real NgRx wiring end-to-end, did not catch - only a full e2e run did). Root store/effects registration therefore stays as `importProvidersFrom(StoreModule.forRoot(...))` / `importProvidersFrom(EffectsModule.forRoot([AppEffects]))`. `provideRouterStore` and `provideStoreDevtools` have no `forFeature` counterpart anywhere else in the app, so they're unaffected and stayed as their native functional forms, as did `provideRouter`/`provideAnimations`/`provideHttpClient` (no forFeature-equivalent coupling exists for those either).
+
+**Why not `importProvidersFrom` for these**: `importProvidersFrom` exists specifically for NgModules that aren't easily rewritten as provider functions (third-party libraries, or modules with non-trivial internal setup). NgRx and the Router ship the provider-function equivalents precisely so apps don't need it for their own root wiring.
+
+### Keep `TranslocoRootModule` and `TranslatrSdkModule` as NgModules, use `importProvidersFrom`
+
+Both encapsulate multiple related `@Injectable()` classes and provider configs that aren't simple 1:1 provider-function swaps, and `TranslatrSdkModule` lives in a separate lib (`libs/translatr-sdk`) that's out of scope for this app-only change. `importProvidersFrom(TranslocoRootModule)` / `importProvidersFrom(TranslatrSdkModule)` is the standard, supported way to consume an NgModule's providers from a standalone bootstrap without touching the module itself.
+
+### `AppComponent`'s own `imports` are near-empty
+
+`AppModule` only ever declared `AppComponent` (`declarations: [AppComponent]`) — every other `NgModule` in its `imports` array (`MatToolbarModule`, `MatButtonModule`, `MatSnackBarModule`, `MatDialogModule`, `LayoutModule`, `FeatureFlagDirective`, `FeatureFlagClassDirective`, `FontAwesomeModule`, `SvgIconComponent`) existed only to make those directives/components available to `AppComponent`'s template. `AppComponent`'s actual template is the single line `` (declared inline, no `.html` file), so none of them are used. Standalone `AppComponent` needs only `RouterModule` (for `router-outlet`) in its own `imports` — the rest were vestigial, matching the "drop unused pooled imports" pattern established throughout the earlier three changes in this migration (NgModules historically pooled imports across every component they declared, not just the one importing them).
+
+### `SidenavComponent` conversion mirrors the earlier `libs`/`shared` pattern
+
+`SidenavModule` is a thin declarations-wrapper around a single component (`SidenavComponent`), identical in shape to modules already converted in the first two changes of this migration. Convert it the same way: `SidenavComponent` becomes `standalone: true` with explicit `imports` derived from its template (tag/directive scan), `SidenavModule` is deleted, and its 8 consumers switch from importing `SidenavModule` to importing `SidenavComponent` directly. `SidenavTestingModule`'s `MockSidenavComponent` converts the same way group 8 of the pages change converted its testing doubles: standalone, file renamed to `mock-sidenav.component.ts`, `SidenavTestingModule` deleted, consumer specs updated to import the class directly.
+
+### `main.ts` provider order and dev-only `StoreDevtoolsModule`
+
+`AppModule`'s `!environment.production ? StoreDevtoolsModule.instrument() : []` becomes `provideStoreDevtools()` gated the same way (`...(!environment.production ? [provideStoreDevtools()] : [])`), preserving prod builds shipping without devtools instrumentation.
+
+### Routes stay defined in `app-routing.module.ts`
+
+`AppRoutingModule`'s `Routes` array does not need to move into `main.ts` — `provideRouter(routes)` accepts the same `Routes` array already exported there. `AppRoutingModule` itself becomes unnecessary once nothing needs its `NgModule` wrapper (its `RouterModule.forRoot(routes, {...})` config becomes `provideRouter(routes, withRouterConfig(...))`-equivalent options passed to `provideRouter`), so it is deleted and `routes` is exported directly from `app-routing.module.ts` (kept as a plain `.ts` file, not renamed, to avoid an unrelated file-move) or inlined into `main.ts` — decided during implementation based on which keeps `main.ts` most readable.
+
+## Risks / Trade-offs
+
+- **Provider-function feature parity**: `provideRouter`'s options (`withRouterConfig`, `withComponentInputBinding`, etc.) are a different shape than `RouterModule.forRoot`'s second argument (`{ enableTracing: environment.routerTracing }`). `enableTracing` maps to `withDebugTracing()` conditionally included, verified against the Router's own provider-function API rather than assumed.
+- **Bootstrap error visibility**: `bootstrapApplication(...).catch(err => console.error(err))` (unchanged) is the only bootstrap-failure signal; no change in behavior from `bootstrapModule`, but worth a manual smoke check after the rewrite since a provider-wiring mistake here breaks the entire app at boot, unlike a single-route wiring mistake in the pages change.
+
+## Migration Plan
+
+1. Convert `SidenavComponent` + update its 8 consumers + convert `SidenavTestingModule` (mirrors the pages change's component-conversion pattern, done first since it has no bootstrap risk).
+2. Convert `AppComponent` to standalone.
+3. Rewrite `main.ts` with `bootstrapApplication` and the full provider list; delete `app.module.ts` and `app-routing.module.ts`'s `NgModule` wrapper (or the whole file, per the decision above).
+4. Verify `nx test translatr`, `nx build translatr`, and a manual smoke test (login flow, a lazy route, NgRx devtools presence in dev) since this is the one change in the whole migration where a mistake can break the entire app rather than one route.
+
+## Open Questions
+
+None — this is a direct architectural continuation of the pattern already validated three times in this migration.
diff --git a/openspec/changes/migrate-translatr-root-standalone/proposal.md b/openspec/changes/migrate-translatr-root-standalone/proposal.md
new file mode 100644
index 00000000..ee4bec2c
--- /dev/null
+++ b/openspec/changes/migrate-translatr-root-standalone/proposal.md
@@ -0,0 +1,22 @@
+# Proposal
+
+## Why
+
+`apps/translatr` has one piece left on `standalone: false`: the root bootstrap (`app.module.ts`, `main.ts`, `AppComponent`, `SidenavComponent`) plus `SidenavModule`/`SidenavTestingModule`. This is the fourth and final layer for `apps/translatr` after `libs/translatr-components`, `apps/translatr/src/app/modules/shared`, and `apps/translatr/src/app/modules/pages` — converting it removes `AppModule` entirely and switches the app to `bootstrapApplication`, the standalone-native bootstrap Angular has recommended since v14 (and the only bootstrap path Angular intends to keep long-term).
+
+## What Changes
+
+- Convert `AppComponent` to `standalone: true`. Its own template is just ``, so its own `imports` need only `RouterModule` — every other import on the current `AppModule` (`MatToolbarModule`, `MatButtonModule`, `MatSnackBarModule`, `MatDialogModule`, `LayoutModule`, `FeatureFlagDirective`, `FeatureFlagClassDirective`, `FontAwesomeModule`, `SvgIconComponent`) is vestigial for `AppComponent` specifically (it was only ever needed by other components that `AppModule` used to declare, and none of those are declared here — `AppModule` only ever declared `AppComponent` itself).
+- Convert `SidenavComponent` to `standalone: true` with explicit `imports` derived from its template. Delete `SidenavModule`; update its 8 consumers (`user-page`, `project-page`, `dashboard-page`, `projects-page`, `users-page`, `registration-page`, `editor-page/editor`, plus their specs) to import `SidenavComponent` directly.
+- Convert `SidenavTestingModule`'s `MockSidenavComponent` to `standalone: true`; rename the file to `mock-sidenav.component.ts`; update every consumer spec; delete `sidenav-testing.module.ts`.
+- Delete `app.module.ts`. Rewrite `main.ts` to call `bootstrapApplication(AppComponent, { providers: [...] })`, replacing `AppModule`'s `imports`/`providers` with the standalone-native provider functions available in this codebase's Angular 22 / NgRx 22: `provideRouter` (from `AppRoutingModule`'s existing `Routes` array), `provideAnimations`, `provideHttpClient(withXhr(), withInterceptorsFromDi())`, `provideStore`, `provideEffects([AppEffects])`, `provideRouterStore({ routerState: RouterState.Minimal })`, `provideStoreDevtools()` (dev only), plus `provideSvgIcons([])` and the existing non-NgModule providers (`AppFacade`, `FeatureFlagFacade`, `WINDOW`, `ENDPOINT_URL`, `LOGIN_URL`, `NotificationService`, `httpInterceptorProviders`, `HotkeysService`) carried over unchanged. `TranslocoRootModule` and `TranslatrSdkModule` (both lib/app NgModules with non-trivial provider setups, out of scope to rewrite) are kept as-is and pulled in via `importProvidersFrom`.
+
+## Capabilities
+
+No spec-level behavior changes — bootstrap, routing, NgRx root state, and Transloco behavior stay identical, only the underlying module/bootstrap architecture changes. Internal architecture change only (`skip_specs: true` set in `.openspec.yaml`).
+
+## Impact
+
+- **Code**: `apps/translatr/src/main.ts`, `apps/translatr/src/app/app.module.ts` (deleted), `apps/translatr/src/app/app.component.ts` (+ spec), `apps/translatr/src/app/modules/nav/sidenav/**` (component, deleted module, testing), and the 8 page components that compose `` (+ their specs).
+- **Providers**: everything `AppModule` provided (NgRx root store/effects/router-store/devtools, HTTP client + interceptors, Transloco root, the SDK module, `AppFacade`, feature-flag facade, hotkeys, SVG icons) moves into `main.ts`'s `bootstrapApplication` providers array, unchanged in substance.
+- **Out of scope**: `apps/translatr-admin` entirely — a separate, later change, if pursued at all (not explicitly planned beyond this point).
diff --git a/openspec/changes/migrate-translatr-root-standalone/tasks.md b/openspec/changes/migrate-translatr-root-standalone/tasks.md
new file mode 100644
index 00000000..927455cd
--- /dev/null
+++ b/openspec/changes/migrate-translatr-root-standalone/tasks.md
@@ -0,0 +1,27 @@
+# Tasks
+
+## 1. SidenavComponent and its consumers
+
+- [x] 1.1 Convert `SidenavComponent` to `standalone: true` with explicit `imports` derived from its template (systematic tag/`*directive` extraction; dropped vestigial `MatSidenavModule`/`MatToolbarModule`/`MatListModule`/`FeatureFlagClassDirective` - not used in its own template, `ngProjectAs="mat-toolbar-row"` is just projection metadata for the caller's content, not a directive SidenavComponent itself needs; kept the `{ provide: LanguageSwicher, useClass: AppFacade }` component-level provider, matching the deleted module's scope)
+- [x] 1.2 Delete `sidenav.module.ts`; update its 8 consumers (`user-page.component.ts`, `project-page.component.ts`, `dashboard-page.component.ts`, `projects-page.component.ts`, `users-page.component.ts`, `registration-page.component.ts`, `editor-page/editor/editor.component.ts`, plus specs) to import `SidenavComponent` directly instead of `SidenavModule`
+- [x] 1.3 Convert `SidenavTestingModule`'s `MockSidenavComponent` to `standalone: true`; renamed file to `mock-sidenav.component.ts`; updated every consumer spec to import the class directly; deleted `sidenav-testing.module.ts`; `sidenav.component.spec.ts` itself needed a `TestBed.overrideComponent` swap (real→mock children) since it previously used `declarations:` against a non-standalone component
+- [x] 1.4 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean)
+
+## 2. AppComponent
+
+- [x] 2.1 Convert `AppComponent` to `standalone: true` with `imports: [RouterModule]` (its template is only ``); update `app.component.spec.ts` (dropped `declarations: [AppComponent]` from the Spectator `createComponentFactory` config - a standalone component can't be declared)
+- [x] 2.2 Verify `nx test translatr` succeeds (deferred the actual run to task 3.4, since `AppComponent` becoming standalone and `app.module.ts` declaring it are mutually exclusive - these two tasks are atomic together, not independently buildable)
+
+## 3. Root bootstrap
+
+- [x] 3.1 Rewrote `main.ts` to call `bootstrapApplication(AppComponent, { providers: [...] })`, replacing every `AppModule` import/provider with its standalone-native equivalent: `provideRouter(routes, ...(environment.routerTracing ? [withDebugTracing()] : []))`, `provideAnimations()`, `provideHttpClient(withXhr(), withInterceptorsFromDi())`, `provideStore({ app: appReducer, router: routerReducer }, {...})` (kept the `router: routerReducer` root-reducer-map entry - `provideRouterStore()` connects the router to the store but does not itself register the reducer, matching its own usage docs), `provideEffects([AppEffects])`, `provideRouterStore({ routerState: RouterState.Minimal })`, `provideStoreDevtools()` gated the same `!environment.production` way as before, `importProvidersFrom(TranslocoRootModule)`, `importProvidersFrom(TranslatrSdkModule)`, plus the existing non-NgModule providers (`AppFacade`, `FeatureFlagFacade`, `WINDOW`, `ENDPOINT_URL`, `LOGIN_URL`, `NotificationService`, `httpInterceptorProviders`, `HotkeysService`, `provideSvgIcons([])`) carried over unchanged
+- [x] 3.2 Deleted `app.module.ts`
+- [x] 3.3 `app-routing.module.ts` kept as a plain file (not renamed) exporting `routes: Routes` directly; deleted its `NgModule`/`RouterModule.forRoot` wrapper
+- [x] 3.4 Verified `nx test translatr` (159/159), `tsc --noEmit` (clean), and `nx build translatr` (clean) succeed. A manual e2e run then caught a real bug the unit suite couldn't: `provideStore()`/`provideEffects()` (the functional forms) don't provide the `StoreRootModule`/`EffectsRootModule` marker tokens that `StoreModule.forFeature()`/`EffectsModule.forFeature()` (used throughout every lazy page module, unchanged/out of scope here) inject in their factories - mixing functional root providers with NgModule-form feature registration threw NG0201 on every lazy route. Fixed by keeping the root store/effects registration as `importProvidersFrom(StoreModule.forRoot(...))` / `importProvidersFrom(EffectsModule.forRoot([AppEffects]))` instead of `provideStore`/`provideEffects`, while `provideRouterStore` and `provideStoreDevtools` (which have no `forFeature` counterpart elsewhere in the app) stayed as their native functional forms. Full local e2e suite: 187/187 passing after the fix
+
+## 4. Cleanup and verification
+
+- [x] 4.1 Grep `apps/translatr` for any remaining reference to `AppModule` or `SidenavModule` and confirm zero matches (confirmed clean)
+- [x] 4.2 Run `nx build translatr` and verify it succeeds with no TypeScript errors (clean)
+- [x] 4.3 Run `nx test translatr` and verify all suites pass (159/159)
+- [x] 4.4 Smoke-tested via the browser pane (main page renders with zero console errors beyond expected unauthenticated 401s, `/login` route resolves) and via the full local e2e suite (187/187 passing), which exercises every lazy route, guard, and NgRx feature state end to end - a stronger signal here than manual devtools inspection, since the one real bug in this change (task 3.4's NG0201) was caught by the e2e run, not by manual browsing
diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts
index d4e7908a..7db559c4 100644
--- a/ui/apps/translatr/src/app/app-routing.module.ts
+++ b/ui/apps/translatr/src/app/app-routing.module.ts
@@ -1,8 +1,6 @@
-import { NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
-import { environment } from '../environments/environment';
+import { Routes } from '@angular/router';
-const routes: Routes = [
+export const routes: Routes = [
{
path: 'login',
loadComponent: () => import('./modules/pages/login-page').then(m => m.LoginPageComponent)
@@ -71,13 +69,3 @@ const routes: Routes = [
)
}
];
-
-@NgModule({
- imports: [
- RouterModule.forRoot(routes, {
- enableTracing: environment.routerTracing
- })
- ],
- exports: [RouterModule]
-})
-export class AppRoutingModule {}
diff --git a/ui/apps/translatr/src/app/app.component.spec.ts b/ui/apps/translatr/src/app/app.component.spec.ts
index 1c57feef..70f736a7 100644
--- a/ui/apps/translatr/src/app/app.component.spec.ts
+++ b/ui/apps/translatr/src/app/app.component.spec.ts
@@ -11,7 +11,6 @@ describe('AppComponent', () => {
const createComponent = createComponentFactory({
component: AppComponent,
imports: [RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), MatDialogModule],
- declarations: [AppComponent],
providers: [
mockProvider(AppFacade, {
me$: mockObservable()
diff --git a/ui/apps/translatr/src/app/app.component.ts b/ui/apps/translatr/src/app/app.component.ts
index 3685ab99..bc6449c0 100644
--- a/ui/apps/translatr/src/app/app.component.ts
+++ b/ui/apps/translatr/src/app/app.component.ts
@@ -1,5 +1,6 @@
import { AfterViewInit, Component, ChangeDetectionStrategy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
+import { RouterModule } from '@angular/router';
import { ThemeService } from '@dev/translatr-components';
import { HotkeysHelpComponent, HotkeysService } from '@ngneat/hotkeys';
import { TranslocoService } from '@jsverse/transloco';
@@ -7,10 +8,11 @@ import { filter } from 'rxjs/operators';
import { AppFacade } from './+state/app.facade';
@Component({
- standalone: false,
+ standalone: true,
selector: 'app-root',
changeDetection: ChangeDetectionStrategy.Eager,
- template: ''
+ template: '',
+ imports: [RouterModule]
})
export class AppComponent implements AfterViewInit {
constructor(
diff --git a/ui/apps/translatr/src/app/app.module.ts b/ui/apps/translatr/src/app/app.module.ts
deleted file mode 100644
index 2591230c..00000000
--- a/ui/apps/translatr/src/app/app.module.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { LayoutModule } from '@angular/cdk/layout';
-import { provideHttpClient, withInterceptorsFromDi, withXhr } from '@angular/common/http';
-import { NgModule } from '@angular/core';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDialogModule } from '@angular/material/dialog';
-import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
-import { MatToolbarModule } from '@angular/material/toolbar';
-import { BrowserModule } from '@angular/platform-browser';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { FeatureFlagDirective, FeatureFlagClassDirective } from '@dev/translatr-components';
-import { FeatureFlagFacade } from '@dev/translatr-model';
-import { NotificationService, TranslatrSdkModule } from '@dev/translatr-sdk';
-import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
-import { HotkeysService } from '@ngneat/hotkeys';
-import { SvgIconComponent, provideSvgIcons } from '@ngneat/svg-icon';
-import { EffectsModule } from '@ngrx/effects';
-import { routerReducer, RouterState, StoreRouterConnectingModule } from '@ngrx/router-store';
-import { StoreModule } from '@ngrx/store';
-import { StoreDevtoolsModule } from '@ngrx/store-devtools';
-import { ENDPOINT_URL, LOGIN_URL, WINDOW } from '@translatr/utils';
-import { environment } from '../environments/environment';
-import { AppEffects } from './+state/app.effects';
-import { AppFacade } from './+state/app.facade';
-import { appReducer } from './+state/app.reducer';
-import { AppRoutingModule } from './app-routing.module';
-import { AppComponent } from './app.component';
-import { httpInterceptorProviders } from './interceptors';
-import { TranslocoRootModule } from './modules/shared/transloco';
-import { MatNotificationService } from './services/mat-notification-service';
-
-@NgModule({
- declarations: [AppComponent],
- imports: [
- BrowserModule,
- BrowserAnimationsModule,
-
- AppRoutingModule,
- TranslatrSdkModule,
- LayoutModule,
- FeatureFlagDirective, FeatureFlagClassDirective,
-
- MatToolbarModule,
- MatButtonModule,
- MatSnackBarModule,
- MatDialogModule,
-
- StoreModule.forRoot(
- {
- app: appReducer,
- router: routerReducer
- },
- {
- metaReducers: [],
- runtimeChecks: {
- strictStateImmutability: true,
- strictActionImmutability: true
- }
- }
- ),
- EffectsModule.forRoot([AppEffects]),
- !environment.production ? StoreDevtoolsModule.instrument() : [],
- StoreRouterConnectingModule.forRoot({ routerState: RouterState.Minimal }),
- TranslocoRootModule,
- FontAwesomeModule,
- SvgIconComponent
- ],
- providers: [
- provideHttpClient(withXhr(), withInterceptorsFromDi()),
- AppFacade,
- { provide: FeatureFlagFacade, useClass: AppFacade },
- { provide: WINDOW, useFactory: () => window },
- { provide: ENDPOINT_URL, useValue: environment.endpointUrl },
- { provide: LOGIN_URL, useValue: `/login` },
- {
- provide: NotificationService,
- useFactory: (snackBar: MatSnackBar) => new MatNotificationService(snackBar),
- deps: [MatSnackBar]
- },
- httpInterceptorProviders,
- HotkeysService,
- provideSvgIcons([])
- ],
- bootstrap: [AppComponent]
-})
-export class AppModule {}
diff --git a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.spec.ts b/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.spec.ts
index c87904b5..bddb13e5 100644
--- a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.spec.ts
@@ -1,16 +1,19 @@
-import { CommonModule } from '@angular/common';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
-import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
-import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslocoTestingModule } from '@jsverse/transloco';
import {
- MockFeatureFlagDirective, MockFeatureFlagClassDirective,
+ AuthBarItemComponent,
+ AuthBarLanguageSwitcherComponent,
+ FeatureFlagDirective,
+ FooterComponent,
+ NavbarComponent
+} from '@dev/translatr-components';
+import {
+ MockFeatureFlagDirective,
MockFooterComponent,
MockNavbarComponent, MockAuthBarItemComponent, MockAuthBarLanguageSwitcherComponent
} from '@translatr/components/testing';
@@ -21,21 +24,33 @@ describe('SidenavComponent', () => {
let fixture: ComponentFixture;
beforeEach(() => {
- TestBed.configureTestingModule({
- declarations: [SidenavComponent],
+ TestBed.overrideComponent(SidenavComponent, {
+ remove: {
+ imports: [
+ NavbarComponent,
+ AuthBarItemComponent,
+ AuthBarLanguageSwitcherComponent,
+ FooterComponent,
+ FeatureFlagDirective
+ ]
+ },
+ add: {
+ imports: [
+ MockNavbarComponent,
+ MockAuthBarItemComponent,
+ MockAuthBarLanguageSwitcherComponent,
+ MockFooterComponent,
+ MockFeatureFlagDirective
+ ]
+ }
+ }).configureTestingModule({
imports: [
- CommonModule,
+ SidenavComponent,
+
RouterTestingModule,
TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }),
- MockNavbarComponent, MockAuthBarItemComponent, MockAuthBarLanguageSwitcherComponent,
- MockFooterComponent,
- MockFeatureFlagDirective, MockFeatureFlagClassDirective,
-
- MatToolbarModule,
MatIconModule,
- MatButtonModule,
- MatListModule,
MatMenuModule,
MatDividerModule,
MatTooltipModule
diff --git a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.ts b/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.ts
index 7ac52ae0..fbdd3e2b 100644
--- a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.ts
+++ b/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.component.ts
@@ -1,14 +1,47 @@
+import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-import { Link } from '@dev/translatr-components';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatIconModule } from '@angular/material/icon';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { RouterModule } from '@angular/router';
+import {
+ AuthBarItemComponent,
+ AuthBarLanguageSwitcherComponent,
+ FeatureFlagDirective,
+ FooterComponent,
+ LanguageSwicher,
+ Link,
+ NavbarComponent
+} from '@dev/translatr-components';
import { Feature, User, UserRole } from '@dev/translatr-model';
+import { TranslocoModule } from '@jsverse/transloco';
+import { AppFacade } from '../../../+state/app.facade';
import { environment } from '../../../../environments/environment';
@Component({
- standalone: false,
+ standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-sidenav',
templateUrl: './sidenav.component.html',
- styleUrls: ['./sidenav.component.scss']
+ styleUrls: ['./sidenav.component.scss'],
+ imports: [
+ CommonModule,
+ RouterModule,
+ TranslocoModule,
+ NavbarComponent,
+ AuthBarItemComponent,
+ AuthBarLanguageSwitcherComponent,
+ FooterComponent,
+ FeatureFlagDirective,
+ MatIconModule,
+ MatButtonModule,
+ MatMenuModule,
+ MatDividerModule,
+ MatTooltipModule
+ ],
+ providers: [{ provide: LanguageSwicher, useClass: AppFacade }]
})
export class SidenavComponent {
@Input() page: string;
diff --git a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.module.ts b/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.module.ts
deleted file mode 100644
index d05c7027..00000000
--- a/ui/apps/translatr/src/app/modules/nav/sidenav/sidenav.module.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDividerModule } from '@angular/material/divider';
-import { MatIconModule } from '@angular/material/icon';
-import { MatListModule } from '@angular/material/list';
-import { MatMenuModule } from '@angular/material/menu';
-import { MatSidenavModule } from '@angular/material/sidenav';
-import { MatToolbarModule } from '@angular/material/toolbar';
-import { MatTooltipModule } from '@angular/material/tooltip';
-import { RouterModule } from '@angular/router';
-import {
- AuthBarItemComponent,
- AuthBarLanguageSwitcherComponent,
- FeatureFlagDirective,
- FeatureFlagClassDirective,
- FooterComponent,
- LanguageSwicher,
- NavbarComponent,
- SearchBarComponent
-} from '@dev/translatr-components';
-import { TranslocoModule } from '@jsverse/transloco';
-import { AppFacade } from '../../../+state/app.facade';
-import { SidenavComponent } from './sidenav.component';
-
-@NgModule({
- declarations: [SidenavComponent],
- imports: [
- CommonModule,
- RouterModule,
- FooterComponent,
- NavbarComponent,
- AuthBarItemComponent,
- AuthBarLanguageSwitcherComponent,
- SearchBarComponent,
-
- MatSidenavModule,
- MatToolbarModule,
- MatIconModule,
- MatButtonModule,
- MatListModule,
- MatMenuModule,
- MatDividerModule,
- MatTooltipModule,
- FeatureFlagDirective, FeatureFlagClassDirective,
- TranslocoModule
- ],
- providers: [{ provide: LanguageSwicher, useClass: AppFacade }],
- exports: [SidenavComponent]
-})
-export class SidenavModule {}
diff --git a/ui/apps/translatr/src/app/modules/nav/sidenav/testing/index.ts b/ui/apps/translatr/src/app/modules/nav/sidenav/testing/index.ts
index d19a51b0..18cc4d57 100644
--- a/ui/apps/translatr/src/app/modules/nav/sidenav/testing/index.ts
+++ b/ui/apps/translatr/src/app/modules/nav/sidenav/testing/index.ts
@@ -1 +1 @@
-export * from './sidenav-testing.module';
+export * from './mock-sidenav.component';
diff --git a/ui/apps/translatr/src/app/modules/nav/sidenav/testing/sidenav-testing.module.ts b/ui/apps/translatr/src/app/modules/nav/sidenav/testing/mock-sidenav.component.ts
similarity index 59%
rename from ui/apps/translatr/src/app/modules/nav/sidenav/testing/sidenav-testing.module.ts
rename to ui/apps/translatr/src/app/modules/nav/sidenav/testing/mock-sidenav.component.ts
index 719843f0..77119a77 100644
--- a/ui/apps/translatr/src/app/modules/nav/sidenav/testing/sidenav-testing.module.ts
+++ b/ui/apps/translatr/src/app/modules/nav/sidenav/testing/mock-sidenav.component.ts
@@ -1,14 +1,14 @@
-import { Component, Input, NgModule, ChangeDetectionStrategy } from '@angular/core';
+import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
import { Link } from '@dev/translatr-components';
import { User } from '@dev/translatr-model';
@Component({
- standalone: false,
+ standalone: true,
selector: 'app-sidenav',
changeDetection: ChangeDetectionStrategy.Eager,
template: ''
})
-class MockSidenavComponent {
+export class MockSidenavComponent {
@Input() page: string;
@Input() backLink: Link;
@Input() me: User | undefined;
@@ -16,9 +16,3 @@ class MockSidenavComponent {
@Input() overlay = false;
@Input() showDashboardLink = false;
}
-
-@NgModule({
- declarations: [MockSidenavComponent],
- exports: [MockSidenavComponent]
-})
-export class SidenavTestingModule {}
diff --git a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.spec.ts
index b4137940..cf001ba6 100644
--- a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.spec.ts
@@ -6,13 +6,13 @@ import { FeatureFlagClassDirective, MetricComponent } from '@dev/translatr-compo
import { MockFeatureFlagClassDirective, MockMetricComponent } from '@translatr/components/testing';
import { of } from 'rxjs';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { ActivityListComponent } from '../../shared/activity-list/activity-list.component';
import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component';
import {
MockActivityListComponent,
MockProjectCardListComponent,
- SidenavTestingModule
+ MockSidenavComponent
} from '../../testing';
import { ProjectsFacade } from '../projects-page/+state/projects.facade';
import { DashboardFacade } from './+state/dashboard.facade';
@@ -26,7 +26,7 @@ describe('DashboardPageComponent', () => {
TestBed.overrideComponent(DashboardPageComponent, {
remove: {
imports: [
- SidenavModule,
+ SidenavComponent,
MetricComponent,
ActivityListComponent,
ProjectCardListComponent,
@@ -35,7 +35,7 @@ describe('DashboardPageComponent', () => {
},
add: {
imports: [
- SidenavTestingModule,
+ MockSidenavComponent,
MockMetricComponent,
MockActivityListComponent,
MockProjectCardListComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.ts b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.ts
index 1887903a..7e28b4d9 100644
--- a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.component.ts
@@ -7,7 +7,7 @@ import { Feature, User } from '@dev/translatr-model';
import { TranslocoModule } from '@jsverse/transloco';
import { filter, take } from 'rxjs/operators';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { ActivityListComponent } from '../../shared/activity-list/activity-list.component';
import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component';
import { openProjectEditDialog } from '../../shared/project-edit-dialog/project-edit-dialog.component';
@@ -24,7 +24,7 @@ import { DashboardFacade } from './+state/dashboard.facade';
CommonModule,
RouterModule,
TranslocoModule,
- SidenavModule,
+ SidenavComponent,
MetricComponent,
ShortNumberPipe,
ProjectCardListComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.spec.ts
index 680a53e9..4c37fc2e 100644
--- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.spec.ts
@@ -19,8 +19,8 @@ import {
} from '@dev/translatr-components';
import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing';
import { EditorFacade } from '../+state/editor.facade';
-import { SidenavModule } from '../../../nav/sidenav/sidenav.module';
-import { SidenavTestingModule } from '../../../nav/sidenav/testing';
+import { SidenavComponent } from '../../../nav/sidenav/sidenav.component';
+import { MockSidenavComponent } from '../../../nav/sidenav/testing';
import { EMPTY, Subject } from 'rxjs';
import { EditorComponent } from './editor.component';
@@ -45,10 +45,10 @@ const editorTestImports = [
const overrideEditorComponent = () =>
TestBed.overrideComponent(EditorComponent, {
- remove: { imports: [SidenavModule, EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent] },
+ remove: { imports: [SidenavComponent, EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent] },
add: {
imports: [
- SidenavTestingModule,
+ MockSidenavComponent,
MockEmptyViewComponent,
MockEmptyViewActionsComponent,
MockEmptyViewContentComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.ts
index 564fe248..3e721b75 100644
--- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor.component.ts
@@ -30,7 +30,7 @@ import { HotkeysService } from '@ngneat/hotkeys';
import { TranslocoModule, TranslocoService } from '@jsverse/transloco';
import { Subscription } from 'rxjs';
import { filter, skip, take } from 'rxjs/operators';
-import { SidenavModule } from '../../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../../nav/sidenav/sidenav.component';
import { EditorFacade } from '../+state/editor.facade';
import { SaveBehavior } from '../save-behavior';
@@ -47,7 +47,7 @@ import 'codemirror/mode/xml/xml';
RouterModule,
FormsModule,
TranslocoModule,
- SidenavModule,
+ SidenavComponent,
MatDividerModule,
MatMenuModule,
MatButtonModule,
diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.spec.ts
index 26e4ee42..f8477383 100644
--- a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.spec.ts
@@ -8,8 +8,8 @@ import { FeatureFlagClassDirective } from '@dev/translatr-components';
import { MockFeatureFlagClassDirective } from '@translatr/components/testing';
import { mockObservable } from '@translatr/utils/testing';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
-import { SidenavTestingModule } from '../../nav/sidenav/testing';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
+import { MockSidenavComponent } from '../../nav/sidenav/testing';
import { ProjectFacade } from '../../shared/project-state';
import { ProjectPageComponent } from './project-page.component';
@@ -22,8 +22,8 @@ describe('ProjectPageComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.overrideComponent(ProjectPageComponent, {
- remove: { imports: [SidenavModule, FeatureFlagClassDirective] },
- add: { imports: [SidenavTestingModule, MockFeatureFlagClassDirective] }
+ remove: { imports: [SidenavComponent, FeatureFlagClassDirective] },
+ add: { imports: [MockSidenavComponent, MockFeatureFlagClassDirective] }
}).configureTestingModule({
imports: [
ProjectPageComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.ts
index 6a84ddb2..81959dfa 100644
--- a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-page.component.ts
@@ -25,7 +25,7 @@ import {
takeUntil
} from 'rxjs/operators';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { ProjectFacade } from '../../shared/project-state';
import { PROJECT_ROUTES } from './project-page.token';
@@ -35,7 +35,7 @@ import { PROJECT_ROUTES } from './project-page.token';
selector: 'app-project-page',
templateUrl: './project-page.component.html',
styleUrls: ['./project-page.component.scss'],
- imports: [CommonModule, RouterModule, TranslocoModule, SidenavModule, MatIconModule, MatTabsModule, FeatureFlagClassDirective]
+ imports: [CommonModule, RouterModule, TranslocoModule, SidenavComponent, MatIconModule, MatTabsModule, FeatureFlagClassDirective]
})
export class ProjectPageComponent implements OnInit, OnDestroy {
me$ = this.appFacade.me$;
diff --git a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.spec.ts
index 102fa2ad..481111e3 100644
--- a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.spec.ts
@@ -9,9 +9,9 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { mockObservable } from '@translatr/utils/testing';
import { FeatureFlagClassDirective } from '@dev/translatr-components';
import { MockFeatureFlagDirective, MockFeatureFlagClassDirective } from '@translatr/components/testing';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { ProjectListComponent } from '../../shared/project-list/project-list.component';
-import { MockProjectListComponent, SidenavTestingModule } from '../../testing';
+import { MockProjectListComponent, MockSidenavComponent } from '../../testing';
import { MatDialog } from '@angular/material/dialog';
import { TranslocoTestingModule } from '@jsverse/transloco';
@@ -22,8 +22,8 @@ describe('UsersPageComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.overrideComponent(ProjectsPageComponent, {
- remove: { imports: [SidenavModule, ProjectListComponent, FeatureFlagClassDirective] },
- add: { imports: [SidenavTestingModule, MockProjectListComponent, MockFeatureFlagClassDirective] }
+ remove: { imports: [SidenavComponent, ProjectListComponent, FeatureFlagClassDirective] },
+ add: { imports: [MockSidenavComponent, MockProjectListComponent, MockFeatureFlagClassDirective] }
}).configureTestingModule({
imports: [
ProjectsPageComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.ts b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.ts
index cdd24fe8..ac431bd0 100644
--- a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.component.ts
@@ -12,7 +12,7 @@ import { navigate } from '@translatr/utils';
import { combineLatest } from 'rxjs';
import { distinctUntilChanged, filter, map, take, takeUntil } from 'rxjs/operators';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { FilterCriteria } from '../../shared/list-header/list-header.component';
import { openProjectEditDialog } from '../../shared/project-edit-dialog/project-edit-dialog.component';
import { ProjectListComponent } from '../../shared/project-list/project-list.component';
@@ -27,7 +27,7 @@ import { ProjectsFacade } from './+state/projects.facade';
imports: [
CommonModule,
TranslocoModule,
- SidenavModule,
+ SidenavComponent,
ProjectListComponent,
MatIconModule,
MatButtonModule,
diff --git a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.ts b/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.ts
index 14a90ab6..e746b3dc 100644
--- a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.ts
@@ -11,7 +11,7 @@ import { faFacebook, faGithub, faGoogle, faTwitter } from '@fortawesome/free-bra
import { faKey } from '@fortawesome/free-solid-svg-icons';
import { TranslocoModule } from '@jsverse/transloco';
import { Subject } from 'rxjs';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
@Component({
standalone: true,
@@ -21,7 +21,7 @@ import { SidenavModule } from '../../nav/sidenav/sidenav.module';
styleUrls: ['./registration-page.component.scss'],
imports: [
CommonModule,
- SidenavModule,
+ SidenavComponent,
TranslocoModule,
FeatureFlagDirective, FeatureFlagClassDirective,
UserEditFormComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.spec.ts
index 8d54ecaf..322c6d25 100644
--- a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.spec.ts
@@ -7,8 +7,8 @@ import { TranslocoTestingModule } from '@jsverse/transloco';
import { FeatureFlagClassDirective } from '@dev/translatr-components';
import { MockFeatureFlagClassDirective } from '@translatr/components/testing';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
-import { SidenavTestingModule } from '../../nav/sidenav/testing';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
+import { MockSidenavComponent } from '../../nav/sidenav/testing';
import { UserFacade } from './+state/user.facade';
import { UserPageComponent } from './user-page.component';
@@ -21,8 +21,8 @@ describe('UserPageComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.overrideComponent(UserPageComponent, {
- remove: { imports: [SidenavModule, FeatureFlagClassDirective] },
- add: { imports: [SidenavTestingModule, MockFeatureFlagClassDirective] }
+ remove: { imports: [SidenavComponent, FeatureFlagClassDirective] },
+ add: { imports: [MockSidenavComponent, MockFeatureFlagClassDirective] }
}).configureTestingModule({
imports: [
UserPageComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.ts
index a00dad78..c3798369 100644
--- a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-page.component.ts
@@ -9,7 +9,7 @@ import { TranslocoModule } from '@jsverse/transloco';
import { canActivate$, NameIconRoute } from '@translatr/utils';
import { Observable } from 'rxjs';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { UserFacade } from './+state/user.facade';
import { USER_ROUTES } from './user-page.token';
@@ -19,7 +19,7 @@ import { USER_ROUTES } from './user-page.token';
templateUrl: './user-page.component.html',
changeDetection: ChangeDetectionStrategy.Eager,
styleUrls: ['./user-page.component.scss'],
- imports: [CommonModule, RouterModule, TranslocoModule, SidenavModule, MatIconModule, MatTabsModule, FeatureFlagClassDirective]
+ imports: [CommonModule, RouterModule, TranslocoModule, SidenavComponent, MatIconModule, MatTabsModule, FeatureFlagClassDirective]
})
export class UserPageComponent implements OnDestroy {
readonly me$ = this.appFacade.me$;
diff --git a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.spec.ts
index 4f9546c9..bd318589 100644
--- a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.spec.ts
+++ b/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.spec.ts
@@ -4,11 +4,11 @@ import { UsersPageComponent } from './users-page.component';
import { RouterTestingModule } from '@angular/router/testing';
import { FeatureFlagClassDirective } from '@dev/translatr-components';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { UserListComponent } from '../../shared/user-list/user-list.component';
import { UsersFacade } from './+state/users.facade';
import { mockObservable } from '@translatr/utils/testing';
-import { SidenavTestingModule } from '../../nav/sidenav/testing';
+import { MockSidenavComponent } from '../../nav/sidenav/testing';
import { MockFeatureFlagClassDirective } from '@translatr/components/testing';
import { MockUserListComponent } from '../../shared/user-list/testing';
import { TranslocoTestingModule } from '@jsverse/transloco';
@@ -20,8 +20,8 @@ describe('UsersPageComponent', () => {
beforeEach(
waitForAsync(() => {
TestBed.overrideComponent(UsersPageComponent, {
- remove: { imports: [SidenavModule, UserListComponent, FeatureFlagClassDirective] },
- add: { imports: [SidenavTestingModule, MockUserListComponent, MockFeatureFlagClassDirective] }
+ remove: { imports: [SidenavComponent, UserListComponent, FeatureFlagClassDirective] },
+ add: { imports: [MockSidenavComponent, MockUserListComponent, MockFeatureFlagClassDirective] }
}).configureTestingModule({
imports: [
UsersPageComponent,
diff --git a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.ts b/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.ts
index f2180277..07390a60 100644
--- a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.ts
+++ b/ui/apps/translatr/src/app/modules/pages/users-page/users-page.component.ts
@@ -7,7 +7,7 @@ import { TranslocoModule } from '@jsverse/transloco';
import { navigate } from '@translatr/utils';
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
import { AppFacade } from '../../../+state/app.facade';
-import { SidenavModule } from '../../nav/sidenav/sidenav.module';
+import { SidenavComponent } from '../../nav/sidenav/sidenav.component';
import { FilterCriteria } from '../../shared/list-header/list-header.component';
import { UserListComponent } from '../../shared/user-list/user-list.component';
import { UsersFacade } from './+state/users.facade';
@@ -18,7 +18,7 @@ import { UsersFacade } from './+state/users.facade';
templateUrl: './users-page.component.html',
changeDetection: ChangeDetectionStrategy.Eager,
styleUrls: ['./users-page.component.scss'],
- imports: [CommonModule, TranslocoModule, SidenavModule, UserListComponent, FeatureFlagClassDirective]
+ imports: [CommonModule, TranslocoModule, SidenavComponent, UserListComponent, FeatureFlagClassDirective]
})
export class UsersPageComponent implements OnInit, OnDestroy {
me$ = this.appFacade.me$;
diff --git a/ui/apps/translatr/src/main.ts b/ui/apps/translatr/src/main.ts
index 6542f3cd..bc880a27 100644
--- a/ui/apps/translatr/src/main.ts
+++ b/ui/apps/translatr/src/main.ts
@@ -1,13 +1,76 @@
-import { enableProdMode, provideZoneChangeDetection } from '@angular/core';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-
-import { AppModule } from './app/app.module';
+import { provideHttpClient, withInterceptorsFromDi, withXhr } from '@angular/common/http';
+import { enableProdMode, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { bootstrapApplication } from '@angular/platform-browser';
+import { provideAnimations } from '@angular/platform-browser/animations';
+import { provideRouter, withDebugTracing } from '@angular/router';
+import { FeatureFlagFacade } from '@dev/translatr-model';
+import { NotificationService, TranslatrSdkModule } from '@dev/translatr-sdk';
+import { HotkeysService } from '@ngneat/hotkeys';
+import { provideSvgIcons } from '@ngneat/svg-icon';
+import { EffectsModule } from '@ngrx/effects';
+import { provideRouterStore, routerReducer, RouterState } from '@ngrx/router-store';
+import { StoreModule } from '@ngrx/store';
+import { provideStoreDevtools } from '@ngrx/store-devtools';
+import { ENDPOINT_URL, LOGIN_URL, WINDOW } from '@translatr/utils';
+import { AppEffects } from './app/+state/app.effects';
+import { AppFacade } from './app/+state/app.facade';
+import { appReducer } from './app/+state/app.reducer';
+import { routes } from './app/app-routing.module';
+import { AppComponent } from './app/app.component';
+import { httpInterceptorProviders } from './app/interceptors';
+import { TranslocoRootModule } from './app/modules/shared/transloco';
+import { MatNotificationService } from './app/services/mat-notification-service';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
-platformBrowserDynamic()
- .bootstrapModule(AppModule, { applicationProviders: [provideZoneChangeDetection()], })
- .catch(err => console.error(err));
+bootstrapApplication(AppComponent, {
+ providers: [
+ provideZoneChangeDetection(),
+ provideAnimations(),
+ provideRouter(routes, ...(environment.routerTracing ? [withDebugTracing()] : [])),
+ provideHttpClient(withXhr(), withInterceptorsFromDi()),
+ // NgRx's forFeature() (StoreModule.forFeature/EffectsModule.forFeature) - used throughout
+ // every lazy page module - requires the root store/effects to be registered via the NgModule
+ // forms (StoreModule.forRoot/EffectsModule.forRoot); their functional equivalents
+ // (provideStore/provideEffects) don't provide the StoreRootModule/EffectsRootModule marker
+ // that StoreFeatureModule/EffectsFeatureModule's factories inject, so mixing them breaks
+ // every lazy-loaded feature with NG0201.
+ importProvidersFrom(
+ StoreModule.forRoot(
+ {
+ app: appReducer,
+ router: routerReducer
+ },
+ {
+ metaReducers: [],
+ runtimeChecks: {
+ strictStateImmutability: true,
+ strictActionImmutability: true
+ }
+ }
+ )
+ ),
+ importProvidersFrom(EffectsModule.forRoot([AppEffects])),
+ provideRouterStore({ routerState: RouterState.Minimal }),
+ ...(!environment.production ? [provideStoreDevtools()] : []),
+ importProvidersFrom(TranslocoRootModule),
+ importProvidersFrom(TranslatrSdkModule),
+ AppFacade,
+ { provide: FeatureFlagFacade, useClass: AppFacade },
+ { provide: WINDOW, useFactory: () => window },
+ { provide: ENDPOINT_URL, useValue: environment.endpointUrl },
+ { provide: LOGIN_URL, useValue: `/login` },
+ {
+ provide: NotificationService,
+ useFactory: (snackBar: MatSnackBar) => new MatNotificationService(snackBar),
+ deps: [MatSnackBar]
+ },
+ httpInterceptorProviders,
+ HotkeysService,
+ provideSvgIcons([])
+ ]
+}).catch(err => console.error(err));