1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-26352] drawers for activity cards (#16895)

* new drawer functions for crit apps

* logic for triggering the drawer functions in components

* cleanup unused logic and rename "navigation" to "action"
- ... since the click is now triggering the drawer instead of navigating to another tab/page

* null check for reportData in drawer methods

* use criticalReportResults$ to avoid duplicating logic

* use criticalReportResults$ to avoid dupe logic

* remove unused code
This commit is contained in:
Alex
2025-10-28 11:44:42 -04:00
committed by GitHub
parent bf66b5ac19
commit 2058c772ac
5 changed files with 97 additions and 38 deletions

View File

@@ -175,6 +175,65 @@ export class RiskInsightsDataService {
}
};
setDrawerForCriticalAtRiskMembers = async (invokerId: string = ""): Promise<void> => {
const { open, activeDrawerType, invokerId: currentInvokerId } = this.drawerDetailsSubject.value;
const shouldClose =
open && activeDrawerType === DrawerType.OrgAtRiskMembers && currentInvokerId === invokerId;
if (shouldClose) {
this.closeDrawer();
} else {
const reportResults = await firstValueFrom(this.criticalReportResults$);
if (!reportResults?.reportData) {
return;
}
// Generate at-risk member list from critical applications
const atRiskMemberDetails = getAtRiskMemberList(reportResults.reportData);
this.drawerDetailsSubject.next({
open: true,
invokerId,
activeDrawerType: DrawerType.OrgAtRiskMembers,
atRiskMemberDetails,
appAtRiskMembers: null,
atRiskAppDetails: null,
});
}
};
setDrawerForCriticalAtRiskApps = async (invokerId: string = ""): Promise<void> => {
const { open, activeDrawerType, invokerId: currentInvokerId } = this.drawerDetailsSubject.value;
const shouldClose =
open && activeDrawerType === DrawerType.OrgAtRiskApps && currentInvokerId === invokerId;
if (shouldClose) {
this.closeDrawer();
} else {
const reportResults = await firstValueFrom(this.criticalReportResults$);
if (!reportResults?.reportData) {
return;
}
// Filter critical applications for those with at-risk passwords
const criticalAtRiskApps = reportResults.reportData
.filter((app) => app.atRiskPasswordCount > 0)
.map((app) => ({
applicationName: app.applicationName,
atRiskPasswordCount: app.atRiskPasswordCount,
}));
this.drawerDetailsSubject.next({
open: true,
invokerId,
activeDrawerType: DrawerType.OrgAtRiskApps,
atRiskMemberDetails: [],
appAtRiskMembers: null,
atRiskAppDetails: criticalAtRiskApps,
});
}
};
// ------------------------------ Critical application methods --------------
saveCriticalApplications(selectedUrls: string[]) {
return this.orchestrator.saveCriticalApplications$(selectedUrls);

View File

@@ -23,11 +23,11 @@
</button>
</div>
}
@if (showNavigationLink && !buttonText) {
@if (showActionLink && !buttonText) {
<div class="tw-flex tw-items-baseline tw-mt-4 tw-gap-2">
<p bitTypography="body1">
<a bitLink (click)="navigateToLink(navigationLink)" rel="noreferrer">
{{ navigationText }}
<a bitLink href="#" (click)="onActionClick(); $event.preventDefault()" rel="noreferrer">
{{ actionText }}
</a>
</p>
</div>

View File

@@ -37,25 +37,14 @@ export class ActivityCardComponent {
@Input() metricDescription: string = "";
/**
* The link to navigate to for more information
* The text to display for the action link
*/
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() navigationLink: string = "";
@Input() actionText: string = "";
/**
* The text to display for the navigation link
* Show action link
*/
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() navigationText: string = "";
/**
* Show Navigation link
*/
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() showNavigationLink: boolean = false;
@Input() showActionLink: boolean = false;
/**
* Icon class to display next to metrics (e.g., "bwi-exclamation-triangle").
@@ -86,13 +75,18 @@ export class ActivityCardComponent {
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
@Output() buttonClick = new EventEmitter<void>();
constructor(private router: Router) {}
/**
* Event emitted when action link is clicked
*/
@Output() actionClick = new EventEmitter<void>();
navigateToLink = async (navigationLink: string) => {
await this.router.navigateByUrl(navigationLink);
};
constructor(private router: Router) {}
onButtonClick = () => {
this.buttonClick.emit();
};
onActionClick = () => {
this.actionClick.emit();
};
}

View File

@@ -13,9 +13,9 @@
[title]="'atRiskMembers' | i18n"
[cardMetrics]="'membersAtRiskCount' | i18n: totalCriticalAppsAtRiskMemberCount"
[metricDescription]="'membersWithAccessToAtRiskItemsForCriticalApps' | i18n"
navigationText="{{ 'viewAtRiskMembers' | i18n }}"
navigationLink="{{ getLinkForRiskInsightsTab(RiskInsightsTabType.AllApps) }}"
[showNavigationLink]="totalCriticalAppsAtRiskMemberCount > 0"
actionText="{{ 'viewAtRiskMembers' | i18n }}"
[showActionLink]="totalCriticalAppsAtRiskMemberCount > 0"
(actionClick)="onViewAtRiskMembers()"
>
</dirt-activity-card>
</li>
@@ -35,9 +35,9 @@
: ('criticalApplicationsAreAtRisk'
| i18n: totalCriticalAppsAtRiskCount : totalCriticalAppsCount)
"
navigationText="{{ 'viewAtRiskApplications' | i18n }}"
navigationLink="{{ getLinkForRiskInsightsTab(RiskInsightsTabType.CriticalApps) }}"
[showNavigationLink]="totalCriticalAppsAtRiskCount > 0"
actionText="{{ 'viewAtRiskApplications' | i18n }}"
[showActionLink]="totalCriticalAppsAtRiskCount > 0"
(actionClick)="onViewAtRiskApplications()"
>
</dirt-activity-card>
</li>

View File

@@ -15,7 +15,6 @@ import { getById } from "@bitwarden/common/platform/misc";
import { DialogService } from "@bitwarden/components";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { RiskInsightsTabType } from "../models/risk-insights.models";
import { ApplicationsLoadingComponent } from "../shared/risk-insights-loading.component";
import { ActivityCardComponent } from "./activity-card.component";
@@ -82,15 +81,6 @@ export class AllActivityComponent implements OnInit {
}
}
get RiskInsightsTabType() {
return RiskInsightsTabType;
}
getLinkForRiskInsightsTab(tabIndex: RiskInsightsTabType): string {
const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId");
return `/organizations/${organizationId}/access-intelligence/risk-insights?tabIndex=${tabIndex}`;
}
/**
* Handles the review new applications button click.
* Opens a dialog showing the list of new applications that can be marked as critical.
@@ -102,4 +92,20 @@ export class AllActivityComponent implements OnInit {
await firstValueFrom(dialogRef.closed);
};
/**
* Handles the "View at-risk members" link click.
* Opens the at-risk members drawer for critical applications only.
*/
onViewAtRiskMembers = async () => {
await this.dataService.setDrawerForCriticalAtRiskMembers("activityTabAtRiskMembers");
};
/**
* Handles the "View at-risk applications" link click.
* Opens the at-risk applications drawer for critical applications only.
*/
onViewAtRiskApplications = async () => {
await this.dataService.setDrawerForCriticalAtRiskApps("activityTabAtRiskApplications");
};
}