-
+ |
{{ r.applicationName }}
|
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 f4d3656071d..00708de282f 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
@@ -19,6 +19,7 @@ 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,
@@ -30,6 +31,8 @@ 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 { OrgAtRiskMembersDialogComponent } from "./org-at-risk-members-dialog.component";
import { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
@Component({
@@ -99,6 +102,7 @@ export class AllApplicationsComponent implements OnInit, OnDestroy {
protected dataService: RiskInsightsDataService,
protected organizationService: OrganizationService,
protected reportService: RiskInsightsReportService,
+ protected dialogService: DialogService,
) {
this.searchControl.valueChanges
.pipe(debounceTime(200), takeUntilDestroyed())
@@ -135,6 +139,21 @@ export class AllApplicationsComponent implements OnInit, OnDestroy {
return item.applicationName;
}
+ showAppAtRiskMembers = async (applicationName: string) => {
+ openAppAtRiskMembersDialog(this.dialogService, {
+ members:
+ this.dataSource.data.find((app) => app.applicationName === applicationName)
+ ?.atRiskMemberDetails ?? [],
+ applicationName,
+ });
+ };
+
+ showOrgAtRiskMembers = async () => {
+ this.dialogService.open(OrgAtRiskMembersDialogComponent, {
+ data: this.reportService.generateAtRiskMemberList(this.dataSource.data),
+ });
+ };
+
onCheckboxChange(id: number, event: Event) {
const isChecked = (event.target as HTMLInputElement).checked;
if (isChecked) {
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
new file mode 100644
index 00000000000..383a1eccabe
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.html
@@ -0,0 +1,21 @@
+
+ {{ 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
new file mode 100644
index 00000000000..d6a757fe897
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/app-at-risk-members-dialog.component.ts
@@ -0,0 +1,35 @@
+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/org-at-risk-members-dialog.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.html
new file mode 100644
index 00000000000..41ac8af7886
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.html
@@ -0,0 +1,27 @@
+
+
+ {{ "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
new file mode 100644
index 00000000000..72518843d94
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/org-at-risk-members-dialog.component.ts
@@ -0,0 +1,24 @@
+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/libs/components/src/section/section-header.component.html b/libs/components/src/section/section-header.component.html
index 3f96e22540f..f070cfeae02 100644
--- a/libs/components/src/section/section-header.component.html
+++ b/libs/components/src/section/section-header.component.html
@@ -2,7 +2,7 @@
-
|