mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-27757] init new apps state (#17200)
* feat(dirt): add "needs review" state for applications needing initial review - Add showNeedsReviewState to display warning when all apps lack review dates - Track noAppsHaveReviewDate flag to identify unreviewed applications - Add i18n strings for organization items count and review prompt - Update activity card to show 3 states: all caught up, needs review, new apps - Apply tw-col-span-2 to needs review card for better visibility * refactor: split activity card states into separate @if blocks for readability * fix: set hasLoadedApplicationData when summary data arrives Previously, hasLoadedApplicationData was only set in the enrichedReportData$ subscription, which fired after reportSummary$ and newApplications$. This caused a timing issue where showNeedsReviewState would remain false even when newApplicationsCount === totalApplicationCount because the flag wasn't set yet. Now we set hasLoadedApplicationData=true as soon as reportSummary$ arrives with totalApplicationCount > 0, ensuring proper synchronization. --------- Co-authored-by: Tom <ttalty@bitwarden.com>
This commit is contained in:
@@ -373,6 +373,21 @@
|
||||
"noNewApplicationsToReviewAtThisTime": {
|
||||
"message": "No new applications to review at this time"
|
||||
},
|
||||
"organizationHasItemsSavedForApplications": {
|
||||
"message": "Your organization has items saved for $COUNT$ applications",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
"example": "310"
|
||||
}
|
||||
}
|
||||
},
|
||||
"reviewApplicationsToSecureItems": {
|
||||
"message": "Review applications to secure the items most critical to your organization's security"
|
||||
},
|
||||
"reviewApplications": {
|
||||
"message": "Review applications"
|
||||
},
|
||||
"prioritizeCriticalApplications": {
|
||||
"message": "Prioritize critical applications"
|
||||
},
|
||||
|
||||
@@ -44,26 +44,53 @@
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
|
||||
<!-- All Caught Up State -->
|
||||
@if (isAllCaughtUp) {
|
||||
<li class="tw-col-span-1">
|
||||
<dirt-activity-card
|
||||
[title]="'newApplicationsCardTitle' | i18n"
|
||||
[cardMetrics]="
|
||||
isAllCaughtUp
|
||||
? ('allCaughtUp' | 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-warning'"
|
||||
[buttonText]="isAllCaughtUp ? '' : ('reviewNow' | i18n)"
|
||||
[title]="'applicationsNeedingReview' | i18n"
|
||||
[cardMetrics]="'allCaughtUp' | i18n"
|
||||
[metricDescription]="'noNewApplicationsToReviewAtThisTime' | i18n"
|
||||
[iconClass]="'bwi-check-circle'"
|
||||
[iconColorClass]="'tw-text-success'"
|
||||
[buttonText]="''"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
}
|
||||
<!-- Needs Review State (No apps have been reviewed) -->
|
||||
@else if (showNeedsReviewState) {
|
||||
<li class="tw-col-span-2">
|
||||
<dirt-activity-card
|
||||
[title]="'applicationsNeedingReview' | i18n"
|
||||
[cardMetrics]="'organizationHasItemsSavedForApplications' | i18n: totalApplicationCount"
|
||||
[metricDescription]="'reviewApplicationsToSecureItems' | i18n"
|
||||
[iconClass]="'bwi-exclamation-triangle'"
|
||||
[iconColorClass]="'tw-text-warning'"
|
||||
[buttonText]="'reviewApplications' | i18n"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
}
|
||||
<!-- Default State (New applications to review) -->
|
||||
@else {
|
||||
<li class="tw-col-span-1">
|
||||
<dirt-activity-card
|
||||
[title]="'applicationsNeedingReview' | i18n"
|
||||
[cardMetrics]="'newApplicationsWithCount' | i18n: newApplicationsCount"
|
||||
[metricDescription]="'newApplicationsDescription' | i18n"
|
||||
[iconClass]="'bwi-exclamation-triangle'"
|
||||
[iconColorClass]="'tw-text-muted'"
|
||||
[buttonText]="'reviewNow' | i18n"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@@ -39,12 +39,14 @@ export class AllActivityComponent implements OnInit {
|
||||
totalCriticalAppsAtRiskMemberCount = 0;
|
||||
totalCriticalAppsCount = 0;
|
||||
totalCriticalAppsAtRiskCount = 0;
|
||||
totalApplicationCount = 0;
|
||||
newApplicationsCount = 0;
|
||||
newApplications: ApplicationHealthReportDetail[] = [];
|
||||
extendPasswordChangeWidget = false;
|
||||
allAppsHaveReviewDate = false;
|
||||
isAllCaughtUp = false;
|
||||
hasLoadedApplicationData = false;
|
||||
showNeedsReviewState = false;
|
||||
|
||||
destroyRef = inject(DestroyRef);
|
||||
|
||||
@@ -65,6 +67,12 @@ export class AllActivityComponent implements OnInit {
|
||||
this.totalCriticalAppsAtRiskMemberCount = summary.totalCriticalAtRiskMemberCount;
|
||||
this.totalCriticalAppsCount = summary.totalCriticalApplicationCount;
|
||||
this.totalCriticalAppsAtRiskCount = summary.totalCriticalAtRiskApplicationCount;
|
||||
this.totalApplicationCount = summary.totalApplicationCount;
|
||||
// If we have application data, mark as loaded
|
||||
if (summary.totalApplicationCount > 0) {
|
||||
this.hasLoadedApplicationData = true;
|
||||
}
|
||||
this.updateShowNeedsReviewState();
|
||||
});
|
||||
|
||||
this.dataService.newApplications$
|
||||
@@ -73,6 +81,7 @@ export class AllActivityComponent implements OnInit {
|
||||
this.newApplications = newApps;
|
||||
this.newApplicationsCount = newApps.length;
|
||||
this.updateIsAllCaughtUp();
|
||||
this.updateShowNeedsReviewState();
|
||||
});
|
||||
|
||||
this.allActivitiesService.extendPasswordChangeWidget$
|
||||
@@ -112,6 +121,20 @@ export class AllActivityComponent implements OnInit {
|
||||
this.allAppsHaveReviewDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the showNeedsReviewState flag based on current state.
|
||||
* This state is shown when:
|
||||
* - Data has been loaded
|
||||
* - There are applications (totalApplicationCount > 0)
|
||||
* - ALL apps do NOT have a review date (newApplicationsCount === totalApplicationCount)
|
||||
*/
|
||||
private updateShowNeedsReviewState(): void {
|
||||
this.showNeedsReviewState =
|
||||
this.hasLoadedApplicationData &&
|
||||
this.totalApplicationCount > 0 &&
|
||||
this.newApplicationsCount === this.totalApplicationCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the review new applications button click.
|
||||
* Opens a dialog showing the list of new applications that can be marked as critical.
|
||||
|
||||
Reference in New Issue
Block a user