1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 00:33:44 +00:00

[PM-28451] Fix icons in application review table (#17512)

* Fix icons in application review table

* Add default icon if none is found in review applications table. Move function to computed signal

* Rename function

* Remove redundant if statement
This commit is contained in:
Leslie Tilton
2025-11-21 09:12:48 -06:00
committed by GitHub
parent e9f67f4fd6
commit 994077f4de
6 changed files with 43 additions and 9 deletions

View File

@@ -28,7 +28,7 @@
</div> </div>
<dirt-review-applications-view <dirt-review-applications-view
[applications]="getApplications()" [applications]="applicationsWithIcons()"
[selectedApplications]="selectedApplications()" [selectedApplications]="selectedApplications()"
(onToggleSelection)="toggleSelection($event)" (onToggleSelection)="toggleSelection($event)"
(onToggleAll)="toggleAll()" (onToggleAll)="toggleAll()"

View File

@@ -33,6 +33,7 @@ import {
} from "@bitwarden/components"; } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common"; import { I18nPipe } from "@bitwarden/ui-common";
import { CipherIcon } from "../../shared/app-table-row-scrollable.component";
import { AccessIntelligenceSecurityTasksService } from "../../shared/security-tasks.service"; import { AccessIntelligenceSecurityTasksService } from "../../shared/security-tasks.service";
import { AssignTasksViewComponent } from "./assign-tasks-view.component"; import { AssignTasksViewComponent } from "./assign-tasks-view.component";
@@ -99,6 +100,16 @@ export class NewApplicationsDialogComponent {
// Applications selected to save as critical applications // Applications selected to save as critical applications
protected readonly selectedApplications = signal<Set<string>>(new Set()); protected readonly selectedApplications = signal<Set<string>>(new Set());
protected readonly applicationIcons = signal<Map<string, CipherIcon>>(
new Map<string, CipherIcon>(),
);
protected readonly applicationsWithIcons = computed(() => {
return this.dialogParams.newApplications.map((app) => {
const iconCipher = this.applicationIcons().get(app.applicationName);
return { ...app, iconCipher } as ApplicationHealthReportDetail & { iconCipher: CipherIcon };
});
});
// Used to determine if there are unassigned at-risk cipher IDs // Used to determine if there are unassigned at-risk cipher IDs
private readonly _tasks!: Signal<SecurityTask[]>; private readonly _tasks!: Signal<SecurityTask[]>;
@@ -150,6 +161,7 @@ export class NewApplicationsDialogComponent {
private securityTasksService: AccessIntelligenceSecurityTasksService, private securityTasksService: AccessIntelligenceSecurityTasksService,
private toastService: ToastService, private toastService: ToastService,
) { ) {
this.setApplicationIconMap(this.dialogParams.newApplications);
// Setup the _tasks signal by manually passing in the injector // Setup the _tasks signal by manually passing in the injector
this._tasks = toSignal(this.securityTasksService.tasks$, { this._tasks = toSignal(this.securityTasksService.tasks$, {
initialValue: [], initialValue: [],
@@ -172,10 +184,6 @@ export class NewApplicationsDialogComponent {
); );
} }
getApplications() {
return this.dialogParams.newApplications;
}
/** /**
* Returns true if the organization has no existing critical applications. * Returns true if the organization has no existing critical applications.
* Used to conditionally show different titles and descriptions. * Used to conditionally show different titles and descriptions.
@@ -184,6 +192,22 @@ export class NewApplicationsDialogComponent {
return !this.dialogParams.hasExistingCriticalApplications; return !this.dialogParams.hasExistingCriticalApplications;
} }
/**
* Maps applications to a corresponding iconCipher
*
* @param applications
*/
setApplicationIconMap(applications: ApplicationHealthReportDetail[]) {
// Map the report data to include the iconCipher for each application
const iconCiphers = new Map<string, CipherIcon>();
applications.forEach((app) => {
const iconCipher =
app.cipherIds.length > 0 ? this.dataService.getCipherIcon(app.cipherIds[0]) : undefined;
iconCiphers.set(app.applicationName, iconCipher);
});
this.applicationIcons.set(iconCiphers);
}
/** /**
* Toggles the selection state of an application. * Toggles the selection state of an application.
* @param applicationName The application to toggle * @param applicationName The application to toggle

View File

@@ -62,7 +62,11 @@
</td> </td>
<td bitTypography="body1" class="tw-py-3 tw-px-2"> <td bitTypography="body1" class="tw-py-3 tw-px-2">
<div class="tw-flex tw-items-center tw-gap-2"> <div class="tw-flex tw-items-center tw-gap-2">
<i class="bwi bwi-globe tw-text-muted" aria-hidden="true"></i> @if (app.iconCipher) {
<app-vault-icon [cipher]="app.iconCipher"></app-vault-icon>
} @else {
<i class="bwi bwi-globe tw-text-muted" aria-hidden="true"></i>
}
<span>{{ app.applicationName }}</span> <span>{{ app.applicationName }}</span>
</div> </div>
</td> </td>

View File

@@ -5,6 +5,9 @@ import { FormsModule } from "@angular/forms";
import { ApplicationHealthReportDetail } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { ApplicationHealthReportDetail } from "@bitwarden/bit-common/dirt/reports/risk-insights";
import { ButtonModule, DialogModule, SearchModule, TypographyModule } from "@bitwarden/components"; import { ButtonModule, DialogModule, SearchModule, TypographyModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common"; import { I18nPipe } from "@bitwarden/ui-common";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { CipherIcon } from "../../shared/app-table-row-scrollable.component";
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@@ -18,10 +21,12 @@ import { I18nPipe } from "@bitwarden/ui-common";
SearchModule, SearchModule,
TypographyModule, TypographyModule,
I18nPipe, I18nPipe,
SharedModule,
], ],
}) })
export class ReviewApplicationsViewComponent { export class ReviewApplicationsViewComponent {
readonly applications = input.required<ApplicationHealthReportDetail[]>(); readonly applications =
input.required<Array<ApplicationHealthReportDetail & { iconCipher: CipherIcon }>>();
readonly selectedApplications = input.required<Set<string>>(); readonly selectedApplications = input.required<Set<string>>();
protected readonly searchText = signal<string>(""); protected readonly searchText = signal<string>("");

View File

@@ -45,7 +45,6 @@
tabindex="0" tabindex="0"
[attr.aria-label]="'viewItem' | i18n" [attr.aria-label]="'viewItem' | i18n"
> >
<!-- Passing the first cipher of the application for app-vault-icon cipher input requirement -->
<app-vault-icon *ngIf="row.iconCipher" [cipher]="row.iconCipher"></app-vault-icon> <app-vault-icon *ngIf="row.iconCipher" [cipher]="row.iconCipher"></app-vault-icon>
</td> </td>
<td <td

View File

@@ -8,8 +8,10 @@ import { MenuModule, TableDataSource, TableModule } from "@bitwarden/components"
import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
export type CipherIcon = CipherViewLike | undefined;
export type ApplicationTableDataSource = ApplicationHealthReportDetailEnriched & { export type ApplicationTableDataSource = ApplicationHealthReportDetailEnriched & {
iconCipher: CipherViewLike | undefined; iconCipher: CipherIcon;
}; };
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush