From 4d1c00a5bc3afd9427bcd85669c5c1a47a09c3d8 Mon Sep 17 00:00:00 2001 From: Alex <55413326+AlexRubik@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:37:21 -0400 Subject: [PATCH] [PM-26941] all caught up state for review card (#17164) * add "All caught up!" state for application review card - Display success state when all applications have been reviewed and no new applications need review - Add iconColorClass input to activity-card component to support conditional icon colors (green checkmark for success state) - Add i18n keys: allCaughtUp and noNewApplicationsToReviewAtThisTime - Check if all apps have review dates via enrichedReportData$ to determine when to show the caught up state * fix "Potential Race Condition with State Initialization" from claude issue and replace getter --- apps/web/src/locales/en/messages.json | 6 ++++ .../activity/activity-card.component.html | 2 +- .../activity/activity-card.component.ts | 8 +++++ .../activity/all-activity.component.html | 17 +++++++--- .../activity/all-activity.component.ts | 34 +++++++++++++++++++ 5 files changed, 62 insertions(+), 5 deletions(-) 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.