diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index 2e0fd99bbcc..36d68429ac6 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -343,6 +343,12 @@
"reviewNow": {
"message": "Review now"
},
+ "allCaughtUp": {
+ "message": "All caught up!"
+ },
+ "noNewApplicationsToReviewAtThisTime": {
+ "message": "No new applications to review at this time"
+ },
"prioritizeCriticalApplications": {
"message": "Prioritize critical applications"
},
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.html
index 756907d24e6..73f98034f0a 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.html
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.html
@@ -2,7 +2,7 @@
{{ title }}
@if (iconClass) {
-
+
}
{{ cardMetrics }}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts
index 427e7262f50..24d931165a7 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts
@@ -58,6 +58,14 @@ export class ActivityCardComponent {
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() iconClass: string | null = null;
+ /**
+ * CSS class for icon color (e.g., "tw-text-success", "tw-text-muted").
+ * Defaults to "tw-text-muted" if not provided.
+ */
+ // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
+ // eslint-disable-next-line @angular-eslint/prefer-signals
+ @Input() iconColorClass: string = "tw-text-muted";
+
/**
* Button text. If provided, a button will be displayed instead of a navigation link.
*/
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html
index 82137c9acca..8cdb927ab65 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html
@@ -45,10 +45,19 @@
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.ts
index b12a8fffedb..150c66ad2d4 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.ts
@@ -42,6 +42,9 @@ export class AllActivityComponent implements OnInit {
newApplicationsCount = 0;
newApplications: string[] = [];
passwordChangeMetricHasProgressBar = false;
+ allAppsHaveReviewDate = false;
+ isAllCaughtUp = false;
+ hasLoadedApplicationData = false;
destroyRef = inject(DestroyRef);
@@ -79,6 +82,7 @@ export class AllActivityComponent implements OnInit {
.subscribe((newApps) => {
this.newApplications = newApps;
this.newApplicationsCount = newApps.length;
+ this.updateIsAllCaughtUp();
});
this.allActivitiesService.passwordChangeProgressMetricHasProgressBar$
@@ -86,9 +90,39 @@ export class AllActivityComponent implements OnInit {
.subscribe((hasProgressBar) => {
this.passwordChangeMetricHasProgressBar = hasProgressBar;
});
+
+ this.dataService.enrichedReportData$
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe((enrichedData) => {
+ if (enrichedData?.applicationData && enrichedData.applicationData.length > 0) {
+ this.hasLoadedApplicationData = true;
+ // Check if all apps have a review date (not null and not undefined)
+ this.allAppsHaveReviewDate = enrichedData.applicationData.every(
+ (app) => app.reviewedDate !== null && app.reviewedDate !== undefined,
+ );
+ } else {
+ this.hasLoadedApplicationData = enrichedData !== null;
+ this.allAppsHaveReviewDate = false;
+ }
+ this.updateIsAllCaughtUp();
+ });
}
}
+ /**
+ * Updates the isAllCaughtUp flag based on current state.
+ * Only shows "All caught up!" when:
+ * - Data has been loaded (hasLoadedApplicationData is true)
+ * - No new applications need review
+ * - All apps have a review date
+ */
+ private updateIsAllCaughtUp(): void {
+ this.isAllCaughtUp =
+ this.hasLoadedApplicationData &&
+ this.newApplicationsCount === 0 &&
+ this.allAppsHaveReviewDate;
+ }
+
/**
* Handles the review new applications button click.
* Opens a dialog showing the list of new applications that can be marked as critical.