1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

PM-27254 Fix password change progress card reactivity (#16984)

This commit is contained in:
Alex
2025-10-23 14:53:39 -04:00
committed by GitHub
parent 2d34a19b23
commit d6785037ba

View File

@@ -1,7 +1,15 @@
import { CommonModule } from "@angular/common";
import { Component, OnInit, ChangeDetectionStrategy } from "@angular/core";
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
OnInit,
inject,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute } from "@angular/router";
import { Subject, switchMap, takeUntil, of, BehaviorSubject, combineLatest } from "rxjs";
import { switchMap, of, BehaviorSubject, combineLatest } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -26,6 +34,8 @@ import { AccessIntelligenceSecurityTasksService } from "../../shared/security-ta
providers: [AccessIntelligenceSecurityTasksService, DefaultAdminTaskService],
})
export class PasswordChangeMetricComponent implements OnInit {
private destroyRef = inject(DestroyRef);
protected taskMetrics$ = new BehaviorSubject<TaskMetrics>({ totalTasks: 0, completedTasks: 0 });
private completedTasks: number = 0;
private totalTasks: number = 0;
@@ -34,14 +44,22 @@ export class PasswordChangeMetricComponent implements OnInit {
atRiskAppsCount: number = 0;
atRiskPasswordsCount: number = 0;
private organizationId!: OrganizationId;
private destroyRef = new Subject<void>();
renderMode: RenderMode = "noCriticalApps";
// Computed properties (formerly getters) - updated when data changes
protected completedPercent = 0;
protected completedTasksCount = 0;
protected totalTasksCount = 0;
protected canAssignTasks = false;
protected hasExistingTasks = false;
protected newAtRiskPasswordsCount = 0;
constructor(
private activatedRoute: ActivatedRoute,
private securityTasksApiService: SecurityTasksApiService,
private allActivitiesService: AllActivitiesService,
protected accessIntelligenceSecurityTasksService: AccessIntelligenceSecurityTasksService,
private cdr: ChangeDetectorRef,
) {}
async ngOnInit(): Promise<void> {
@@ -55,10 +73,11 @@ export class PasswordChangeMetricComponent implements OnInit {
}
return of({ totalTasks: 0, completedTasks: 0 });
}),
takeUntil(this.destroyRef),
takeUntilDestroyed(this.destroyRef),
)
.subscribe((metrics) => {
this.taskMetrics$.next(metrics);
this.cdr.markForCheck();
});
combineLatest([
@@ -67,7 +86,7 @@ export class PasswordChangeMetricComponent implements OnInit {
this.allActivitiesService.atRiskPasswordsCount$,
this.allActivitiesService.allApplicationsDetails$,
])
.pipe(takeUntil(this.destroyRef))
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([taskMetrics, summary, atRiskPasswordsCount, allApplicationsDetails]) => {
this.atRiskAppsCount = summary.totalCriticalAtRiskApplicationCount;
this.atRiskPasswordsCount = atRiskPasswordsCount;
@@ -81,6 +100,11 @@ export class PasswordChangeMetricComponent implements OnInit {
this.allActivitiesService.setPasswordChangeProgressMetricHasProgressBar(
this.renderMode === RenderMode.criticalAppsWithAtRiskAppsAndTasks,
);
// Update all computed properties when data changes
this.updateComputedProperties();
this.cdr.markForCheck();
});
}
@@ -116,57 +140,48 @@ export class PasswordChangeMetricComponent implements OnInit {
return RenderMode.noCriticalApps;
}
get completedPercent(): number {
if (this.totalTasks === 0) {
return 0;
}
return Math.round((this.completedTasks / this.totalTasks) * 100);
}
/**
* Updates all computed properties based on current state.
* Called whenever data changes to avoid recalculation on every change detection cycle.
*/
private updateComputedProperties(): void {
// Calculate completion percentage
this.completedPercent =
this.totalTasks === 0 ? 0 : Math.round((this.completedTasks / this.totalTasks) * 100);
get completedTasksCount(): number {
// Calculate completed tasks count based on render mode
switch (this.renderMode) {
case RenderMode.noCriticalApps:
case RenderMode.criticalAppsWithAtRiskAppsAndNoTasks:
return 0;
this.completedTasksCount = 0;
break;
case RenderMode.criticalAppsWithAtRiskAppsAndTasks:
return this.completedTasks;
this.completedTasksCount = this.completedTasks;
break;
default:
return 0;
this.completedTasksCount = 0;
}
}
get totalTasksCount(): number {
// Calculate total tasks count based on render mode
switch (this.renderMode) {
case RenderMode.noCriticalApps:
return 0;
this.totalTasksCount = 0;
break;
case RenderMode.criticalAppsWithAtRiskAppsAndNoTasks:
return this.atRiskAppsCount;
this.totalTasksCount = this.atRiskAppsCount;
break;
case RenderMode.criticalAppsWithAtRiskAppsAndTasks:
return this.totalTasks;
this.totalTasksCount = this.totalTasks;
break;
default:
return 0;
this.totalTasksCount = 0;
}
}
get canAssignTasks(): boolean {
return this.atRiskPasswordsCount > this.totalTasks;
}
get hasExistingTasks(): boolean {
return this.totalTasks > 0;
}
get newAtRiskPasswordsCount(): number {
// Calculate new at-risk passwords as the difference between current count and tasks created
if (this.atRiskPasswordsCount > this.totalTasks) {
return this.atRiskPasswordsCount - this.totalTasks;
}
return 0;
// Calculate flags and counts
this.canAssignTasks = this.atRiskPasswordsCount > this.totalTasks;
this.hasExistingTasks = this.totalTasks > 0;
this.newAtRiskPasswordsCount =
this.atRiskPasswordsCount > this.totalTasks ? this.atRiskPasswordsCount - this.totalTasks : 0;
}
get renderModes() {