1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

[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
This commit is contained in:
Alex
2025-10-31 16:37:21 -04:00
committed by GitHub
parent 2da0b48c3d
commit 4d1c00a5bc
5 changed files with 62 additions and 5 deletions

View File

@@ -343,6 +343,12 @@
"reviewNow": { "reviewNow": {
"message": "Review now" "message": "Review now"
}, },
"allCaughtUp": {
"message": "All caught up!"
},
"noNewApplicationsToReviewAtThisTime": {
"message": "No new applications to review at this time"
},
"prioritizeCriticalApplications": { "prioritizeCriticalApplications": {
"message": "Prioritize critical applications" "message": "Prioritize critical applications"
}, },

View File

@@ -2,7 +2,7 @@
<span bitTypography="h6" class="tw-flex tw-text-main">{{ title }}</span> <span bitTypography="h6" class="tw-flex tw-text-main">{{ title }}</span>
<div class="tw-flex tw-items-baseline tw-gap-2"> <div class="tw-flex tw-items-baseline tw-gap-2">
@if (iconClass) { @if (iconClass) {
<i class="bwi {{ iconClass }} tw-text-muted" aria-hidden="true"></i> <i class="bwi {{ iconClass }} {{ iconColorClass }}" aria-hidden="true"></i>
} }
<span bitTypography="h3">{{ cardMetrics }}</span> <span bitTypography="h3">{{ cardMetrics }}</span>
</div> </div>

View File

@@ -58,6 +58,14 @@ export class ActivityCardComponent {
// eslint-disable-next-line @angular-eslint/prefer-signals // eslint-disable-next-line @angular-eslint/prefer-signals
@Input() iconClass: string | null = null; @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. * Button text. If provided, a button will be displayed instead of a navigation link.
*/ */

View File

@@ -45,10 +45,19 @@
<li class="tw-col-span-1"> <li class="tw-col-span-1">
<dirt-activity-card <dirt-activity-card
[title]="'applicationsNeedingReview' | i18n" [title]="'applicationsNeedingReview' | i18n"
[cardMetrics]="'newApplicationsWithCount' | i18n: newApplicationsCount" [cardMetrics]="
[metricDescription]="'newApplicationsDescription' | i18n" isAllCaughtUp
[iconClass]="'bwi-exclamation-triangle'" ? ('allCaughtUp' | i18n)
[buttonText]="'reviewNow' | i18n" : ('newApplicationsWithCount' | i18n: newApplicationsCount)
"
[metricDescription]="
isAllCaughtUp
? ('noNewApplicationsToReviewAtThisTime' | i18n)
: ('newApplicationsDescription' | i18n)
"
[iconClass]="isAllCaughtUp ? 'bwi-check-circle' : 'bwi-exclamation-triangle'"
[iconColorClass]="isAllCaughtUp ? 'tw-text-success' : 'tw-text-muted'"
[buttonText]="isAllCaughtUp ? '' : ('reviewNow' | i18n)"
[buttonType]="'primary'" [buttonType]="'primary'"
(buttonClick)="onReviewNewApplications()" (buttonClick)="onReviewNewApplications()"
> >

View File

@@ -42,6 +42,9 @@ export class AllActivityComponent implements OnInit {
newApplicationsCount = 0; newApplicationsCount = 0;
newApplications: string[] = []; newApplications: string[] = [];
passwordChangeMetricHasProgressBar = false; passwordChangeMetricHasProgressBar = false;
allAppsHaveReviewDate = false;
isAllCaughtUp = false;
hasLoadedApplicationData = false;
destroyRef = inject(DestroyRef); destroyRef = inject(DestroyRef);
@@ -79,6 +82,7 @@ export class AllActivityComponent implements OnInit {
.subscribe((newApps) => { .subscribe((newApps) => {
this.newApplications = newApps; this.newApplications = newApps;
this.newApplicationsCount = newApps.length; this.newApplicationsCount = newApps.length;
this.updateIsAllCaughtUp();
}); });
this.allActivitiesService.passwordChangeProgressMetricHasProgressBar$ this.allActivitiesService.passwordChangeProgressMetricHasProgressBar$
@@ -86,9 +90,39 @@ export class AllActivityComponent implements OnInit {
.subscribe((hasProgressBar) => { .subscribe((hasProgressBar) => {
this.passwordChangeMetricHasProgressBar = 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. * Handles the review new applications button click.
* Opens a dialog showing the list of new applications that can be marked as critical. * Opens a dialog showing the list of new applications that can be marked as critical.