1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 05:13:29 +00:00

add CSV download buttons to at risk members and applications drawers (#17172)

* add CSV download buttons to at risk members and applications drawers

---------

Co-authored-by: Alex <55413326+AlexRubik@users.noreply.github.com>
This commit is contained in:
Maximilian Power
2025-11-06 16:32:51 +01:00
committed by GitHub
parent 7cbfcd23a8
commit 87dceff0c8
2 changed files with 87 additions and 1 deletions

View File

@@ -118,6 +118,15 @@
) | i18n
}}</span>
<ng-container *ngIf="drawerDetails.atRiskMemberDetails.length > 0">
<button
bitLink
type="button"
class="tw-my-4 tw-font-bold tw-block"
(click)="downloadAtRiskMembers()"
>
<i class="bwi bwi-download tw-mr-1"></i>
{{ "downloadCSV" | i18n }}
</button>
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
<div bitTypography="body2" class="tw-text-sm tw-font-bold">
{{ "email" | i18n }}
@@ -173,6 +182,15 @@
) | i18n
}}</span>
<ng-container *ngIf="drawerDetails.atRiskAppDetails.length > 0">
<button
bitLink
type="button"
class="tw-my-4 tw-font-bold tw-block"
(click)="downloadAtRiskApplications()"
>
<i class="bwi bwi-download tw-mr-1"></i>
{{ "downloadCSV" | i18n }}
</button>
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
<div bitTypography="body2" class="tw-text-sm tw-font-bold">
{{ "application" | i18n }}

View File

@@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common";
import { Component, DestroyRef, OnDestroy, OnInit, inject } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { combineLatest, EMPTY } from "rxjs";
import { combineLatest, EMPTY, firstValueFrom } from "rxjs";
import { map, tap } from "rxjs/operators";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -13,7 +13,9 @@ import {
} from "@bitwarden/bit-common/dirt/reports/risk-insights";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { OrganizationId } from "@bitwarden/common/types/guid";
import {
AsyncActionsModule,
@@ -23,6 +25,8 @@ import {
DrawerHeaderComponent,
TabsModule,
} from "@bitwarden/components";
import { ExportHelper } from "@bitwarden/vault-export-core";
import { exportToCSV } from "@bitwarden/web-vault/app/dirt/reports/report-utils";
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
import { AllActivityComponent } from "./activity/all-activity.component";
@@ -88,6 +92,8 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
private configService: ConfigService,
protected dataService: RiskInsightsDataService,
protected i18nService: I18nService,
private fileDownloadService: FileDownloadService,
private logService: LogService,
) {
this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(({ tabIndex }) => {
this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps;
@@ -207,4 +213,66 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
"import",
]);
};
/**
* downloads at risk members as CSV
*/
downloadAtRiskMembers = async () => {
try {
const drawerDetails = await firstValueFrom(this.dataService.drawerDetails$);
// Validate drawer is open and showing the correct drawer type
if (
!drawerDetails.open ||
drawerDetails.activeDrawerType !== DrawerType.OrgAtRiskMembers ||
!drawerDetails.atRiskMemberDetails ||
drawerDetails.atRiskMemberDetails.length === 0
) {
return;
}
this.fileDownloadService.download({
fileName: ExportHelper.getFileName("at-risk-members"),
blobData: exportToCSV(drawerDetails.atRiskMemberDetails, {
email: this.i18nService.t("email"),
atRiskPasswordCount: this.i18nService.t("atRiskPasswords"),
}),
blobOptions: { type: "text/plain" },
});
} catch (error) {
// Log error for debugging
this.logService.error("Failed to download at-risk members", error);
}
};
/**
* downloads at risk applications as CSV
*/
downloadAtRiskApplications = async () => {
try {
const drawerDetails = await firstValueFrom(this.dataService.drawerDetails$);
// Validate drawer is open and showing the correct drawer type
if (
!drawerDetails.open ||
drawerDetails.activeDrawerType !== DrawerType.OrgAtRiskApps ||
!drawerDetails.atRiskAppDetails ||
drawerDetails.atRiskAppDetails.length === 0
) {
return;
}
this.fileDownloadService.download({
fileName: ExportHelper.getFileName("at-risk-applications"),
blobData: exportToCSV(drawerDetails.atRiskAppDetails, {
applicationName: this.i18nService.t("application"),
atRiskPasswordCount: this.i18nService.t("atRiskPasswords"),
}),
blobOptions: { type: "text/plain" },
});
} catch (error) {
// Log error for debugging
this.logService.error("Failed to download at-risk applications", error);
}
};
}