From 6f49dfffe460a3835cbdb6de88c1855b9810939d Mon Sep 17 00:00:00 2001 From: JaredScar Date: Thu, 22 Jan 2026 14:45:57 -0500 Subject: [PATCH] Refactor organization integrations components to use signals and observables; enhance async handling in templates and add debug logging --- .../integrations.component.html | 3 ++ .../integrations.component.ts | 5 ++- .../integrations.pipe.ts | 5 ++- .../organization-integrations.resolver.ts | 3 ++ .../organization-integrations.state.ts | 32 +++++++++---------- .../single-sign-on.component.html | 1 + .../single-sign-on.component.ts | 19 ++++++++--- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.html b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.html index 1c8c3e9bd0e..694385bd4f4 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.html @@ -1,3 +1,6 @@ +@let organization = organization$ | async; +@let integrations = integrations$ | async; + @if (organization) { diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.ts index dcb27461d05..b74d9ed9e1a 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.component.ts @@ -5,7 +5,6 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { OrganizationIntegrationsState } from "./organization-integrations.state"; -// attempted, but because bit-tab-group is not OnPush, caused more issues than it solved // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ @@ -14,8 +13,8 @@ import { OrganizationIntegrationsState } from "./organization-integrations.state imports: [SharedModule, HeaderModule], }) export class AdminConsoleIntegrationsComponent { - integrations = this.state.integrations; - organization = this.state.organization; + integrations$ = this.state.integrations$; + organization$ = this.state.organization$; constructor(private state: OrganizationIntegrationsState) {} } diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.pipe.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.pipe.ts index 7a420ade4b5..10ee251a921 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.pipe.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integrations.pipe.ts @@ -7,7 +7,10 @@ import { IntegrationType } from "@bitwarden/common/enums"; name: "filterIntegrations", }) export class FilterIntegrationsPipe implements PipeTransform { - transform(integrations: Integration[], type: IntegrationType): Integration[] { + transform(integrations: Integration[] | null | undefined, type: IntegrationType): Integration[] { + if (!integrations) { + return []; + } return integrations.filter((integration) => integration.type === type); } } diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts index c51797a8ca7..dedaff9c925 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.resolver.ts @@ -264,6 +264,9 @@ export class OrganizationIntegrationsResolver implements Resolve { this.state.setIntegrations(merged); + // eslint-disable-next-line no-console + console.log("[DEBUG] Loaded integrations:", merged); + return true; } } diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.state.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.state.ts index 7b6e1ac9b6e..6bbb1a72f9b 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.state.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/organization-integrations.state.ts @@ -1,29 +1,27 @@ -import { Injectable } from "@angular/core"; -import { BehaviorSubject } from "rxjs"; +import { Injectable, signal } from "@angular/core"; +import { toObservable } from "@angular/core/rxjs-interop"; import { Integration } from "@bitwarden/bit-common/dirt/organization-integrations/models/integration"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @Injectable({ providedIn: "root" }) export class OrganizationIntegrationsState { - private integrationsSource = new BehaviorSubject([]); - private organizationSource = new BehaviorSubject(null); - integrations$ = this.integrationsSource.asObservable(); - organization$ = this.organizationSource.asObservable(); + private readonly _integrations = signal([]); + private readonly _organization = signal(null); - setOrganization(val: Organization) { - this.organizationSource.next(val); + // Signals + integrations = this._integrations.asReadonly(); + organization = this._organization.asReadonly(); + + // Observables for backward compatibility + integrations$ = toObservable(this._integrations); + organization$ = toObservable(this._organization); + + setOrganization(val: Organization | null) { + this._organization.set(val); } setIntegrations(val: Integration[]) { - this.integrationsSource.next(val); - } - - get organization() { - return this.organizationSource.value; - } - - get integrations() { - return this.integrationsSource.value; + this._integrations.set(val); } } diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.html b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.html index 3aae4d73c85..f53e381ae29 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.html @@ -1,3 +1,4 @@ +@let integrationsList = integrationsList$ | async;

{{ "singleSignOn" | i18n }}

diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.ts index 7ac9bfebb58..7f045378800 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/single-sign-on/single-sign-on.component.ts @@ -6,6 +6,8 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { IntegrationGridComponent } from "../integration-grid/integration-grid.component"; import { FilterIntegrationsPipe } from "../integrations.pipe"; import { OrganizationIntegrationsState } from "../organization-integrations.state"; +import { tap } from "rxjs/operators"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -15,13 +17,20 @@ import { OrganizationIntegrationsState } from "../organization-integrations.stat imports: [SharedModule, IntegrationGridComponent, FilterIntegrationsPipe], }) export class SingleSignOnComponent implements OnInit { - integrationsList = this.state.integrations; + integrationsList$ = this.state.integrations$; + IntegrationType = IntegrationType; constructor(private state: OrganizationIntegrationsState) {} - ngOnInit() {} - - get IntegrationType(): typeof IntegrationType { - return IntegrationType; + ngOnInit() { + // eslint-disable-next-line no-console + this.state.integrations$ + .pipe( + tap((integrations) => + console.log("[DEBUG] integrations in single-sign-on.component.ts =>", integrations), + ), + ) + .pipe(takeUntilDestroyed()) + .subscribe(); } }