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:
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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()"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user