1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-02 09:43:29 +00:00

changes suggested by claude

This commit is contained in:
cd-bitwarden
2025-11-14 17:56:54 -05:00
parent 8cd39690ed
commit e2a7a936a3
4 changed files with 182 additions and 126 deletions

View File

@@ -65,17 +65,19 @@
</sm-section>
<sm-section>
<h2 slot="summary" class="tw-mb-0" bitTypography="h2" noMargin>{{ "secrets" | i18n }}</h2>
<sm-secrets-list
baseRoute="secrets"
(deleteSecretsEvent)="openDeleteSecret($event)"
(newSecretEvent)="openNewSecretDialog()"
(editSecretEvent)="openEditSecret($event)"
(viewSecretEvent)="openViewSecret($event)"
(copySecretNameEvent)="copySecretName($event)"
(copySecretValueEvent)="copySecretValue($event)"
(copySecretUuidEvent)="copySecretUuid($event)"
[secrets]="view.latestSecrets"
></sm-secrets-list>
<div class="tw-overflow-auto">
<sm-secrets-list
baseRoute="secrets"
(deleteSecretsEvent)="openDeleteSecret($event)"
(newSecretEvent)="openNewSecretDialog()"
(editSecretEvent)="openEditSecret($event)"
(viewSecretEvent)="openViewSecret($event)"
(copySecretNameEvent)="copySecretName($event)"
(copySecretValueEvent)="copySecretValue($event)"
(copySecretUuidEvent)="copySecretUuid($event)"
[secrets]="view.latestSecrets"
></sm-secrets-list>
</div>
<div *ngIf="view.allSecrets.length > 0" class="tw-ml-auto tw-mt-4 tw-max-w-max">
{{ "showingPortionOfTotal" | i18n: view.latestSecrets.length : view.counts.secrets }}
<a bitLink routerLink="secrets" class="tw-ml-2">{{ "viewAll" | i18n }}</a>

View File

@@ -23,126 +23,160 @@
</bit-no-items>
</ng-container>
<bit-table-scroll *ngIf="secrets?.length >= 1" [dataSource]="dataSource" [rowSize]="66">
<ng-container header>
<th bitCell class="tw-w-0">
<label class="!tw-mb-0 tw-flex tw-w-fit tw-gap-2 !tw-font-bold !tw-text-muted">
<input
type="checkbox"
(change)="$event ? toggleAll() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
/>
{{ "all" | i18n }}
</label>
</th>
<th bitCell bitSortable="name" default>{{ "name" | i18n }}</th>
<th bitCell bitSortable="projects" [fn]="sortProjects">{{ "project" | i18n }}</th>
<th bitCell bitSortable="revisionDate">{{ "lastEdited" | i18n }}</th>
<th bitCell class="tw-w-0">
<button
type="button"
bitIconButton="bwi-ellipsis-v"
buttonType="main"
[label]="'options' | i18n"
[bitMenuTriggerFor]="tableMenu"
></button>
</th>
</ng-container>
<ng-template bitRowDef let-row>
<td bitCell>
<input
type="checkbox"
(change)="$event ? selection.toggle(row.id) : null"
[checked]="selection.isSelected(row.id)"
/>
</td>
<td bitCell>
<div class="tw-flex tw-items-center tw-gap-4 tw-break-all">
<i class="bwi bwi-key tw-text-muted" aria-hidden="true"></i>
<div>
<div *ngIf="!trash">
<button type="button" bitLink (click)="editSecret(row)">
{{ row.name }}
</button>
</div>
<div *ngIf="trash">{{ row.name }}</div>
<div class="tw-text-sm tw-text-muted">
{{ row.id }}
<cdk-virtual-scroll-viewport
*ngIf="secrets?.length >= 1"
[itemSize]="rowHeight"
bitScrollLayout
[style.height]="viewportHeight"
class="tw-block"
>
<bit-table [dataSource]="dataSource">
<ng-container header>
<tr>
<th bitCell class="tw-w-0">
<label class="!tw-mb-0 tw-flex tw-w-fit tw-gap-2 !tw-font-bold !tw-text-muted">
<input
type="checkbox"
(change)="$event ? toggleAll() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
/>
{{ "all" | i18n }}
</label>
</th>
<th bitCell bitSortable="name" default>{{ "name" | i18n }}</th>
<th bitCell bitSortable="projects" [fn]="sortProjects">{{ "project" | i18n }}</th>
<th bitCell bitSortable="revisionDate">{{ "lastEdited" | i18n }}</th>
<th bitCell class="tw-w-0">
<button
type="button"
bitIconButton="bwi-ellipsis-v"
buttonType="main"
[label]="'options' | i18n"
[bitMenuTriggerFor]="tableMenu"
></button>
</th>
</tr>
</ng-container>
<ng-template body let-rows$>
<ng-container *cdkVirtualFor="let row of rows$; templateCacheSize: 0">
<tr bitRow [ngClass]="rowHeightClass">
<td bitCell>
<input
type="checkbox"
(change)="$event ? selection.toggle(row.id) : null"
[checked]="selection.isSelected(row.id)"
/>
</td>
<td bitCell>
<div class="tw-flex tw-items-center tw-gap-4 tw-break-all">
<i class="bwi bwi-key tw-text-muted" aria-hidden="true"></i>
<div>
<div *ngIf="!trash">
<button type="button" bitLink (click)="editSecret(row)">
{{ row.name }}
</button>
</div>
<div *ngIf="trash">{{ row.name }}</div>
<div class="tw-text-sm tw-text-muted">
{{ row.id }}
<button
type="button"
bitIconButton="bwi-clone"
buttonType="main"
size="small"
[label]="'copyUuid' | i18n"
(click)="copySecretUuidEvent.emit(row.id)"
></button>
</div>
</div>
</div>
</td>
<td bitCell>
<span
*ngFor="let project of row.projects"
bitBadge
variant="secondary"
class="tw-ml-1"
[title]="project.name"
maxWidthClass="tw-max-w-60"
>
{{ project.name }}
</span>
<span *ngIf="row.projects.length === 0" bitBadge variant="warning" class="tw-ml-1"
><i class="bwi bwi-fw bwi-exclamation-triangle tw-mr-1" aria-hidden="true"></i
>{{ "unassigned" | i18n }}</span
>
</td>
<td bitCell class="tw-whitespace-nowrap">{{ row.revisionDate | date: "medium" }}</td>
<td bitCell>
<button
type="button"
bitIconButton="bwi-clone"
bitIconButton="bwi-ellipsis-v"
buttonType="main"
size="small"
[label]="'copyUuid' | i18n"
(click)="copySecretUuidEvent.emit(row.id)"
[label]="'options' | i18n"
[bitMenuTriggerFor]="secretMenu"
></button>
</div>
</div>
</div>
</td>
<td bitCell>
<span
*ngFor="let project of row.projects"
bitBadge
variant="secondary"
class="tw-ml-1"
[title]="project.name"
maxWidthClass="tw-max-w-60"
>
{{ project.name }}
</span>
<span *ngIf="row.projects.length === 0" bitBadge variant="warning" class="tw-ml-1"
><i class="bwi bwi-fw bwi-exclamation-triangle tw-mr-1" aria-hidden="true"></i
>{{ "unassigned" | i18n }}</span
>
</td>
<td bitCell class="tw-whitespace-nowrap">{{ row.revisionDate | date: "medium" }}</td>
<td bitCell>
<button
type="button"
bitIconButton="bwi-ellipsis-v"
buttonType="main"
[label]="'options' | i18n"
[bitMenuTriggerFor]="secretMenu"
></button>
</td>
</td>
<bit-menu #secretMenu>
<button type="button" bitMenuItem (click)="editSecret(row)" *ngIf="row.write && !trash">
<i class="bwi bwi-fw bwi-pencil" aria-hidden="true"></i>
{{ "editSecret" | i18n }}
</button>
<button type="button" bitMenuItem (click)="copySecretNameEvent.emit(row.name)" *ngIf="!trash">
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
{{ "copySecretName" | i18n }}
</button>
<button type="button" bitMenuItem (click)="copySecretValueEvent.emit(row.id)" *ngIf="!trash">
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
{{ "copySecretValue" | i18n }}
</button>
<button type="button" bitMenuItem (click)="restoreSecretsEvent.emit([row.id])" *ngIf="trash">
<i class="bwi bwi-fw bwi-refresh" aria-hidden="true"></i>
{{ "restoreSecret" | i18n }}
</button>
<button
type="button"
bitMenuItem
*ngIf="viewEventsAllowed$ | async as allowed"
(click)="openEventsDialog(row)"
>
<i class="bwi bwi-fw bwi-billing" aria-hidden="true"></i>
<span> {{ "viewEvents" | i18n }} </span>
</button>
<button type="button" bitMenuItem (click)="deleteSecretsEvent.emit([row])" *ngIf="row.write">
<i class="bwi bwi-fw bwi-trash tw-text-danger" aria-hidden="true"></i>
<span class="tw-text-danger">{{
(trash ? "permanentlyDelete" : "deleteSecret") | i18n
}}</span>
</button>
</bit-menu>
</ng-template>
</bit-table-scroll>
<bit-menu #secretMenu>
<button type="button" bitMenuItem (click)="editSecret(row)" *ngIf="row.write && !trash">
<i class="bwi bwi-fw bwi-pencil" aria-hidden="true"></i>
{{ "editSecret" | i18n }}
</button>
<button
type="button"
bitMenuItem
(click)="copySecretNameEvent.emit(row.name)"
*ngIf="!trash"
>
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
{{ "copySecretName" | i18n }}
</button>
<button
type="button"
bitMenuItem
(click)="copySecretValueEvent.emit(row.id)"
*ngIf="!trash"
>
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i>
{{ "copySecretValue" | i18n }}
</button>
<button
type="button"
bitMenuItem
(click)="restoreSecretsEvent.emit([row.id])"
*ngIf="trash"
>
<i class="bwi bwi-fw bwi-refresh" aria-hidden="true"></i>
{{ "restoreSecret" | i18n }}
</button>
<button
type="button"
bitMenuItem
*ngIf="viewEventsAllowed$ | async as allowed"
(click)="openEventsDialog(row)"
>
<i class="bwi bwi-fw bwi-billing" aria-hidden="true"></i>
<span> {{ "viewEvents" | i18n }} </span>
</button>
<button
type="button"
bitMenuItem
(click)="deleteSecretsEvent.emit([row])"
*ngIf="row.write"
>
<i class="bwi bwi-fw bwi-trash tw-text-danger" aria-hidden="true"></i>
<span class="tw-text-danger">{{
(trash ? "permanentlyDelete" : "deleteSecret") | i18n
}}</span>
</button>
</bit-menu>
</tr>
</ng-container>
</ng-template>
</bit-table>
</cdk-virtual-scroll-viewport>
<bit-menu #tableMenu>
<button type="button" bitMenuItem (click)="bulkRestoreSecrets()" *ngIf="trash">

View File

@@ -89,6 +89,22 @@ export class SecretsListComponent implements OnDestroy, OnInit {
selection = new SelectionModel<string>(true, []);
protected viewEventsAllowed$: Observable<boolean>;
protected isAdmin$: Observable<boolean>;
protected rowHeight = 66;
protected rowHeightClass = `tw-h-[66px]`;
protected get viewportHeight(): string {
if (!this.secrets || this.secrets.length === 0) {
return "0px";
}
// Calculate height: (number of items * row height) + header height + buffer
const itemsHeight = this.secrets.length * this.rowHeight;
const headerHeight = 65; // Header with borders
const buffer = 8; // Small buffer for borders and spacing
const totalHeight = itemsHeight + headerHeight + buffer;
// Use min() to set exact height, capping at 60vh or 500px for responsive behavior
// This ensures the viewport is exactly the size of the content, unless it exceeds the max
return `min(${totalHeight}px, 60vh, 500px)`;
}
constructor(
private i18nService: I18nService,

View File

@@ -1,3 +1,4 @@
import { ScrollingModule } from "@angular/cdk/scrolling";
import { NgModule } from "@angular/core";
import {
@@ -7,6 +8,7 @@ import {
SelectModule,
NoItemsModule,
FormFieldModule,
ScrollLayoutDirective,
} from "@bitwarden/components";
import { CoreOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/core";
import { DynamicAvatarComponent } from "@bitwarden/web-vault/app/components/dynamic-avatar.component";
@@ -25,6 +27,7 @@ import { SecretsListComponent } from "./secrets-list.component";
@NgModule({
imports: [
SharedModule,
ScrollingModule,
ProductSwitcherModule,
MultiSelectModule,
CoreOrganizationModule,
@@ -35,6 +38,7 @@ import { SecretsListComponent } from "./secrets-list.component";
HeaderModule,
CardComponent,
FormFieldModule,
ScrollLayoutDirective,
],
exports: [
AccessPolicySelectorComponent,