1
0
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:
Alex
2025-11-11 13:11:33 -05:00
committed by GitHub
parent b05abdb99c
commit 089caf57c2
3 changed files with 86 additions and 21 deletions

View File

@@ -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"
},

View File

@@ -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>
}

View File

@@ -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.