mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-29 22:31:29 +01:00
PM-17392 Slide out drawer (#13096)
This commit is contained in:
parent
26a0594056
commit
d0018548ed
@ -117,6 +117,11 @@ export type AtRiskApplicationDetail = {
|
||||
atRiskPasswordCount: number;
|
||||
};
|
||||
|
||||
export type AppAtRiskMembersDialogParams = {
|
||||
members: MemberDetailsFlat[];
|
||||
applicationName: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request to drop a password health report application
|
||||
* Model is expected by the API endpoint
|
||||
@ -143,4 +148,11 @@ export interface PasswordHealthReportApplicationsRequest {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export enum DrawerType {
|
||||
None = 0,
|
||||
AppAtRiskMembers = 1,
|
||||
OrgAtRiskMembers = 2,
|
||||
OrgAtRiskApps = 3,
|
||||
}
|
||||
|
||||
export type PasswordHealthReportApplicationId = Opaque<string, "PasswordHealthReportApplicationId">;
|
||||
|
@ -1,10 +1,15 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { finalize } from "rxjs/operators";
|
||||
|
||||
import { ApplicationHealthReportDetail } from "../models/password-health";
|
||||
import {
|
||||
AppAtRiskMembersDialogParams,
|
||||
ApplicationHealthReportDetail,
|
||||
AtRiskApplicationDetail,
|
||||
AtRiskMemberDetail,
|
||||
DrawerType,
|
||||
} from "../models/password-health";
|
||||
|
||||
import { RiskInsightsReportService } from "./risk-insights-report.service";
|
||||
|
||||
export class RiskInsightsDataService {
|
||||
private applicationsSubject = new BehaviorSubject<ApplicationHealthReportDetail[] | null>(null);
|
||||
|
||||
@ -22,6 +27,12 @@ export class RiskInsightsDataService {
|
||||
private dataLastUpdatedSubject = new BehaviorSubject<Date | null>(null);
|
||||
dataLastUpdated$ = this.dataLastUpdatedSubject.asObservable();
|
||||
|
||||
openDrawer = false;
|
||||
activeDrawerType: DrawerType = DrawerType.None;
|
||||
atRiskMemberDetails: AtRiskMemberDetail[] = [];
|
||||
appAtRiskMembers: AppAtRiskMembersDialogParams | null = null;
|
||||
atRiskAppDetails: AtRiskApplicationDetail[] | null = null;
|
||||
|
||||
constructor(private reportService: RiskInsightsReportService) {}
|
||||
|
||||
/**
|
||||
@ -57,4 +68,46 @@ export class RiskInsightsDataService {
|
||||
refreshApplicationsReport(organizationId: string): void {
|
||||
this.fetchApplicationsReport(organizationId, true);
|
||||
}
|
||||
|
||||
isActiveDrawerType = (drawerType: DrawerType): boolean => {
|
||||
return this.activeDrawerType === drawerType;
|
||||
};
|
||||
|
||||
setDrawerForOrgAtRiskMembers = (atRiskMemberDetails: AtRiskMemberDetail[]): void => {
|
||||
this.resetDrawer(DrawerType.OrgAtRiskMembers);
|
||||
this.activeDrawerType = DrawerType.OrgAtRiskMembers;
|
||||
this.atRiskMemberDetails = atRiskMemberDetails;
|
||||
this.openDrawer = !this.openDrawer;
|
||||
};
|
||||
|
||||
setDrawerForAppAtRiskMembers = (
|
||||
atRiskMembersDialogParams: AppAtRiskMembersDialogParams,
|
||||
): void => {
|
||||
this.resetDrawer(DrawerType.None);
|
||||
this.activeDrawerType = DrawerType.AppAtRiskMembers;
|
||||
this.appAtRiskMembers = atRiskMembersDialogParams;
|
||||
this.openDrawer = !this.openDrawer;
|
||||
};
|
||||
|
||||
setDrawerForOrgAtRiskApps = (atRiskApps: AtRiskApplicationDetail[]): void => {
|
||||
this.resetDrawer(DrawerType.OrgAtRiskApps);
|
||||
this.activeDrawerType = DrawerType.OrgAtRiskApps;
|
||||
this.atRiskAppDetails = atRiskApps;
|
||||
this.openDrawer = !this.openDrawer;
|
||||
};
|
||||
|
||||
closeDrawer = (): void => {
|
||||
this.resetDrawer(DrawerType.None);
|
||||
};
|
||||
|
||||
private resetDrawer = (drawerType: DrawerType): void => {
|
||||
if (this.activeDrawerType !== drawerType) {
|
||||
this.openDrawer = false;
|
||||
}
|
||||
|
||||
this.activeDrawerType = DrawerType.None;
|
||||
this.atRiskMemberDetails = [];
|
||||
this.appAtRiskMembers = null;
|
||||
this.atRiskAppDetails = null;
|
||||
};
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import {
|
||||
DialogService,
|
||||
Icons,
|
||||
NoItemsModule,
|
||||
SearchModule,
|
||||
@ -38,9 +37,6 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
|
||||
|
||||
import { openAppAtRiskMembersDialog } from "./app-at-risk-members-dialog.component";
|
||||
import { OrgAtRiskAppsDialogComponent } from "./org-at-risk-apps-dialog.component";
|
||||
import { OrgAtRiskMembersDialogComponent } from "./org-at-risk-members-dialog.component";
|
||||
import { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
|
||||
|
||||
@Component({
|
||||
@ -131,7 +127,6 @@ export class AllApplicationsComponent implements OnInit {
|
||||
protected reportService: RiskInsightsReportService,
|
||||
private accountService: AccountService,
|
||||
protected criticalAppsService: CriticalAppsService,
|
||||
protected dialogService: DialogService,
|
||||
) {
|
||||
this.searchControl.valueChanges
|
||||
.pipe(debounceTime(200), takeUntilDestroyed())
|
||||
@ -176,24 +171,23 @@ export class AllApplicationsComponent implements OnInit {
|
||||
}
|
||||
|
||||
showAppAtRiskMembers = async (applicationName: string) => {
|
||||
openAppAtRiskMembersDialog(this.dialogService, {
|
||||
const info = {
|
||||
members:
|
||||
this.dataSource.data.find((app) => app.applicationName === applicationName)
|
||||
?.atRiskMemberDetails ?? [],
|
||||
applicationName,
|
||||
});
|
||||
};
|
||||
this.dataService.setDrawerForAppAtRiskMembers(info);
|
||||
};
|
||||
|
||||
showOrgAtRiskMembers = async () => {
|
||||
this.dialogService.open(OrgAtRiskMembersDialogComponent, {
|
||||
data: this.reportService.generateAtRiskMemberList(this.dataSource.data),
|
||||
});
|
||||
const dialogData = this.reportService.generateAtRiskMemberList(this.dataSource.data);
|
||||
this.dataService.setDrawerForOrgAtRiskMembers(dialogData);
|
||||
};
|
||||
|
||||
showOrgAtRiskApps = async () => {
|
||||
this.dialogService.open(OrgAtRiskAppsDialogComponent, {
|
||||
data: this.reportService.generateAtRiskApplicationList(this.dataSource.data),
|
||||
});
|
||||
const data = this.reportService.generateAtRiskApplicationList(this.dataSource.data);
|
||||
this.dataService.setDrawerForOrgAtRiskApps(data);
|
||||
};
|
||||
|
||||
onCheckboxChange(applicationName: string, event: Event) {
|
||||
|
@ -1,19 +0,0 @@
|
||||
<bit-dialog>
|
||||
<span bitDialogTitle>{{ applicationName }}</span>
|
||||
<ng-container bitDialogContent>
|
||||
<div class="tw-flex tw-flex-col tw-gap-2">
|
||||
<span bitDialogTitle>{{ "atRiskMembersWithCount" | i18n: members.length }} </span>
|
||||
<span>{{ "atRiskMembersDescriptionWithApp" | i18n: applicationName }}</span>
|
||||
<div class="tw-mt-1">
|
||||
<ng-container *ngFor="let member of members">
|
||||
<div>{{ member.email }}</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitDialogClose buttonType="secondary" type="button">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
@ -1,35 +0,0 @@
|
||||
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { MemberDetailsFlat } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
|
||||
import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components";
|
||||
|
||||
type AppAtRiskMembersDialogParams = {
|
||||
members: MemberDetailsFlat[];
|
||||
applicationName: string;
|
||||
};
|
||||
|
||||
export const openAppAtRiskMembersDialog = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: AppAtRiskMembersDialogParams,
|
||||
) =>
|
||||
dialogService.open<boolean, AppAtRiskMembersDialogParams>(AppAtRiskMembersDialogComponent, {
|
||||
data: dialogConfig,
|
||||
});
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
templateUrl: "./app-at-risk-members-dialog.component.html",
|
||||
imports: [ButtonModule, CommonModule, JslibModule, DialogModule],
|
||||
})
|
||||
export class AppAtRiskMembersDialogComponent {
|
||||
protected members: MemberDetailsFlat[];
|
||||
protected applicationName: string;
|
||||
|
||||
constructor(@Inject(DIALOG_DATA) private params: AppAtRiskMembersDialogParams) {
|
||||
this.members = params.members;
|
||||
this.applicationName = params.applicationName;
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@
|
||||
>
|
||||
</tools-card>
|
||||
<tools-card
|
||||
class="tw-flex-1"
|
||||
class="tw-flex-1 tw-cursor-pointer"
|
||||
[title]="'atRiskApplications' | i18n"
|
||||
[value]="applicationSummary.totalAtRiskApplicationCount"
|
||||
[maxValue]="applicationSummary.totalApplicationCount"
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
DialogService,
|
||||
Icons,
|
||||
NoItemsModule,
|
||||
SearchModule,
|
||||
@ -30,9 +29,6 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
|
||||
|
||||
import { openAppAtRiskMembersDialog } from "./app-at-risk-members-dialog.component";
|
||||
import { OrgAtRiskAppsDialogComponent } from "./org-at-risk-apps-dialog.component";
|
||||
import { OrgAtRiskMembersDialogComponent } from "./org-at-risk-members-dialog.component";
|
||||
import { RiskInsightsTabType } from "./risk-insights.component";
|
||||
|
||||
@Component({
|
||||
@ -114,7 +110,6 @@ export class CriticalApplicationsComponent implements OnInit {
|
||||
protected dataService: RiskInsightsDataService,
|
||||
protected criticalAppsService: CriticalAppsService,
|
||||
protected reportService: RiskInsightsReportService,
|
||||
protected dialogService: DialogService,
|
||||
protected i18nService: I18nService,
|
||||
) {
|
||||
this.searchControl.valueChanges
|
||||
@ -123,24 +118,23 @@ export class CriticalApplicationsComponent implements OnInit {
|
||||
}
|
||||
|
||||
showAppAtRiskMembers = async (applicationName: string) => {
|
||||
openAppAtRiskMembersDialog(this.dialogService, {
|
||||
const data = {
|
||||
members:
|
||||
this.dataSource.data.find((app) => app.applicationName === applicationName)
|
||||
?.atRiskMemberDetails ?? [],
|
||||
applicationName,
|
||||
});
|
||||
};
|
||||
this.dataService.setDrawerForAppAtRiskMembers(data);
|
||||
};
|
||||
|
||||
showOrgAtRiskMembers = async () => {
|
||||
this.dialogService.open(OrgAtRiskMembersDialogComponent, {
|
||||
data: this.reportService.generateAtRiskMemberList(this.dataSource.data),
|
||||
});
|
||||
const data = this.reportService.generateAtRiskMemberList(this.dataSource.data);
|
||||
this.dataService.setDrawerForOrgAtRiskMembers(data);
|
||||
};
|
||||
|
||||
showOrgAtRiskApps = async () => {
|
||||
this.dialogService.open(OrgAtRiskAppsDialogComponent, {
|
||||
data: this.reportService.generateAtRiskApplicationList(this.dataSource.data),
|
||||
});
|
||||
const data = this.reportService.generateAtRiskApplicationList(this.dataSource.data);
|
||||
this.dataService.setDrawerForOrgAtRiskApps(data);
|
||||
};
|
||||
|
||||
trackByFunction(_: number, item: ApplicationHealthReportDetailWithCriticalFlag) {
|
||||
|
@ -1,25 +0,0 @@
|
||||
<bit-dialog>
|
||||
<ng-container bitDialogTitle>
|
||||
<span bitDialogTitle>{{ "atRiskApplicationsWithCount" | i18n: atRiskApps.length }} </span>
|
||||
</ng-container>
|
||||
<ng-container bitDialogContent>
|
||||
<div class="tw-flex tw-flex-col tw-gap-2">
|
||||
<span bitTypography="body1">{{ "atRiskApplicationsDescription" | i18n }}</span>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "application" | i18n }}</div>
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "atRiskPasswords" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngFor="let app of atRiskApps">
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div>{{ app.applicationName }}</div>
|
||||
<div>{{ app.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitDialogClose buttonType="secondary" type="button">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
@ -1,24 +0,0 @@
|
||||
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AtRiskApplicationDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
|
||||
import { ButtonModule, DialogModule, DialogService, TypographyModule } from "@bitwarden/components";
|
||||
|
||||
export const openOrgAtRiskMembersDialog = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: AtRiskApplicationDetail[],
|
||||
) =>
|
||||
dialogService.open<boolean, AtRiskApplicationDetail[]>(OrgAtRiskAppsDialogComponent, {
|
||||
data: dialogConfig,
|
||||
});
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
templateUrl: "./org-at-risk-apps-dialog.component.html",
|
||||
imports: [ButtonModule, CommonModule, DialogModule, JslibModule, TypographyModule],
|
||||
})
|
||||
export class OrgAtRiskAppsDialogComponent {
|
||||
constructor(@Inject(DIALOG_DATA) protected atRiskApps: AtRiskApplicationDetail[]) {}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
<bit-dialog>
|
||||
<ng-container bitDialogTitle>
|
||||
<span bitDialogTitle>{{ "atRiskMembersWithCount" | i18n: atRiskMembers.length }} </span>
|
||||
</ng-container>
|
||||
<ng-container bitDialogContent>
|
||||
<div class="tw-flex tw-flex-col tw-gap-2">
|
||||
<span bitTypography="body1">{{ "atRiskMembersDescription" | i18n }}</span>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "email" | i18n }}</div>
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "atRiskPasswords" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngFor="let member of atRiskMembers">
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div>{{ member.email }}</div>
|
||||
<div>{{ member.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitDialogClose buttonType="secondary" type="button">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
@ -1,24 +0,0 @@
|
||||
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AtRiskMemberDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
|
||||
import { ButtonModule, DialogModule, DialogService, TypographyModule } from "@bitwarden/components";
|
||||
|
||||
export const openOrgAtRiskMembersDialog = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: AtRiskMemberDetail[],
|
||||
) =>
|
||||
dialogService.open<boolean, AtRiskMemberDetail[]>(OrgAtRiskMembersDialogComponent, {
|
||||
data: dialogConfig,
|
||||
});
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
templateUrl: "./org-at-risk-members-dialog.component.html",
|
||||
imports: [ButtonModule, CommonModule, DialogModule, JslibModule, TypographyModule],
|
||||
})
|
||||
export class OrgAtRiskMembersDialogComponent {
|
||||
constructor(@Inject(DIALOG_DATA) protected atRiskMembers: AtRiskMemberDetail[]) {}
|
||||
}
|
@ -1,57 +1,126 @@
|
||||
<ng-container>
|
||||
<div class="tw-mb-1 text-primary" bitTypography="body1">{{ "accessIntelligence" | i18n }}</div>
|
||||
<h1 bitTypography="h1">{{ "riskInsights" | i18n }}</h1>
|
||||
<div class="tw-text-muted tw-max-w-4xl tw-mb-2">
|
||||
{{ "reviewAtRiskPasswords" | i18n }}
|
||||
</div>
|
||||
<div
|
||||
class="tw-bg-primary-100 tw-rounded-lg tw-w-full tw-px-8 tw-py-4 tw-my-4 tw-flex tw-items-center"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-exclamation-triangle bwi-lg tw-text-[1.2rem] text-muted"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-mx-4">{{
|
||||
"dataLastUpdated" | i18n: (dataLastUpdated$ | async | date: "MMMM d, y 'at' h:mm a")
|
||||
}}</span>
|
||||
<span class="tw-flex tw-justify-center tw-w-16">
|
||||
<a
|
||||
*ngIf="!(isRefreshing$ | async)"
|
||||
bitButton
|
||||
buttonType="unstyled"
|
||||
class="tw-border-none !tw-font-normal tw-cursor-pointer !tw-py-0"
|
||||
[bitAction]="refreshData.bind(this)"
|
||||
>
|
||||
{{ "refresh" | i18n }}
|
||||
</a>
|
||||
<span>
|
||||
<i
|
||||
*ngIf="isRefreshing$ | async"
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted tw-text-[1.2rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<bit-layout>
|
||||
<div class="tw-mb-1 text-primary" bitTypography="body1">{{ "accessIntelligence" | i18n }}</div>
|
||||
<h1 bitTypography="h1">{{ "riskInsights" | i18n }}</h1>
|
||||
<div class="tw-text-muted tw-max-w-4xl tw-mb-2">
|
||||
{{ "reviewAtRiskPasswords" | i18n }}
|
||||
</div>
|
||||
<div
|
||||
class="tw-bg-primary-100 tw-rounded-lg tw-w-full tw-px-8 tw-py-4 tw-my-4 tw-flex tw-items-center"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-exclamation-triangle bwi-lg tw-text-[1.2rem] text-muted"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-mx-4">{{
|
||||
"dataLastUpdated" | i18n: (dataLastUpdated$ | async | date: "MMMM d, y 'at' h:mm a")
|
||||
}}</span>
|
||||
<span class="tw-flex tw-justify-center tw-w-16">
|
||||
<a
|
||||
*ngIf="!(isRefreshing$ | async)"
|
||||
bitButton
|
||||
buttonType="unstyled"
|
||||
class="tw-border-none !tw-font-normal tw-cursor-pointer !tw-py-0"
|
||||
[bitAction]="refreshData.bind(this)"
|
||||
>
|
||||
{{ "refresh" | i18n }}
|
||||
</a>
|
||||
<span>
|
||||
<i
|
||||
*ngIf="isRefreshing$ | async"
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted tw-text-[1.2rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<bit-tab-group [(selectedIndex)]="tabIndex" (selectedIndexChange)="onTabChange($event)">
|
||||
<bit-tab label="{{ 'allApplicationsWithCount' | i18n: appsCount }}">
|
||||
<tools-all-applications></tools-all-applications>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="isCriticalAppsFeatureEnabled">
|
||||
<ng-template bitTabLabel>
|
||||
<i class="bwi bwi-star"></i>
|
||||
{{ "criticalApplicationsWithCount" | i18n: (criticalApps$ | async)?.length ?? 0 }}
|
||||
</ng-template>
|
||||
<tools-critical-applications></tools-critical-applications>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data">
|
||||
<tools-password-health></tools-password-health>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data + members">
|
||||
<tools-password-health-members></tools-password-health-members>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data + uri">
|
||||
<tools-password-health-members-uri></tools-password-health-members-uri>
|
||||
</bit-tab>
|
||||
</bit-tab-group>
|
||||
</div>
|
||||
<bit-tab-group [(selectedIndex)]="tabIndex" (selectedIndexChange)="onTabChange($event)">
|
||||
<bit-tab label="{{ 'allApplicationsWithCount' | i18n: appsCount }}">
|
||||
<tools-all-applications></tools-all-applications>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="isCriticalAppsFeatureEnabled">
|
||||
<ng-template bitTabLabel>
|
||||
<i class="bwi bwi-star"></i>
|
||||
{{ "criticalApplicationsWithCount" | i18n: (criticalApps$ | async)?.length ?? 0 }}
|
||||
</ng-template>
|
||||
<tools-critical-applications></tools-critical-applications>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data">
|
||||
<tools-password-health></tools-password-health>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data + members">
|
||||
<tools-password-health-members></tools-password-health-members>
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="showDebugTabs" label="Raw Data + uri">
|
||||
<tools-password-health-members-uri></tools-password-health-members-uri>
|
||||
</bit-tab>
|
||||
</bit-tab-group>
|
||||
|
||||
<bit-drawer style="width: 30%" [(open)]="dataService.openDrawer">
|
||||
<ng-container *ngIf="dataService.isActiveDrawerType(drawerTypes.OrgAtRiskMembers)">
|
||||
<bit-drawer-header
|
||||
title="{{ 'atRiskMembersWithCount' | i18n: dataService.atRiskMemberDetails.length }}"
|
||||
>
|
||||
</bit-drawer-header>
|
||||
<bit-drawer-body>
|
||||
<span bitTypography="body1" class="tw-text-muted tw-text-sm">{{
|
||||
"atRiskMembersDescription" | i18n
|
||||
}}</span>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "email" | i18n }}</div>
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "atRiskPasswords" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngFor="let member of dataService.atRiskMemberDetails">
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div>{{ member.email }}</div>
|
||||
<div>{{ member.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</bit-drawer-body>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="dataService.isActiveDrawerType(drawerTypes.AppAtRiskMembers)">
|
||||
<bit-drawer-header title="{{ dataService.appAtRiskMembers.applicationName }}">
|
||||
</bit-drawer-header>
|
||||
<bit-drawer-body>
|
||||
<div bitTypography="body1" class="tw-mb-2">
|
||||
{{ "atRiskMembersWithCount" | i18n: dataService.appAtRiskMembers.members.length }}
|
||||
</div>
|
||||
<div bitTypography="body1" class="tw-text-muted tw-text-sm tw-mb-2">
|
||||
{{
|
||||
"atRiskMembersDescriptionWithApp" | i18n: dataService.appAtRiskMembers.applicationName
|
||||
}}
|
||||
</div>
|
||||
<div class="tw-mt-1">
|
||||
<ng-container *ngFor="let member of dataService.appAtRiskMembers.members">
|
||||
<div>{{ member.email }}</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</bit-drawer-body>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="dataService.isActiveDrawerType(drawerTypes.OrgAtRiskApps)">
|
||||
<bit-drawer-header
|
||||
title="{{ 'atRiskApplicationsWithCount' | i18n: dataService.atRiskAppDetails.length }}"
|
||||
>
|
||||
</bit-drawer-header>
|
||||
|
||||
<bit-drawer-body>
|
||||
<span bitTypography="body2" class="tw-text-muted tw-text-sm">{{
|
||||
"atRiskApplicationsDescription" | i18n
|
||||
}}</span>
|
||||
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "application" | i18n }}</div>
|
||||
<div bitTypography="body2" class="tw-font-bold">{{ "atRiskPasswords" | i18n }}</div>
|
||||
</div>
|
||||
<ng-container *ngFor="let app of dataService.atRiskAppDetails">
|
||||
<div class="tw-flex tw-justify-between tw-mt-2">
|
||||
<div>{{ app.applicationName }}</div>
|
||||
<div>{{ app.atRiskPasswordCount }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</bit-drawer-body>
|
||||
</ng-container>
|
||||
</bit-drawer>
|
||||
</bit-layout>
|
||||
</ng-container>
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
} from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||
import {
|
||||
ApplicationHealthReportDetail,
|
||||
DrawerType,
|
||||
PasswordHealthReportApplicationsResponse,
|
||||
} from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
|
||||
// eslint-disable-next-line no-restricted-imports -- used for dependency injection
|
||||
@ -19,7 +20,15 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { devFlagEnabled } from "@bitwarden/common/platform/misc/flags";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { AsyncActionsModule, ButtonModule, TabsModule } from "@bitwarden/components";
|
||||
import {
|
||||
AsyncActionsModule,
|
||||
ButtonModule,
|
||||
DrawerBodyComponent,
|
||||
DrawerComponent,
|
||||
DrawerHeaderComponent,
|
||||
LayoutComponent,
|
||||
TabsModule,
|
||||
} from "@bitwarden/components";
|
||||
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
|
||||
|
||||
import { AllApplicationsComponent } from "./all-applications.component";
|
||||
@ -51,6 +60,10 @@ export enum RiskInsightsTabType {
|
||||
PasswordHealthMembersURIComponent,
|
||||
NotifiedMembersTableComponent,
|
||||
TabsModule,
|
||||
DrawerComponent,
|
||||
DrawerBodyComponent,
|
||||
DrawerHeaderComponent,
|
||||
LayoutComponent,
|
||||
],
|
||||
})
|
||||
export class RiskInsightsComponent implements OnInit {
|
||||
@ -77,7 +90,7 @@ export class RiskInsightsComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private configService: ConfigService,
|
||||
private dataService: RiskInsightsDataService,
|
||||
protected dataService: RiskInsightsDataService,
|
||||
private criticalAppsService: CriticalAppsService,
|
||||
) {
|
||||
this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => {
|
||||
@ -137,5 +150,13 @@ export class RiskInsightsComponent implements OnInit {
|
||||
queryParams: { tabIndex: newIndex },
|
||||
queryParamsHandling: "merge",
|
||||
});
|
||||
|
||||
// close drawer when tabs are changed
|
||||
this.dataService.closeDrawer();
|
||||
}
|
||||
|
||||
// Get a list of drawer types
|
||||
get drawerTypes(): typeof DrawerType {
|
||||
return DrawerType;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user