- @for (report of reports; track report) {
+ @for (report of reports(); track report) {
([]);
}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts
index 60b53f7405d..c1a00731100 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts
@@ -2,7 +2,6 @@ import {
ChangeDetectionStrategy,
Component,
DestroyRef,
- Injector,
OnInit,
Signal,
computed,
@@ -11,7 +10,7 @@ import {
input,
signal,
} from "@angular/core";
-import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
+import { toSignal } from "@angular/core/rxjs-interop";
import { map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -59,6 +58,9 @@ export class PasswordChangeMetricComponent implements OnInit {
private readonly _tasks: Signal = signal([]);
private readonly _atRiskCipherIds: Signal = signal([]);
private readonly _hasCriticalApplications: Signal = signal(false);
+ private readonly _reportGeneratedAt: Signal = signal(
+ undefined,
+ );
// Computed properties
readonly tasksCount = computed(() => this._tasks().length);
@@ -80,8 +82,24 @@ export class PasswordChangeMetricComponent implements OnInit {
}
const inProgressTasks = tasks.filter((task) => task.status === SecurityTaskStatus.Pending);
- const assignedIdSet = new Set(inProgressTasks.map((task) => task.cipherId));
- const unassignedIds = atRiskIds.filter((id) => !assignedIdSet.has(id));
+ const inProgressTaskIds = new Set(inProgressTasks.map((task) => task.cipherId));
+
+ const reportGeneratedAt = this._reportGeneratedAt();
+ const completedTasksAfterReportGeneration = reportGeneratedAt
+ ? tasks.filter(
+ (task) =>
+ task.status === SecurityTaskStatus.Completed &&
+ new Date(task.revisionDate) >= reportGeneratedAt,
+ )
+ : [];
+ const completedTaskIds = new Set(
+ completedTasksAfterReportGeneration.map((task) => task.cipherId),
+ );
+
+ // find cipher ids from last report that do not have a corresponding in progress task (awaiting password reset) OR completed task
+ const unassignedIds = atRiskIds.filter(
+ (id) => !inProgressTaskIds.has(id) && !completedTaskIds.has(id),
+ );
return unassignedIds.length;
});
@@ -109,36 +127,26 @@ export class PasswordChangeMetricComponent implements OnInit {
constructor(
private allActivitiesService: AllActivitiesService,
private i18nService: I18nService,
- private injector: Injector,
private riskInsightsDataService: RiskInsightsDataService,
protected securityTasksService: AccessIntelligenceSecurityTasksService,
private toastService: ToastService,
) {
- // Setup the _tasks signal by manually passing in the injector
- this._tasks = toSignal(this.securityTasksService.tasks$, {
- initialValue: [],
- injector: this.injector,
- });
- // Setup the _atRiskCipherIds signal by manually passing in the injector
+ this._tasks = toSignal(this.securityTasksService.tasks$, { initialValue: [] });
this._atRiskCipherIds = toSignal(
this.riskInsightsDataService.criticalApplicationAtRiskCipherIds$,
- {
- initialValue: [],
- injector: this.injector,
- },
+ { initialValue: [] },
);
-
this._hasCriticalApplications = toSignal(
this.riskInsightsDataService.criticalReportResults$.pipe(
- takeUntilDestroyed(this.destroyRef),
map((report) => {
return report != null && (report.reportData?.length ?? 0) > 0;
}),
),
- {
- initialValue: false,
- injector: this.injector,
- },
+ { initialValue: false },
+ );
+ this._reportGeneratedAt = toSignal(
+ this.riskInsightsDataService.enrichedReportData$.pipe(map((report) => report?.creationDate)),
+ { initialValue: undefined },
);
effect(() => {