mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[PM-26185] new app metric card (#16658)
* new messages.json keys * button changes for dirt activity card * dummy data * newApplicationsCount and temp toast * Added third dirt-activity-card component after the existing two cards * added newApplications to setAllAppsReportSummary * make button smaller * cleanup/nice-to-haves * remove comment * simplify activity card icon logic to use nullable iconClass * use buttonText presence to determine button display in activity card * apps needing review card - I think accidentally deleted when resolving merge conflicts * buttonClick.observed && buttonText
This commit is contained in:
@@ -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"
|
||||
},
|
||||
|
||||
@@ -68,6 +68,7 @@ export class AllActivitiesService {
|
||||
totalAtRiskMemberCount: summary.totalAtRiskMemberCount,
|
||||
totalApplicationCount: summary.totalApplicationCount,
|
||||
totalAtRiskApplicationCount: summary.totalAtRiskApplicationCount,
|
||||
newApplications: summary.newApplications,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
<div class="tw-flex-col">
|
||||
<span bitTypography="h6" class="tw-flex tw-text-main">{{ title }}</span>
|
||||
<div class="tw-flex tw-items-baseline tw-gap-2">
|
||||
@if (iconClass) {
|
||||
<i class="bwi {{ iconClass }} tw-text-muted" aria-hidden="true"></i>
|
||||
}
|
||||
<span bitTypography="h3">{{ cardMetrics }}</span>
|
||||
</div>
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<span bitTypography="body2">{{ metricDescription }}</span>
|
||||
</div>
|
||||
@if (showNavigationLink) {
|
||||
@if (buttonClick.observed && buttonText) {
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
size="small"
|
||||
[buttonType]="buttonType"
|
||||
[attr.aria-label]="buttonText"
|
||||
(click)="onButtonClick()"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
@if (showNavigationLink && !buttonText) {
|
||||
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
|
||||
<p bitTypography="body1">
|
||||
<a bitLink (click)="navigateToLink(navigationLink)" rel="noreferrer">
|
||||
|
||||
@@ -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<void>();
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
||||
navigateToLink = async (navigationLink: string) => {
|
||||
await this.router.navigateByUrl(navigationLink);
|
||||
};
|
||||
|
||||
onButtonClick = () => {
|
||||
this.buttonClick.emit();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,5 +41,18 @@
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
|
||||
<li class="tw-col-span-1">
|
||||
<dirt-activity-card
|
||||
[title]="'applicationsNeedingReview' | i18n"
|
||||
[cardMetrics]="'newApplicationsWithCount' | i18n: newApplicationsCount"
|
||||
[metricDescription]="'newApplicationsDescription' | i18n"
|
||||
[iconClass]="'bwi-exclamation-triangle'"
|
||||
[buttonText]="'reviewNow' | i18n"
|
||||
[buttonType]="'primary'"
|
||||
(buttonClick)="onReviewNewApplications()"
|
||||
>
|
||||
</dirt-activity-card>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user