1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-28 14:13:22 +00:00
Files
browser/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts
Alex Morask e7b50e790a [AC-2828] Add provider portal members page behind FF (#9949)
* Add provider portal members page behind a FF

* Fix reinvite issue

* Import scrolling module

* Add deprecations to old classes

* Move members.component init to constructor

* Rename new-base.people.component to base.members.component

* Hide bulk reinvite when no users can be re-invited on AC members page

* Rename events() to openEventsDialog()

* Fix return type for members component getUsers()

* Make table headers sortable

* Extract row height class to ts file

* Convert open methods to static methods for bulk dialogs

* Rename and refactor member-dialog.component

* Prevent event emission for searchControl and set filter in members component constructor

* use featureFlaggedRoute rather than using FF in components

* Add BaseBulkConfirmComponent for use in both web and bit-web

* Add BaseBulkRemoveComponent for use in both web and bit-web

* Thomas' feedback on base confirm/remove

* Remaining feedback
2024-07-15 11:56:11 -04:00

244 lines
8.4 KiB
TypeScript

import { DialogRef } from "@angular/cdk/dialog";
import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { combineLatest, lastValueFrom, switchMap } from "rxjs";
import { first } from "rxjs/operators";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import {
OrganizationUserStatusType,
ProviderUserStatusType,
ProviderUserType,
} from "@bitwarden/common/admin-console/enums";
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-confirm.request";
import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogService, ToastService } from "@bitwarden/components";
import { BaseMembersComponent } from "@bitwarden/web-vault/app/admin-console/common/base-members.component";
import {
peopleFilter,
PeopleTableDataSource,
} from "@bitwarden/web-vault/app/admin-console/common/people-table-data-source";
import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component";
import { BulkStatusComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
import {
AddEditMemberDialogComponent,
AddEditMemberDialogParams,
AddEditMemberDialogResultType,
} from "./dialogs/add-edit-member-dialog.component";
import { BulkConfirmDialogComponent } from "./dialogs/bulk-confirm-dialog.component";
import { BulkRemoveDialogComponent } from "./dialogs/bulk-remove-dialog.component";
type ProviderUser = ProviderUserUserDetailsResponse;
class MembersTableDataSource extends PeopleTableDataSource<ProviderUser> {
protected statusType = OrganizationUserStatusType;
}
@Component({
templateUrl: "members.component.html",
})
export class MembersComponent extends BaseMembersComponent<ProviderUser> {
accessEvents = false;
dataSource = new MembersTableDataSource();
loading = true;
providerId: string;
rowHeight = 62;
rowHeightClass = `tw-h-[62px]`;
status: ProviderUserStatusType = null;
userStatusType = ProviderUserStatusType;
userType = ProviderUserType;
constructor(
apiService: ApiService,
cryptoService: CryptoService,
dialogService: DialogService,
i18nService: I18nService,
logService: LogService,
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
toastService: ToastService,
userNamePipe: UserNamePipe,
validationService: ValidationService,
private activatedRoute: ActivatedRoute,
private providerService: ProviderService,
private router: Router,
) {
super(
apiService,
i18nService,
cryptoService,
validationService,
logService,
userNamePipe,
dialogService,
organizationManagementPreferencesService,
toastService,
);
combineLatest([
this.activatedRoute.parent.params,
this.activatedRoute.queryParams.pipe(first()),
])
.pipe(
switchMap(async ([urlParams, queryParams]) => {
this.searchControl.setValue(queryParams.search, { emitEvent: false });
this.dataSource.filter = peopleFilter(queryParams.search, null);
this.providerId = urlParams.providerId;
const provider = await this.providerService.get(this.providerId);
if (!provider || !provider.canManageUsers) {
return await this.router.navigate(["../"], { relativeTo: this.activatedRoute });
}
this.accessEvents = provider.useEvents;
await this.load();
if (queryParams.viewEvents != null) {
const user = this.dataSource.data.find((user) => user.id === queryParams.viewEvents);
if (user && user.status === ProviderUserStatusType.Confirmed) {
this.openEventsDialog(user);
}
}
}),
takeUntilDestroyed(),
)
.subscribe();
}
async bulkConfirm(): Promise<void> {
if (this.actionPromise != null) {
return;
}
const dialogRef = BulkConfirmDialogComponent.open(this.dialogService, {
data: {
providerId: this.providerId,
users: this.dataSource.getCheckedUsers(),
},
});
await lastValueFrom(dialogRef.closed);
await this.load();
}
async bulkReinvite(): Promise<void> {
if (this.actionPromise != null) {
return;
}
const checkedUsers = this.dataSource.getCheckedUsers();
const checkedInvitedUsers = checkedUsers.filter(
(user) => user.status === ProviderUserStatusType.Invited,
);
if (checkedInvitedUsers.length <= 0) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("noSelectedUsersApplicable"),
});
return;
}
try {
const request = this.apiService.postManyProviderUserReinvite(
this.providerId,
new ProviderUserBulkRequest(checkedInvitedUsers.map((user) => user.id)),
);
const dialogRef = BulkStatusComponent.open(this.dialogService, {
data: {
users: checkedUsers,
filteredUsers: checkedInvitedUsers,
request,
successfulMessage: this.i18nService.t("bulkReinviteMessage"),
},
});
await lastValueFrom(dialogRef.closed);
} catch (error) {
this.validationService.showError(error);
}
}
async bulkRemove(): Promise<void> {
if (this.actionPromise != null) {
return;
}
const dialogRef = BulkRemoveDialogComponent.open(this.dialogService, {
data: {
providerId: this.providerId,
users: this.dataSource.getCheckedUsers(),
},
});
await lastValueFrom(dialogRef.closed);
await this.load();
}
async confirmUser(user: ProviderUser, publicKey: Uint8Array): Promise<void> {
const providerKey = await this.cryptoService.getProviderKey(this.providerId);
const key = await this.cryptoService.rsaEncrypt(providerKey.key, publicKey);
const request = new ProviderUserConfirmRequest();
request.key = key.encryptedString;
await this.apiService.postProviderUserConfirm(this.providerId, user.id, request);
}
deleteUser = (id: string): Promise<void> =>
this.apiService.deleteProviderUser(this.providerId, id);
edit = async (user: ProviderUser | null): Promise<void> => {
const data: AddEditMemberDialogParams = {
providerId: this.providerId,
};
if (user != null) {
data.user = {
id: user.id,
name: this.userNamePipe.transform(user),
type: user.type,
};
}
const dialogRef = AddEditMemberDialogComponent.open(this.dialogService, {
data,
});
const result = await lastValueFrom(dialogRef.closed);
switch (result) {
case AddEditMemberDialogResultType.Saved:
case AddEditMemberDialogResultType.Deleted:
await this.load();
break;
}
};
openEventsDialog = (user: ProviderUser): DialogRef<void> =>
openEntityEventsDialog(this.dialogService, {
data: {
name: this.userNamePipe.transform(user),
providerId: this.providerId,
entityId: user.id,
showUser: false,
entity: "user",
},
});
getUsers = (): Promise<ListResponse<ProviderUser>> =>
this.apiService.getProviderUsers(this.providerId);
reinviteUser = (id: string): Promise<void> =>
this.apiService.postProviderUserReinvite(this.providerId, id);
}