From 374edf7842655d76ae02497ff13606fe8c9bf491 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 08:45:38 +0200 Subject: [PATCH 01/11] docs(openspec): propose translatr pages standalone migration Adds proposal, design, and tasks for converting apps/translatr/src/app/modules/pages (10 page modules, 28 components, 4 testing doubles) to standalone components. Third and final layer of apps/translatr's standalone rollout, after libs/translatr-components and apps/translatr/src/app/modules/shared. Key finding: each page's *-routing.module.ts is already zero-component route config, so app-routing.module.ts's loadChildren entries repoint to it instead of being rewritten to loadComponent, avoiding a much larger routing rewrite. NgRx forFeature wiring for the 4 stateful pages relocates into the surviving routing module. Co-Authored-By: Claude Sonnet 5 --- .../.openspec.yaml | 3 + .../design.md | 59 +++++++++++++++++++ .../proposal.md | 26 ++++++++ .../tasks.md | 58 ++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 openspec/changes/migrate-translatr-pages-standalone/.openspec.yaml create mode 100644 openspec/changes/migrate-translatr-pages-standalone/design.md create mode 100644 openspec/changes/migrate-translatr-pages-standalone/proposal.md create mode 100644 openspec/changes/migrate-translatr-pages-standalone/tasks.md diff --git a/openspec/changes/migrate-translatr-pages-standalone/.openspec.yaml b/openspec/changes/migrate-translatr-pages-standalone/.openspec.yaml new file mode 100644 index 00000000..4ade2b29 --- /dev/null +++ b/openspec/changes/migrate-translatr-pages-standalone/.openspec.yaml @@ -0,0 +1,3 @@ +schema: spec-driven +created: 2026-09-19 +skip_specs: true diff --git a/openspec/changes/migrate-translatr-pages-standalone/design.md b/openspec/changes/migrate-translatr-pages-standalone/design.md new file mode 100644 index 00000000..d7723aca --- /dev/null +++ b/openspec/changes/migrate-translatr-pages-standalone/design.md @@ -0,0 +1,59 @@ +# Design + +## Context + +`apps/translatr/src/app/modules/pages` has 10 page folders, each with a declarations module (`*.module.ts`, declares the page's components) and a routing module (`*-routing.module.ts`, pure `RouterModule.forChild(routes)`, zero components). `app-routing.module.ts` lazy-loads each page via `loadChildren: () => import(...).then(m => m.XxxPageModule)`. + +Verified by reading every `*-routing.module.ts`: each is genuinely zero-component route configuration - some single-route (`forbidden-page`, `main-page`, `not-found-page`, `projects-page`, `registration-page`, `users-page`, `dashboard-page`), some multi-route with nested `children` for tabs (`project-page`, `user-page`) or sibling deep-link routes (`editor-page`). None declare or import components themselves; they only reference component classes via the `component:` route property, which works identically for standalone components. + +Verified by reading every declarations module and the corresponding component templates: + +- **NgRx `forFeature` state** lives in the declarations module for `dashboard-page`, `editor-page`, `projects-page`, `user-page` (`StoreModule.forFeature`, `EffectsModule.forFeature`, facade `providers`). `project-page.module.ts` instead imports `ProjectStateModule` (from the `shared` layer, already zero-component infra, untouched). `users-page` keeps its NgRx state in a separate `users-page/+state/users.module.ts`, already zero-component, already untouched by this change (same category as `ProjectStateModule`). +- **Real (non-routed) template composition**: `dashboard-page` and `projects-page` render several shared/lib list and card components directly. `project-page`'s tab components (`ProjectKeysComponent`, `ProjectLocalesComponent`, `ProjectMembersComponent`) each directly compose their own list component (`KeyListComponent`, `LocaleListComponent`, `MemberListComponent`) - all three declared in `project-page.module.ts` alongside the tabs. `editor-page`'s `KeyEditorPageComponent`/`LocaleEditorPageComponent` directly compose `EditorComponent` and `EditorSelectorComponent`. `users-page` composes lib `UserCardComponent`/`UserCardLinkComponent` and its own `UserListComponent`. +- **Routed (not composed) children**: `ProjectPageComponent` and `UserPageComponent` render their tab/sub-page components purely via `` - confirmed by grepping their templates for `router-outlet` and finding no direct tag usage of the child component selectors. They need no `imports` entries for their tab children. + +See proposal.md - Why / What Changes for motivation and scope. + +## Goals / Non-Goals + +**Goals:** +- Every component under `apps/translatr/src/app/modules/pages` becomes `standalone: true` with its own explicit `imports`. +- All 10 page declarations modules removed once nothing references them. +- `app-routing.module.ts`'s 9 remaining `loadChildren` entries repointed to each page's surviving `*-routing.module.ts`. +- NgRx `forFeature` registration (state + effects + facade providers) for the 4 pages that have it relocated into their routing module, preserving exactly one registration per lazy-loaded route subtree. +- The 4 testing-module doubles under `pages/**` converted the same way as the two prior changes. + +**Non-Goals:** +- Rewriting any route's shape, guards, or `children` structure - only the *module* each route lazy-loads changes, not the routes themselves. +- `app.module.ts` root bootstrap, `main.ts`, or `apps/translatr-admin` - separate later changes. +- Converting `ProjectStateModule`, `users-page/+state/users.module.ts`, or any `*-routing.module.ts` to anything other than what they already are (plain zero-component `NgModule`s) - none of them declare a component, so none are in scope for standalone conversion. + +## Decisions + +**Repoint `loadChildren` to the routing module, not `loadComponent`.** Every page except the already-`loadComponent` `login-page` has either multiple routes (`editor-page`) or nested `children` (`project-page`, `user-page`), which `loadComponent` cannot express (it lazy-loads exactly one component for exactly one route). Since each page's `*-routing.module.ts` already exists as a zero-component `NgModule` wrapping `RouterModule.forChild(routes)`, the mechanical fix is to change each `app-routing.module.ts` entry from `.then(m => m.XxxPageModule)` to `.then(m => m.XxxPageRoutingModule)` - the routes, guards, and children are untouched, only the lazy-load target changes. Alternative considered: flatten every page's routes into `app-routing.module.ts` directly and use `loadComponent` per leaf route - rejected as a much larger, riskier rewrite of routing structure for no behavioral benefit, and it would scatter route configuration (including the guards and the `PROJECT_ROUTES`/`USER_ROUTES` injection tokens) away from where it's already organized. + +**Move NgRx `forFeature` wiring into the surviving routing module.** For `dashboard-page`, `editor-page`, `projects-page`, `user-page`: their `StoreModule.forFeature(...)`, `EffectsModule.forFeature([...])`, and facade `providers` move from the deleted declarations module into the corresponding `*-routing.module.ts`'s own `imports`/`providers`. For `project-page`: its `ProjectStateModule` import moves the same way, into `project-page-routing.module.ts`. Rationale: the routing module is the only `NgModule` that survives and is still the thing `app-routing.module.ts` lazy-loads for that route subtree, so it's the natural new home for state that must be registered exactly once per that subtree - moving it anywhere else (e.g. duplicating the provider into every standalone component under the page) would either register the feature state multiple times or require guessing which component "owns" it. Alternative considered: register each page's feature state as a route-level `providers` array directly in `app-routing.module.ts` - rejected as it would split each page's own concerns (routes + guards + state) across two files instead of keeping them together in the page's own routing module, for no benefit. + +**Router-outlet children need no import; directly-composed children do.** Confirmed per-page by reading both the routing module (for `children` routes) and the parent's own template (for direct tag usage) before writing each component's `imports` array - `ProjectPageComponent`/`UserPageComponent` need zero tab-related imports, while `ProjectKeysComponent` etc. need their composed list component. This mirrors the same investigation done for every multi-component group in the prior two changes and is the direct cause of several `TestBed.overrideComponent` cases in tasks below. + +**Testing-module conversion follows the same pattern as the prior two changes.** Convert each `Mock*` class to `standalone: true`, delete the wrapper `NgModule`, rename the file to `mock-*.component.ts`, update every consumer spec to import the mock directly (or via `TestBed.overrideComponent` where the real component now bakes in the thing being mocked). + +## Risks / Trade-offs + +- [A page's own component template composes a child directly but the child isn't added to the parent's `imports`, breaking at compile or render time] → Every composition relationship in this document was verified by grep before writing the corresponding `imports` array; `nx build translatr` and `tsc --noEmit` (used throughout the prior two changes to catch stale-path and missing-import errors precisely) catch anything missed. +- [Moving NgRx `forFeature` registration to the routing module changes exactly when/where the feature reducer and effects are registered, potentially affecting facade injection for a component that expected the declarations module's injector scope] → Both the old declarations module and the new routing module are lazy-loaded as the single `loadChildren` target for that route subtree, so the injector scope covering all of that page's components is identical either way - only the "which sibling `NgModule` provides it" changes, not the reachable subtree. +- [`app-routing.module.ts`'s `loadChildren` entries are a single shared file - a mistake in one entry could break every route] → Each entry is changed and verified (`nx build`, `nx test`, `tsc --noEmit`) one page at a time per the task breakdown, not all 9 at once, so a mistake is caught and bisectable immediately rather than surfacing only after all changes land. + +## Migration Plan + +1. Convert the pages with no NgRx state and minimal/no composition first: `forbidden-page`, `not-found-page`, `main-page`, `registration-page` (all compose only already-standalone lib/shared components). +2. Convert `project-page`'s leaf list components (`KeyListComponent`, `LocaleListComponent`, `MemberListComponent`), then its tab components that compose them, then `ProjectPageComponent` (router-outlet only); move `ProjectStateModule` into `project-page-routing.module.ts`. +3. Convert `user-page`'s tab components, then `UserPageComponent` (router-outlet only); move NgRx `forFeature` wiring into `user-page-routing.module.ts`. +4. Convert `editor-page`'s `EditorSelectorComponent` and `EditorComponent`, then `KeyEditorPageComponent`/`LocaleEditorPageComponent` that compose them; move NgRx `forFeature` wiring into `editor-page-routing.module.ts`. +5. Convert `dashboard-page` and `projects-page` (compose already-standalone shared/lib components from steps done in the prior two changes); move NgRx `forFeature` wiring into their routing modules. +6. Convert `users-page` (composes already-standalone lib components); `users-page/+state/users.module.ts` stays untouched (already zero-component). +7. Repoint all 9 `app-routing.module.ts` `loadChildren` entries from the deleted `*Module` to the surviving `*RoutingModule`. +8. Convert the 4 testing-module doubles under `pages/**`. +9. Grep-verify zero remaining page `*Module` imports; full `nx build translatr`, `nx test translatr`, `tsc --noEmit` sweep; manual smoke test covering at least one direct-composition page, one router-outlet page, and the routing repoint itself (navigate into a nested tab route). + +Each step lands as its own commit; a regression reverts at the step that introduced it without unwinding later steps. diff --git a/openspec/changes/migrate-translatr-pages-standalone/proposal.md b/openspec/changes/migrate-translatr-pages-standalone/proposal.md new file mode 100644 index 00000000..22d49cf6 --- /dev/null +++ b/openspec/changes/migrate-translatr-pages-standalone/proposal.md @@ -0,0 +1,26 @@ +# Proposal + +## Why + +`apps/translatr/src/app/modules/pages` still declares every page component via `@NgModule` (10 page modules, 28 components, each currently lazy-loaded from `app-routing.module.ts`). This is the third and final layer of `apps/translatr` after `libs/translatr-components` and `apps/translatr/src/app/modules/shared` - converting it clears the way for the last remaining piece, root bootstrap (`app.module.ts`, `main.ts`, NgRx root wiring, Transloco root config), which depends on every page being standalone first. + +## What Changes + +- Convert all 28 components across the 10 page modules to `standalone: true`, each declaring its own `imports`. +- Remove the 10 page `*.module.ts` declaration wrappers once nothing imports them. +- Each page's `*-routing.module.ts` (pure `RouterModule.forChild(routes)` config, zero components) stays as-is and becomes the new lazy-load target in `app-routing.module.ts`, replacing the deleted declarations module. For the 4 pages with NgRx `forFeature` state (`dashboard-page`, `editor-page`, `projects-page`, `user-page`), their `StoreModule.forFeature`/`EffectsModule.forFeature`/`providers` move from the deleted declarations module into the surviving routing module, so the feature state is still registered exactly once per lazy-loaded route subtree. +- Update `app-routing.module.ts`'s 9 `loadChildren` entries (everything except `login`, already `loadComponent` from the `libs/translatr-components` change) to import each page's `*RoutingModule` instead of its now-deleted `*Module`. +- Convert the 4 testing-module doubles under `pages/**` (`editor-testing`, `key-list-testing`, `locale-list-testing`, `member-list-testing`) the same way. +- **BREAKING** (internal-only): any code importing a page's `*Module` class switches to importing the component class directly; anything relying on `app-routing.module.ts`'s lazy-load target switches with it in the same change. + +## Capabilities + +No spec-level behavior changes - routes, guards, rendering, and NgRx feature-state behavior stay identical, only the underlying module architecture changes. Internal architecture change only (`skip_specs: true` set in `.openspec.yaml`). + +## Impact + +- **Code**: `apps/translatr/src/app/modules/pages/**` (all page `*.module.ts` files, their components, testing doubles) and `apps/translatr/src/app/app-routing.module.ts` (9 `loadChildren` targets repointed). +- **NgRx**: `StoreModule.forFeature`/`EffectsModule.forFeature`/facade `providers` for `dashboard-page`, `editor-page`, `projects-page`, and `user-page` relocate from the deleted declarations module into the corresponding surviving `*-routing.module.ts`. `project-page.module.ts`'s `ProjectStateModule` import (shared NgRx state for the whole project-page route subtree) moves the same way, into `project-page-routing.module.ts`. +- **Real (non-routed) template composition** discovered while mapping dependencies, needing the same `TestBed.overrideComponent` spec pattern used throughout the prior two changes: `dashboard-page` → `ActivityListComponent`/`MetricComponent`/`ProjectCardListComponent`/`ProjectListComponent`; `projects-page` → `ProjectCardComponent`/`ProjectCardLinkComponent`; `project-page`'s tab components → `KeyListComponent`/`LocaleListComponent`/`MemberListComponent`; `editor-page`'s `KeyEditorPageComponent`/`LocaleEditorPageComponent` → `EditorComponent`/`EditorSelectorComponent`; `users-page` → `UserCardComponent`/`UserCardLinkComponent`/`UserListComponent`. +- **Routed (not composed) children**: `project-page` and `user-page` render their tab components via ``, not direct template composition - `ProjectPageComponent`/`UserPageComponent` do not need to import their tab children. +- **Out of scope**: `apps/translatr/src/app/app.module.ts` root bootstrap (NgRx root store/effects/router-store/devtools, Transloco root, `platformBrowserDynamic`) and `apps/translatr-admin` entirely - separate, later changes. diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md new file mode 100644 index 00000000..84b81f3e --- /dev/null +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -0,0 +1,58 @@ +# Tasks + +## 1. No-state, minimal-composition pages + +- [ ] 1.1 Convert `ForbiddenPageComponent` to `standalone: true` with explicit `imports`, delete `forbidden-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed +- [ ] 1.2 Convert `NotFoundPageComponent` to `standalone: true` with explicit `imports`, delete `not-found-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed +- [ ] 1.3 Convert `MainPageComponent` to `standalone: true` with explicit `imports`, delete `main-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed +- [ ] 1.4 Convert `RegistrationPageComponent` to `standalone: true` with explicit `imports`, delete `registration-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed + +## 2. project-page + +- [ ] 2.1 Convert `KeyListComponent` to `standalone: true` with explicit `imports`, update consumers +- [ ] 2.2 Convert `LocaleListComponent` to `standalone: true` with explicit `imports`, update consumers +- [ ] 2.3 Convert `MemberListComponent` to `standalone: true` with explicit `imports`, update consumers +- [ ] 2.4 Convert `ProjectInfoComponent`, `ProjectKeysComponent` (imports `KeyListComponent`), `ProjectLocalesComponent` (imports `LocaleListComponent`), `ProjectMembersComponent` (imports `MemberListComponent`), `ProjectActivityComponent`, `ProjectSettingsComponent` to `standalone: true` with explicit `imports` +- [ ] 2.5 Convert `ProjectPageComponent` to `standalone: true` with explicit `imports` (router-outlet only - no tab-component imports needed); delete `project-page.module.ts`; move the `ProjectStateModule` import from the deleted module into `project-page-routing.module.ts`; update consumers +- [ ] 2.6 Verify `nx test translatr` and `nx build translatr` succeed + +## 3. user-page + +- [ ] 3.1 Convert `UserInfoComponent`, `UserProjectsComponent`, `UserAccessTokensComponent`, `UserAccessTokenComponent`, `UserActivityComponent`, `UserSettingsComponent` to `standalone: true` with explicit `imports` +- [ ] 3.2 Convert `UserPageComponent` to `standalone: true` with explicit `imports` (router-outlet only); delete `user-page.module.ts`; move `StoreModule.forFeature(USER_FEATURE_KEY, ...)`, `EffectsModule.forFeature([UserEffects])`, and `providers: [UserFacade, UserGuard]` from the deleted module into `user-page-routing.module.ts`; update consumers +- [ ] 3.3 Verify `nx test translatr` and `nx build translatr` succeed + +## 4. editor-page + +- [ ] 4.1 Convert `EditorSelectorComponent` to `standalone: true` with explicit `imports` +- [ ] 4.2 Convert `EditorComponent` to `standalone: true` with explicit `imports` +- [ ] 4.3 Convert `KeyEditorPageComponent` and `LocaleEditorPageComponent` to `standalone: true` with explicit `imports` (including `EditorComponent` and `EditorSelectorComponent`); delete `editor-page.module.ts`; move `StoreModule.forFeature(EDITOR_FEATURE_KEY, ...)`, `EffectsModule.forFeature([EditorEffects])`, and `providers: [EditorFacade, { provide: LanguageSwicher, useClass: AppFacade }]` from the deleted module into `editor-page-routing.module.ts`; update consumers +- [ ] 4.4 Verify `nx test translatr` and `nx build translatr` succeed + +## 5. dashboard-page and projects-page + +- [ ] 5.1 Convert `DashboardPageComponent` to `standalone: true` with explicit `imports` (including `ActivityListComponent`, `MetricComponent`, `ProjectCardListComponent`, `ProjectEditDialogComponent`, `ProjectListComponent`); delete `dashboard-page.module.ts`; move `StoreModule.forFeature(DASHBOARD_FEATURE_KEY, ...)`, `EffectsModule.forFeature([DashboardEffects])`, and `providers: [DashboardFacade]` into `dashboard-page-routing.module.ts`; update consumers +- [ ] 5.2 Convert `ProjectsPageComponent` to `standalone: true` with explicit `imports` (including `ProjectCardComponent`, `ProjectCardLinkComponent`, `ProjectEditDialogComponent`, `ProjectListComponent`); delete `projects-page.module.ts`; move `StoreModule.forFeature(PROJECTS_FEATURE_KEY, ...)`, `EffectsModule.forFeature([ProjectsEffects])`, and `providers: [ProjectsFacade]` into `projects-page-routing.module.ts`; update consumers +- [ ] 5.3 Verify `nx test translatr` and `nx build translatr` succeed + +## 6. users-page + +- [ ] 6.1 Convert `UsersPageComponent` to `standalone: true` with explicit `imports` (including `UserCardComponent`, `UserCardLinkComponent`, `UserListComponent`); delete `users-page.module.ts`; update consumers (`users-page/+state/users.module.ts` stays untouched - already zero-component) +- [ ] 6.2 Verify `nx test translatr` and `nx build translatr` succeed + +## 7. Routing repoint + +- [ ] 7.1 In `app-routing.module.ts`, change all 9 remaining `loadChildren` entries (`register`, `dashboard`, `users`, `projects`, `not-found`, `forbidden`, `''` main, `''` user-page, `''` project-page, `''` editor-page) from `.then(m => m.XxxPageModule)` to `.then(m => m.XxxPageRoutingModule)` +- [ ] 7.2 Verify `nx build translatr` succeeds with no TypeScript errors + +## 8. Testing modules + +- [ ] 8.1 Convert the `Mock*` classes in `editor/testing/editor-testing.module.ts`, `project-keys/key-list/testing/key-list-testing.module.ts`, `project-locales/locale-list/testing/locale-list-testing.module.ts`, `project-members/member-list/testing/member-list-testing.module.ts` to `standalone: true`; rename each file to `mock-*.component.ts`; update every consumer spec; delete the 4 `*-testing.module.ts` files +- [ ] 8.2 Run `nx test translatr` and verify all specs pass + +## 9. Cleanup and verification + +- [ ] 9.1 Grep `apps/translatr` for any remaining `*Module` import of a class from `apps/translatr/src/app/modules/pages/**` (excluding the untouched `*RoutingModule`s and `users-page/+state/users.module.ts`) and confirm zero matches +- [ ] 9.2 Run `nx build translatr` and verify it succeeds with no TypeScript errors +- [ ] 9.3 Run `nx test translatr` and verify all suites pass +- [ ] 9.4 Manually smoke-test the app (`nx serve translatr`) covering a direct-composition page (dashboard or projects), a router-outlet page's nested tab route (e.g. a project's locales tab), and the login/main-page flow; verify no console errors and correct rendering From 489ebc026871782fef4978fd906d70401e707bfa Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 08:54:05 +0200 Subject: [PATCH 02/11] feat(translatr): convert no-state pages to standalone Tasks 1.1-1.4, completing group 1. Converts ForbiddenPageComponent, NotFoundPageComponent, MainPageComponent, and RegistrationPageComponent to standalone: true, deleting their NgModule wrappers. Repoints each page's app-routing.module.ts loadChildren entry to the already-existing, already-zero-component *RoutingModule atomically with its own conversion (not batched into a later step) so every commit stays independently buildable. Caught a real bug in main-page: provideSvgIcons() returns EnvironmentProviders, which Angular rejects in a component's own providers array (NG0207) - moved it to main-page-routing.module.ts's NgModule providers instead, which is still environment-scoped. registration-page had the same FontAwesome module-constructor icon registration pattern seen in login-page during the libs change - fixed the same way, with direct IconDefinition references. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 8 +-- .../translatr/src/app/app-routing.module.ts | 12 ++-- .../forbidden-page.component.spec.ts | 9 ++- .../forbidden-page.component.ts | 10 ++- .../forbidden-page/forbidden-page.module.ts | 19 ----- .../main-page/main-page-routing.module.ts | 15 +++- .../main-page/main-page.component.spec.ts | 39 +++++++--- .../pages/main-page/main-page.component.ts | 46 +++++++++++- .../pages/main-page/main-page.module.ts | 72 ------------------- .../not-found-page.component.spec.ts | 9 ++- .../not-found-page.component.ts | 19 ++++- .../not-found-page/not-found-page.module.ts | 25 ------- .../registration-page.component.spec.ts | 15 +--- .../registration-page.component.ts | 33 +++++++-- .../registration-page.module.ts | 33 --------- 15 files changed, 162 insertions(+), 202 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.module.ts delete mode 100644 ui/apps/translatr/src/app/modules/pages/main-page/main-page.module.ts delete mode 100644 ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.module.ts delete mode 100644 ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 84b81f3e..f6e10dfe 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -2,10 +2,10 @@ ## 1. No-state, minimal-composition pages -- [ ] 1.1 Convert `ForbiddenPageComponent` to `standalone: true` with explicit `imports`, delete `forbidden-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed -- [ ] 1.2 Convert `NotFoundPageComponent` to `standalone: true` with explicit `imports`, delete `not-found-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed -- [ ] 1.3 Convert `MainPageComponent` to `standalone: true` with explicit `imports`, delete `main-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed -- [ ] 1.4 Convert `RegistrationPageComponent` to `standalone: true` with explicit `imports`, delete `registration-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed +- [x] 1.1 Convert `ForbiddenPageComponent` to `standalone: true` with explicit `imports`, delete `forbidden-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed (repointed app-routing.module.ts's `forbidden` loadChildren to `ForbiddenPageRoutingModule` in the same step, not deferred to group 7 - each page's route repoint happens atomically with its own conversion to keep every step independently buildable; translatr-admin has its own unrelated same-named `ForbiddenPageModule`, left untouched) +- [x] 1.2 Convert `NotFoundPageComponent` to `standalone: true` with explicit `imports`, delete `not-found-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed (dropped unused `SidenavModule`/`MatCardModule`/`MatDividerModule` imports - not used in its own template; `RouterModule` added since `routerLink` was used but was missing from the old module's own import list, apparently relying on a transitive re-export) +- [x] 1.3 Convert `MainPageComponent` to `standalone: true` with explicit `imports`, delete `main-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed (caught a real bug: `provideSvgIcons()` returns `EnvironmentProviders`, which NG0207 rejects in a component's own `providers` - moved it to `main-page-routing.module.ts`'s `@NgModule` `providers` instead, which is still environment-scoped; `{ provide: LanguageSwicher, useClass: AppFacade }` is a plain provider and stayed on the component) +- [x] 1.4 Convert `RegistrationPageComponent` to `standalone: true` with explicit `imports`, delete `registration-page.module.ts`, update consumers; verify `nx test translatr` and `nx build translatr` succeed (same FontAwesome module-constructor icon-registration pattern as `login-page` in the libs change - fixed the same way, direct `IconDefinition` references instead of name-string lookup, removing the registration side effect entirely) ## 2. project-page diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index d2930b32..e7442c9f 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -10,8 +10,8 @@ const routes: Routes = [ { path: 'register', loadChildren: () => - import('./modules/pages/registration-page/registration-page.module').then( - m => m.RegistrationPageModule + import('./modules/pages/registration-page/registration-page-routing.module').then( + m => m.RegistrationPageRoutingModule ) }, { @@ -34,20 +34,20 @@ const routes: Routes = [ { path: 'not-found', loadChildren: () => - import('./modules/pages/not-found-page/not-found-page.module').then(m => m.NotFoundPageModule) + import('./modules/pages/not-found-page/not-found-page-routing.module').then(m => m.NotFoundPageRoutingModule) }, { path: 'forbidden', loadChildren: () => - import('./modules/pages/forbidden-page/forbidden-page.module').then( - m => m.ForbiddenPageModule + import('./modules/pages/forbidden-page/forbidden-page-routing.module').then( + m => m.ForbiddenPageRoutingModule ) }, { path: '', pathMatch: 'full', loadChildren: () => - import('./modules/pages/main-page/main-page.module').then(m => m.MainPageModule) + import('./modules/pages/main-page/main-page-routing.module').then(m => m.MainPageRoutingModule) }, { path: '', diff --git a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.spec.ts index e80f62d3..bf2de47b 100644 --- a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; import { MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { AppFacade } from '../../../+state/app.facade'; @@ -12,9 +13,11 @@ describe('NotAllowedPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ForbiddenPageComponent], - imports: [RouterTestingModule, MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent], + TestBed.overrideComponent(ForbiddenPageComponent, { + remove: { imports: [ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent] }, + add: { imports: [MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent] } + }).configureTestingModule({ + imports: [ForbiddenPageComponent, RouterTestingModule], providers: [ { provide: AppFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.ts b/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.ts index 6094d4e4..59d51fd5 100644 --- a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.component.ts @@ -1,13 +1,17 @@ +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { MatButtonModule } from '@angular/material/button'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; import { AppFacade } from '../../../+state/app.facade'; @Component({ - standalone: false, + standalone: true, selector: 'app-forbidden-page', templateUrl: './forbidden-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./forbidden-page.component.scss'] + styleUrls: ['./forbidden-page.component.scss'], + imports: [CommonModule, MatButtonModule, RouterModule, ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent] }) export class ForbiddenPageComponent { me$ = this.facade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.module.ts b/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.module.ts deleted file mode 100644 index 021a807c..00000000 --- a/ui/apps/translatr/src/app/modules/pages/forbidden-page/forbidden-page.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { RouterModule } from '@angular/router'; -import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; -import { ForbiddenPageRoutingModule } from './forbidden-page-routing.module'; -import { ForbiddenPageComponent } from './forbidden-page.component'; - -@NgModule({ - declarations: [ForbiddenPageComponent], - imports: [ - ForbiddenPageRoutingModule, - CommonModule, - MatButtonModule, - RouterModule, - ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent - ] -}) -export class ForbiddenPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/main-page/main-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/main-page/main-page-routing.module.ts index bbd7b489..1c8b0982 100644 --- a/ui/apps/translatr/src/app/modules/pages/main-page/main-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/main-page/main-page-routing.module.ts @@ -1,5 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { provideSvgIcons } from '@ngneat/svg-icon'; +import { + appChat, + appDocument, + appEducation, + appPulse, + appThumbsUp, + appUser, + appWrite +} from '../../../../assets'; import { MainPageComponent } from './main-page.component'; const routes: Routes = [ @@ -11,6 +21,9 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + exports: [RouterModule], + providers: [ + provideSvgIcons([appPulse, appUser, appThumbsUp, appEducation, appChat, appWrite, appDocument]) + ] }) export class MainPageRoutingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.spec.ts index 76ae60c9..0906f224 100644 --- a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.spec.ts @@ -10,12 +10,20 @@ import { ShortNumberPipe } from '@dev/translatr-components'; import { SvgIconComponent, provideSvgIcons } from '@ngneat/svg-icon'; import { MainPageComponent } from './main-page.component'; +import { + ActivityGraphComponent, + AuthBarLanguageSwitcherComponent, + FeatureFlagDirective, FeatureFlagClassDirective, + FooterComponent, + MetricComponent, + NavbarComponent +} from '@dev/translatr-components'; import { MockActivityGraphComponent, MockFeatureFlagDirective, MockFeatureFlagClassDirective, MockFooterComponent, MockMetricComponent, - MockNavbarComponent, MockAuthBarItemComponent, MockAuthBarLanguageSwitcherComponent + MockNavbarComponent, MockAuthBarLanguageSwitcherComponent } from '@translatr/components/testing'; import { MatDividerModule } from '@angular/material/divider'; import { MatTooltipModule } from '@angular/material/tooltip'; @@ -31,14 +39,29 @@ describe('MainPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [MainPageComponent], + TestBed.overrideComponent(MainPageComponent, { + remove: { + imports: [ + NavbarComponent, + AuthBarLanguageSwitcherComponent, + FooterComponent, + ActivityGraphComponent, + FeatureFlagDirective, FeatureFlagClassDirective, + MetricComponent + ] + }, + add: { + imports: [ + MockNavbarComponent, MockAuthBarLanguageSwitcherComponent, + MockFooterComponent, + MockActivityGraphComponent, + MockFeatureFlagDirective, MockFeatureFlagClassDirective, + MockMetricComponent + ] + } + }).configureTestingModule({ imports: [ - MockNavbarComponent, MockAuthBarItemComponent, MockAuthBarLanguageSwitcherComponent, - MockFooterComponent, - MockActivityGraphComponent, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, - MockMetricComponent, + MainPageComponent, ShortNumberPipe, NoopAnimationsModule, diff --git a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts index 136fa55e..00f69e9d 100644 --- a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts @@ -1,16 +1,58 @@ +import { LayoutModule } from '@angular/cdk/layout'; +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatGridListModule } from '@angular/material/grid-list'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { + ActivityGraphComponent, + AuthBarLanguageSwitcherComponent, + FeatureFlagDirective, FeatureFlagClassDirective, + FooterComponent, + LanguageSwicher, + MetricComponent, + NavbarComponent, + ShortNumberPipe +} from '@dev/translatr-components'; import { Aggregate, Feature, PagedList, User, UserRole } from '@dev/translatr-model'; import { ActivityService, StatisticService } from '@dev/translatr-sdk'; +import { SvgIconComponent } from '@ngneat/svg-icon'; +import { TranslocoModule } from '@jsverse/transloco'; import { shareReplay, map, startWith } from 'rxjs/operators'; import { AppFacade } from '../../../+state/app.facade'; import { environment } from '../../../../environments/environment'; @Component({ - standalone: false, + standalone: true, selector: 'app-main-page', templateUrl: './main-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./main-page.component.scss'] + styleUrls: ['./main-page.component.scss'], + imports: [ + CommonModule, + NavbarComponent, + AuthBarLanguageSwitcherComponent, + MatGridListModule, + MatCardModule, + MatIconModule, + MatButtonModule, + MatMenuModule, + LayoutModule, + MatDividerModule, + MatTooltipModule, + FooterComponent, + ActivityGraphComponent, + TranslocoModule, + FeatureFlagDirective, FeatureFlagClassDirective, + MetricComponent, + ShortNumberPipe, + SvgIconComponent + ], + providers: [{ provide: LanguageSwicher, useClass: AppFacade }] }) export class MainPageComponent { me$ = this.facade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.module.ts b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.module.ts deleted file mode 100644 index fadd411e..00000000 --- a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.module.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { LayoutModule } from '@angular/cdk/layout'; -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatGridListModule } from '@angular/material/grid-list'; -import { MatIconModule } from '@angular/material/icon'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { - ActivityGraphComponent, - AuthBarLanguageSwitcherComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - FooterComponent, - LanguageSwicher, - MetricComponent, - NavbarComponent, - ShortNumberPipe -} from '@dev/translatr-components'; -import { SvgIconComponent, provideSvgIcons } from '@ngneat/svg-icon'; -import { TranslocoModule } from '@jsverse/transloco'; -import { AppFacade } from '../../../+state/app.facade'; -import { - appChat, - appDocument, - appEducation, - appPulse, - appThumbsUp, - appUser, - appWrite -} from '../../../../assets'; -import { MainPageRoutingModule } from './main-page-routing.module'; -import { MainPageComponent } from './main-page.component'; - -@NgModule({ - declarations: [MainPageComponent], - imports: [ - MainPageRoutingModule, - CommonModule, - NavbarComponent, - AuthBarLanguageSwitcherComponent, - MatGridListModule, - MatCardModule, - MatIconModule, - MatButtonModule, - MatMenuModule, - LayoutModule, - MatDividerModule, - MatTooltipModule, - FooterComponent, - ActivityGraphComponent, - TranslocoModule, - FeatureFlagDirective, FeatureFlagClassDirective, - MetricComponent, - ShortNumberPipe, - SvgIconComponent - ], - providers: [ - provideSvgIcons([ - appPulse, - appUser, - appThumbsUp, - appEducation, - appChat, - appWrite, - appDocument - ]), - { provide: LanguageSwicher, useClass: AppFacade } - ] -}) -export class MainPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.spec.ts index 607bc15b..21006345 100644 --- a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; +import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; import { MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { AppFacade } from '../../../+state/app.facade'; @@ -12,9 +13,11 @@ describe('NotFoundPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [NotFoundPageComponent], - imports: [MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } })], + TestBed.overrideComponent(NotFoundPageComponent, { + remove: { imports: [ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent] }, + add: { imports: [MockErrorPageComponent, MockErrorPageHeaderComponent, MockErrorPageMessageComponent] } + }).configureTestingModule({ + imports: [NotFoundPageComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } })], providers: [ { provide: AppFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.ts b/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.ts index 0bfd63bc..cfd76c40 100644 --- a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.component.ts @@ -1,13 +1,26 @@ +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { MatButtonModule } from '@angular/material/button'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; +import { TranslocoModule } from '@jsverse/transloco'; import { AppFacade } from '../../../+state/app.facade'; @Component({ - standalone: false, + standalone: true, selector: 'app-not-found-page', templateUrl: './not-found-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./not-found-page.component.scss'] + styleUrls: ['./not-found-page.component.scss'], + imports: [ + CommonModule, + MatButtonModule, + RouterModule, + TranslocoModule, + ErrorPageComponent, + ErrorPageHeaderComponent, + ErrorPageMessageComponent + ] }) export class NotFoundPageComponent { me$ = this.facade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.module.ts b/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.module.ts deleted file mode 100644 index 43794e79..00000000 --- a/ui/apps/translatr/src/app/modules/pages/not-found-page/not-found-page.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent } from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { NotFoundPageRoutingModule } from './not-found-page-routing.module'; -import { NotFoundPageComponent } from './not-found-page.component'; - -@NgModule({ - declarations: [NotFoundPageComponent], - imports: [ - CommonModule, - NotFoundPageRoutingModule, - SidenavModule, - MatCardModule, - MatDividerModule, - MatButtonModule, - TranslocoModule, - ErrorPageComponent, ErrorPageHeaderComponent, ErrorPageMessageComponent - ] -}) -export class NotFoundPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.spec.ts index 81ec52f1..065ea0ed 100644 --- a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.component.spec.ts @@ -1,14 +1,9 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; -import { FeatureFlagDirective, FeatureFlagClassDirective, UserEditFormComponent } from '@dev/translatr-components'; import { LanguageProvider } from '@dev/translatr-sdk'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { TranslocoTestingModule } from '@jsverse/transloco'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; import { RegistrationPageRoutingModule } from './registration-page-routing.module'; import { RegistrationPageComponent } from './registration-page.component'; @@ -20,20 +15,14 @@ describe('RegistrationPageComponent', () => { beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [RegistrationPageComponent], imports: [ + RegistrationPageComponent, HttpClientTestingModule, RouterTestingModule, NoopAnimationsModule, RegistrationPageRoutingModule, - SidenavModule, - TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), - FeatureFlagDirective, FeatureFlagClassDirective, - UserEditFormComponent, - MatCardModule, - MatButtonModule, - FontAwesomeModule + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ], providers: [LanguageProvider] }).compileComponents(); 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 58a7f8e4..14a90ab6 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 @@ -1,15 +1,34 @@ +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; import { Router } from '@angular/router'; +import { FeatureFlagDirective, FeatureFlagClassDirective, UserEditFormComponent } from '@dev/translatr-components'; import { ConstraintViolationErrorInfo, Feature, User } from '@dev/translatr-model'; import { UserService } from '@dev/translatr-sdk'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { faFacebook, faGithub, faGoogle, faTwitter } from '@fortawesome/free-brands-svg-icons'; +import { faKey } from '@fortawesome/free-solid-svg-icons'; +import { TranslocoModule } from '@jsverse/transloco'; import { Subject } from 'rxjs'; +import { SidenavModule } from '../../nav/sidenav/sidenav.module'; @Component({ - standalone: false, + standalone: true, selector: 'app-registration-page', templateUrl: './registration-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./registration-page.component.scss'] + styleUrls: ['./registration-page.component.scss'], + imports: [ + CommonModule, + SidenavModule, + TranslocoModule, + FeatureFlagDirective, FeatureFlagClassDirective, + UserEditFormComponent, + MatCardModule, + MatButtonModule, + FontAwesomeModule + ] }) export class RegistrationPageComponent { readonly profile$ = this.userService.authProfile(); @@ -17,11 +36,11 @@ export class RegistrationPageComponent { readonly Feature = Feature; readonly icons = { - google: ['fab', 'google'], - keycloak: ['fas', 'key'], - github: ['fab', 'github'], - facebook: ['fab', 'facebook'], - twitter: ['fab', 'twitter'] + google: faGoogle, + keycloak: faKey, + github: faGithub, + facebook: faFacebook, + twitter: faTwitter }; constructor(private readonly userService: UserService, private readonly router: Router) {} diff --git a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.module.ts b/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.module.ts deleted file mode 100644 index 7b729331..00000000 --- a/ui/apps/translatr/src/app/modules/pages/registration-page/registration-page.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { FeatureFlagDirective, FeatureFlagClassDirective, UserEditFormComponent } from '@dev/translatr-components'; -import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { faFacebook, faGithub, faGoogle, faTwitter } from '@fortawesome/free-brands-svg-icons'; -import { faKey } from '@fortawesome/free-solid-svg-icons'; -import { TranslocoModule } from '@jsverse/transloco'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { RegistrationPageRoutingModule } from './registration-page-routing.module'; -import { RegistrationPageComponent } from './registration-page.component'; - -@NgModule({ - declarations: [RegistrationPageComponent], - imports: [ - CommonModule, - - RegistrationPageRoutingModule, - SidenavModule, - TranslocoModule, - FeatureFlagDirective, FeatureFlagClassDirective, - UserEditFormComponent, - MatCardModule, - MatButtonModule, - FontAwesomeModule - ] -}) -export class RegistrationPageModule { - constructor(readonly library: FaIconLibrary) { - library.addIcons(faGoogle, faGithub, faFacebook, faTwitter, faKey); - } -} From cfa903051dfdcd8546b962c5f8af4ce752d08234 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:07:18 +0200 Subject: [PATCH 03/11] feat(translatr): convert project-page to standalone Tasks 2.1-2.6, completing group 2. Converts all 10 project-page components (KeyListComponent, LocaleListComponent, MemberListComponent, ProjectInfoComponent, ProjectKeysComponent, ProjectLocalesComponent, ProjectMembersComponent, ProjectActivityComponent, ProjectSettingsComponent, ProjectPageComponent) to standalone: true, deletes project-page.module.ts, and repoints app-routing.module.ts's project-page loadChildren to the surviving ProjectPageRoutingModule. ProjectStateModule (shared NgRx feature state for the whole project-page route subtree) and the AppFacade/guard providers that used to live on the deleted declarations module move into project-page-routing.module.ts instead, preserving the same one-registration-per-lazy-subtree semantics. A grep-based scan of project-info.component.html initially missed FeatureFlagDirective and MetricComponent; project-page.component.html missed FeatureFlagClassDirective (different attribute name than FeatureFlagDirective's [featureFlag]). Switched to systematically extracting every custom tag and *directive from each template before finalizing its imports array, and retroactively re-verified the already-converted list components the same way. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 12 +- .../translatr/src/app/app-routing.module.ts | 2 +- .../project-activity.component.spec.ts | 10 +- .../project-activity.component.ts | 8 +- .../project-info.component.spec.ts | 43 +++++-- .../project-info/project-info.component.ts | 47 ++++++- .../key-list/key-list.component.spec.ts | 47 ++++--- .../key-list/key-list.component.ts | 37 +++++- .../project-keys.component.spec.ts | 9 +- .../project-keys/project-keys.component.ts | 7 +- .../locale-list/locale-list.component.spec.ts | 49 +++++--- .../locale-list/locale-list.component.ts | 39 +++++- .../project-locales.component.spec.ts | 9 +- .../project-locales.component.ts | 7 +- .../member-list/member-list.component.spec.ts | 33 ++--- .../member-list/member-list.component.ts | 27 +++- .../project-members.component.spec.ts | 9 +- .../project-members.component.ts | 7 +- .../project-page-routing.module.ts | 13 +- .../project-page.component.spec.ts | 13 +- .../project-page/project-page.component.ts | 14 ++- .../pages/project-page/project-page.module.ts | 118 ------------------ .../project-settings.component.spec.ts | 11 +- .../project-settings.component.ts | 23 +++- 24 files changed, 351 insertions(+), 243 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/project-page/project-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index f6e10dfe..41fb7b69 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -9,12 +9,12 @@ ## 2. project-page -- [ ] 2.1 Convert `KeyListComponent` to `standalone: true` with explicit `imports`, update consumers -- [ ] 2.2 Convert `LocaleListComponent` to `standalone: true` with explicit `imports`, update consumers -- [ ] 2.3 Convert `MemberListComponent` to `standalone: true` with explicit `imports`, update consumers -- [ ] 2.4 Convert `ProjectInfoComponent`, `ProjectKeysComponent` (imports `KeyListComponent`), `ProjectLocalesComponent` (imports `LocaleListComponent`), `ProjectMembersComponent` (imports `MemberListComponent`), `ProjectActivityComponent`, `ProjectSettingsComponent` to `standalone: true` with explicit `imports` -- [ ] 2.5 Convert `ProjectPageComponent` to `standalone: true` with explicit `imports` (router-outlet only - no tab-component imports needed); delete `project-page.module.ts`; move the `ProjectStateModule` import from the deleted module into `project-page-routing.module.ts`; update consumers -- [ ] 2.6 Verify `nx test translatr` and `nx build translatr` succeed +- [x] 2.1 Convert `KeyListComponent` to `standalone: true` with explicit `imports`, update consumers (its spec needed `TestBed.overrideComponent` for the real `NavListComponent`/`ConfirmButtonComponent`/`EmptyView*` and `TranslocoTestingModule` for the same real-Transloco-activation reason seen throughout) +- [x] 2.2 Convert `LocaleListComponent` to `standalone: true` with explicit `imports`, update consumers (same pattern as 2.1, plus `MatMenuModule` for the file-type dropdown) +- [x] 2.3 Convert `MemberListComponent` to `standalone: true` with explicit `imports`, update consumers (uses `ngxGravatar` directly, not `UserCardComponent`; only `NavListComponent`/`ConfirmButtonComponent` needed the mock swap) +- [x] 2.4 Convert `ProjectInfoComponent`, `ProjectKeysComponent` (imports `KeyListComponent`), `ProjectLocalesComponent` (imports `LocaleListComponent`), `ProjectMembersComponent` (imports `MemberListComponent`), `ProjectActivityComponent`, `ProjectSettingsComponent` to `standalone: true` with explicit `imports` (an initial grep-based scan of `project-info.component.html` missed `FeatureFlagDirective` and `MetricComponent` - both used via `*featureFlag` and `dev-metric` respectively; switched to systematically extracting every custom tag/`*directive` from each template before finalizing `imports`, and retroactively re-verified 2.1-2.3 the same way with no further misses) +- [x] 2.5 Convert `ProjectPageComponent` to `standalone: true` with explicit `imports` (router-outlet only - no tab-component imports needed, but the same tag-extraction pass caught a missed `FeatureFlagClassDirective` via `[featureFlagClass]`); delete `project-page.module.ts`; moved `ProjectStateModule`, plus `AppFacade`/`ProjectGuard`/`ProjectAccessGuard`/`ProjectEditGuard` providers, from the deleted module into `project-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s project-page `loadChildren` to `ProjectPageRoutingModule` +- [x] 2.6 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean) ## 3. user-page diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index e7442c9f..d805dafb 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -57,7 +57,7 @@ const routes: Routes = [ { path: '', loadChildren: () => - import('./modules/pages/project-page/project-page.module').then(m => m.ProjectPageModule) + import('./modules/pages/project-page/project-page-routing.module').then(m => m.ProjectPageRoutingModule) }, { path: '', diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.spec.ts index 0dce36fa..3e3d643e 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.spec.ts @@ -1,7 +1,9 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ActivityGraphComponent } from '@dev/translatr-components'; import { MockActivityGraphComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; import { MockActivityListComponent } from '../../../shared/activity-list/testing'; import { ProjectActivityComponent } from './project-activity.component'; @@ -11,9 +13,11 @@ describe('ProjectActivityComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectActivityComponent], - imports: [MockActivityListComponent, MockActivityGraphComponent], + TestBed.overrideComponent(ProjectActivityComponent, { + remove: { imports: [ActivityListComponent, ActivityGraphComponent] }, + add: { imports: [MockActivityListComponent, MockActivityGraphComponent] } + }).configureTestingModule({ + imports: [ProjectActivityComponent], providers: [ { provide: ProjectFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.ts index 3375dcf7..4be5fe8b 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-activity/project-activity.component.ts @@ -1,13 +1,17 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { ActivityGraphComponent } from '@dev/translatr-components'; import { filter, map, takeUntil, withLatestFrom } from 'rxjs/operators'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; import { ProjectFacade } from '../../../shared/project-state'; @Component({ - standalone: false, + standalone: true, selector: 'app-project-activity', templateUrl: './project-activity.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./project-activity.component.scss'] + styleUrls: ['./project-activity.component.scss'], + imports: [CommonModule, ActivityGraphComponent, ActivityListComponent] }) export class ProjectActivityComponent implements OnInit { project$ = this.facade.project$; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.spec.ts index 6d2fb790..dd1232d0 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.spec.ts @@ -12,14 +12,25 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterTestingModule } from '@angular/router/testing'; +import { TranslocoTestingModule } from '@jsverse/transloco'; import { WINDOW } from '@translatr/utils'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewActionsComponent, + FeatureFlagDirective, + MetricComponent, + ProjectInfographicComponent +} from '@dev/translatr-components'; import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, MockFeatureFlagDirective, MockFeatureFlagClassDirective, MockMetricComponent, MockProjectInfographicComponent } from '@translatr/components/testing'; +import { NavListComponent } from '../../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../../shared/nav-list/testing'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; import { MockActivityListComponent } from '../../../shared/activity-list/testing'; import { ShortNumberPipe } from '@dev/translatr-components'; import { mockObservable } from '@translatr/utils/testing'; @@ -31,18 +42,34 @@ describe('ProjectInfoComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectInfoComponent], + TestBed.overrideComponent(ProjectInfoComponent, { + remove: { + imports: [ + MetricComponent, + NavListComponent, + ActivityListComponent, + FeatureFlagDirective, + EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewActionsComponent, + ProjectInfographicComponent + ] + }, + add: { + imports: [ + MockMetricComponent, + MockNavListComponent, + MockActivityListComponent, + MockFeatureFlagDirective, MockFeatureFlagClassDirective, + MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, + MockProjectInfographicComponent + ] + } + }).configureTestingModule({ imports: [ - MockMetricComponent, - MockNavListComponent, - MockActivityListComponent, + ProjectInfoComponent, ShortNumberPipe, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - MockProjectInfographicComponent, RouterTestingModule, + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), MatDialogModule, MatCardModule, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts index 8398278e..722eb436 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts @@ -1,6 +1,23 @@ +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewActionsComponent, + FeatureFlagDirective, + MetricComponent, + ProjectInfographicComponent, + ShortNumberPipe +} from '@dev/translatr-components'; import { AccessToken, Activity, @@ -14,10 +31,13 @@ import { PagedList, Project } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { slicePagedList, WINDOW } from '@translatr/utils'; import { Observable } from 'rxjs'; import { filter, map, take } from 'rxjs/operators'; import { AppFacade } from '../../../../+state/app.facade'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; +import { NavListComponent } from '../../../shared/nav-list/nav-list.component'; import { openKeyEditDialog } from '../../../shared/key-edit-dialog/key-edit-dialog.component'; import { openLocaleEditDialog } from '../../../shared/locale-edit-dialog/locale-edit-dialog.component'; import { ProjectFacade } from '../../../shared/project-state'; @@ -27,11 +47,32 @@ function endpointFromLocation(location: Location) { } @Component({ - standalone: false, + standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-project-info', templateUrl: './project-info.component.html', - styleUrls: ['./project-info.component.scss'] + styleUrls: ['./project-info.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + NavListComponent, + ActivityListComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewActionsComponent, + FeatureFlagDirective, + MetricComponent, + ProjectInfographicComponent, + ShortNumberPipe, + MatButtonModule, + MatCardModule, + MatFormFieldModule, + MatIconModule, + MatProgressBarModule, + MatSelectModule, + MatTooltipModule + ] }) export class ProjectInfoComponent { project$ = this.facade.project$.pipe(filter(x => !!x)); diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.spec.ts index 0d359b25..c5608c0d 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.spec.ts @@ -1,12 +1,16 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { KeyListComponent } from './key-list.component'; import { RouterTestingModule } from '@angular/router/testing'; -import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; -import { MatIconModule } from '@angular/material/icon'; -import { MatListModule } from '@angular/material/list'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslocoTestingModule } from '@jsverse/transloco'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../../../shared/nav-list/testing'; import { MockConfirmButtonComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing'; @@ -16,21 +20,30 @@ describe('KeyListComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [KeyListComponent], + TestBed.overrideComponent(KeyListComponent, { + remove: { + imports: [ + NavListComponent, + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent + ] + }, + add: { + imports: [ + MockNavListComponent, + MockConfirmButtonComponent, + MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent + ] + } + }).configureTestingModule({ imports: [ - MockNavListComponent, - MockConfirmButtonComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - + KeyListComponent, RouterTestingModule, - MatDialogModule, - MatListModule, - MatIconModule, - MatProgressBarModule, - MatTooltipModule, - MatButtonModule + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ] }).compileComponents(); }) diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.ts index 45f4a2d8..47dc71d4 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/key-list.component.ts @@ -1,18 +1,49 @@ +import { CommonModule } from '@angular/common'; import { Component, EventEmitter, HostBinding, Input, Output, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; import { Key, KeyCriteria, PagedList, Project } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { trackByFn } from '@translatr/utils'; import { filter, take } from 'rxjs/operators'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { openKeyEditDialog } from '../../../../shared/key-edit-dialog/key-edit-dialog.component'; import { FilterCriteria } from '../../../../shared/list-header/list-header.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-key-list', templateUrl: './key-list.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./key-list.component.scss'] + styleUrls: ['./key-list.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + NavListComponent, + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + MatListModule, + MatIconModule, + MatProgressBarModule, + MatTooltipModule, + MatButtonModule + ] }) export class KeyListComponent { @Input() project: Project; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts index a6630e57..3e99f4e7 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts @@ -3,6 +3,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { RouterTestingModule } from '@angular/router/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; +import { KeyListComponent } from './key-list/key-list.component'; import { KeyListTestingModule } from './key-list/testing'; import { ProjectKeysComponent } from './project-keys.component'; @@ -13,9 +14,11 @@ describe('ProjectKeysComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectKeysComponent], - imports: [KeyListTestingModule, RouterTestingModule], + TestBed.overrideComponent(ProjectKeysComponent, { + remove: { imports: [KeyListComponent] }, + add: { imports: [KeyListTestingModule] } + }).configureTestingModule({ + imports: [ProjectKeysComponent, RouterTestingModule], providers: [ { provide: ProjectFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.ts index 76b4001e..3c64d1d5 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; @@ -5,14 +6,16 @@ import { Key, KeyCriteria, Project } from '@dev/translatr-model'; import { navigate } from '@translatr/utils'; import { combineLatest } from 'rxjs'; import { filter, take, takeUntil } from 'rxjs/operators'; +import { KeyListComponent } from './key-list/key-list.component'; import { ProjectFacade } from '../../../shared/project-state'; @Component({ - standalone: false, + standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-project-keys', templateUrl: './project-keys.component.html', - styleUrls: ['./project-keys.component.scss'] + styleUrls: ['./project-keys.component.scss'], + imports: [CommonModule, KeyListComponent] }) export class ProjectKeysComponent { readonly project$ = this.facade.project$.pipe(filter(x => !!x)); diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.spec.ts index db76969c..b4b78604 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.spec.ts @@ -1,16 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { LocaleListComponent } from './locale-list.component'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../../../shared/nav-list/testing'; import { MockConfirmButtonComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; -import { MatIconModule } from '@angular/material/icon'; -import { MatListModule } from '@angular/material/list'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslocoTestingModule } from '@jsverse/transloco'; describe('LocaleListComponent', () => { let component: LocaleListComponent; @@ -18,22 +21,30 @@ describe('LocaleListComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [LocaleListComponent], + TestBed.overrideComponent(LocaleListComponent, { + remove: { + imports: [ + NavListComponent, + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent + ] + }, + add: { + imports: [ + MockNavListComponent, + MockConfirmButtonComponent, + MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent + ] + } + }).configureTestingModule({ imports: [ - MockNavListComponent, - MockConfirmButtonComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - + LocaleListComponent, RouterTestingModule, - MatDialogModule, - MatListModule, - MatIconModule, - MatProgressBarModule, - MatTooltipModule, - MatButtonModule, - MatMenuModule + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ] }).compileComponents(); }) diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.ts index 928f7daf..cd8b208f 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/locale-list.component.ts @@ -1,6 +1,20 @@ +import { CommonModule } from '@angular/common'; import { Component, EventEmitter, HostBinding, Input, Output, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; import { fileTypeNames, fileTypes, @@ -9,17 +23,36 @@ import { PagedList, Project } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { trackByFn } from '@translatr/utils'; import { filter, take } from 'rxjs/operators'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { FilterCriteria } from '../../../../shared/list-header/list-header.component'; import { openLocaleEditDialog } from '../../../../shared/locale-edit-dialog/locale-edit-dialog.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-locale-list', templateUrl: './locale-list.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./locale-list.component.scss'] + styleUrls: ['./locale-list.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + NavListComponent, + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + MatListModule, + MatIconModule, + MatProgressBarModule, + MatTooltipModule, + MatButtonModule, + MatMenuModule + ] }) export class LocaleListComponent { @Input() project: Project; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts index fbf88e86..eefeea54 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts @@ -3,6 +3,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { RouterTestingModule } from '@angular/router/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; +import { LocaleListComponent } from './locale-list/locale-list.component'; import { LocaleListTestingModule } from './locale-list/testing'; import { ProjectLocalesComponent } from './project-locales.component'; @@ -12,9 +13,11 @@ describe('ProjectLocalesComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectLocalesComponent], - imports: [LocaleListTestingModule, RouterTestingModule], + TestBed.overrideComponent(ProjectLocalesComponent, { + remove: { imports: [LocaleListComponent] }, + add: { imports: [LocaleListTestingModule] } + }).configureTestingModule({ + imports: [ProjectLocalesComponent, RouterTestingModule], providers: [ { provide: ProjectFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.ts index 649dbe8c..52f3313d 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.ts @@ -1,17 +1,20 @@ +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; import { Locale, LocaleCriteria, Project } from '@dev/translatr-model'; import { navigate } from '@translatr/utils'; import { filter, take, takeUntil, withLatestFrom } from 'rxjs/operators'; +import { LocaleListComponent } from './locale-list/locale-list.component'; import { ProjectFacade } from '../../../shared/project-state'; @Component({ - standalone: false, + standalone: true, selector: 'app-project-locales', templateUrl: './project-locales.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./project-locales.component.scss'] + styleUrls: ['./project-locales.component.scss'], + imports: [CommonModule, LocaleListComponent] }) export class ProjectLocalesComponent { project$ = this.facade.project$.pipe(filter(x => !!x)); diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.spec.ts index 21a73676..852a9fff 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.spec.ts @@ -1,16 +1,11 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule } from '@angular/material/dialog'; -import { MatIconModule } from '@angular/material/icon'; -import { MatListModule } from '@angular/material/list'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; -import { MockConfirmButtonComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing'; -import { GravatarModule } from 'ngx-gravatar'; -import { TimeAgoPipe } from '@dev/translatr-components'; +import { ConfirmButtonComponent } from '@dev/translatr-components'; +import { MockConfirmButtonComponent } from '@translatr/components/testing'; import { AppFacade } from '../../../../../+state/app.facade'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../../../shared/nav-list/testing'; import { MemberListComponent } from './member-list.component'; @@ -21,25 +16,15 @@ describe('MemberListComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [MemberListComponent], + TestBed.overrideComponent(MemberListComponent, { + remove: { imports: [NavListComponent, ConfirmButtonComponent] }, + add: { imports: [MockNavListComponent, MockConfirmButtonComponent] } + }).configureTestingModule({ imports: [ - MockNavListComponent, - MockConfirmButtonComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - + MemberListComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), - - MatDialogModule, - MatListModule, - MatIconModule, - MatProgressBarModule, - MatTooltipModule, - MatButtonModule, - - GravatarModule, - TimeAgoPipe + MatDialogModule ], providers: [{ provide: AppFacade, useFactory: () => ({}) }] }).compileComponents(); diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts index 5d22f163..10de4cb7 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts @@ -1,9 +1,18 @@ +import { CommonModule } from '@angular/common'; import { Component, EventEmitter, HostBinding, Input, Output, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; -import { Router } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { Router, RouterModule } from '@angular/router'; +import { ConfirmButtonComponent } from '@dev/translatr-components'; import { Member, MemberRole, PagedList, Project, RequestCriteria } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { filter, switchMapTo, take } from 'rxjs/operators'; +import { GravatarModule } from 'ngx-gravatar'; import { AppFacade } from '../../../../../+state/app.facade'; +import { NavListComponent } from '../../../../shared/nav-list/nav-list.component'; import { defaultFilters, FilterCriteria @@ -12,11 +21,23 @@ import { openProjectMemberEditDialog } from '../../../../shared/project-member-e import { openProjectOwnerEditDialog } from '../../../../shared/project-owner-edit-dialog/project-owner-edit-dialog.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-member-list', templateUrl: './member-list.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./member-list.component.scss'] + styleUrls: ['./member-list.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + NavListComponent, + ConfirmButtonComponent, + GravatarModule, + MatListModule, + MatIconModule, + MatTooltipModule, + MatButtonModule + ] }) export class MemberListComponent { ownerCount: number; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts index b2fbbf46..0422e2ad 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts @@ -4,6 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; import { AppFacade } from '../../../../+state/app.facade'; +import { MemberListComponent } from './member-list/member-list.component'; import { MemberListTestingModule } from './member-list/testing'; import { ProjectMembersComponent } from './project-members.component'; @@ -14,9 +15,11 @@ describe('ProjectMembersComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectMembersComponent], - imports: [MemberListTestingModule, RouterTestingModule], + TestBed.overrideComponent(ProjectMembersComponent, { + remove: { imports: [MemberListComponent] }, + add: { imports: [MemberListTestingModule] } + }).configureTestingModule({ + imports: [ProjectMembersComponent, RouterTestingModule], providers: [ { provide: ProjectFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.ts index dda3ba9b..5a8eda2f 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; @@ -6,15 +7,17 @@ import { navigate } from '@translatr/utils'; import { combineLatest, Observable } from 'rxjs'; import { filter, map, take, takeUntil, withLatestFrom } from 'rxjs/operators'; import { AppFacade } from '../../../../+state/app.facade'; +import { MemberListComponent } from './member-list/member-list.component'; import { FilterCriteria } from '../../../shared/list-header/list-header.component'; import { ProjectFacade } from '../../../shared/project-state'; @Component({ - standalone: false, + standalone: true, selector: 'app-project-members', templateUrl: './project-members.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./project-members.component.scss'] + styleUrls: ['./project-members.component.scss'], + imports: [CommonModule, MemberListComponent] }) export class ProjectMembersComponent { project$ = this.facade.project$; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-page-routing.module.ts index 06eacd5c..e90c8ee6 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-page-routing.module.ts @@ -1,6 +1,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { AppFacade } from '../../../+state/app.facade'; import { AuthGuard } from '../../../guards/auth.guard'; +import { ProjectStateModule } from '../../shared/project-state'; +import { ProjectAccessGuard } from './project-access.guard'; import { ProjectActivityComponent } from './project-activity/project-activity.component'; import { ProjectEditGuard } from './project-edit.guard'; import { ProjectInfoComponent } from './project-info/project-info.component'; @@ -72,8 +75,14 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], + imports: [RouterModule.forChild(routes), ProjectStateModule], exports: [RouterModule], - providers: [{ provide: PROJECT_ROUTES, useValue: routes }] + providers: [ + { provide: PROJECT_ROUTES, useValue: routes }, + AppFacade, + ProjectGuard, + ProjectAccessGuard, + ProjectEditGuard + ] }) export class ProjectPageRoutingModule {} 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 48444cf0..26e4ee42 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 @@ -4,9 +4,11 @@ import { MatIconModule } from '@angular/material/icon'; import { MatTabsModule } from '@angular/material/tabs'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; -import { MockFeatureFlagDirective, MockFeatureFlagClassDirective } from '@translatr/components/testing'; +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 { ProjectFacade } from '../../shared/project-state'; @@ -19,11 +21,12 @@ describe('ProjectPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectPageComponent], + TestBed.overrideComponent(ProjectPageComponent, { + remove: { imports: [SidenavModule, FeatureFlagClassDirective] }, + add: { imports: [SidenavTestingModule, MockFeatureFlagClassDirective] } + }).configureTestingModule({ imports: [ - SidenavTestingModule, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, + ProjectPageComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), 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 c56bbcdc..6a84ddb2 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 @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -6,9 +7,12 @@ import { OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute, CanActivate, Route } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabsModule } from '@angular/material/tabs'; +import { ActivatedRoute, CanActivate, Route, RouterModule } from '@angular/router'; +import { FeatureFlagClassDirective } from '@dev/translatr-components'; import { Feature, Project } from '@dev/translatr-model'; -import { TranslocoService } from '@jsverse/transloco'; +import { TranslocoModule, TranslocoService } from '@jsverse/transloco'; import { canActivate$, NameIconRoute } from '@translatr/utils'; import { combineLatest, merge, Observable } from 'rxjs'; import { @@ -21,15 +25,17 @@ import { takeUntil } from 'rxjs/operators'; import { AppFacade } from '../../../+state/app.facade'; +import { SidenavModule } from '../../nav/sidenav/sidenav.module'; import { ProjectFacade } from '../../shared/project-state'; import { PROJECT_ROUTES } from './project-page.token'; @Component({ - standalone: false, + standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-project-page', templateUrl: './project-page.component.html', - styleUrls: ['./project-page.component.scss'] + styleUrls: ['./project-page.component.scss'], + imports: [CommonModule, RouterModule, TranslocoModule, SidenavModule, MatIconModule, MatTabsModule, FeatureFlagClassDirective] }) export class ProjectPageComponent implements OnInit, OnDestroy { me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.module.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-page.module.ts deleted file mode 100644 index d7919884..00000000 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-page.module.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatSelectModule } from '@angular/material/select'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatTabsModule } from '@angular/material/tabs'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { - ActivityGraphComponent, - ConfirmButtonComponent, - EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - MetricComponent, - ProjectInfographicComponent, - ShortNumberPipe, - UserCardComponent, UserCardLinkComponent -} from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { GravatarModule } from 'ngx-gravatar'; -import { TimeAgoPipe } from '@dev/translatr-components'; -import { AppFacade } from '../../../+state/app.facade'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { ActivityListComponent } from '../../shared/activity-list/activity-list.component'; -import { KeyEditDialogComponent } from '../../shared/key-edit-dialog/key-edit-dialog.component'; -import { ListHeaderComponent } from '../../shared/list-header/list-header.component'; -import { LocaleEditDialogComponent } from '../../shared/locale-edit-dialog/locale-edit-dialog.component'; -import { NavListComponent } from '../../shared/nav-list/nav-list.component'; -import { ProjectDeleteDialogComponent } from '../../shared/project-delete-dialog/project-delete-dialog.component'; -import { ProjectMemberEditDialogComponent } from '../../shared/project-member-edit-dialog/project-member-edit-dialog.component'; -import { ProjectOwnerEditDialogComponent } from '../../shared/project-owner-edit-dialog/project-owner-edit-dialog.component'; -import { ProjectStateModule } from '../../shared/project-state'; -import { ProjectAccessGuard } from './project-access.guard'; -import { ProjectActivityComponent } from './project-activity/project-activity.component'; -import { ProjectEditGuard } from './project-edit.guard'; -import { ProjectInfoComponent } from './project-info/project-info.component'; -import { KeyListComponent } from './project-keys/key-list/key-list.component'; -import { ProjectKeysComponent } from './project-keys/project-keys.component'; -import { LocaleListComponent } from './project-locales/locale-list/locale-list.component'; -import { ProjectLocalesComponent } from './project-locales/project-locales.component'; -import { MemberListComponent } from './project-members/member-list/member-list.component'; -import { ProjectMembersComponent } from './project-members/project-members.component'; -import { ProjectPageRoutingModule } from './project-page-routing.module'; -import { ProjectPageComponent } from './project-page.component'; -import { ProjectSettingsComponent } from './project-settings/project-settings.component'; -import { ProjectGuard } from './project.guard'; - -@NgModule({ - declarations: [ - ProjectPageComponent, - ProjectInfoComponent, - ProjectKeysComponent, - KeyListComponent, - ProjectLocalesComponent, - LocaleListComponent, - ProjectMembersComponent, - MemberListComponent, - ProjectActivityComponent, - ProjectSettingsComponent - ], - imports: [ - ProjectPageRoutingModule, - SidenavModule, - ActivityListComponent, - NavListComponent, - LocaleEditDialogComponent, - KeyEditDialogComponent, - ProjectMemberEditDialogComponent, - ProjectDeleteDialogComponent, - ProjectOwnerEditDialogComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - UserCardComponent, UserCardLinkComponent, - ListHeaderComponent, - MetricComponent, - EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, - ConfirmButtonComponent, - ShortNumberPipe, - ActivityGraphComponent, - TranslocoModule, - ProjectInfographicComponent, - - CommonModule, - FormsModule, - ReactiveFormsModule, - ProjectStateModule, - - MatToolbarModule, - MatButtonModule, - MatIconModule, - MatCardModule, - MatChipsModule, - MatTabsModule, - MatListModule, - MatInputModule, - MatFormFieldModule, - MatSnackBarModule, - MatTooltipModule, - MatTooltipModule, - MatFormFieldModule, - MatMenuModule, - MatProgressBarModule, - MatSelectModule, - - TimeAgoPipe, - GravatarModule - ], - providers: [AppFacade, ProjectGuard, ProjectAccessGuard, ProjectEditGuard] -}) -export class ProjectPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.spec.ts index c8557ebf..58a73b2b 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.spec.ts @@ -9,7 +9,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { ProjectService } from '@dev/translatr-sdk'; import { TranslocoTestingModule } from '@jsverse/transloco'; -import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing'; +import { EmptyViewComponent } from '@dev/translatr-components'; +import { MockEmptyViewComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; import { AppFacade } from '../../../../+state/app.facade'; @@ -22,10 +23,12 @@ describe('ProjectSettingsComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectSettingsComponent], + TestBed.overrideComponent(ProjectSettingsComponent, { + remove: { imports: [EmptyViewComponent] }, + add: { imports: [MockEmptyViewComponent] } + }).configureTestingModule({ imports: [ - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, + ProjectSettingsComponent, FormsModule, ReactiveFormsModule, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.ts index 89a953cf..297f6cfe 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-settings/project-settings.component.ts @@ -1,9 +1,16 @@ +import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { FormBuilder, Validators } from '@angular/forms'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; +import { EmptyViewComponent } from '@dev/translatr-components'; import { Project } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { filter, switchMap, take, takeUntil } from 'rxjs/operators'; import { AppFacade } from '../../../../+state/app.facade'; import { BaseEditFormComponent } from '../../../shared/edit-form/base-edit-form.component'; @@ -11,11 +18,21 @@ import { openProjectDeleteDialog } from '../../../shared/project-delete-dialog/p import { ProjectFacade } from '../../../shared/project-state'; @Component({ - standalone: false, + standalone: true, selector: 'app-project-settings', templateUrl: './project-settings.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./project-settings.component.scss'] + styleUrls: ['./project-settings.component.scss'], + imports: [ + CommonModule, + ReactiveFormsModule, + TranslocoModule, + EmptyViewComponent, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule + ] }) export class ProjectSettingsComponent extends BaseEditFormComponent From 9273146970ce817ba1ecbfbb6585d4036689357b Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:15:35 +0200 Subject: [PATCH 04/11] feat(translatr): convert user-page to standalone Convert all 6 user-page sub-components and UserPageComponent to standalone: true. Delete user-page.module.ts, moving its NgRx forFeature wiring and UserFacade/UserGuard providers into user-page-routing.module.ts. Repoint app-routing.module.ts's user-page loadChildren to UserPageRoutingModule. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 6 +- .../translatr/src/app/app-routing.module.ts | 2 +- .../user-access-token.component.spec.ts | 9 +- .../user-access-token.component.ts | 8 +- .../user-access-tokens.component.spec.ts | 26 ++++- .../user-access-tokens.component.ts | 35 +++++- .../user-activity.component.spec.ts | 10 +- .../user-activity/user-activity.component.ts | 8 +- .../user-info/user-info.component.spec.ts | 19 +-- .../user-info/user-info.component.ts | 24 +++- .../user-page/user-page-routing.module.ts | 19 ++- .../user-page/user-page.component.spec.ts | 17 +-- .../pages/user-page/user-page.component.ts | 13 ++- .../pages/user-page/user-page.module.ts | 109 ------------------ .../user-projects.component.spec.ts | 9 +- .../user-projects/user-projects.component.ts | 9 +- .../user-settings.component.spec.ts | 7 +- .../user-settings/user-settings.component.ts | 26 ++++- 18 files changed, 187 insertions(+), 169 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/user-page/user-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 41fb7b69..12ef4643 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -18,9 +18,9 @@ ## 3. user-page -- [ ] 3.1 Convert `UserInfoComponent`, `UserProjectsComponent`, `UserAccessTokensComponent`, `UserAccessTokenComponent`, `UserActivityComponent`, `UserSettingsComponent` to `standalone: true` with explicit `imports` -- [ ] 3.2 Convert `UserPageComponent` to `standalone: true` with explicit `imports` (router-outlet only); delete `user-page.module.ts`; move `StoreModule.forFeature(USER_FEATURE_KEY, ...)`, `EffectsModule.forFeature([UserEffects])`, and `providers: [UserFacade, UserGuard]` from the deleted module into `user-page-routing.module.ts`; update consumers -- [ ] 3.3 Verify `nx test translatr` and `nx build translatr` succeed +- [x] 3.1 Convert `UserInfoComponent`, `UserProjectsComponent`, `UserAccessTokensComponent`, `UserAccessTokenComponent`, `UserActivityComponent`, `UserSettingsComponent` to `standalone: true` with explicit `imports` (`user-info`/`user-access-tokens` specs needed `TranslocoTestingModule` for the same real-Transloco-activation reason seen throughout; `user-settings` needed no `overrideComponent` swap despite its spec importing now-vestigial Mock `EmptyView*` classes - it doesn't actually use `dev-empty-view` in its template) +- [x] 3.2 Convert `UserPageComponent` to `standalone: true` with explicit `imports` (router-outlet only, same shape as `ProjectPageComponent` - `[featureFlagClass]` caught via the systematic tag-extraction scan); delete `user-page.module.ts`; moved `StoreModule.forFeature(USER_FEATURE_KEY, ...)`, `EffectsModule.forFeature([UserEffects])`, and `providers: [UserFacade, UserGuard]` from the deleted module into `user-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s user-page `loadChildren` to `UserPageRoutingModule` +- [x] 3.3 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean) ## 4. editor-page diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index d805dafb..0c97d712 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -52,7 +52,7 @@ const routes: Routes = [ { path: '', loadChildren: () => - import('./modules/pages/user-page/user-page.module').then(m => m.UserPageModule) + import('./modules/pages/user-page/user-page-routing.module').then(m => m.UserPageRoutingModule) }, { path: '', diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.spec.ts index 23c46cca..7a0a485f 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.spec.ts @@ -5,6 +5,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; import { mockObservable } from '@translatr/utils/testing'; import { UserFacade } from '../+state/user.facade'; +import { AccessTokenEditFormComponent } from '../../../shared/access-token-edit-form/access-token-edit-form.component'; import { MockAccessTokenEditFormComponent } from '../../../shared/access-token-edit-form/testing'; import { UserAccessTokenComponent } from './user-access-token.component'; @@ -15,10 +16,12 @@ describe('UserAccessTokenComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserAccessTokenComponent], + TestBed.overrideComponent(UserAccessTokenComponent, { + remove: { imports: [AccessTokenEditFormComponent] }, + add: { imports: [MockAccessTokenEditFormComponent] } + }).configureTestingModule({ imports: [ - MockAccessTokenEditFormComponent, + UserAccessTokenComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts index eb42f55f..e4ef4a11 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts @@ -1,14 +1,18 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; import { ActivatedRoute } from '@angular/router'; import { filter, take } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; +import { AccessTokenEditFormComponent } from '../../../shared/access-token-edit-form/access-token-edit-form.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-access-token', templateUrl: './user-access-token.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-access-token.component.scss'] + styleUrls: ['./user-access-token.component.scss'], + imports: [CommonModule, MatCardModule, AccessTokenEditFormComponent] }) export class UserAccessTokenComponent implements OnInit { accessToken$ = this.facade.accessToken$.pipe(filter(x => !!x)); diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.spec.ts index 5c5235c6..f785fe1e 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.spec.ts @@ -1,10 +1,19 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MatSnackBar } from '@angular/material/snack-bar'; import { UserAccessTokensComponent } from './user-access-tokens.component'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; +import { NavListComponent } from '../../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../../shared/nav-list/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; +import { TranslocoTestingModule } from '@jsverse/transloco'; import { MatListModule } from '@angular/material/list'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TimeAgoPipe } from '@dev/translatr-components'; @@ -18,19 +27,24 @@ describe('UserAccessTokensComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserAccessTokensComponent], + TestBed.overrideComponent(UserAccessTokensComponent, { + remove: { + imports: [NavListComponent, ConfirmButtonComponent, EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent] + }, + add: { + imports: [MockNavListComponent, MockConfirmButtonComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent] + } + }).configureTestingModule({ imports: [ - MockNavListComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - MockConfirmButtonComponent, + UserAccessTokensComponent, RouterTestingModule, TimeAgoPipe, MatListModule, MatIconModule, - MatTooltipModule + MatTooltipModule, + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ], providers: [ { diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts index dceb3879..d7272cb3 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts @@ -1,20 +1,49 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { ActivatedRoute, Router } from '@angular/router'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent +} from '@dev/translatr-components'; import { AccessToken } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { navigate, trackByFn } from '@translatr/utils'; import { filter, take, takeUntil, withLatestFrom } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; +import { NavListComponent } from '../../../shared/nav-list/nav-list.component'; import { openAccessTokenEditDialog } from '../../../shared/access-token-edit-dialog/access-token-edit-dialog.component'; import { FilterCriteria } from '../../../shared/list-header/list-header.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-access-tokens', templateUrl: './user-access-tokens.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-access-tokens.component.scss'] + styleUrls: ['./user-access-tokens.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + NavListComponent, + ConfirmButtonComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + MatListModule, + MatIconModule, + MatTooltipModule, + MatButtonModule + ] }) export class UserAccessTokensComponent implements OnInit { readonly user$ = this.facade.user$; diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.spec.ts index 23ff973d..a7f7b20d 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.spec.ts @@ -1,7 +1,9 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ActivityGraphComponent } from '@dev/translatr-components'; import { MockActivityGraphComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { UserFacade } from '../+state/user.facade'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; import { MockActivityListComponent } from '../../../shared/activity-list/testing'; import { UserActivityComponent } from './user-activity.component'; @@ -11,9 +13,11 @@ describe('UserActivityComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserActivityComponent], - imports: [MockActivityListComponent, MockActivityGraphComponent], + TestBed.overrideComponent(UserActivityComponent, { + remove: { imports: [ActivityListComponent, ActivityGraphComponent] }, + add: { imports: [MockActivityListComponent, MockActivityGraphComponent] } + }).configureTestingModule({ + imports: [UserActivityComponent], providers: [ { provide: UserFacade, diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.ts index 437e054a..d63c7d33 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-activity/user-activity.component.ts @@ -1,14 +1,18 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { ActivityGraphComponent } from '@dev/translatr-components'; import { User, UserCriteria } from '@dev/translatr-model'; import { filter, map, takeUntil, withLatestFrom } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-activity', templateUrl: './user-activity.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-activity.component.scss'] + styleUrls: ['./user-activity.component.scss'], + imports: [CommonModule, ActivityGraphComponent, ActivityListComponent] }) export class UserActivityComponent implements OnInit { readonly criteria$ = this.facade.criteria$; diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.spec.ts index 1e43ce07..72334ba0 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.spec.ts @@ -2,14 +2,17 @@ import { Injector } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; +import { TranslocoTestingModule } from '@jsverse/transloco'; import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterTestingModule } from '@angular/router/testing'; -import { ShortNumberPipe } from '@dev/translatr-components'; +import { MetricComponent, ShortNumberPipe, UserCardComponent } from '@dev/translatr-components'; import { MockMetricComponent, MockUserCardComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { TimeAgoPipe } from '@dev/translatr-components'; import { UserFacade } from '../+state/user.facade'; +import { ActivityListComponent } from '../../../shared/activity-list/activity-list.component'; import { MockActivityListComponent } from '../../../shared/activity-list/testing'; +import { ProjectCardListComponent } from '../../../shared/project-card-list/project-card-list.component'; import { MockProjectCardListComponent } from '../../../shared/project-card-list/testing'; import { USER_ROUTES } from '../user-page.token'; @@ -21,20 +24,20 @@ describe('UserInfoComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserInfoComponent], + TestBed.overrideComponent(UserInfoComponent, { + remove: { imports: [UserCardComponent, MetricComponent, ActivityListComponent, ProjectCardListComponent] }, + add: { imports: [MockUserCardComponent, MockMetricComponent, MockProjectCardListComponent, MockActivityListComponent] } + }).configureTestingModule({ imports: [ - MockUserCardComponent, - MockMetricComponent, + UserInfoComponent, ShortNumberPipe, - MockProjectCardListComponent, - MockActivityListComponent, RouterTestingModule, TimeAgoPipe, MatIconModule, - MatTooltipModule + MatTooltipModule, + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ], providers: [ { provide: Injector, useFactory: () => ({}) }, diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts index 99100625..cba60e5d 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts @@ -1,20 +1,38 @@ +import { CommonModule } from '@angular/common'; import { Component, Inject, Injector, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, CanActivate, Router } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { ActivatedRoute, CanActivate, Router, RouterModule } from '@angular/router'; +import { MetricComponent, UserCardComponent } from '@dev/translatr-components'; import { PagedList, Project, User } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { canActivate$, NameIconRoute, slicePagedList } from '@translatr/utils'; import { Observable, of } from 'rxjs'; import { filter, map, take, takeUntil } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; +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'; import { USER_ROUTES } from '../user-page.token'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-info', templateUrl: './user-info.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-info.component.scss'] + styleUrls: ['./user-info.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + ActivityListComponent, + ProjectCardListComponent, + MetricComponent, + UserCardComponent, + MatIconModule, + MatTooltipModule + ] }) export class UserInfoComponent implements OnInit { readonly user$ = this.facade.user$; diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-page-routing.module.ts index 15391ecc..52d03445 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-page-routing.module.ts @@ -1,7 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; import { AuthGuard } from '../../../guards/auth.guard'; import { MyselfGuard } from '../../../guards/myself.guard'; +import { UserEffects } from './+state/user.effects'; +import { UserFacade } from './+state/user.facade'; +import { + initialState as userInitialState, + USER_FEATURE_KEY, + userReducer +} from './+state/user.reducer'; import { UserAccessTokenComponent } from './user-access-token/user-access-token.component'; import { UserAccessTokensComponent } from './user-access-tokens/user-access-tokens.component'; import { UserActivityComponent } from './user-activity/user-activity.component'; @@ -84,8 +93,14 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], + imports: [ + RouterModule.forChild(routes), + StoreModule.forFeature(USER_FEATURE_KEY, userReducer, { + initialState: userInitialState + }), + EffectsModule.forFeature([UserEffects]) + ], exports: [RouterModule], - providers: [{ provide: USER_ROUTES, useValue: routes }] + providers: [{ provide: USER_ROUTES, useValue: routes }, UserFacade, UserGuard] }) export class UserPageRoutingModule {} 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 cdd39acc..8d54ecaf 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 @@ -3,9 +3,11 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MatIconModule } from '@angular/material/icon'; import { MatTabsModule } from '@angular/material/tabs'; import { RouterTestingModule } from '@angular/router/testing'; -import { MockFeatureFlagDirective, MockFeatureFlagClassDirective } from '@translatr/components/testing'; -import { GravatarModule } from 'ngx-gravatar'; +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 { UserFacade } from './+state/user.facade'; @@ -18,14 +20,15 @@ describe('UserPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserPageComponent], + TestBed.overrideComponent(UserPageComponent, { + remove: { imports: [SidenavModule, FeatureFlagClassDirective] }, + add: { imports: [SidenavTestingModule, MockFeatureFlagClassDirective] } + }).configureTestingModule({ imports: [ - SidenavTestingModule, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, + UserPageComponent, RouterTestingModule, - GravatarModule, + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), MatTabsModule, MatIconModule 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 5aaa3ec7..a00dad78 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 @@ -1,18 +1,25 @@ +import { CommonModule } from '@angular/common'; import { Component, Inject, Injector, OnDestroy, ChangeDetectionStrategy } from '@angular/core'; -import { ActivatedRoute, CanActivate, Route } from '@angular/router'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabsModule } from '@angular/material/tabs'; +import { ActivatedRoute, CanActivate, Route, RouterModule } from '@angular/router'; +import { FeatureFlagClassDirective } from '@dev/translatr-components'; import { Feature, User } from '@dev/translatr-model'; +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 { UserFacade } from './+state/user.facade'; import { USER_ROUTES } from './user-page.token'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-page', templateUrl: './user-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-page.component.scss'] + styleUrls: ['./user-page.component.scss'], + imports: [CommonModule, RouterModule, TranslocoModule, SidenavModule, MatIconModule, MatTabsModule, FeatureFlagClassDirective] }) export class UserPageComponent implements OnDestroy { readonly me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.module.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-page.module.ts deleted file mode 100644 index 62b8f28c..00000000 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-page.module.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatButtonToggleModule } from '@angular/material/button-toggle'; -import { MatCardModule } from '@angular/material/card'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; -import { MatTabsModule } from '@angular/material/tabs'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { - ActivityGraphComponent, - ConfirmButtonComponent, - EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - MetricComponent, - ShortNumberPipe, - UserCardComponent, UserCardLinkComponent -} from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { EffectsModule } from '@ngrx/effects'; -import { StoreModule } from '@ngrx/store'; -import { GravatarModule } from 'ngx-gravatar'; -import { TimeAgoPipe } from '@dev/translatr-components'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { AccessTokenEditDialogComponent } from '../../shared/access-token-edit-dialog/access-token-edit-dialog.component'; -import { AccessTokenEditFormComponent } from '../../shared/access-token-edit-form/access-token-edit-form.component'; -import { ActivityListComponent } from '../../shared/activity-list/activity-list.component'; -import { NavListComponent } from '../../shared/nav-list/nav-list.component'; -import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component'; -import { ProjectCardComponent } from '../../shared/project-card/project-card.component'; -import { ProjectCardLinkComponent } from '../../shared/project-card/project-card-link.component'; -import { ProjectEditDialogComponent } from '../../shared/project-edit-dialog/project-edit-dialog.component'; -import { ProjectListComponent } from '../../shared/project-list/project-list.component'; -import { UserEffects } from './+state/user.effects'; -import { UserFacade } from './+state/user.facade'; -import { - initialState as userInitialState, - USER_FEATURE_KEY, - userReducer -} from './+state/user.reducer'; -import { UserAccessTokenComponent } from './user-access-token/user-access-token.component'; -import { UserAccessTokensComponent } from './user-access-tokens/user-access-tokens.component'; -import { UserActivityComponent } from './user-activity/user-activity.component'; -import { UserInfoComponent } from './user-info/user-info.component'; -import { UserPageRoutingModule } from './user-page-routing.module'; -import { UserPageComponent } from './user-page.component'; -import { UserProjectsComponent } from './user-projects/user-projects.component'; -import { UserSettingsComponent } from './user-settings/user-settings.component'; -import { UserGuard } from './user.guard'; - -@NgModule({ - declarations: [ - UserPageComponent, - UserProjectsComponent, - UserInfoComponent, - UserActivityComponent, - UserAccessTokensComponent, - UserAccessTokenComponent, - UserSettingsComponent - ], - imports: [ - CommonModule, - UserPageRoutingModule, - SidenavModule, - ProjectListComponent, - ActivityListComponent, - UserCardComponent, UserCardLinkComponent, - ProjectCardComponent, ProjectCardLinkComponent, - ProjectEditDialogComponent, - AccessTokenEditDialogComponent, - NavListComponent, - AccessTokenEditFormComponent, - ProjectCardListComponent, - EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, - ConfirmButtonComponent, - - MatIconModule, - MatTabsModule, - MatChipsModule, - MatCardModule, - MatButtonModule, - MatButtonToggleModule, - TimeAgoPipe, - GravatarModule, - MatDialogModule, - MatListModule, - MatTooltipModule, - MatFormFieldModule, - MatInputModule, - ReactiveFormsModule, - FeatureFlagDirective, FeatureFlagClassDirective, - - StoreModule.forFeature(USER_FEATURE_KEY, userReducer, { - initialState: userInitialState - }), - EffectsModule.forFeature([UserEffects]), - MetricComponent, - ShortNumberPipe, - ActivityGraphComponent, - TranslocoModule - ], - providers: [UserFacade, UserGuard] -}) -export class UserPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts index 6e7821c3..bf357f2b 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts @@ -7,6 +7,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { ProjectListComponent } from '../../../shared/project-list/project-list.component'; import { MockProjectListComponent } from '../../../shared/project-list/testing'; import { RouterTestingModule } from '@angular/router/testing'; @@ -16,10 +17,12 @@ describe('UserProjectsComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UserProjectsComponent], + TestBed.overrideComponent(UserProjectsComponent, { + remove: { imports: [ProjectListComponent] }, + add: { imports: [MockProjectListComponent] } + }).configureTestingModule({ imports: [ - MockProjectListComponent, + UserProjectsComponent, RouterTestingModule, diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts index b3dc5e5d..7e728b81 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts @@ -1,19 +1,24 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { Router } from '@angular/router'; import { ProjectCriteria, User } from '@dev/translatr-model'; import { navigate } from '@translatr/utils'; import { filter, take, takeUntil, withLatestFrom } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; +import { ProjectListComponent } from '../../../shared/project-list/project-list.component'; import { FilterCriteria } from '../../../shared/list-header/list-header.component'; import { openProjectEditDialog } from '../../../shared/project-edit-dialog/project-edit-dialog.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-projects', templateUrl: './user-projects.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-projects.component.scss'] + styleUrls: ['./user-projects.component.scss'], + imports: [CommonModule, ProjectListComponent, MatIconModule, MatTooltipModule] }) export class UserProjectsComponent implements OnInit { projects$ = this.facade.projects$; diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.spec.ts index b697d706..9f0248dd 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.spec.ts @@ -10,10 +10,9 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; -import { FeatureFlagDirective, FeatureFlagClassDirective, ThemeService } from '@dev/translatr-components'; +import { ThemeService } from '@dev/translatr-components'; import { FeatureFlagFacade } from '@dev/translatr-model'; import { TranslocoTestingModule } from '@jsverse/transloco'; -import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { of } from 'rxjs'; import { UserFacade } from '../+state/user.facade'; @@ -27,10 +26,8 @@ describe('UserSettingsComponent', () => { const createComponent = async (flagEnabled = false, preference = 'system') => { TestBed.resetTestingModule(); await TestBed.configureTestingModule({ - declarations: [UserSettingsComponent], imports: [ - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, - FeatureFlagDirective, FeatureFlagClassDirective, + UserSettingsComponent, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), FormsModule, diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.ts index 018dad54..3cb65afc 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-settings/user-settings.component.ts @@ -1,9 +1,16 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { FormBuilder, Validators } from '@angular/forms'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatCardModule } from '@angular/material/card'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router } from '@angular/router'; -import { ThemePreference, ThemeService } from '@dev/translatr-components'; +import { FeatureFlagDirective, ThemePreference, ThemeService } from '@dev/translatr-components'; import { Feature, User } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { findParam } from '@translatr/utils'; import { merge, throwError } from 'rxjs'; import { filter, skip, switchMap } from 'rxjs/operators'; @@ -11,11 +18,22 @@ import { UserFacade } from '../+state/user.facade'; import { AbstractEditFormComponent } from '../../../shared/edit-form/abstract-edit-form-component'; @Component({ - standalone: false, + standalone: true, selector: 'app-user-settings', templateUrl: './user-settings.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./user-settings.component.scss'] + styleUrls: ['./user-settings.component.scss'], + imports: [ + CommonModule, + ReactiveFormsModule, + TranslocoModule, + FeatureFlagDirective, + MatButtonToggleModule, + MatCardModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule + ] }) export class UserSettingsComponent extends AbstractEditFormComponent implements OnInit { From 6cdf1ddc454d3943c597b8eedbc3c1d82ce17ae3 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:26:03 +0200 Subject: [PATCH 05/11] feat(translatr): convert editor-page to standalone Convert EditorSelectorComponent, EditorComponent, KeyEditorPageComponent, and LocaleEditorPageComponent to standalone: true. Delete editor-page.module.ts, moving its ProjectStateModule import, NgRx forFeature wiring, and EditorFacade/LanguageSwicher providers into editor-page-routing.module.ts. Repoint app-routing.module.ts's editor-page loadChildren to EditorPageRoutingModule. Fixes a masked bug where KeyEditorPageComponent's standalone imports were missing MatInputModule - its matInput input only worked in tests because the TestBed's ambient imports covered it. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 8 +- .../translatr/src/app/app-routing.module.ts | 4 +- .../editor-page/editor-page-routing.module.ts | 24 +++++- .../pages/editor-page/editor-page.module.ts | 79 ------------------- .../editor/editor-selector.component.ts | 2 +- .../editor/editor.component.spec.ts | 29 +++++-- .../editor-page/editor/editor.component.ts | 43 ++++++++-- .../key-editor-page.component.spec.ts | 44 +++++++++-- .../editor-page/key-editor-page.component.ts | 38 ++++++++- .../locale-editor-page.component.spec.ts | 44 +++++++++-- .../locale-editor-page.component.ts | 36 ++++++++- 11 files changed, 230 insertions(+), 121 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/editor-page/editor-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 12ef4643..06e2f120 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -24,10 +24,10 @@ ## 4. editor-page -- [ ] 4.1 Convert `EditorSelectorComponent` to `standalone: true` with explicit `imports` -- [ ] 4.2 Convert `EditorComponent` to `standalone: true` with explicit `imports` -- [ ] 4.3 Convert `KeyEditorPageComponent` and `LocaleEditorPageComponent` to `standalone: true` with explicit `imports` (including `EditorComponent` and `EditorSelectorComponent`); delete `editor-page.module.ts`; move `StoreModule.forFeature(EDITOR_FEATURE_KEY, ...)`, `EffectsModule.forFeature([EditorEffects])`, and `providers: [EditorFacade, { provide: LanguageSwicher, useClass: AppFacade }]` from the deleted module into `editor-page-routing.module.ts`; update consumers -- [ ] 4.4 Verify `nx test translatr` and `nx build translatr` succeed +- [x] 4.1 Convert `EditorSelectorComponent` to `standalone: true` with explicit `imports` (trivial inline-template, ng-content-only component - no imports needed) +- [x] 4.2 Convert `EditorComponent` to `standalone: true` with explicit `imports` +- [x] 4.3 Convert `KeyEditorPageComponent` and `LocaleEditorPageComponent` to `standalone: true` with explicit `imports` (including `EditorComponent` and `EditorSelectorComponent`); delete `editor-page.module.ts`; moved `ProjectStateModule`, `StoreModule.forFeature(EDITOR_FEATURE_KEY, ...)`, `EffectsModule.forFeature([EditorEffects])`, and `providers: [EditorFacade, { provide: LanguageSwicher, useClass: AppFacade }]` from the deleted module into `editor-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s editor-page `loadChildren` to `EditorPageRoutingModule` (caught a real bug: `KeyEditorPageComponent`'s own standalone `imports` was missing `MatInputModule` - its `matInput`-directive input only worked in tests because the TestBed's ambient imports covered it, masking that a standalone component's template only resolves directives from its own `imports` array, not the surrounding TestBed module; fixed by adding `MatInputModule` to the component itself) +- [x] 4.4 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean) ## 5. dashboard-page and projects-page diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index 0c97d712..c111055c 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -62,7 +62,9 @@ const routes: Routes = [ { path: '', loadChildren: () => - import('./modules/pages/editor-page/editor-page.module').then(m => m.EditorPageModule) + import('./modules/pages/editor-page/editor-page-routing.module').then( + m => m.EditorPageRoutingModule + ) } ]; diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page-routing.module.ts index c1420f2a..87b8d743 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page-routing.module.ts @@ -1,6 +1,18 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { LanguageSwicher } from '@dev/translatr-components'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; +import { AppFacade } from '../../../+state/app.facade'; import { AuthGuard } from '../../../guards/auth.guard'; +import { ProjectStateModule } from '../../shared/project-state'; +import { EditorEffects } from './+state/editor.effects'; +import { EditorFacade } from './+state/editor.facade'; +import { + EDITOR_FEATURE_KEY, + editorReducer, + initialState as editorInitialState +} from './+state/editor.reducer'; import { KeyEditorPageComponent } from './key-editor-page.component'; import { LocaleEditorPageComponent } from './locale-editor-page.component'; @@ -18,7 +30,15 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [ + RouterModule.forChild(routes), + ProjectStateModule, + StoreModule.forFeature(EDITOR_FEATURE_KEY, editorReducer, { + initialState: editorInitialState + }), + EffectsModule.forFeature([EditorEffects]) + ], + exports: [RouterModule], + providers: [EditorFacade, { provide: LanguageSwicher, useClass: AppFacade }] }) export class EditorPageRoutingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page.module.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page.module.ts deleted file mode 100644 index fca28baf..00000000 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor-page.module.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatListModule } from '@angular/material/list'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { MatTabsModule } from '@angular/material/tabs'; -import { CodemirrorModule } from '@ctrl/ngx-codemirror'; -import { EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, FilterFieldComponent, LanguageSwicher } from '@dev/translatr-components'; -import { HotkeysDirective } from '@ngneat/hotkeys'; -import { TranslocoModule } from '@jsverse/transloco'; -import { EffectsModule } from '@ngrx/effects'; -import { StoreModule } from '@ngrx/store'; -import { AppFacade } from '../../../+state/app.facade'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { NavListComponent } from '../../shared/nav-list/nav-list.component'; -import { ProjectStateModule } from '../../shared/project-state'; -import { EditorEffects } from './+state/editor.effects'; -import { EditorFacade } from './+state/editor.facade'; -import { - EDITOR_FEATURE_KEY, - editorReducer, - initialState as editorInitialState -} from './+state/editor.reducer'; -import { EditorPageRoutingModule } from './editor-page-routing.module'; -import { EditorSelectorComponent } from './editor/editor-selector.component'; -import { EditorComponent } from './editor/editor.component'; -import { KeyEditorPageComponent } from './key-editor-page.component'; -import { LocaleEditorPageComponent } from './locale-editor-page.component'; - -@NgModule({ - declarations: [ - LocaleEditorPageComponent, - EditorComponent, - EditorSelectorComponent, - KeyEditorPageComponent - ], - imports: [ - CommonModule, - EditorPageRoutingModule, - SidenavModule, - FormsModule, - NavListComponent, - FilterFieldComponent, - EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, EmptyViewActionsComponent, - ProjectStateModule, - - MatFormFieldModule, - MatInputModule, - MatMenuModule, - MatDividerModule, - MatButtonModule, - MatListModule, - MatTabsModule, - MatIconModule, - MatSnackBarModule, - MatCardModule, - MatDialogModule, - - CodemirrorModule, - - StoreModule.forFeature(EDITOR_FEATURE_KEY, editorReducer, { - initialState: editorInitialState - }), - EffectsModule.forFeature([EditorEffects]), - TranslocoModule, - - HotkeysDirective - ], - providers: [EditorFacade, { provide: LanguageSwicher, useClass: AppFacade }] -}) -export class EditorPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor-selector.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor-selector.component.ts index 67bd5d08..885a811d 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor-selector.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/editor-selector.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; @Component({ - standalone: false, + standalone: true, selector: 'app-editor-selector', template: '', changeDetection: ChangeDetectionStrategy.Eager, 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 841c3b05..680a53e9 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 @@ -12,20 +12,25 @@ import { RouterTestingModule } from '@angular/router/testing'; import { CodemirrorModule } from '@ctrl/ngx-codemirror'; import { HotkeysService } from '@ngneat/hotkeys'; import { TranslocoTestingModule } from '@jsverse/transloco'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent +} 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 { EMPTY, Subject } from 'rxjs'; import { EditorComponent } from './editor.component'; const editorTestImports = [ - SidenavTestingModule, + EditorComponent, RouterTestingModule, FormsModule, NoopAnimationsModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, MatButtonModule, MatDividerModule, @@ -38,14 +43,27 @@ const editorTestImports = [ CodemirrorModule ]; +const overrideEditorComponent = () => + TestBed.overrideComponent(EditorComponent, { + remove: { imports: [SidenavModule, EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent] }, + add: { + imports: [ + SidenavTestingModule, + MockEmptyViewComponent, + MockEmptyViewActionsComponent, + MockEmptyViewContentComponent, + MockEmptyViewHeaderComponent + ] + } + }); + describe('EditorComponent', () => { let component: EditorComponent; let fixture: ComponentFixture; beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [EditorComponent], + overrideEditorComponent().configureTestingModule({ imports: editorTestImports, providers: [ { @@ -98,8 +116,7 @@ describe('EditorComponent save shortcut', () => { }; TestBed.resetTestingModule(); - TestBed.configureTestingModule({ - declarations: [EditorComponent], + overrideEditorComponent().configureTestingModule({ imports: editorTestImports, providers: [ { provide: EditorFacade, useValue: facade }, 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 845e9eb5..564fe248 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 @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { AfterViewChecked, ChangeDetectionStrategy, @@ -8,26 +9,56 @@ import { Output, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { MatTabGroup } from '@angular/material/tabs'; -import { CodemirrorComponent } from '@ctrl/ngx-codemirror'; -import { Link } from '@dev/translatr-components'; +import { MatTabGroup, MatTabsModule } from '@angular/material/tabs'; +import { RouterModule } from '@angular/router'; +import { CodemirrorComponent, CodemirrorModule } from '@ctrl/ngx-codemirror'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + Link +} from '@dev/translatr-components'; import { Message, User } from '@dev/translatr-model'; import { HotkeysService } from '@ngneat/hotkeys'; -import { TranslocoService } from '@jsverse/transloco'; +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 { EditorFacade } from '../+state/editor.facade'; import { SaveBehavior } from '../save-behavior'; import 'codemirror/mode/xml/xml'; @Component({ - standalone: false, + standalone: true, selector: 'app-editor', templateUrl: './editor.component.html', styleUrls: ['./editor.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + RouterModule, + FormsModule, + TranslocoModule, + SidenavModule, + MatDividerModule, + MatMenuModule, + MatButtonModule, + MatIconModule, + MatTabsModule, + MatCardModule, + CodemirrorModule, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent + ] }) export class EditorComponent implements AfterViewChecked, OnDestroy { private _message: Message; diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts index 99996c77..9776a7f9 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts @@ -2,16 +2,26 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { readFileSync } from 'fs'; import { join } from 'path'; import { MatDialogModule } from '@angular/material/dialog'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent +} from '@dev/translatr-components'; import { ProjectFacade } from '../../shared/project-state/+state'; import { KeyEditorPageComponent } from './key-editor-page.component'; import { RouterTestingModule } from '@angular/router/testing'; import { AppFacade } from '../../../+state/app.facade'; import { EditorFacade } from './+state/editor.facade'; +import { EditorSelectorComponent } from './editor/editor-selector.component'; +import { EditorComponent } from './editor/editor.component'; import { EditorTestingModule } from './editor/testing'; import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, MockFilterFieldComponent } from '@translatr/components/testing'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; +import { NavListComponent } from '../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { mockObservable } from '@translatr/utils/testing'; @@ -23,15 +33,35 @@ describe('KeyEditorPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [KeyEditorPageComponent], + TestBed.overrideComponent(KeyEditorPageComponent, { + remove: { + imports: [ + EditorComponent, + EditorSelectorComponent, + NavListComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent + ] + }, + add: { + imports: [ + EditorTestingModule, + MockNavListComponent, + MockEmptyViewComponent, + MockEmptyViewActionsComponent, + MockEmptyViewContentComponent, + MockEmptyViewHeaderComponent, + MockFilterFieldComponent + ] + } + }).configureTestingModule({ imports: [ - RouterTestingModule, - EditorTestingModule, - MockFilterFieldComponent, - MockNavListComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, + KeyEditorPageComponent, + RouterTestingModule, NoopAnimationsModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts index e3bd3780..72a882ad 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts @@ -1,16 +1,31 @@ +import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { FilterFieldFilter, handleFilterFieldSelection } from '@dev/translatr-components'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent, + FilterFieldFilter, + handleFilterFieldSelection +} from '@dev/translatr-components'; import { Locale, Message, PagedList } from '@dev/translatr-model'; -import { TranslocoService } from '@jsverse/transloco'; +import { TranslocoModule, TranslocoService } from '@jsverse/transloco'; import { trackByFn } from '@translatr/utils'; import { combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, take, takeUntil, tap } from 'rxjs/operators'; import { AppFacade } from '../../../+state/app.facade'; +import { NavListComponent } from '../../shared/nav-list/nav-list.component'; import { openLocaleEditDialog } from '../../shared/locale-edit-dialog/locale-edit-dialog.component'; import { ProjectFacade } from '../../shared/project-state'; import { EditorFacade } from './+state/editor.facade'; +import { EditorSelectorComponent } from './editor/editor-selector.component'; +import { EditorComponent } from './editor/editor.component'; import { MessageItem } from './message-item'; import { navigateItems } from './navigate-utils'; @@ -21,11 +36,26 @@ const filterComparator = (a: Params, b: Params): boolean => a.search === b.search && a.missing === b.missing; @Component({ - standalone: false, + standalone: true, selector: 'app-key-editor-page', templateUrl: './key-editor-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./key-editor-page.component.scss'] + styleUrls: ['./key-editor-page.component.scss'], + imports: [ + CommonModule, + TranslocoModule, + EditorComponent, + EditorSelectorComponent, + NavListComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent, + MatFormFieldModule, + MatInputModule, + MatIconModule + ] }) export class KeyEditorPageComponent implements OnInit, OnDestroy { readonly me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts index d6c40d0e..e52ccb91 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts @@ -9,12 +9,22 @@ import { MatMenuModule } from '@angular/material/menu'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent +} from '@dev/translatr-components'; import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, MockFilterFieldComponent } from '@translatr/components/testing'; import { mockObservable } from '@translatr/utils/testing'; import { AppFacade } from '../../../+state/app.facade'; +import { NavListComponent } from '../../shared/nav-list/nav-list.component'; import { MockNavListComponent } from '../../shared/nav-list/testing'; import { ProjectFacade } from '../../shared/project-state/+state'; import { EditorFacade } from './+state/editor.facade'; +import { EditorSelectorComponent } from './editor/editor-selector.component'; +import { EditorComponent } from './editor/editor.component'; import { EditorTestingModule } from './editor/testing'; import { LocaleEditorPageComponent } from './locale-editor-page.component'; @@ -24,15 +34,35 @@ describe('LocaleEditorPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [LocaleEditorPageComponent], + TestBed.overrideComponent(LocaleEditorPageComponent, { + remove: { + imports: [ + EditorComponent, + EditorSelectorComponent, + NavListComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent + ] + }, + add: { + imports: [ + EditorTestingModule, + MockNavListComponent, + MockEmptyViewComponent, + MockEmptyViewActionsComponent, + MockEmptyViewContentComponent, + MockEmptyViewHeaderComponent, + MockFilterFieldComponent + ] + } + }).configureTestingModule({ imports: [ - RouterTestingModule, - EditorTestingModule, - MockFilterFieldComponent, - MockNavListComponent, - MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, + LocaleEditorPageComponent, + RouterTestingModule, NoopAnimationsModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts index d3dcfaa8..bb62298b 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts @@ -1,16 +1,30 @@ +import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; import { Params, Router } from '@angular/router'; -import { FilterFieldFilter, handleFilterFieldSelection } from '@dev/translatr-components'; +import { + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent, + FilterFieldFilter, + handleFilterFieldSelection +} from '@dev/translatr-components'; import { Key, Message } from '@dev/translatr-model'; -import { TranslocoService } from '@jsverse/transloco'; +import { TranslocoModule, TranslocoService } from '@jsverse/transloco'; import { trackByFn } from '@translatr/utils'; import { combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, take, takeUntil, tap } from 'rxjs/operators'; import { AppFacade } from '../../../+state/app.facade'; +import { NavListComponent } from '../../shared/nav-list/nav-list.component'; import { openKeyEditDialog } from '../../shared/key-edit-dialog/key-edit-dialog.component'; import { ProjectFacade } from '../../shared/project-state/+state'; import { EditorFacade } from './+state/editor.facade'; +import { EditorSelectorComponent } from './editor/editor-selector.component'; +import { EditorComponent } from './editor/editor.component'; import { navigateItems } from './navigate-utils'; const localeComparator = (a: Params, b: Params): boolean => @@ -20,11 +34,25 @@ const filterComparator = (a: Params, b: Params): boolean => a.search === b.search && a.missing === b.missing; @Component({ - standalone: false, + standalone: true, selector: 'app-locale-editor-page', templateUrl: './locale-editor-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./locale-editor-page.component.scss'] + styleUrls: ['./locale-editor-page.component.scss'], + imports: [ + CommonModule, + TranslocoModule, + EditorComponent, + EditorSelectorComponent, + NavListComponent, + EmptyViewComponent, + EmptyViewHeaderComponent, + EmptyViewContentComponent, + EmptyViewActionsComponent, + FilterFieldComponent, + MatIconModule, + MatMenuModule + ] }) export class LocaleEditorPageComponent implements OnInit, OnDestroy { readonly me$ = this.appFacade.me$; From dd8fd62d7ffbcd898615862e3318d8752246a239 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:31:30 +0200 Subject: [PATCH 06/11] feat(translatr): convert dashboard-page and projects-page to standalone Convert DashboardPageComponent and ProjectsPageComponent to standalone: true. Delete dashboard-page.module.ts and projects-page.module.ts, moving their NgRx forFeature wiring and facade providers into their respective routing modules. DashboardPageComponent's dependency on ProjectsFacade (previously satisfied by importing all of ProjectsPageModule) is now satisfied by importing ProjectsPageRoutingModule. Repoint app-routing.module.ts's dashboard and projects loadChildren entries to the routing modules. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 6 +-- .../translatr/src/app/app-routing.module.ts | 8 +-- .../dashboard-page-routing.module.ts | 22 +++++++- .../dashboard-page.component.spec.ts | 39 ++++++++++---- .../dashboard-page.component.ts | 23 ++++++-- .../dashboard-page/dashboard-page.module.ts | 54 ------------------- .../projects-page-routing.module.ts | 20 ++++++- .../projects-page.component.spec.ts | 15 ++++-- .../projects-page/projects-page.component.ts | 22 +++++++- .../projects-page/projects-page.module.ts | 54 ------------------- 10 files changed, 125 insertions(+), 138 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.module.ts delete mode 100644 ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 06e2f120..9e2e239e 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -31,9 +31,9 @@ ## 5. dashboard-page and projects-page -- [ ] 5.1 Convert `DashboardPageComponent` to `standalone: true` with explicit `imports` (including `ActivityListComponent`, `MetricComponent`, `ProjectCardListComponent`, `ProjectEditDialogComponent`, `ProjectListComponent`); delete `dashboard-page.module.ts`; move `StoreModule.forFeature(DASHBOARD_FEATURE_KEY, ...)`, `EffectsModule.forFeature([DashboardEffects])`, and `providers: [DashboardFacade]` into `dashboard-page-routing.module.ts`; update consumers -- [ ] 5.2 Convert `ProjectsPageComponent` to `standalone: true` with explicit `imports` (including `ProjectCardComponent`, `ProjectCardLinkComponent`, `ProjectEditDialogComponent`, `ProjectListComponent`); delete `projects-page.module.ts`; move `StoreModule.forFeature(PROJECTS_FEATURE_KEY, ...)`, `EffectsModule.forFeature([ProjectsEffects])`, and `providers: [ProjectsFacade]` into `projects-page-routing.module.ts`; update consumers -- [ ] 5.3 Verify `nx test translatr` and `nx build translatr` succeed +- [x] 5.1 Convert `DashboardPageComponent` to `standalone: true` with explicit `imports` (`MetricComponent`, `ShortNumberPipe`, `ProjectCardListComponent`, `ActivityListComponent`, `FeatureFlagClassDirective` - neither `ProjectEditDialogComponent` nor `ProjectListComponent` are used directly in its own template, only via facade/dialog-service calls and `ProjectCardListComponent`'s own composition, so weren't carried over); delete `dashboard-page.module.ts`; moved `ProjectsPageRoutingModule` (dashboard's own `DashboardPageComponent` also injects `ProjectsFacade`, previously obtained by importing the whole `ProjectsPageModule` - preserved by importing `ProjectsPageRoutingModule` instead, once it existed after 5.2), plus `StoreModule.forFeature(DASHBOARD_FEATURE_KEY, ...)`, `EffectsModule.forFeature([DashboardEffects])`, and `providers: [DashboardFacade]` into `dashboard-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s dashboard-page `loadChildren` to `DashboardPageRoutingModule` (converted 5.2 first since 5.1 depends on it) +- [x] 5.2 Convert `ProjectsPageComponent` to `standalone: true` with explicit `imports` (`ProjectListComponent`, `MatButtonModule`, `MatTooltipModule`, `MatIconModule`, `FeatureFlagClassDirective` - `ProjectCardComponent`/`ProjectCardLinkComponent` are used inside `ProjectListComponent`, not directly here, and `ProjectEditDialogComponent`/`MatDialogModule` aren't needed since the dialog is opened via the `MatDialog` service, which is `providedIn: 'root'`); delete `projects-page.module.ts`; moved `StoreModule.forFeature(PROJECTS_FEATURE_KEY, ...)`, `EffectsModule.forFeature([ProjectsEffects])`, and `providers: [ProjectsFacade]` into `projects-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s projects-page `loadChildren` to `ProjectsPageRoutingModule` +- [x] 5.3 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean) ## 6. users-page diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index c111055c..b2281a88 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -17,8 +17,8 @@ const routes: Routes = [ { path: 'dashboard', loadChildren: () => - import('./modules/pages/dashboard-page/dashboard-page.module').then( - m => m.DashboardPageModule + import('./modules/pages/dashboard-page/dashboard-page-routing.module').then( + m => m.DashboardPageRoutingModule ) }, { @@ -29,7 +29,9 @@ const routes: Routes = [ { path: 'projects', loadChildren: () => - import('./modules/pages/projects-page/projects-page.module').then(m => m.ProjectsPageModule) + import('./modules/pages/projects-page/projects-page-routing.module').then( + m => m.ProjectsPageRoutingModule + ) }, { path: 'not-found', diff --git a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts index 6f87430f..8de774d3 100644 --- a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts @@ -1,6 +1,16 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; import { AuthGuard } from '../../../guards/auth.guard'; +import { ProjectsPageRoutingModule } from '../projects-page/projects-page-routing.module'; +import { DashboardEffects } from './+state/dashboard.effects'; +import { DashboardFacade } from './+state/dashboard.facade'; +import { + DASHBOARD_FEATURE_KEY, + dashboardReducer, + initialState as dashboardInitialState +} from './+state/dashboard.reducer'; import { DashboardPageComponent } from './dashboard-page.component'; const routes: Routes = [ @@ -12,7 +22,15 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [ + RouterModule.forChild(routes), + ProjectsPageRoutingModule, + StoreModule.forFeature(DASHBOARD_FEATURE_KEY, dashboardReducer, { + initialState: dashboardInitialState + }), + EffectsModule.forFeature([DashboardEffects]) + ], + exports: [RouterModule], + providers: [DashboardFacade] }) export class DashboardPageRoutingModule {} 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 792eb0f8..b4137940 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 @@ -1,10 +1,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatDialog } from '@angular/material/dialog'; import { RouterTestingModule } from '@angular/router/testing'; -import { ShortNumberPipe } from '@dev/translatr-components'; -import { MockFeatureFlagDirective, MockFeatureFlagClassDirective, MockMetricComponent } from '@translatr/components/testing'; +import { TranslocoTestingModule } from '@jsverse/transloco'; +import { FeatureFlagClassDirective, MetricComponent } from '@dev/translatr-components'; +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 { ActivityListComponent } from '../../shared/activity-list/activity-list.component'; +import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component'; import { MockActivityListComponent, MockProjectCardListComponent, @@ -19,16 +23,31 @@ describe('DashboardPageComponent', () => { let fixture: ComponentFixture; beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [DashboardPageComponent], + TestBed.overrideComponent(DashboardPageComponent, { + remove: { + imports: [ + SidenavModule, + MetricComponent, + ActivityListComponent, + ProjectCardListComponent, + FeatureFlagClassDirective + ] + }, + add: { + imports: [ + SidenavTestingModule, + MockMetricComponent, + MockActivityListComponent, + MockProjectCardListComponent, + MockFeatureFlagClassDirective + ] + } + }).configureTestingModule({ imports: [ - MockFeatureFlagDirective, MockFeatureFlagClassDirective, - ShortNumberPipe, + DashboardPageComponent, + RouterTestingModule, - MockActivityListComponent, - MockProjectCardListComponent, - MockMetricComponent, - SidenavTestingModule + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) ], providers: [ { 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 f7e332ff..1887903a 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 @@ -1,19 +1,36 @@ +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { Router } from '@angular/router'; +import { Router, RouterModule } from '@angular/router'; +import { FeatureFlagClassDirective, MetricComponent, ShortNumberPipe } from '@dev/translatr-components'; 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 { 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'; import { ProjectsFacade } from '../projects-page/+state/projects.facade'; import { DashboardFacade } from './+state/dashboard.facade'; @Component({ - standalone: false, + standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-dashboard-page', templateUrl: './dashboard-page.component.html', - styleUrls: ['./dashboard-page.component.scss'] + styleUrls: ['./dashboard-page.component.scss'], + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + SidenavModule, + MetricComponent, + ShortNumberPipe, + ProjectCardListComponent, + ActivityListComponent, + FeatureFlagClassDirective + ] }) export class DashboardPageComponent implements OnInit { readonly me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.module.ts b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.module.ts deleted file mode 100644 index 82d12df7..00000000 --- a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatIconModule } from '@angular/material/icon'; -import { FeatureFlagDirective, FeatureFlagClassDirective, MetricComponent, ShortNumberPipe } from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { EffectsModule } from '@ngrx/effects'; -import { StoreModule } from '@ngrx/store'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { ActivityListComponent } from '../../shared/activity-list/activity-list.component'; -import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component'; -import { ProjectEditDialogComponent } from '../../shared/project-edit-dialog/project-edit-dialog.component'; -import { ProjectListComponent } from '../../shared/project-list/project-list.component'; -import { ProjectsPageModule } from '../projects-page/projects-page.module'; -import { DashboardEffects } from './+state/dashboard.effects'; -import { DashboardFacade } from './+state/dashboard.facade'; -import { - DASHBOARD_FEATURE_KEY, - dashboardReducer, - initialState as dashboardInitialState -} from './+state/dashboard.reducer'; -import { DashboardPageRoutingModule } from './dashboard-page-routing.module'; -import { DashboardPageComponent } from './dashboard-page.component'; - -@NgModule({ - declarations: [DashboardPageComponent], - imports: [ - DashboardPageRoutingModule, - ProjectsPageModule, - SidenavModule, - ProjectListComponent, - ProjectEditDialogComponent, - ActivityListComponent, - ProjectCardListComponent, - MetricComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - ShortNumberPipe, - - CommonModule, - TranslocoModule, - - MatIconModule, - MatDialogModule, - MatButtonModule, - - StoreModule.forFeature(DASHBOARD_FEATURE_KEY, dashboardReducer, { - initialState: dashboardInitialState - }), - EffectsModule.forFeature([DashboardEffects]) - ], - providers: [DashboardFacade] -}) -export class DashboardPageModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page-routing.module.ts index daeeee9d..7d0e388b 100644 --- a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page-routing.module.ts @@ -1,6 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { EffectsModule } from '@ngrx/effects'; +import { StoreModule } from '@ngrx/store'; import { AuthGuard } from '../../../guards/auth.guard'; +import { ProjectsEffects } from './+state/projects.effects'; +import { ProjectsFacade } from './+state/projects.facade'; +import { + initialState as projectsInitialState, + PROJECTS_FEATURE_KEY, + projectsReducer +} from './+state/projects.reducer'; import { ProjectsPageComponent } from './projects-page.component'; const routes: Routes = [ @@ -12,7 +21,14 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [ + RouterModule.forChild(routes), + StoreModule.forFeature(PROJECTS_FEATURE_KEY, projectsReducer, { + initialState: projectsInitialState + }), + EffectsModule.forFeature([ProjectsEffects]) + ], + exports: [RouterModule], + providers: [ProjectsFacade] }) export class ProjectsPageRoutingModule {} 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 c388ef90..102fa2ad 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 @@ -7,7 +7,10 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; 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 { ProjectListComponent } from '../../shared/project-list/project-list.component'; import { MockProjectListComponent, SidenavTestingModule } from '../../testing'; import { MatDialog } from '@angular/material/dialog'; import { TranslocoTestingModule } from '@jsverse/transloco'; @@ -18,12 +21,14 @@ describe('UsersPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProjectsPageComponent], + TestBed.overrideComponent(ProjectsPageComponent, { + remove: { imports: [SidenavModule, ProjectListComponent, FeatureFlagClassDirective] }, + add: { imports: [SidenavTestingModule, MockProjectListComponent, MockFeatureFlagClassDirective] } + }).configureTestingModule({ imports: [ - SidenavTestingModule, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, - MockProjectListComponent, + ProjectsPageComponent, + + MockFeatureFlagDirective, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), 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 90c5f5f8..cdd24fe8 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 @@ -1,21 +1,39 @@ +import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { Params, Router } from '@angular/router'; +import { FeatureFlagClassDirective } from '@dev/translatr-components'; import { Feature, ProjectCriteria, User } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; 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 { 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'; import { ProjectsFacade } from './+state/projects.facade'; @Component({ - standalone: false, + standalone: true, selector: 'app-projects-page', templateUrl: './projects-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./projects-page.component.scss'] + styleUrls: ['./projects-page.component.scss'], + imports: [ + CommonModule, + TranslocoModule, + SidenavModule, + ProjectListComponent, + MatIconModule, + MatButtonModule, + MatTooltipModule, + FeatureFlagClassDirective + ] }) export class ProjectsPageComponent implements OnInit, OnDestroy { me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.module.ts b/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.module.ts deleted file mode 100644 index 16b9fcc6..00000000 --- a/ui/apps/translatr/src/app/modules/pages/projects-page/projects-page.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatIconModule } from '@angular/material/icon'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { FeatureFlagDirective, FeatureFlagClassDirective } from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { EffectsModule } from '@ngrx/effects'; -import { StoreModule } from '@ngrx/store'; -import { GravatarModule } from 'ngx-gravatar'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { ProjectCardListComponent } from '../../shared/project-card-list/project-card-list.component'; -import { ProjectCardComponent } from '../../shared/project-card/project-card.component'; -import { ProjectCardLinkComponent } from '../../shared/project-card/project-card-link.component'; -import { ProjectEditDialogComponent } from '../../shared/project-edit-dialog/project-edit-dialog.component'; -import { ProjectListComponent } from '../../shared/project-list/project-list.component'; -import { ProjectsEffects } from './+state/projects.effects'; -import { ProjectsFacade } from './+state/projects.facade'; -import { - initialState as projectsInitialState, - PROJECTS_FEATURE_KEY, - projectsReducer -} from './+state/projects.reducer'; -import { ProjectsPageRoutingModule } from './projects-page-routing.module'; -import { ProjectsPageComponent } from './projects-page.component'; - -@NgModule({ - declarations: [ProjectsPageComponent], - imports: [ - CommonModule, - ProjectsPageRoutingModule, - SidenavModule, - ProjectListComponent, - ProjectCardComponent, ProjectCardLinkComponent, - ProjectEditDialogComponent, - - MatIconModule, - MatDialogModule, - MatButtonModule, - - GravatarModule, - StoreModule.forFeature(PROJECTS_FEATURE_KEY, projectsReducer, { - initialState: projectsInitialState - }), - EffectsModule.forFeature([ProjectsEffects]), - MatTooltipModule, - ProjectCardListComponent, - FeatureFlagDirective, FeatureFlagClassDirective, - TranslocoModule - ], - providers: [ProjectsFacade] -}) -export class ProjectsPageModule {} From fb4d6fc8c710271a24c0ec068b1153c1d1f39812 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:33:39 +0200 Subject: [PATCH 07/11] feat(translatr): convert users-page to standalone Convert UsersPageComponent to standalone: true. Delete users-page.module.ts, moving its zero-component UsersModule (+state/users.module.ts) import into users-page-routing.module.ts. Repoint app-routing.module.ts's users-page loadChildren to UsersPageRoutingModule. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 4 +-- .../translatr/src/app/app-routing.module.ts | 4 ++- .../users-page/users-page-routing.module.ts | 3 ++- .../users-page/users-page.component.spec.ts | 15 ++++++----- .../pages/users-page/users-page.component.ts | 10 +++++-- .../pages/users-page/users-page.module.ts | 26 ------------------- 6 files changed, 24 insertions(+), 38 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/users-page/users-page.module.ts diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 9e2e239e..a347bdea 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -37,8 +37,8 @@ ## 6. users-page -- [ ] 6.1 Convert `UsersPageComponent` to `standalone: true` with explicit `imports` (including `UserCardComponent`, `UserCardLinkComponent`, `UserListComponent`); delete `users-page.module.ts`; update consumers (`users-page/+state/users.module.ts` stays untouched - already zero-component) -- [ ] 6.2 Verify `nx test translatr` and `nx build translatr` succeed +- [x] 6.1 Convert `UsersPageComponent` to `standalone: true` with explicit `imports` (`SidenavModule`, `UserListComponent`, `FeatureFlagClassDirective`, `TranslocoModule` - `UserCardComponent`/`UserCardLinkComponent` are used inside `UserListComponent`, not directly in this component's own template); delete `users-page.module.ts`; moved `UsersModule` (the zero-component `+state/users.module.ts`, previously imported by the deleted declarations module) into `users-page-routing.module.ts`; update consumers; repointed `app-routing.module.ts`'s users-page `loadChildren` to `UsersPageRoutingModule` +- [x] 6.2 Verify `nx test translatr` and `nx build translatr` succeed (159/159, clean build, `tsc --noEmit` clean) ## 7. Routing repoint diff --git a/ui/apps/translatr/src/app/app-routing.module.ts b/ui/apps/translatr/src/app/app-routing.module.ts index b2281a88..d4e7908a 100644 --- a/ui/apps/translatr/src/app/app-routing.module.ts +++ b/ui/apps/translatr/src/app/app-routing.module.ts @@ -24,7 +24,9 @@ const routes: Routes = [ { path: 'users', loadChildren: () => - import('./modules/pages/users-page/users-page.module').then(m => m.UsersPageModule) + import('./modules/pages/users-page/users-page-routing.module').then( + m => m.UsersPageRoutingModule + ) }, { path: 'projects', diff --git a/ui/apps/translatr/src/app/modules/pages/users-page/users-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/users-page/users-page-routing.module.ts index 247238d0..7faae01f 100644 --- a/ui/apps/translatr/src/app/modules/pages/users-page/users-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/users-page/users-page-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuard } from '../../../guards/auth.guard'; +import { UsersModule } from './+state/users.module'; import { UsersPageComponent } from './users-page.component'; const routes: Routes = [ @@ -12,7 +13,7 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes)], + imports: [RouterModule.forChild(routes), UsersModule], exports: [RouterModule] }) export class UsersPageRoutingModule {} 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 10737125..4f9546c9 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 @@ -2,11 +2,14 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; 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 { 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 { MockFeatureFlagDirective, MockFeatureFlagClassDirective } from '@translatr/components/testing'; +import { MockFeatureFlagClassDirective } from '@translatr/components/testing'; import { MockUserListComponent } from '../../shared/user-list/testing'; import { TranslocoTestingModule } from '@jsverse/transloco'; @@ -16,12 +19,12 @@ describe('UsersPageComponent', () => { beforeEach( waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [UsersPageComponent], + TestBed.overrideComponent(UsersPageComponent, { + remove: { imports: [SidenavModule, UserListComponent, FeatureFlagClassDirective] }, + add: { imports: [SidenavTestingModule, MockUserListComponent, MockFeatureFlagClassDirective] } + }).configureTestingModule({ imports: [ - SidenavTestingModule, - MockFeatureFlagDirective, MockFeatureFlagClassDirective, - MockUserListComponent, + UsersPageComponent, RouterTestingModule, TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }) 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 5d8f96b7..f2180277 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 @@ -1,18 +1,24 @@ +import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Params, Router } from '@angular/router'; +import { FeatureFlagClassDirective } from '@dev/translatr-components'; import { Feature, UserCriteria } from '@dev/translatr-model'; +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 { FilterCriteria } from '../../shared/list-header/list-header.component'; +import { UserListComponent } from '../../shared/user-list/user-list.component'; import { UsersFacade } from './+state/users.facade'; @Component({ - standalone: false, + standalone: true, selector: 'app-projects-page', templateUrl: './users-page.component.html', changeDetection: ChangeDetectionStrategy.Eager, - styleUrls: ['./users-page.component.scss'] + styleUrls: ['./users-page.component.scss'], + imports: [CommonModule, TranslocoModule, SidenavModule, UserListComponent, FeatureFlagClassDirective] }) export class UsersPageComponent implements OnInit, OnDestroy { me$ = this.appFacade.me$; diff --git a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.module.ts b/ui/apps/translatr/src/app/modules/pages/users-page/users-page.module.ts deleted file mode 100644 index 75df7a1e..00000000 --- a/ui/apps/translatr/src/app/modules/pages/users-page/users-page.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { MatIconModule } from '@angular/material/icon'; -import { FeatureFlagDirective, FeatureFlagClassDirective, UserCardComponent, UserCardLinkComponent } from '@dev/translatr-components'; -import { TranslocoModule } from '@jsverse/transloco'; -import { SidenavModule } from '../../nav/sidenav/sidenav.module'; -import { UserListComponent } from '../../shared/user-list/user-list.component'; -import { UsersModule } from './+state/users.module'; -import { UsersPageRoutingModule } from './users-page-routing.module'; -import { UsersPageComponent } from './users-page.component'; - -@NgModule({ - declarations: [UsersPageComponent], - imports: [ - CommonModule, - UsersPageRoutingModule, - SidenavModule, - UserListComponent, - UserCardComponent, UserCardLinkComponent, - UsersModule, - MatIconModule, - FeatureFlagDirective, FeatureFlagClassDirective, - TranslocoModule - ] -}) -export class UsersPageModule {} From 00ae17f4dc822fc00225c59f4a2e6a7d4ff977ec Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:33:54 +0200 Subject: [PATCH 08/11] docs(openspec): mark pages-standalone group 7 (routing repoint) complete All 9 loadChildren entries in app-routing.module.ts were already repointed to *RoutingModule atomically during their own group's conversion; this verifies the sweep found nothing left to do. Co-Authored-By: Claude Sonnet 5 --- openspec/changes/migrate-translatr-pages-standalone/tasks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index a347bdea..47d61d58 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -42,8 +42,8 @@ ## 7. Routing repoint -- [ ] 7.1 In `app-routing.module.ts`, change all 9 remaining `loadChildren` entries (`register`, `dashboard`, `users`, `projects`, `not-found`, `forbidden`, `''` main, `''` user-page, `''` project-page, `''` editor-page) from `.then(m => m.XxxPageModule)` to `.then(m => m.XxxPageRoutingModule)` -- [ ] 7.2 Verify `nx build translatr` succeeds with no TypeScript errors +- [x] 7.1 In `app-routing.module.ts`, change all 9 remaining `loadChildren` entries (`register`, `dashboard`, `users`, `projects`, `not-found`, `forbidden`, `''` main, `''` user-page, `''` project-page, `''` editor-page) from `.then(m => m.XxxPageModule)` to `.then(m => m.XxxPageRoutingModule)` (all 9 were already repointed atomically during their own group's conversion, per the deviation logged in group 1 - this task reduced to a verification sweep, which found all entries already pointing at `*RoutingModule`, plus the pre-existing `login` route which was already `loadComponent` from before this change) +- [x] 7.2 Verify `nx build translatr` succeeds with no TypeScript errors (clean) ## 8. Testing modules From b12c157563d7fb0532a56aa0a3692e831af672a9 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:36:31 +0200 Subject: [PATCH 09/11] feat(translatr): convert page-scoped testing doubles to standalone Convert the Mock* test-double components in editor-page, key-list, locale-list, and member-list testing modules to standalone: true, renaming each file to mock-*.component.ts. Update consumer specs to import the Mock*Component classes directly instead of the now-deleted *TestingModule NgModule wrappers. Co-Authored-By: Claude Sonnet 5 --- .../tasks.md | 4 +-- .../editor/testing/editor-testing.module.ts | 33 ------------------- .../pages/editor-page/editor/testing/index.ts | 3 +- .../testing/mock-editor-selector.component.ts | 9 +++++ .../editor/testing/mock-editor.component.ts | 19 +++++++++++ .../key-editor-page.component.spec.ts | 7 ++-- .../locale-editor-page.component.spec.ts | 7 ++-- .../project-keys/key-list/testing/index.ts | 2 +- ...g.module.ts => mock-key-list.component.ts} | 12 ++----- .../project-keys.component.spec.ts | 4 +-- .../locale-list/testing/index.ts | 2 +- ...odule.ts => mock-locale-list.component.ts} | 12 ++----- .../project-locales.component.spec.ts | 4 +-- .../member-list/testing/index.ts | 2 +- ...odule.ts => mock-member-list.component.ts} | 12 ++----- .../project-members.component.spec.ts | 4 +-- 16 files changed, 58 insertions(+), 78 deletions(-) delete mode 100644 ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/editor-testing.module.ts create mode 100644 ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor-selector.component.ts create mode 100644 ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor.component.ts rename ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/{key-list-testing.module.ts => mock-key-list.component.ts} (63%) rename ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/{locale-list-testing.module.ts => mock-locale-list.component.ts} (65%) rename ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/{member-list-testing.module.ts => mock-member-list.component.ts} (69%) diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index 47d61d58..f918cebf 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -47,8 +47,8 @@ ## 8. Testing modules -- [ ] 8.1 Convert the `Mock*` classes in `editor/testing/editor-testing.module.ts`, `project-keys/key-list/testing/key-list-testing.module.ts`, `project-locales/locale-list/testing/locale-list-testing.module.ts`, `project-members/member-list/testing/member-list-testing.module.ts` to `standalone: true`; rename each file to `mock-*.component.ts`; update every consumer spec; delete the 4 `*-testing.module.ts` files -- [ ] 8.2 Run `nx test translatr` and verify all specs pass +- [x] 8.1 Convert the `Mock*` classes in `editor/testing/editor-testing.module.ts`, `project-keys/key-list/testing/key-list-testing.module.ts`, `project-locales/locale-list/testing/locale-list-testing.module.ts`, `project-members/member-list/testing/member-list-testing.module.ts` to `standalone: true`; renamed each file to `mock-*.component.ts` (the editor one split into two files, `mock-editor.component.ts` and `mock-editor-selector.component.ts`, matching its two exported classes); updated every consumer spec (`key-editor-page`, `locale-editor-page`, `project-keys`, `project-locales`, `project-members`) to import the `Mock*Component` classes directly instead of the `*TestingModule` wrapper; deleted the 4 `*-testing.module.ts` files +- [x] 8.2 Run `nx test translatr` and verify all specs pass (159/159) ## 9. Cleanup and verification diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/editor-testing.module.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/editor-testing.module.ts deleted file mode 100644 index 123ee368..00000000 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/editor-testing.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, Input, NgModule, ChangeDetectionStrategy } from '@angular/core'; -import { Link } from '@dev/translatr-components'; -import { Message, User } from '@dev/translatr-model'; - -@Component({ - standalone: false, - selector: 'app-editor', - changeDetection: ChangeDetectionStrategy.Eager, - template: '' -}) -class MockEditorComponent { - @Input() me: User; - @Input() ownerName: string; - @Input() projectName: string; - @Input() name: string; - @Input() message: Message; - @Input() messages: Array; - @Input() backLink: Link; -} - -@Component({ - standalone: false, - selector: 'app-editor-selector', - changeDetection: ChangeDetectionStrategy.Eager, - template: '' -}) -class MockEditorSelectorComponent {} - -@NgModule({ - declarations: [MockEditorComponent, MockEditorSelectorComponent], - exports: [MockEditorComponent, MockEditorSelectorComponent] -}) -export class EditorTestingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/index.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/index.ts index e3a25278..2533cb61 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/index.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/index.ts @@ -1 +1,2 @@ -export * from './editor-testing.module'; +export * from './mock-editor.component'; +export * from './mock-editor-selector.component'; diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor-selector.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor-selector.component.ts new file mode 100644 index 00000000..9ad046a2 --- /dev/null +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor-selector.component.ts @@ -0,0 +1,9 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; + +@Component({ + standalone: true, + selector: 'app-editor-selector', + changeDetection: ChangeDetectionStrategy.Eager, + template: '' +}) +export class MockEditorSelectorComponent {} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor.component.ts new file mode 100644 index 00000000..99916db0 --- /dev/null +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/editor/testing/mock-editor.component.ts @@ -0,0 +1,19 @@ +import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; +import { Link } from '@dev/translatr-components'; +import { Message, User } from '@dev/translatr-model'; + +@Component({ + standalone: true, + selector: 'app-editor', + changeDetection: ChangeDetectionStrategy.Eager, + template: '' +}) +export class MockEditorComponent { + @Input() me: User; + @Input() ownerName: string; + @Input() projectName: string; + @Input() name: string; + @Input() message: Message; + @Input() messages: Array; + @Input() backLink: Link; +} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts index 9776a7f9..7c604f17 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.spec.ts @@ -16,7 +16,7 @@ import { AppFacade } from '../../../+state/app.facade'; import { EditorFacade } from './+state/editor.facade'; import { EditorSelectorComponent } from './editor/editor-selector.component'; import { EditorComponent } from './editor/editor.component'; -import { EditorTestingModule } from './editor/testing'; +import { MockEditorComponent, MockEditorSelectorComponent } from './editor/testing'; import { MockEmptyViewComponent, MockEmptyViewActionsComponent, MockEmptyViewContentComponent, MockEmptyViewHeaderComponent, MockFilterFieldComponent } from '@translatr/components/testing'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; @@ -48,7 +48,8 @@ describe('KeyEditorPageComponent', () => { }, add: { imports: [ - EditorTestingModule, + MockEditorComponent, + MockEditorSelectorComponent, MockNavListComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, @@ -109,7 +110,7 @@ describe('KeyEditorPageComponent', () => { it('renders its search field with an outline appearance, matching the rest of the UI', () => { // The `dev-filter-field` lives inside ``'s projected content, which the - // EditorTestingModule stub (empty template, no ) never renders into the DOM - + // mock EditorComponent/EditorSelectorComponent (empty template, no ) never renders into the DOM - // so this reads the real template source instead of querying the rendered fixture. const template = readFileSync(join(__dirname, 'key-editor-page.component.html'), 'utf8'); const filterFieldMarkup = template.match(//)[0]; diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts index e52ccb91..dd8c7f43 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.spec.ts @@ -25,7 +25,7 @@ import { ProjectFacade } from '../../shared/project-state/+state'; import { EditorFacade } from './+state/editor.facade'; import { EditorSelectorComponent } from './editor/editor-selector.component'; import { EditorComponent } from './editor/editor.component'; -import { EditorTestingModule } from './editor/testing'; +import { MockEditorComponent, MockEditorSelectorComponent } from './editor/testing'; import { LocaleEditorPageComponent } from './locale-editor-page.component'; describe('LocaleEditorPageComponent', () => { @@ -49,7 +49,8 @@ describe('LocaleEditorPageComponent', () => { }, add: { imports: [ - EditorTestingModule, + MockEditorComponent, + MockEditorSelectorComponent, MockNavListComponent, MockEmptyViewComponent, MockEmptyViewActionsComponent, @@ -111,7 +112,7 @@ describe('LocaleEditorPageComponent', () => { it('renders its search field with an outline appearance, matching the rest of the UI', () => { // The `dev-filter-field` lives inside ``'s projected content, which the - // EditorTestingModule stub (empty template, no ) never renders into the DOM - + // mock EditorComponent/EditorSelectorComponent (empty template, no ) never renders into the DOM - // so this reads the real template source instead of querying the rendered fixture. const template = readFileSync(join(__dirname, 'locale-editor-page.component.html'), 'utf8'); const filterFieldMarkup = template.match(//)[0]; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/index.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/index.ts index ed3bf5a4..ada766df 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/index.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/index.ts @@ -1 +1 @@ -export * from './key-list-testing.module'; +export * from './mock-key-list.component'; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/key-list-testing.module.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/mock-key-list.component.ts similarity index 63% rename from ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/key-list-testing.module.ts rename to ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/mock-key-list.component.ts index 1a4c434d..a26bca93 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/key-list-testing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/key-list/testing/mock-key-list.component.ts @@ -1,13 +1,13 @@ -import { Component, EventEmitter, Input, NgModule, Output, ChangeDetectionStrategy } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core'; import { Key, KeyCriteria, PagedList, Project } from '@dev/translatr-model'; @Component({ - standalone: false, + standalone: true, selector: 'app-key-list', changeDetection: ChangeDetectionStrategy.Eager, template: '' }) -class MockKeyListComponent { +export class MockKeyListComponent { @Input() project: Project; @Input() keys: PagedList; @Input() criteria: KeyCriteria | undefined; @@ -18,9 +18,3 @@ class MockKeyListComponent { @Output() edit = new EventEmitter(); @Output() delete = new EventEmitter(); } - -@NgModule({ - declarations: [MockKeyListComponent], - exports: [MockKeyListComponent] -}) -export class KeyListTestingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts index 3e99f4e7..534030f3 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-keys/project-keys.component.spec.ts @@ -4,7 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; import { KeyListComponent } from './key-list/key-list.component'; -import { KeyListTestingModule } from './key-list/testing'; +import { MockKeyListComponent } from './key-list/testing'; import { ProjectKeysComponent } from './project-keys.component'; @@ -16,7 +16,7 @@ describe('ProjectKeysComponent', () => { waitForAsync(() => { TestBed.overrideComponent(ProjectKeysComponent, { remove: { imports: [KeyListComponent] }, - add: { imports: [KeyListTestingModule] } + add: { imports: [MockKeyListComponent] } }).configureTestingModule({ imports: [ProjectKeysComponent, RouterTestingModule], providers: [ diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/index.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/index.ts index 892c57a1..f7bdf822 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/index.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/index.ts @@ -1 +1 @@ -export * from './locale-list-testing.module'; +export * from './mock-locale-list.component'; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/locale-list-testing.module.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/mock-locale-list.component.ts similarity index 65% rename from ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/locale-list-testing.module.ts rename to ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/mock-locale-list.component.ts index a96b4868..aca826a1 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/locale-list-testing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/locale-list/testing/mock-locale-list.component.ts @@ -1,13 +1,13 @@ -import { Component, EventEmitter, Input, NgModule, Output, ChangeDetectionStrategy } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core'; import { Locale, LocaleCriteria, PagedList, Project } from '@dev/translatr-model'; @Component({ - standalone: false, + standalone: true, selector: 'app-locale-list', changeDetection: ChangeDetectionStrategy.Eager, template: '' }) -class MockLocaleListComponent { +export class MockLocaleListComponent { @Input() project: Project; @Input() locales: PagedList; @Input() criteria: LocaleCriteria | undefined; @@ -19,9 +19,3 @@ class MockLocaleListComponent { @Output() edit = new EventEmitter(); @Output() delete = new EventEmitter(); } - -@NgModule({ - declarations: [MockLocaleListComponent], - exports: [MockLocaleListComponent] -}) -export class LocaleListTestingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts index eefeea54..8092703f 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-locales/project-locales.component.spec.ts @@ -4,7 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; import { LocaleListComponent } from './locale-list/locale-list.component'; -import { LocaleListTestingModule } from './locale-list/testing'; +import { MockLocaleListComponent } from './locale-list/testing'; import { ProjectLocalesComponent } from './project-locales.component'; describe('ProjectLocalesComponent', () => { @@ -15,7 +15,7 @@ describe('ProjectLocalesComponent', () => { waitForAsync(() => { TestBed.overrideComponent(ProjectLocalesComponent, { remove: { imports: [LocaleListComponent] }, - add: { imports: [LocaleListTestingModule] } + add: { imports: [MockLocaleListComponent] } }).configureTestingModule({ imports: [ProjectLocalesComponent, RouterTestingModule], providers: [ diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/index.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/index.ts index 67f2b5ca..8e58a658 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/index.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/index.ts @@ -1 +1 @@ -export * from './member-list-testing.module'; +export * from './mock-member-list.component'; diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/member-list-testing.module.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/mock-member-list.component.ts similarity index 69% rename from ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/member-list-testing.module.ts rename to ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/mock-member-list.component.ts index 6d4ea658..c84cafc2 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/member-list-testing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/testing/mock-member-list.component.ts @@ -1,14 +1,14 @@ -import { Component, EventEmitter, Input, NgModule, Output, ChangeDetectionStrategy } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core'; import { Member, PagedList, Project, RequestCriteria } from '@dev/translatr-model'; import { FilterCriteria } from '../../../../../shared/list-header/list-header.component'; @Component({ - standalone: false, + standalone: true, selector: 'app-member-list', changeDetection: ChangeDetectionStrategy.Eager, template: '' }) -class MockMemberListComponent { +export class MockMemberListComponent { @Input() criteria: RequestCriteria; @Input() project: Project; @Input() canCreate = false; @@ -21,9 +21,3 @@ class MockMemberListComponent { @Output() edit = new EventEmitter(); @Output() delete = new EventEmitter(); } - -@NgModule({ - declarations: [MockMemberListComponent], - exports: [MockMemberListComponent] -}) -export class MemberListTestingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts index 0422e2ad..ce4a8247 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/project-members.component.spec.ts @@ -5,7 +5,7 @@ import { mockObservable } from '@translatr/utils/testing'; import { ProjectFacade } from '../../../shared/project-state'; import { AppFacade } from '../../../../+state/app.facade'; import { MemberListComponent } from './member-list/member-list.component'; -import { MemberListTestingModule } from './member-list/testing'; +import { MockMemberListComponent } from './member-list/testing'; import { ProjectMembersComponent } from './project-members.component'; @@ -17,7 +17,7 @@ describe('ProjectMembersComponent', () => { waitForAsync(() => { TestBed.overrideComponent(ProjectMembersComponent, { remove: { imports: [MemberListComponent] }, - add: { imports: [MemberListTestingModule] } + add: { imports: [MockMemberListComponent] } }).configureTestingModule({ imports: [ProjectMembersComponent, RouterTestingModule], providers: [ From bedda75517b718b454ad0ab76fa1ac80165808af Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 09:39:33 +0200 Subject: [PATCH 10/11] docs(openspec): mark pages-standalone migration complete (groups 7-9) All groups done: routing repoint verified, testing doubles converted, and final cleanup/build/test verification passed (159/159 tests, clean build, clean tsc). Manual smoke test covered the login/main-page flow and confirmed all guarded lazy routes resolve without DI or router errors. Co-Authored-By: Claude Sonnet 5 --- .../changes/migrate-translatr-pages-standalone/tasks.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openspec/changes/migrate-translatr-pages-standalone/tasks.md b/openspec/changes/migrate-translatr-pages-standalone/tasks.md index f918cebf..1e52e77d 100644 --- a/openspec/changes/migrate-translatr-pages-standalone/tasks.md +++ b/openspec/changes/migrate-translatr-pages-standalone/tasks.md @@ -52,7 +52,7 @@ ## 9. Cleanup and verification -- [ ] 9.1 Grep `apps/translatr` for any remaining `*Module` import of a class from `apps/translatr/src/app/modules/pages/**` (excluding the untouched `*RoutingModule`s and `users-page/+state/users.module.ts`) and confirm zero matches -- [ ] 9.2 Run `nx build translatr` and verify it succeeds with no TypeScript errors -- [ ] 9.3 Run `nx test translatr` and verify all suites pass -- [ ] 9.4 Manually smoke-test the app (`nx serve translatr`) covering a direct-composition page (dashboard or projects), a router-outlet page's nested tab route (e.g. a project's locales tab), and the login/main-page flow; verify no console errors and correct rendering +- [x] 9.1 Grep `apps/translatr` for any remaining `*Module` import of a class from `apps/translatr/src/app/modules/pages/**` (excluding the untouched `*RoutingModule`s and `users-page/+state/users.module.ts`) and confirm zero matches (confirmed: only `users.module.ts`'s own empty `declarations: []` state module remains, exactly as intended) +- [x] 9.2 Run `nx build translatr` and verify it succeeds with no TypeScript errors (clean, `--skip-nx-cache`) +- [x] 9.3 Run `nx test translatr` and verify all suites pass (159/159, `--skip-nx-cache`) +- [x] 9.4 Manually smoke-test the app (using the user's already-running `nx serve translatr` on port 4210) covering the login/main-page flow and the guarded lazy routes (dashboard, users, projects, editor, user-page, project-page); verified via the browser pane: main page (already-standalone, unaffected) renders with zero console errors beyond the expected unauthenticated `401`s; every guarded route (dashboard/users/projects/etc.) correctly redirects to login with no DI or router errors, which confirms their lazy-loaded `*RoutingModule`s - now carrying the relocated NgRx `forFeature` wiring, facades, and guards - resolve cleanly; full interactive smoke test of authenticated nested-tab routes (e.g. a project's locales tab) wasn't possible without the user's own credentials, so this is a partial substitute for that deeper manual check From c27658766fbd296018f96eee7702675a2e9eeb74 Mon Sep 17 00:00:00 2001 From: "Panzar, Rene" Date: Sat, 19 Sep 2026 13:10:47 +0200 Subject: [PATCH 11/11] fix(translatr): repair e2e-only regressions in standalone conversions e2e ran the real component tree (unlike unit specs, which override real children with mocks), surfacing several components missing directives/ pipes their templates actually use - each masked in unit tests by the TestBed's ambient modules covering for a standalone component's own incomplete imports: - UserAccessTokensComponent, UserInfoComponent, MemberListComponent: missing TimeAgoPipe/ShortNumberPipe (amTimeAgo/shortNumber pipes used in templates) - UserProjectsComponent: missing TranslocoModule, so its outer *transloco structural directive never activated and the entire template (including the real ProjectListComponent) silently never rendered - UserAccessTokenComponent, KeyEditorPageComponent, LocaleEditorPageComponent, MainPageComponent, ProjectInfoComponent: missing RouterModule/MatButtonModule/MatListModule for routerLink/mat-button/mat-list-item usages in their templates Also fixes a routing bug introduced in this branch: DashboardPageRoutingModule imported the whole ProjectsPageRoutingModule just to reuse its NgRx state wiring, which also pulled in ProjectsPageRoutingModule's own RouterModule.forChild([{path: '', component: ProjectsPageComponent}]) - colliding with dashboard's own path: '' route and making /dashboard render ProjectsPageComponent instead. Fixed by importing the Projects state pieces (StoreModule.forFeature, EffectsModule.forFeature, ProjectsFacade) directly instead of the whole routing module. Also fixes a pre-existing bug on main (predates this branch, in ProjectMemberEditDialogComponent/ProjectOwnerEditDialogComponent from the shared-layer standalone conversion): both dialogs inject ProjectFacade (directly or via ProjectMemberEditFormComponent) but never imported ProjectStateModule themselves, unlike the sibling KeyEditDialogComponent/ LocaleEditDialogComponent which already follow that pattern - since MatDialog creates dialog content in a fresh injector chained to root, not the caller's local route injector, ProjectFacade was never resolvable. All 159 unit tests and the full local e2e suite (187 specs) pass. Co-Authored-By: Claude Sonnet 5 --- .../dashboard-page-routing.module.ts | 17 +++++++++++++---- .../editor-page/key-editor-page.component.ts | 9 +++++++-- .../editor-page/locale-editor-page.component.ts | 9 +++++++-- .../pages/main-page/main-page.component.ts | 2 ++ .../project-info/project-info.component.ts | 2 ++ .../member-list/member-list.component.ts | 5 +++-- .../user-access-token.component.ts | 13 +++++++++++-- .../user-access-tokens.component.ts | 6 ++++-- .../user-page/user-info/user-info.component.ts | 6 ++++-- .../user-projects.component.spec.ts | 2 ++ .../user-projects/user-projects.component.ts | 11 ++++++++++- ...project-member-edit-dialog.component.spec.ts | 3 ++- .../project-member-edit-dialog.component.ts | 2 ++ .../project-owner-edit-dialog.component.spec.ts | 10 +++++++++- .../project-owner-edit-dialog.component.ts | 2 ++ 15 files changed, 80 insertions(+), 19 deletions(-) diff --git a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts index 8de774d3..b83ca5ef 100644 --- a/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts +++ b/ui/apps/translatr/src/app/modules/pages/dashboard-page/dashboard-page-routing.module.ts @@ -3,7 +3,13 @@ import { RouterModule, Routes } from '@angular/router'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; import { AuthGuard } from '../../../guards/auth.guard'; -import { ProjectsPageRoutingModule } from '../projects-page/projects-page-routing.module'; +import { ProjectsEffects } from '../projects-page/+state/projects.effects'; +import { ProjectsFacade } from '../projects-page/+state/projects.facade'; +import { + initialState as projectsInitialState, + PROJECTS_FEATURE_KEY, + projectsReducer +} from '../projects-page/+state/projects.reducer'; import { DashboardEffects } from './+state/dashboard.effects'; import { DashboardFacade } from './+state/dashboard.facade'; import { @@ -24,13 +30,16 @@ const routes: Routes = [ @NgModule({ imports: [ RouterModule.forChild(routes), - ProjectsPageRoutingModule, StoreModule.forFeature(DASHBOARD_FEATURE_KEY, dashboardReducer, { initialState: dashboardInitialState }), - EffectsModule.forFeature([DashboardEffects]) + EffectsModule.forFeature([DashboardEffects]), + StoreModule.forFeature(PROJECTS_FEATURE_KEY, projectsReducer, { + initialState: projectsInitialState + }), + EffectsModule.forFeature([ProjectsEffects]) ], exports: [RouterModule], - providers: [DashboardFacade] + providers: [DashboardFacade, ProjectsFacade] }) export class DashboardPageRoutingModule {} diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts index 72a882ad..3a37f9ae 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/key-editor-page.component.ts @@ -1,10 +1,12 @@ import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { MatListModule } from '@angular/material/list'; +import { ActivatedRoute, Params, Router, RouterModule } from '@angular/router'; import { EmptyViewComponent, EmptyViewHeaderComponent, @@ -43,6 +45,7 @@ const filterComparator = (a: Params, b: Params): boolean => styleUrls: ['./key-editor-page.component.scss'], imports: [ CommonModule, + RouterModule, TranslocoModule, EditorComponent, EditorSelectorComponent, @@ -54,7 +57,9 @@ const filterComparator = (a: Params, b: Params): boolean => FilterFieldComponent, MatFormFieldModule, MatInputModule, - MatIconModule + MatIconModule, + MatButtonModule, + MatListModule ] }) export class KeyEditorPageComponent implements OnInit, OnDestroy { diff --git a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts index bb62298b..6ddef13e 100644 --- a/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/editor-page/locale-editor-page.component.ts @@ -1,9 +1,11 @@ import { CommonModule } from '@angular/common'; import { Component, OnDestroy, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; -import { Params, Router } from '@angular/router'; +import { Params, Router, RouterModule } from '@angular/router'; import { EmptyViewComponent, EmptyViewHeaderComponent, @@ -41,6 +43,7 @@ const filterComparator = (a: Params, b: Params): boolean => styleUrls: ['./locale-editor-page.component.scss'], imports: [ CommonModule, + RouterModule, TranslocoModule, EditorComponent, EditorSelectorComponent, @@ -51,7 +54,9 @@ const filterComparator = (a: Params, b: Params): boolean => EmptyViewActionsComponent, FilterFieldComponent, MatIconModule, - MatMenuModule + MatMenuModule, + MatButtonModule, + MatListModule ] }) export class LocaleEditorPageComponent implements OnInit, OnDestroy { diff --git a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts index 00f69e9d..c5838a40 100644 --- a/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/main-page/main-page.component.ts @@ -8,6 +8,7 @@ import { MatGridListModule } from '@angular/material/grid-list'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; import { ActivityGraphComponent, AuthBarLanguageSwitcherComponent, @@ -34,6 +35,7 @@ import { environment } from '../../../../environments/environment'; styleUrls: ['./main-page.component.scss'], imports: [ CommonModule, + RouterModule, NavbarComponent, AuthBarLanguageSwitcherComponent, MatGridListModule, diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts index 722eb436..f4d94405 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-info/project-info.component.ts @@ -5,6 +5,7 @@ import { MatCardModule } from '@angular/material/card'; import { MatDialog } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; @@ -69,6 +70,7 @@ function endpointFromLocation(location: Location) { MatCardModule, MatFormFieldModule, MatIconModule, + MatListModule, MatProgressBarModule, MatSelectModule, MatTooltipModule diff --git a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts index 10de4cb7..7e13d4fe 100644 --- a/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/project-page/project-members/member-list/member-list.component.ts @@ -6,7 +6,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Router, RouterModule } from '@angular/router'; -import { ConfirmButtonComponent } from '@dev/translatr-components'; +import { ConfirmButtonComponent, TimeAgoPipe } from '@dev/translatr-components'; import { Member, MemberRole, PagedList, Project, RequestCriteria } from '@dev/translatr-model'; import { TranslocoModule } from '@jsverse/transloco'; import { filter, switchMapTo, take } from 'rxjs/operators'; @@ -36,7 +36,8 @@ import { openProjectOwnerEditDialog } from '../../../../shared/project-owner-edi MatListModule, MatIconModule, MatTooltipModule, - MatButtonModule + MatButtonModule, + TimeAgoPipe ] }) export class MemberListComponent { diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts index e4ef4a11..85f3f753 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-token/user-access-token.component.ts @@ -1,7 +1,9 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import { TranslocoModule } from '@jsverse/transloco'; import { filter, take } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; import { AccessTokenEditFormComponent } from '../../../shared/access-token-edit-form/access-token-edit-form.component'; @@ -12,7 +14,14 @@ import { AccessTokenEditFormComponent } from '../../../shared/access-token-edit- templateUrl: './user-access-token.component.html', changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./user-access-token.component.scss'], - imports: [CommonModule, MatCardModule, AccessTokenEditFormComponent] + imports: [ + CommonModule, + RouterModule, + TranslocoModule, + MatCardModule, + MatButtonModule, + AccessTokenEditFormComponent + ] }) export class UserAccessTokenComponent implements OnInit { accessToken$ = this.facade.accessToken$.pipe(filter(x => !!x)); diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts index d7272cb3..1a9f36f7 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-access-tokens/user-access-tokens.component.ts @@ -12,7 +12,8 @@ import { EmptyViewComponent, EmptyViewHeaderComponent, EmptyViewContentComponent, - EmptyViewActionsComponent + EmptyViewActionsComponent, + TimeAgoPipe } from '@dev/translatr-components'; import { AccessToken } from '@dev/translatr-model'; import { TranslocoModule } from '@jsverse/transloco'; @@ -42,7 +43,8 @@ import { FilterCriteria } from '../../../shared/list-header/list-header.componen MatListModule, MatIconModule, MatTooltipModule, - MatButtonModule + MatButtonModule, + TimeAgoPipe ] }) export class UserAccessTokensComponent implements OnInit { diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts index cba60e5d..871f6cc3 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-info/user-info.component.ts @@ -4,7 +4,7 @@ import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { ActivatedRoute, CanActivate, Router, RouterModule } from '@angular/router'; -import { MetricComponent, UserCardComponent } from '@dev/translatr-components'; +import { MetricComponent, ShortNumberPipe, TimeAgoPipe, UserCardComponent } from '@dev/translatr-components'; import { PagedList, Project, User } from '@dev/translatr-model'; import { TranslocoModule } from '@jsverse/transloco'; import { canActivate$, NameIconRoute, slicePagedList } from '@translatr/utils'; @@ -31,7 +31,9 @@ import { USER_ROUTES } from '../user-page.token'; MetricComponent, UserCardComponent, MatIconModule, - MatTooltipModule + MatTooltipModule, + ShortNumberPipe, + TimeAgoPipe ] }) export class UserInfoComponent implements OnInit { diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts index bf357f2b..ecfafd70 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.spec.ts @@ -10,6 +10,7 @@ import { MatTooltipModule } from '@angular/material/tooltip'; import { ProjectListComponent } from '../../../shared/project-list/project-list.component'; import { MockProjectListComponent } from '../../../shared/project-list/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import { TranslocoTestingModule } from '@jsverse/transloco'; describe('UserProjectsComponent', () => { let component: UserProjectsComponent; @@ -25,6 +26,7 @@ describe('UserProjectsComponent', () => { UserProjectsComponent, RouterTestingModule, + TranslocoTestingModule.forRoot({ langs: {}, translocoConfig: { availableLangs: ['en'] } }), MatTooltipModule, MatButtonModule, diff --git a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts index 7e728b81..babf757a 100644 --- a/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts +++ b/ui/apps/translatr/src/app/modules/pages/user-page/user-projects/user-projects.component.ts @@ -1,10 +1,12 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { Router } from '@angular/router'; import { ProjectCriteria, User } from '@dev/translatr-model'; +import { TranslocoModule } from '@jsverse/transloco'; import { navigate } from '@translatr/utils'; import { filter, take, takeUntil, withLatestFrom } from 'rxjs/operators'; import { UserFacade } from '../+state/user.facade'; @@ -18,7 +20,14 @@ import { openProjectEditDialog } from '../../../shared/project-edit-dialog/proje templateUrl: './user-projects.component.html', changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./user-projects.component.scss'], - imports: [CommonModule, ProjectListComponent, MatIconModule, MatTooltipModule] + imports: [ + CommonModule, + TranslocoModule, + ProjectListComponent, + MatIconModule, + MatTooltipModule, + MatButtonModule + ] }) export class UserProjectsComponent implements OnInit { projects$ = this.facade.projects$; diff --git a/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.spec.ts b/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.spec.ts index 6fe24c7e..49bd3d97 100644 --- a/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.spec.ts @@ -7,6 +7,7 @@ import { AppFacade } from '../../../+state/app.facade'; import { UsersModule } from '../../pages/users-page/+state/users.module'; import { UsersFacade } from '../../pages/users-page/+state/users.facade'; import { ProjectMemberEditFormComponent } from '../project-member-edit-form/project-member-edit-form.component'; +import { ProjectStateModule } from '../project-state'; import { MockProjectMemberEditFormComponent } from '../testing'; import { ProjectMemberEditDialogComponent } from './project-member-edit-dialog.component'; @@ -18,7 +19,7 @@ describe('ProjectMemberEditDialogComponent', () => { beforeEach( waitForAsync(() => { TestBed.overrideComponent(ProjectMemberEditDialogComponent, { - remove: { imports: [ProjectMemberEditFormComponent, UsersModule] }, + remove: { imports: [ProjectMemberEditFormComponent, UsersModule, ProjectStateModule] }, add: { imports: [MockProjectMemberEditFormComponent] } }).configureTestingModule({ imports: [ diff --git a/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.ts b/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.ts index 793cd242..fb377885 100644 --- a/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.ts +++ b/ui/apps/translatr/src/app/modules/shared/project-member-edit-dialog/project-member-edit-dialog.component.ts @@ -13,6 +13,7 @@ import { AppFacade } from '../../../+state/app.facade'; import { UsersModule } from '../../pages/users-page/+state/users.module'; import { UsersFacade } from '../../pages/users-page/+state/users.facade'; import { ProjectMemberEditFormComponent } from '../project-member-edit-form/project-member-edit-form.component'; +import { ProjectStateModule } from '../project-state'; interface Data { member: Partial; @@ -35,6 +36,7 @@ interface Data { MatDialogModule, ProjectMemberEditFormComponent, UsersModule, + ProjectStateModule, TranslocoModule ] }) diff --git a/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.spec.ts b/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.spec.ts index 132bf3e0..b25a4a6a 100644 --- a/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.spec.ts +++ b/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.spec.ts @@ -8,6 +8,7 @@ import { UsersModule } from '../../pages/users-page/+state/users.module'; import { UsersFacade } from '../../pages/users-page/+state/users.facade'; import { ProjectMemberEditFormComponent } from '../project-member-edit-form/project-member-edit-form.component'; import { ProjectOwnerEditFormComponent } from '../project-owner-edit-form/project-owner-edit-form.component'; +import { ProjectStateModule } from '../project-state'; import { MockProjectMemberEditFormComponent, MockProjectOwnerEditFormComponent } from '../testing'; import { ProjectOwnerEditDialogComponent } from './project-owner-edit-dialog.component'; @@ -18,7 +19,14 @@ describe('ProjectMemberEditDialogComponent', () => { beforeEach( waitForAsync(() => { TestBed.overrideComponent(ProjectOwnerEditDialogComponent, { - remove: { imports: [ProjectMemberEditFormComponent, ProjectOwnerEditFormComponent, UsersModule] }, + remove: { + imports: [ + ProjectMemberEditFormComponent, + ProjectOwnerEditFormComponent, + UsersModule, + ProjectStateModule + ] + }, add: { imports: [MockProjectMemberEditFormComponent, MockProjectOwnerEditFormComponent] } }).configureTestingModule({ imports: [ diff --git a/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.ts b/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.ts index e31d960d..45419c97 100644 --- a/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.ts +++ b/ui/apps/translatr/src/app/modules/shared/project-owner-edit-dialog/project-owner-edit-dialog.component.ts @@ -12,6 +12,7 @@ import { UsersModule } from '../../pages/users-page/+state/users.module'; import { UsersFacade } from '../../pages/users-page/+state/users.facade'; import { ProjectMemberEditFormComponent } from '../project-member-edit-form/project-member-edit-form.component'; import { ProjectOwnerEditFormComponent } from '../project-owner-edit-form/project-owner-edit-form.component'; +import { ProjectStateModule } from '../project-state'; import { ProjectFacade } from '../project-state/+state'; @Component({ @@ -31,6 +32,7 @@ import { ProjectFacade } from '../project-state/+state'; ProjectMemberEditFormComponent, UsersModule, ProjectOwnerEditFormComponent, + ProjectStateModule, TranslocoModule ] })