mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[PM-14554] Condition the display of members bulk actions based on selection (#11922)
* Refactor organization user API service to support bulk deletion of users
* Add copy for bulk user delete dialog
* Add bulk user delete dialog component
* Add bulk user delete functionality to members component
* Make bulk user actions in the members component conditional based on the selected members
* Refactor members component to only display bulk user deletion option if the Account Deprovisioning flag is enabled
* Refactor bulk action visibility in members component based on Account Deprovisioning flag
* Patch build process
* Refactor bulk action visibility conditions in members component
* Simplify status checks in members component by using loose equality and removing redundant boolean comparisons
* Refactor bulk action visibility logic in members component to use computed properties for improved readability and maintainability
* Refactor bulk action visibility logic in members component to eliminate redundant methods and improve readability
* Revert "Patch build process"
This reverts commit 917c969f00.
* Refactor bulk action visibility logic in members component
---------
Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
This commit is contained in:
@@ -123,25 +123,40 @@
|
|||||||
{{ "confirmSelected" | i18n }}
|
{{ "confirmSelected" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitMenuItem (click)="bulkRestore()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkRestore()"
|
||||||
|
*ngIf="showBulkRestoreUsers"
|
||||||
|
>
|
||||||
<i class="bwi bwi-fw bwi-plus-circle" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-plus-circle" aria-hidden="true"></i>
|
||||||
{{ "restoreAccess" | i18n }}
|
{{ "restoreAccess" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitMenuItem (click)="bulkRevoke()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkRevoke()"
|
||||||
|
*ngIf="showBulkRevokeUsers"
|
||||||
|
>
|
||||||
<i class="bwi bwi-fw bwi-minus-circle" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-minus-circle" aria-hidden="true"></i>
|
||||||
{{ "revokeAccess" | i18n }}
|
{{ "revokeAccess" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitMenuItem (click)="bulkRemove()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkRemove()"
|
||||||
|
*ngIf="showBulkRemoveUsers"
|
||||||
|
>
|
||||||
<span class="tw-text-danger">
|
<span class="tw-text-danger">
|
||||||
<i aria-hidden="true" class="bwi bwi-close"></i>
|
<i aria-hidden="true" class="bwi bwi-close"></i>
|
||||||
{{ "remove" | i18n }}
|
{{ "remove" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="accountDeprovisioningEnabled$ | async"
|
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="bulkDelete()"
|
(click)="bulkDelete()"
|
||||||
|
*ngIf="showBulkDeleteUsers"
|
||||||
>
|
>
|
||||||
<span class="tw-text-danger">
|
<span class="tw-text-danger">
|
||||||
<i aria-hidden="true" class="bwi bwi-trash"></i>
|
<i aria-hidden="true" class="bwi bwi-trash"></i>
|
||||||
@@ -327,7 +342,7 @@
|
|||||||
{{ "revokeAccess" | i18n }}
|
{{ "revokeAccess" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="!(accountDeprovisioningEnabled$ | async) || !u.managedByOrganization"
|
*ngIf="!accountDeprovisioningEnabled || !u.managedByOrganization"
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="remove(u)"
|
(click)="remove(u)"
|
||||||
@@ -337,7 +352,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="(accountDeprovisioningEnabled$ | async) && u.managedByOrganization"
|
*ngIf="accountDeprovisioningEnabled && u.managedByOrganization"
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="deleteUser(u)"
|
(click)="deleteUser(u)"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import {
|
import {
|
||||||
@@ -83,7 +83,7 @@ class MembersTableDataSource extends PeopleTableDataSource<OrganizationUserView>
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: "members.component.html",
|
templateUrl: "members.component.html",
|
||||||
})
|
})
|
||||||
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> {
|
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> implements OnInit {
|
||||||
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
||||||
resetPasswordModalRef: ViewContainerRef;
|
resetPasswordModalRef: ViewContainerRef;
|
||||||
|
|
||||||
@@ -96,13 +96,10 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
status: OrganizationUserStatusType = null;
|
status: OrganizationUserStatusType = null;
|
||||||
orgResetPasswordPolicyEnabled = false;
|
orgResetPasswordPolicyEnabled = false;
|
||||||
orgIsOnSecretsManagerStandalone = false;
|
orgIsOnSecretsManagerStandalone = false;
|
||||||
|
accountDeprovisioningEnabled = false;
|
||||||
|
|
||||||
protected canUseSecretsManager$: Observable<boolean>;
|
protected canUseSecretsManager$: Observable<boolean>;
|
||||||
|
|
||||||
protected accountDeprovisioningEnabled$: Observable<boolean> = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fixed sizes used for cdkVirtualScroll
|
// Fixed sizes used for cdkVirtualScroll
|
||||||
protected rowHeight = 69;
|
protected rowHeight = 69;
|
||||||
protected rowHeightClass = `tw-h-[69px]`;
|
protected rowHeightClass = `tw-h-[69px]`;
|
||||||
@@ -216,6 +213,12 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.accountDeprovisioningEnabled = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.AccountDeprovisioning,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async getUsers(): Promise<OrganizationUserView[]> {
|
async getUsers(): Promise<OrganizationUserView[]> {
|
||||||
let groupsPromise: Promise<Map<string, string>>;
|
let groupsPromise: Promise<Map<string, string>>;
|
||||||
let collectionsPromise: Promise<Map<string, string>>;
|
let collectionsPromise: Promise<Map<string, string>>;
|
||||||
@@ -779,4 +782,65 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
type: "warning",
|
type: "warning",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showBulkConfirmUsers(): boolean {
|
||||||
|
if (!this.accountDeprovisioningEnabled) {
|
||||||
|
return super.showBulkConfirmUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status == this.userStatusType.Accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showBulkReinviteUsers(): boolean {
|
||||||
|
if (!this.accountDeprovisioningEnabled) {
|
||||||
|
return super.showBulkReinviteUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status == this.userStatusType.Invited);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showBulkRestoreUsers(): boolean {
|
||||||
|
return (
|
||||||
|
!this.accountDeprovisioningEnabled ||
|
||||||
|
this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status == this.userStatusType.Revoked)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showBulkRevokeUsers(): boolean {
|
||||||
|
return (
|
||||||
|
!this.accountDeprovisioningEnabled ||
|
||||||
|
this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status != this.userStatusType.Revoked)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showBulkRemoveUsers(): boolean {
|
||||||
|
return (
|
||||||
|
!this.accountDeprovisioningEnabled ||
|
||||||
|
this.dataSource.getCheckedUsers().every((member) => !member.managedByOrganization)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get showBulkDeleteUsers(): boolean {
|
||||||
|
if (!this.accountDeprovisioningEnabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validStatuses = [
|
||||||
|
this.userStatusType.Accepted,
|
||||||
|
this.userStatusType.Confirmed,
|
||||||
|
this.userStatusType.Revoked,
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.managedByOrganization && validStatuses.includes(member.status));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user