mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
[PM-25869] add a new tab called Activity in risk insights (#16454)
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
"allApplications": {
|
"allApplications": {
|
||||||
"message": "All applications"
|
"message": "All applications"
|
||||||
},
|
},
|
||||||
|
"activity": {
|
||||||
|
"message": "Activity"
|
||||||
|
},
|
||||||
"appLogoLabel": {
|
"appLogoLabel": {
|
||||||
"message": "Bitwarden logo"
|
"message": "Bitwarden logo"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div *ngIf="isLoading$ | async">
|
||||||
|
<tools-risk-insights-loading></tools-risk-insights-loading>
|
||||||
|
</div>
|
||||||
|
<div class="tw-mt-4" *ngIf="!(isLoading$ | async) && !atRiskAppDetails?.length">
|
||||||
|
<bit-no-items class="tw-text-main">
|
||||||
|
<ng-container slot="title">
|
||||||
|
<h2 class="tw-font-semibold tw-mt-4">
|
||||||
|
{{ organization.name }}
|
||||||
|
</h2>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container slot="description">
|
||||||
|
<div class="tw-flex tw-flex-col tw-mb-2">
|
||||||
|
<a class="tw-text-primary-600" routerLink="/login">{{ "learnMore" | i18n }}</a>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</bit-no-items>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { firstValueFrom, Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { RiskInsightsDataService } from "@bitwarden/bit-common/dirt/reports/risk-insights";
|
||||||
|
import { AtRiskApplicationDetail } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
|
import { getById } from "@bitwarden/common/platform/misc";
|
||||||
|
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||||
|
|
||||||
|
import { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "tools-all-activity",
|
||||||
|
imports: [ApplicationsLoadingComponent, SharedModule],
|
||||||
|
templateUrl: "./all-activity.component.html",
|
||||||
|
})
|
||||||
|
export class AllActivityComponent implements OnInit {
|
||||||
|
isLoading$: Observable<boolean> = this.dataService.isLoading$;
|
||||||
|
atRiskAppDetails: AtRiskApplicationDetail[] = [];
|
||||||
|
organization: Organization | null = null;
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId");
|
||||||
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
|
||||||
|
if (organizationId) {
|
||||||
|
this.organization =
|
||||||
|
(await firstValueFrom(
|
||||||
|
this.organizationService.organizations$(userId).pipe(getById(organizationId)),
|
||||||
|
)) ?? null;
|
||||||
|
|
||||||
|
this.atRiskAppDetails = this.dataService.atRiskAppDetails ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected activatedRoute: ActivatedRoute,
|
||||||
|
private accountService: AccountService,
|
||||||
|
protected organizationService: OrganizationService,
|
||||||
|
protected dataService: RiskInsightsDataService,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -34,6 +34,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<bit-tab-group [(selectedIndex)]="tabIndex" (selectedIndexChange)="onTabChange($event)">
|
<bit-tab-group [(selectedIndex)]="tabIndex" (selectedIndexChange)="onTabChange($event)">
|
||||||
|
@if (isRiskInsightsActivityTabFeatureEnabled) {
|
||||||
|
<bit-tab label="{{ 'activity' | i18n }}">
|
||||||
|
<tools-all-activity></tools-all-activity>
|
||||||
|
</bit-tab>
|
||||||
|
}
|
||||||
<bit-tab label="{{ 'allApplicationsWithCount' | i18n: appsCount }}">
|
<bit-tab label="{{ 'allApplicationsWithCount' | i18n: appsCount }}">
|
||||||
<tools-all-applications></tools-all-applications>
|
<tools-all-applications></tools-all-applications>
|
||||||
</bit-tab>
|
</bit-tab>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
} from "@bitwarden/bit-common/dirt/reports/risk-insights/models/password-health";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
@@ -29,15 +30,17 @@ import {
|
|||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
|
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
|
||||||
|
|
||||||
|
import { AllActivityComponent } from "./all-activity.component";
|
||||||
import { AllApplicationsComponent } from "./all-applications.component";
|
import { AllApplicationsComponent } from "./all-applications.component";
|
||||||
import { CriticalApplicationsComponent } from "./critical-applications.component";
|
import { CriticalApplicationsComponent } from "./critical-applications.component";
|
||||||
|
|
||||||
// FIXME: update to use a const object instead of a typescript enum
|
// FIXME: update to use a const object instead of a typescript enum
|
||||||
// eslint-disable-next-line @bitwarden/platform/no-enums
|
// eslint-disable-next-line @bitwarden/platform/no-enums
|
||||||
export enum RiskInsightsTabType {
|
export enum RiskInsightsTabType {
|
||||||
AllApps = 0,
|
AllActivity = 0,
|
||||||
CriticalApps = 1,
|
AllApps = 1,
|
||||||
NotifiedMembers = 2,
|
CriticalApps = 2,
|
||||||
|
NotifiedMembers = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -54,10 +57,12 @@ export enum RiskInsightsTabType {
|
|||||||
DrawerComponent,
|
DrawerComponent,
|
||||||
DrawerBodyComponent,
|
DrawerBodyComponent,
|
||||||
DrawerHeaderComponent,
|
DrawerHeaderComponent,
|
||||||
|
AllActivityComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class RiskInsightsComponent implements OnInit {
|
export class RiskInsightsComponent implements OnInit {
|
||||||
tabIndex: RiskInsightsTabType = RiskInsightsTabType.AllApps;
|
tabIndex: RiskInsightsTabType = RiskInsightsTabType.AllApps;
|
||||||
|
isRiskInsightsActivityTabFeatureEnabled: boolean = false;
|
||||||
|
|
||||||
dataLastUpdated: Date = new Date();
|
dataLastUpdated: Date = new Date();
|
||||||
|
|
||||||
@@ -69,6 +74,7 @@ export class RiskInsightsComponent implements OnInit {
|
|||||||
|
|
||||||
private organizationId: OrganizationId = "" as OrganizationId;
|
private organizationId: OrganizationId = "" as OrganizationId;
|
||||||
private destroyRef = inject(DestroyRef);
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
isLoading$: Observable<boolean> = new Observable<boolean>();
|
isLoading$: Observable<boolean> = new Observable<boolean>();
|
||||||
isRefreshing$: Observable<boolean> = new Observable<boolean>();
|
isRefreshing$: Observable<boolean> = new Observable<boolean>();
|
||||||
dataLastUpdated$: Observable<Date | null> = new Observable<Date | null>();
|
dataLastUpdated$: Observable<Date | null> = new Observable<Date | null>();
|
||||||
@@ -85,6 +91,14 @@ export class RiskInsightsComponent implements OnInit {
|
|||||||
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
|
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
|
||||||
this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps;
|
this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.configService
|
||||||
|
.getFeatureFlag$(FeatureFlag.PM22887_RiskInsightsActivityTab)
|
||||||
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||||
|
.subscribe((isEnabled) => {
|
||||||
|
this.isRiskInsightsActivityTabFeatureEnabled = isEnabled;
|
||||||
|
this.tabIndex = 0; // default to first tab
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user