mirror of
https://github.com/bitwarden/browser
synced 2026-02-04 10:43:47 +00:00
Merge branch 'main' into dirt/pm-27706/columns-for-new-apps-dialog
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { concatMap, filter, firstValueFrom, lastValueFrom, map, switchMap, takeUntil } from "rxjs";
|
||||
import { concatMap, firstValueFrom, lastValueFrom, map, of, switchMap, takeUntil, tap } from "rxjs";
|
||||
|
||||
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||
@@ -143,17 +143,23 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
|
||||
getUserId,
|
||||
switchMap((userId) => this.providerService.get$(this.organization.providerId, userId)),
|
||||
map((provider) => provider != null && provider.canManageUsers),
|
||||
filter((result) => result),
|
||||
switchMap(() => this.apiService.getProviderUsers(this.organization.id)),
|
||||
map((providerUsersResponse) =>
|
||||
providerUsersResponse.data.forEach((u) => {
|
||||
const name = this.userNamePipe.transform(u);
|
||||
this.orgUsersUserIdMap.set(u.userId, {
|
||||
name: `${name} (${this.organization.providerName})`,
|
||||
email: u.email,
|
||||
switchMap((canManage) => {
|
||||
if (canManage) {
|
||||
return this.apiService.getProviderUsers(this.organization.providerId);
|
||||
}
|
||||
return of(null);
|
||||
}),
|
||||
tap((providerUsersResponse) => {
|
||||
if (providerUsersResponse) {
|
||||
providerUsersResponse.data.forEach((u) => {
|
||||
const name = this.userNamePipe.transform(u);
|
||||
this.orgUsersUserIdMap.set(u.userId, {
|
||||
name: `${name} (${this.organization.providerName})`,
|
||||
email: u.email,
|
||||
});
|
||||
});
|
||||
}),
|
||||
),
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
@@ -355,6 +355,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"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span bitTypography="h6" class="tw-flex tw-text-main">{{ title }}</span>
|
||||
<div class="tw-flex tw-items-baseline tw-gap-2">
|
||||
@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>
|
||||
</div>
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -45,10 +45,19 @@
|
||||
<li class="tw-col-span-1">
|
||||
<dirt-activity-card
|
||||
[title]="'applicationsNeedingReview' | i18n"
|
||||
[cardMetrics]="'newApplicationsWithCount' | i18n: newApplicationsCount"
|
||||
[metricDescription]="'newApplicationsDescription' | i18n"
|
||||
[iconClass]="'bwi-exclamation-triangle'"
|
||||
[buttonText]="'reviewNow' | 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-muted'"
|
||||
[buttonText]="isAllCaughtUp ? '' : ('reviewNow' | i18n)"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
>
|
||||
|
||||
@@ -43,6 +43,9 @@ export class AllActivityComponent implements OnInit {
|
||||
newApplicationsCount = 0;
|
||||
newApplications: NewApplicationDetail[] = [];
|
||||
passwordChangeMetricHasProgressBar = false;
|
||||
allAppsHaveReviewDate = false;
|
||||
isAllCaughtUp = false;
|
||||
hasLoadedApplicationData = false;
|
||||
|
||||
destroyRef = inject(DestroyRef);
|
||||
|
||||
@@ -80,6 +83,7 @@ export class AllActivityComponent implements OnInit {
|
||||
.subscribe((newApps) => {
|
||||
this.newApplications = newApps;
|
||||
this.newApplicationsCount = newApps.length;
|
||||
this.updateIsAllCaughtUp();
|
||||
});
|
||||
|
||||
this.allActivitiesService.passwordChangeProgressMetricHasProgressBar$
|
||||
@@ -87,9 +91,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.
|
||||
|
||||
Reference in New Issue
Block a user