diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index dafd7f00a04..cda4e70f915 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -280,6 +280,24 @@
"totalApplications": {
"message": "Total applications"
},
+ "applicationsNeedingReview": {
+ "message": "Applications needing review"
+ },
+ "newApplicationsWithCount": {
+ "message": "$COUNT$ new applications",
+ "placeholders": {
+ "count": {
+ "content": "$1",
+ "example": "13"
+ }
+ }
+ },
+ "newApplicationsDescription": {
+ "message": "Review new applications to mark as critical and keep your organization secure"
+ },
+ "reviewNow": {
+ "message": "Review now"
+ },
"unmarkAsCritical": {
"message": "Unmark as critical"
},
diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/all-activities.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/all-activities.service.ts
index b8992b1a05f..42e4b8975c4 100644
--- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/all-activities.service.ts
+++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/all-activities.service.ts
@@ -68,6 +68,7 @@ export class AllActivitiesService {
totalAtRiskMemberCount: summary.totalAtRiskMemberCount,
totalApplicationCount: summary.totalApplicationCount,
totalAtRiskApplicationCount: summary.totalAtRiskApplicationCount,
+ newApplications: summary.newApplications,
});
}
diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts
index c9a51e804dc..dc078d810c2 100644
--- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts
+++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/risk-insights-report.service.ts
@@ -226,7 +226,23 @@ export class RiskInsightsReportService {
const atRiskMembers = reports.flatMap((x) => x.atRiskMemberDetails);
const uniqueAtRiskMembers = getUniqueMembers(atRiskMembers);
- // TODO: totalCriticalMemberCount, totalCriticalAtRiskMemberCount, totalCriticalApplicationCount, totalCriticalAtRiskApplicationCount, and newApplications will be handled with future logic implementation
+ // TODO: Replace with actual new applications detection logic (PM-26185)
+ const dummyNewApplications = [
+ "github.com",
+ "google.com",
+ "stackoverflow.com",
+ "gitlab.com",
+ "bitbucket.org",
+ "npmjs.com",
+ "docker.com",
+ "aws.amazon.com",
+ "azure.microsoft.com",
+ "jenkins.io",
+ "terraform.io",
+ "kubernetes.io",
+ "atlassian.net",
+ ];
+
return {
totalMemberCount: uniqueMembers.length,
totalAtRiskMemberCount: uniqueAtRiskMembers.length,
@@ -236,7 +252,7 @@ export class RiskInsightsReportService {
totalCriticalAtRiskMemberCount: 0,
totalCriticalApplicationCount: 0,
totalCriticalAtRiskApplicationCount: 0,
- newApplications: [],
+ newApplications: dummyNewApplications,
};
}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.html
index 17ae964dbed..0eb9b30367c 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.html
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.html
@@ -1,12 +1,29 @@
{{ title }}
+ @if (iconClass) {
+
+ }
{{ cardMetrics }}
{{ metricDescription }}
- @if (showNavigationLink) {
+ @if (buttonClick.observed && buttonText) {
+
+
+
+ }
+ @if (showNavigationLink && !buttonText) {
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.ts
index 2dc7c6a9c79..c8c73cd0e5a 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity-card.component.ts
@@ -1,9 +1,9 @@
import { CommonModule } from "@angular/common";
-import { Component, Input } from "@angular/core";
+import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Router } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
-import { ButtonModule, LinkModule, TypographyModule } from "@bitwarden/components";
+import { ButtonModule, ButtonType, LinkModule, TypographyModule } from "@bitwarden/components";
@Component({
selector: "dirt-activity-card",
@@ -43,9 +43,34 @@ export class ActivityCardComponent {
*/
@Input() showNavigationLink: boolean = false;
+ /**
+ * Icon class to display next to metrics (e.g., "bwi-exclamation-triangle").
+ * If null, no icon is displayed.
+ */
+ @Input() iconClass: string | null = null;
+
+ /**
+ * Button text. If provided, a button will be displayed instead of a navigation link.
+ */
+ @Input() buttonText: string = "";
+
+ /**
+ * Button type (e.g., "primary", "secondary")
+ */
+ @Input() buttonType: ButtonType = "primary";
+
+ /**
+ * Event emitted when button is clicked
+ */
+ @Output() buttonClick = new EventEmitter();
+
constructor(private router: Router) {}
navigateToLink = async (navigationLink: string) => {
await this.router.navigateByUrl(navigationLink);
};
+
+ onButtonClick = () => {
+ this.buttonClick.emit();
+ };
}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.html
index 390102aced9..844b2f92bb3 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.html
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.html
@@ -41,5 +41,18 @@
>
+
+
+
+
+
}
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.ts
index 83ce743d6d0..31ab0351162 100644
--- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.ts
+++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-activity.component.ts
@@ -11,7 +11,9 @@ import { OrganizationService } from "@bitwarden/common/admin-console/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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { getById } from "@bitwarden/common/platform/misc";
+import { ToastService } from "@bitwarden/components";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { ActivityCardComponent } from "./activity-card.component";
@@ -34,6 +36,7 @@ export class AllActivityComponent implements OnInit {
totalCriticalAppsAtRiskMemberCount = 0;
totalCriticalAppsCount = 0;
totalCriticalAppsAtRiskCount = 0;
+ newApplicationsCount = 0;
passwordChangeMetricHasProgressBar = false;
destroyRef = inject(DestroyRef);
@@ -54,6 +57,7 @@ export class AllActivityComponent implements OnInit {
this.totalCriticalAppsAtRiskMemberCount = summary.totalCriticalAtRiskMemberCount;
this.totalCriticalAppsCount = summary.totalCriticalApplicationCount;
this.totalCriticalAppsAtRiskCount = summary.totalCriticalAtRiskApplicationCount;
+ this.newApplicationsCount = summary.newApplications.length;
});
this.allActivitiesService.passwordChangeProgressMetricHasProgressBar$
@@ -70,6 +74,8 @@ export class AllActivityComponent implements OnInit {
protected organizationService: OrganizationService,
protected dataService: RiskInsightsDataService,
protected allActivitiesService: AllActivitiesService,
+ private toastService: ToastService,
+ private i18nService: I18nService,
) {}
get RiskInsightsTabType() {
@@ -80,4 +86,17 @@ export class AllActivityComponent implements OnInit {
const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId");
return `/organizations/${organizationId}/access-intelligence/risk-insights?tabIndex=${tabIndex}`;
}
+
+ /**
+ * Handles the review new applications button click.
+ * Shows a toast notification as a placeholder until the dialog is implemented.
+ * TODO: Implement dialog for reviewing new applications (follow-up task)
+ */
+ onReviewNewApplications = () => {
+ this.toastService.showToast({
+ variant: "info",
+ title: this.i18nService.t("applicationsNeedingReview"),
+ message: this.i18nService.t("newApplicationsWithCount", this.newApplicationsCount.toString()),
+ });
+ };
}