From 615a0eb555f1d7b3401aafff20e4d0208bd53ba4 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 30 Aug 2025 19:10:36 -0400 Subject: [PATCH] delete custom component, put custom formatting in simple dialog, add trad modal behavior --- .../all-applications.component.html | 65 +++++++++++++++ .../all-applications.component.ts | 79 ++++++++++++++---- .../no-data-modal.component.html | 83 ------------------- .../no-data-modal.component.ts | 30 ------- 4 files changed, 126 insertions(+), 131 deletions(-) delete mode 100644 bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.html delete mode 100644 bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.ts diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html index 5cd7e700ce6..f67a6600ca8 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.html @@ -1,3 +1,68 @@ + +@if (showFirstReportPromptDialog) { + +
+ +
+ + Welcome to Risk insights + +
+

+ To get started, follow these steps to find potential security risks. +

+ +
+
+

+ 1. Enforce organization data ownership + (recommended) +

+

+ Turn on the + enforce organization data ownership policy + to get complete visibility of all organization vault data. This setting can be + enabled at any time. +

+
+
+

2. Run the report

+

+ Run the report to see a detailed view of potential at-risk passwords across your + most critical applications. +

+
+
+
+
+ + + +
+
+
+} + @if (dataService.isLoading$ | async) { } @else { diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts index eca2320a7ff..b301c23d363 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications.component.ts @@ -1,3 +1,4 @@ +import { CdkTrapFocus } from "@angular/cdk/a11y"; import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; @@ -26,6 +27,8 @@ import { TableDataSource, ToastService, DialogService, + ButtonModule, + DialogModule, } from "@bitwarden/components"; import { CardComponent } from "@bitwarden/dirt-card"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; @@ -33,12 +36,14 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { AppTableRowScrollableComponent } from "./app-table-row-scrollable.component"; -import { NoDataModalComponent } from "./no-data-modal.component"; import { ApplicationsLoadingComponent } from "./risk-insights-loading.component"; @Component({ selector: "tools-all-applications", templateUrl: "./all-applications.component.html", + host: { + "(keydown.escape)": "handleEscape($event)", // escape dialog with keyboard + }, imports: [ ApplicationsLoadingComponent, HeaderModule, @@ -49,6 +54,9 @@ import { ApplicationsLoadingComponent } from "./risk-insights-loading.component" SharedModule, AppTableRowScrollableComponent, IconButtonModule, + ButtonModule, + DialogModule, + CdkTrapFocus, ], }) export class AllApplicationsComponent implements OnInit { @@ -64,8 +72,9 @@ export class AllApplicationsComponent implements OnInit { totalAtRiskApplicationCount: 0, }; - private hasShownNoDataModal = false; // Flag to prevent multiple modals + private hasShownFirstReportPrompt = false; // Flag to prevent multiple prompts private organizationId: string | null = null; + protected showFirstReportPromptDialog = false; // Controls visibility of the simple dialog destroyRef = inject(DestroyRef); constructor( @@ -95,36 +104,70 @@ export class AllApplicationsComponent implements OnInit { .subscribe((report) => { // Check if we already have data in the dataSource (from previous session) if (this.dataSource.data.length > 0) { - this.hasShownNoDataModal = false; // Reset flag since we have data + this.hasShownFirstReportPrompt = false; // Reset flag since we have data return; } if (report && report.data && report.data.length > 0) { this.dataSource.data = report.data; // this.applicationSummary = this.reportService.generateApplicationsSummary(report.data); - this.hasShownNoDataModal = false; // Reset flag when data is available - } else if (!this.hasShownNoDataModal) { - // Show modal only once when no report data is available - void this.showNoDataModal(); + this.hasShownFirstReportPrompt = false; // Reset flag when data is available + if (this.showFirstReportPromptDialog) { + this.closePrompt(); // Use closePrompt method to properly restore scroll + } + } else if (!this.hasShownFirstReportPrompt) { + // Show prompt only once when no report data is available + void this.showFirstReportPrompt(); } }); } } /** - * Shows a modal prompting users to run a report when no data is available + * Shows a prompt encouraging users to run their first report */ - private async showNoDataModal(): Promise { - // Set flag to prevent multiple modals - this.hasShownNoDataModal = true; + private async showFirstReportPrompt(): Promise { + // Set flag to prevent multiple prompts + this.hasShownFirstReportPrompt = true; - this.dialogService.open(NoDataModalComponent, { - data: { - organizationId: this.organizationId, - riskInsightsDataService: this.dataService, - }, - disableClose: false, - }); + // Show the simple dialog and prevent body scroll + this.showFirstReportPromptDialog = true; + document.body.classList.add("tw-overflow-hidden"); + } + + /** + * Runs the report and closes the prompt + */ + async runReport(): Promise { + // Close the prompt first + this.closePrompt(); + + // Add a small delay to ensure prompt closes before triggering the report + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Trigger the report generation + if (this.dataService) { + this.dataService.triggerReport(); + } + } + + /** + * Closes the prompt (called when clicking backdrop or pressing Escape) + */ + closePrompt(): void { + this.showFirstReportPromptDialog = false; + // Restore body scroll + document.body.classList.remove("tw-overflow-hidden"); + } + + /** + * Handles Escape key press to close prompt + */ + handleEscape(event: KeyboardEvent): void { + if (this.showFirstReportPromptDialog) { + this.closePrompt(); + event.stopPropagation(); + } } goToCreateNewLoginItem = async () => { diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.html deleted file mode 100644 index e3fedc7e6bd..00000000000 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.html +++ /dev/null @@ -1,83 +0,0 @@ -
- -
-

- Welcome to Risk insights -

-
- - -
-
- -

- To get started, follow these steps to find potential security risks. -

- - -
- -
-
-
- 1 -
-
-

- Enforce organization data ownership - (recommended) -

-

- Turn on the - enforce organization data ownership policy - to get complete visibility of all organization vault data. This setting can be - enabled at any time. -

-
-
-
- - -
-
-
- 2 -
-
-

Run the report

-

- Run the report to see a detailed view of potential at-risk passwords across your - most critical applications. -

-
-
-
-
-
-
- - -
- -
-
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.ts deleted file mode 100644 index 4ea42b8acd5..00000000000 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/no-data-modal.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; - -import { ButtonModule } from "@bitwarden/components"; - -@Component({ - selector: "tools-no-data-modal", - templateUrl: "./no-data-modal.component.html", - standalone: true, - imports: [ButtonModule], -}) -export class NoDataModalComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) public data: any, - ) {} - - async runReport(): Promise { - // Close the modal first - this.dialogRef.close(true); - - // Add a small delay to ensure modal closes before triggering the report - await new Promise((resolve) => setTimeout(resolve, 100)); - - // Trigger the report generation - if (this.data.riskInsightsDataService) { - this.data.riskInsightsDataService.triggerReport(); - } - } -}