diff --git a/bitwarden_license/bit-common/src/tools/reports/risk-insights/models/password-health.ts b/bitwarden_license/bit-common/src/tools/reports/risk-insights/models/password-health.ts index 7d5e5e255f..2260f9257a 100644 --- a/bitwarden_license/bit-common/src/tools/reports/risk-insights/models/password-health.ts +++ b/bitwarden_license/bit-common/src/tools/reports/risk-insights/models/password-health.ts @@ -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; diff --git a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/risk-insights-data.service.ts b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/risk-insights-data.service.ts index 42bab69fca..668fb18725 100644 --- a/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/risk-insights-data.service.ts +++ b/bitwarden_license/bit-common/src/tools/reports/risk-insights/services/risk-insights-data.service.ts @@ -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(null); @@ -22,6 +27,12 @@ export class RiskInsightsDataService { private dataLastUpdatedSubject = new BehaviorSubject(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; + }; } diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts index 8d8112587c..64dc5a21fc 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts @@ -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) { diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.html deleted file mode 100644 index fa58678be0..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.html +++ /dev/null @@ -1,19 +0,0 @@ - - {{ applicationName }} - -
- {{ "atRiskMembersWithCount" | i18n: members.length }} - {{ "atRiskMembersDescriptionWithApp" | i18n: applicationName }} -
- -
{{ member.email }}
-
-
-
-
- - - -
diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.ts deleted file mode 100644 index d6a757fe89..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.ts +++ /dev/null @@ -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(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; - } -} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html index 41d256c073..38e017fd5c 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html @@ -43,7 +43,7 @@ > { - 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) { diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.html deleted file mode 100644 index 298011b215..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.html +++ /dev/null @@ -1,25 +0,0 @@ - - - {{ "atRiskApplicationsWithCount" | i18n: atRiskApps.length }} - - -
- {{ "atRiskApplicationsDescription" | i18n }} -
-
{{ "application" | i18n }}
-
{{ "atRiskPasswords" | i18n }}
-
- -
-
{{ app.applicationName }}
-
{{ app.atRiskPasswordCount }}
-
-
-
-
- - - -
diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.ts deleted file mode 100644 index 0ae00f6087..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-apps-dialog.component.ts +++ /dev/null @@ -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(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[]) {} -} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.html deleted file mode 100644 index 1f1de10366..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.html +++ /dev/null @@ -1,25 +0,0 @@ - - - {{ "atRiskMembersWithCount" | i18n: atRiskMembers.length }} - - -
- {{ "atRiskMembersDescription" | i18n }} -
-
{{ "email" | i18n }}
-
{{ "atRiskPasswords" | i18n }}
-
- -
-
{{ member.email }}
-
{{ member.atRiskPasswordCount }}
-
-
-
-
- - - -
diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.ts deleted file mode 100644 index 72518843d9..0000000000 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.ts +++ /dev/null @@ -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(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[]) {} -} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html index ae8bd94e5f..a368f5c0c1 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html @@ -1,57 +1,126 @@ -
{{ "accessIntelligence" | i18n }}
-

{{ "riskInsights" | i18n }}

-
- {{ "reviewAtRiskPasswords" | i18n }} -
-
- - {{ - "dataLastUpdated" | i18n: (dataLastUpdated$ | async | date: "MMMM d, y 'at' h:mm a") - }} - - - {{ "refresh" | i18n }} - - - + +
{{ "accessIntelligence" | i18n }}
+

{{ "riskInsights" | i18n }}

+
+ {{ "reviewAtRiskPasswords" | i18n }} +
+
+ + {{ + "dataLastUpdated" | i18n: (dataLastUpdated$ | async | date: "MMMM d, y 'at' h:mm a") + }} + + + {{ "refresh" | i18n }} + + + + - -
- - - - - - - - {{ "criticalApplicationsWithCount" | i18n: (criticalApps$ | async)?.length ?? 0 }} - - - - - - - - - - - - - +
+ + + + + + + + {{ "criticalApplicationsWithCount" | i18n: (criticalApps$ | async)?.length ?? 0 }} + + + + + + + + + + + + + + + + + + + + {{ + "atRiskMembersDescription" | i18n + }} +
+
{{ "email" | i18n }}
+
{{ "atRiskPasswords" | i18n }}
+
+ +
+
{{ member.email }}
+
{{ member.atRiskPasswordCount }}
+
+
+
+
+ + + + + +
+ {{ "atRiskMembersWithCount" | i18n: dataService.appAtRiskMembers.members.length }} +
+
+ {{ + "atRiskMembersDescriptionWithApp" | i18n: dataService.appAtRiskMembers.applicationName + }} +
+
+ +
{{ member.email }}
+
+
+
+
+ + + + + + + {{ + "atRiskApplicationsDescription" | i18n + }} +
+
{{ "application" | i18n }}
+
{{ "atRiskPasswords" | i18n }}
+
+ +
+
{{ app.applicationName }}
+
{{ app.atRiskPasswordCount }}
+
+
+
+
+
+
diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts index 6d39a710e2..20dc320de2 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts @@ -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; } }