mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
[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
This commit is contained in:
parent
4edbd65faf
commit
e7b50e790a
@ -4,7 +4,6 @@ import { FormControl } from "@angular/forms";
|
|||||||
import { firstValueFrom, lastValueFrom, debounceTime, combineLatest, BehaviorSubject } from "rxjs";
|
import { firstValueFrom, lastValueFrom, debounceTime, combineLatest, BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||||
import {
|
import {
|
||||||
@ -35,7 +34,7 @@ export type UserViewTypes = ProviderUserUserDetailsResponse | OrganizationUserVi
|
|||||||
* This will replace BasePeopleComponent once all subclasses have been changed over to use this class.
|
* This will replace BasePeopleComponent once all subclasses have been changed over to use this class.
|
||||||
*/
|
*/
|
||||||
@Directive()
|
@Directive()
|
||||||
export abstract class NewBasePeopleComponent<UserView extends UserViewTypes> {
|
export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
|
||||||
/**
|
/**
|
||||||
* Shows a banner alerting the admin that users need to be confirmed.
|
* Shows a banner alerting the admin that users need to be confirmed.
|
||||||
*/
|
*/
|
||||||
@ -52,6 +51,10 @@ export abstract class NewBasePeopleComponent<UserView extends UserViewTypes> {
|
|||||||
return this.dataSource.acceptedUserCount > 0;
|
return this.dataSource.acceptedUserCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showBulkReinviteUsers(): boolean {
|
||||||
|
return this.dataSource.invitedUserCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
abstract userType: typeof OrganizationUserType | typeof ProviderUserType;
|
abstract userType: typeof OrganizationUserType | typeof ProviderUserType;
|
||||||
abstract userStatusType: typeof OrganizationUserStatusType | typeof ProviderUserStatusType;
|
abstract userStatusType: typeof OrganizationUserStatusType | typeof ProviderUserStatusType;
|
||||||
|
|
||||||
@ -77,7 +80,6 @@ export abstract class NewBasePeopleComponent<UserView extends UserViewTypes> {
|
|||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected cryptoService: CryptoService,
|
protected cryptoService: CryptoService,
|
||||||
protected validationService: ValidationService,
|
protected validationService: ValidationService,
|
||||||
protected modalService: ModalService,
|
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
protected userNamePipe: UserNamePipe,
|
protected userNamePipe: UserNamePipe,
|
||||||
protected dialogService: DialogService,
|
protected dialogService: DialogService,
|
@ -4,7 +4,7 @@ import {
|
|||||||
} from "@bitwarden/common/admin-console/enums";
|
} from "@bitwarden/common/admin-console/enums";
|
||||||
import { TableDataSource } from "@bitwarden/components";
|
import { TableDataSource } from "@bitwarden/components";
|
||||||
|
|
||||||
import { StatusType, UserViewTypes } from "./new-base.people.component";
|
import { StatusType, UserViewTypes } from "./base-members.component";
|
||||||
|
|
||||||
const MaxCheckedCount = 500;
|
const MaxCheckedCount = 500;
|
||||||
|
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
import { Directive, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import {
|
||||||
|
OrganizationUserBulkPublicKeyResponse,
|
||||||
|
OrganizationUserBulkResponse,
|
||||||
|
} from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
|
import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response";
|
||||||
|
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
|
import { BulkUserDetails } from "./bulk-status.component";
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export abstract class BaseBulkConfirmComponent implements OnInit {
|
||||||
|
protected users: BulkUserDetails[];
|
||||||
|
|
||||||
|
protected excludedUsers: BulkUserDetails[];
|
||||||
|
protected filteredUsers: BulkUserDetails[];
|
||||||
|
|
||||||
|
protected publicKeys: Map<string, Uint8Array> = new Map();
|
||||||
|
protected fingerprints: Map<string, string> = new Map();
|
||||||
|
protected statuses: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
protected done = false;
|
||||||
|
protected loading = true;
|
||||||
|
protected error: string;
|
||||||
|
|
||||||
|
protected constructor(
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.excludedUsers = this.users.filter((user) => !this.isAccepted(user));
|
||||||
|
this.filteredUsers = this.users.filter((user) => this.isAccepted(user));
|
||||||
|
|
||||||
|
if (this.filteredUsers.length <= 0) {
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicKeysResponse = await this.getPublicKeys();
|
||||||
|
|
||||||
|
for (const entry of publicKeysResponse.data) {
|
||||||
|
const publicKey = Utils.fromB64ToArray(entry.key);
|
||||||
|
const fingerprint = await this.cryptoService.getFingerprint(entry.userId, publicKey);
|
||||||
|
if (fingerprint != null) {
|
||||||
|
this.publicKeys.set(entry.id, publicKey);
|
||||||
|
this.fingerprints.set(entry.id, fingerprint.join("-"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const key = await this.getCryptoKey();
|
||||||
|
const userIdsWithKeys: { id: string; key: string }[] = [];
|
||||||
|
|
||||||
|
for (const user of this.filteredUsers) {
|
||||||
|
const publicKey = this.publicKeys.get(user.id);
|
||||||
|
if (publicKey == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const encryptedKey = await this.cryptoService.rsaEncrypt(key.key, publicKey);
|
||||||
|
userIdsWithKeys.push({
|
||||||
|
id: user.id,
|
||||||
|
key: encryptedKey.encryptedString,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const userBulkResponse = await this.postConfirmRequest(userIdsWithKeys);
|
||||||
|
|
||||||
|
userBulkResponse.data.forEach((entry) => {
|
||||||
|
const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkConfirmMessage");
|
||||||
|
this.statuses.set(entry.id, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.done = true;
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e.message;
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected abstract getCryptoKey(): Promise<SymmetricCryptoKey>;
|
||||||
|
protected abstract getPublicKeys(): Promise<
|
||||||
|
ListResponse<OrganizationUserBulkPublicKeyResponse | ProviderUserBulkPublicKeyResponse>
|
||||||
|
>;
|
||||||
|
protected abstract isAccepted(user: BulkUserDetails): boolean;
|
||||||
|
protected abstract postConfirmRequest(
|
||||||
|
userIdsWithKeys: { id: string; key: string }[],
|
||||||
|
): Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>>;
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import { Directive } from "@angular/core";
|
||||||
|
|
||||||
|
import { OrganizationUserBulkResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
|
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response";
|
||||||
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export abstract class BaseBulkRemoveComponent {
|
||||||
|
protected showNoMasterPasswordWarning: boolean;
|
||||||
|
protected statuses: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
protected done = false;
|
||||||
|
protected loading = false;
|
||||||
|
protected error: string;
|
||||||
|
|
||||||
|
protected constructor(protected i18nService: I18nService) {}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const deleteUsersResponse = await this.deleteUsers();
|
||||||
|
deleteUsersResponse.data.forEach((entry) => {
|
||||||
|
const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkRemovedMessage");
|
||||||
|
this.statuses.set(entry.id, error);
|
||||||
|
});
|
||||||
|
this.done = true;
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected abstract deleteUsers(): Promise<
|
||||||
|
ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>
|
||||||
|
>;
|
||||||
|
|
||||||
|
protected abstract get removeUsersWarning(): string;
|
||||||
|
}
|
@ -33,7 +33,7 @@ type BulkStatusDialogData = {
|
|||||||
users: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
users: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||||
filteredUsers: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
filteredUsers: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||||
request: Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>>;
|
request: Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>>;
|
||||||
successfullMessage: string;
|
successfulMessage: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -67,7 +67,7 @@ export class BulkStatusComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.users = data.users.map((user) => {
|
this.users = data.users.map((user) => {
|
||||||
let message = keyedErrors[user.id] ?? data.successfullMessage;
|
let message = keyedErrors[user.id] ?? data.successfulMessage;
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
||||||
message = this.i18nService.t("bulkFilteredMessage");
|
message = this.i18nService.t("bulkFilteredMessage");
|
||||||
|
@ -103,7 +103,12 @@
|
|||||||
</button>
|
</button>
|
||||||
<bit-menu-divider></bit-menu-divider>
|
<bit-menu-divider></bit-menu-divider>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<button type="button" bitMenuItem (click)="bulkReinvite()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkReinvite()"
|
||||||
|
*ngIf="showBulkReinviteUsers"
|
||||||
|
>
|
||||||
<i class="bwi bwi-fw bwi-envelope" aria-hidden="true"></i>
|
<i class="bwi bwi-fw bwi-envelope" aria-hidden="true"></i>
|
||||||
{{ "reinviteSelected" | i18n }}
|
{{ "reinviteSelected" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -45,7 +45,7 @@ import { Collection } from "@bitwarden/common/vault/models/domain/collection";
|
|||||||
import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response";
|
import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response";
|
||||||
import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/components";
|
import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { NewBasePeopleComponent } from "../../common/new-base.people.component";
|
import { BaseMembersComponent } from "../../common/base-members.component";
|
||||||
import { PeopleTableDataSource } from "../../common/people-table-data-source";
|
import { PeopleTableDataSource } from "../../common/people-table-data-source";
|
||||||
import { GroupService } from "../core";
|
import { GroupService } from "../core";
|
||||||
import { OrganizationUserView } from "../core/views/organization-user.view";
|
import { OrganizationUserView } from "../core/views/organization-user.view";
|
||||||
@ -70,7 +70,7 @@ class MembersTableDataSource extends PeopleTableDataSource<OrganizationUserView>
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: "members.component.html",
|
templateUrl: "members.component.html",
|
||||||
})
|
})
|
||||||
export class MembersComponent extends NewBasePeopleComponent<OrganizationUserView> {
|
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> {
|
||||||
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
||||||
resetPasswordModalRef: ViewContainerRef;
|
resetPasswordModalRef: ViewContainerRef;
|
||||||
|
|
||||||
@ -94,7 +94,6 @@ export class MembersComponent extends NewBasePeopleComponent<OrganizationUserVie
|
|||||||
apiService: ApiService,
|
apiService: ApiService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||||
modalService: ModalService,
|
|
||||||
cryptoService: CryptoService,
|
cryptoService: CryptoService,
|
||||||
validationService: ValidationService,
|
validationService: ValidationService,
|
||||||
logService: LogService,
|
logService: LogService,
|
||||||
@ -112,13 +111,13 @@ export class MembersComponent extends NewBasePeopleComponent<OrganizationUserVie
|
|||||||
private groupService: GroupService,
|
private groupService: GroupService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
|
private modalService: ModalService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
apiService,
|
apiService,
|
||||||
i18nService,
|
i18nService,
|
||||||
cryptoService,
|
cryptoService,
|
||||||
validationService,
|
validationService,
|
||||||
modalService,
|
|
||||||
logService,
|
logService,
|
||||||
userNamePipe,
|
userNamePipe,
|
||||||
dialogService,
|
dialogService,
|
||||||
@ -564,7 +563,7 @@ export class MembersComponent extends NewBasePeopleComponent<OrganizationUserVie
|
|||||||
users: users,
|
users: users,
|
||||||
filteredUsers: filteredUsers,
|
filteredUsers: filteredUsers,
|
||||||
request: response,
|
request: response,
|
||||||
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
successfulMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await lastValueFrom(dialogRef.closed);
|
await lastValueFrom(dialogRef.closed);
|
||||||
|
@ -7,6 +7,9 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym
|
|||||||
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-confirm.component";
|
import { BulkConfirmComponent as OrganizationBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-confirm.component";
|
||||||
import { BulkUserDetails } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
|
import { BulkUserDetails } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use the {@link BulkConfirmDialogComponent} instead.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl:
|
templateUrl:
|
||||||
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html",
|
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html",
|
||||||
|
@ -3,6 +3,9 @@ import { Component, Input } from "@angular/core";
|
|||||||
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
|
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
|
||||||
import { BulkRemoveComponent as OrganizationBulkRemoveComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-remove.component";
|
import { BulkRemoveComponent as OrganizationBulkRemoveComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-remove.component";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use the {@link BulkRemoveDialogComponent} instead.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl:
|
templateUrl:
|
||||||
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html",
|
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html",
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
|
<bit-dialog dialogSize="large" [loading]="loading">
|
||||||
|
<span bitDialogTitle>
|
||||||
|
{{ title | uppercase }}
|
||||||
|
<small class="tw-text-muted" *ngIf="dialogParams.user">{{ dialogParams.user.name }}</small>
|
||||||
|
</span>
|
||||||
|
<div bitDialogContent>
|
||||||
|
<ng-container *ngIf="!editing">
|
||||||
|
<p>{{ "providerInviteUserDesc" | i18n }}</p>
|
||||||
|
<div class="tw-mb-4">
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>
|
||||||
|
{{ "email" | i18n }}
|
||||||
|
</bit-label>
|
||||||
|
<input type="text" bitInput formControlName="emails" />
|
||||||
|
<bit-hint>{{ "inviteMultipleEmailDesc" | i18n: "20" }}</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container>
|
||||||
|
<h3>
|
||||||
|
{{ "userType" | i18n | uppercase }}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
href="https://bitwarden.com/help/provider-users/"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
<bit-radio-group formControlName="type">
|
||||||
|
<bit-radio-button [value]="UserType.ServiceUser">
|
||||||
|
<bit-label>
|
||||||
|
{{ "serviceUser" | i18n }}
|
||||||
|
</bit-label>
|
||||||
|
<bit-hint>{{ "serviceUserDesc" | i18n }}</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
<bit-radio-button [value]="UserType.ProviderAdmin">
|
||||||
|
<bit-label>
|
||||||
|
{{ "providerAdmin" | i18n }}
|
||||||
|
</bit-label>
|
||||||
|
<bit-hint>{{ "providerAdminDesc" | i18n }}</bit-hint>
|
||||||
|
</bit-radio-button>
|
||||||
|
</bit-radio-group>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button type="submit" bitButton bitFormButton buttonType="primary" [disabled]="loading">
|
||||||
|
{{ "save" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.Closed">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
<div class="tw-ml-auto" *ngIf="editing">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitIconButton="bwi-trash"
|
||||||
|
buttonType="danger"
|
||||||
|
bitFormButton
|
||||||
|
[appA11yTitle]="'delete' | i18n"
|
||||||
|
[bitAction]="delete"
|
||||||
|
[disabled]="loading"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
|
</form>
|
@ -0,0 +1,127 @@
|
|||||||
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { ProviderUserInviteRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-invite.request";
|
||||||
|
import { ProviderUserUpdateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-update.request";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
export type AddEditMemberDialogParams = {
|
||||||
|
providerId: string;
|
||||||
|
user?: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: ProviderUserType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum AddEditMemberDialogResultType {
|
||||||
|
Closed = "closed",
|
||||||
|
Deleted = "deleted",
|
||||||
|
Saved = "saved",
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "add-edit-member-dialog.component.html",
|
||||||
|
})
|
||||||
|
export class AddEditMemberDialogComponent {
|
||||||
|
editing = false;
|
||||||
|
loading = true;
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
protected ResultType = AddEditMemberDialogResultType;
|
||||||
|
protected UserType = ProviderUserType;
|
||||||
|
|
||||||
|
protected formGroup = new FormGroup({
|
||||||
|
emails: new FormControl<string>("", [Validators.required]),
|
||||||
|
type: new FormControl(this.dialogParams.user?.type ?? ProviderUserType.ServiceUser),
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
@Inject(DIALOG_DATA) protected dialogParams: AddEditMemberDialogParams,
|
||||||
|
private dialogRef: DialogRef<AddEditMemberDialogResultType>,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
) {
|
||||||
|
this.editing = this.loading = this.dialogParams.user != null;
|
||||||
|
if (this.editing) {
|
||||||
|
this.title = this.i18nService.t("editMember");
|
||||||
|
const emailControl = this.formGroup.controls.emails;
|
||||||
|
emailControl.removeValidators(Validators.required);
|
||||||
|
emailControl.disable();
|
||||||
|
} else {
|
||||||
|
this.title = this.i18nService.t("inviteMember");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete = async (): Promise<void> => {
|
||||||
|
if (!this.editing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
title: this.dialogParams.user.name,
|
||||||
|
content: { key: "removeUserConfirmation" },
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.apiService.deleteProviderUser(
|
||||||
|
this.dialogParams.providerId,
|
||||||
|
this.dialogParams.user.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("removedUserId", this.dialogParams.user.name),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(AddEditMemberDialogResultType.Deleted);
|
||||||
|
};
|
||||||
|
|
||||||
|
submit = async (): Promise<void> => {
|
||||||
|
if (this.editing) {
|
||||||
|
const request = new ProviderUserUpdateRequest();
|
||||||
|
request.type = this.formGroup.value.type;
|
||||||
|
await this.apiService.putProviderUser(
|
||||||
|
this.dialogParams.providerId,
|
||||||
|
this.dialogParams.user.id,
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const request = new ProviderUserInviteRequest();
|
||||||
|
request.emails = this.formGroup.value.emails.trim().split(/\s*,\s*/);
|
||||||
|
request.type = this.formGroup.value.type;
|
||||||
|
await this.apiService.postProviderUserInvite(this.dialogParams.providerId, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t(
|
||||||
|
this.editing ? "editedUserId" : "invitedUsers",
|
||||||
|
this.dialogParams.user?.name,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(AddEditMemberDialogResultType.Saved);
|
||||||
|
};
|
||||||
|
|
||||||
|
static open(dialogService: DialogService, dialogConfig: DialogConfig<AddEditMemberDialogParams>) {
|
||||||
|
return dialogService.open<AddEditMemberDialogResultType, AddEditMemberDialogParams>(
|
||||||
|
AddEditMemberDialogComponent,
|
||||||
|
dialogConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject } from "@angular/core";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import {
|
||||||
|
OrganizationUserBulkPublicKeyResponse,
|
||||||
|
OrganizationUserBulkResponse,
|
||||||
|
} from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
|
import { ProviderUserStatusType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { ProviderUserBulkConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk-confirm.request";
|
||||||
|
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
|
||||||
|
import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response";
|
||||||
|
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.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 { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
import { BaseBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component";
|
||||||
|
import { BulkUserDetails } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
|
||||||
|
|
||||||
|
type BulkConfirmDialogParams = {
|
||||||
|
providerId: string;
|
||||||
|
users: BulkUserDetails[];
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl:
|
||||||
|
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.html",
|
||||||
|
})
|
||||||
|
export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent {
|
||||||
|
providerId: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
@Inject(DIALOG_DATA) protected dialogParams: BulkConfirmDialogParams,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
) {
|
||||||
|
super(cryptoService, i18nService);
|
||||||
|
|
||||||
|
this.providerId = dialogParams.providerId;
|
||||||
|
this.users = dialogParams.users;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCryptoKey = (): Promise<SymmetricCryptoKey> =>
|
||||||
|
this.cryptoService.getProviderKey(this.providerId);
|
||||||
|
|
||||||
|
protected getPublicKeys = async (): Promise<
|
||||||
|
ListResponse<OrganizationUserBulkPublicKeyResponse | ProviderUserBulkPublicKeyResponse>
|
||||||
|
> => {
|
||||||
|
const request = new ProviderUserBulkRequest(this.filteredUsers.map((user) => user.id));
|
||||||
|
return await this.apiService.postProviderUsersPublicKey(this.providerId, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected isAccepted = (user: BulkUserDetails): boolean =>
|
||||||
|
user.status === ProviderUserStatusType.Accepted;
|
||||||
|
|
||||||
|
protected postConfirmRequest = async (
|
||||||
|
userIdsWithKeys: { id: string; key: string }[],
|
||||||
|
): Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>> => {
|
||||||
|
const request = new ProviderUserBulkConfirmRequest(userIdsWithKeys);
|
||||||
|
return await this.apiService.postProviderUserBulkConfirm(this.providerId, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
static open(dialogService: DialogService, dialogConfig: DialogConfig<BulkConfirmDialogParams>) {
|
||||||
|
return dialogService.open(BulkConfirmDialogComponent, dialogConfig);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject } from "@angular/core";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
|
||||||
|
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response";
|
||||||
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
import { BaseBulkRemoveComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/base-bulk-remove.component";
|
||||||
|
import { BulkUserDetails } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component";
|
||||||
|
|
||||||
|
type BulkRemoveDialogParams = {
|
||||||
|
providerId: string;
|
||||||
|
users: BulkUserDetails[];
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl:
|
||||||
|
"../../../../../../../../apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.html",
|
||||||
|
})
|
||||||
|
export class BulkRemoveDialogComponent extends BaseBulkRemoveComponent {
|
||||||
|
providerId: string;
|
||||||
|
users: BulkUserDetails[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
@Inject(DIALOG_DATA) dialogParams: BulkRemoveDialogParams,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
) {
|
||||||
|
super(i18nService);
|
||||||
|
|
||||||
|
this.providerId = dialogParams.providerId;
|
||||||
|
this.users = dialogParams.users;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected deleteUsers = (): Promise<ListResponse<ProviderUserBulkResponse>> => {
|
||||||
|
const request = new ProviderUserBulkRequest(this.users.map((user) => user.id));
|
||||||
|
return this.apiService.deleteManyProviderUsers(this.providerId, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected get removeUsersWarning() {
|
||||||
|
return this.i18nService.t("removeOrgUsersConfirmation");
|
||||||
|
}
|
||||||
|
|
||||||
|
static open(dialogService: DialogService, dialogConfig: DialogConfig<BulkRemoveDialogParams>) {
|
||||||
|
return dialogService.open(BulkRemoveDialogComponent, dialogConfig);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,225 @@
|
|||||||
|
<app-header>
|
||||||
|
<bit-search class="tw-grow" [formControl]="searchControl" [placeholder]="'searchMembers' | i18n">
|
||||||
|
</bit-search>
|
||||||
|
<button type="button" bitButton buttonType="primary" (click)="invite()">
|
||||||
|
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||||
|
{{ "inviteMember" | i18n }}
|
||||||
|
</button>
|
||||||
|
</app-header>
|
||||||
|
|
||||||
|
<div class="tw-mb-4 tw-flex tw-flex-col tw-space-y-4">
|
||||||
|
<bit-toggle-group
|
||||||
|
[selected]="status"
|
||||||
|
(selectedChange)="statusToggle.next($event)"
|
||||||
|
[attr.aria-label]="'memberStatusFilter' | i18n"
|
||||||
|
>
|
||||||
|
<bit-toggle [value]="null">
|
||||||
|
{{ "all" | i18n }}
|
||||||
|
<span bitBadge variant="info" *ngIf="dataSource.activeUserCount as allCount">
|
||||||
|
{{ allCount }}
|
||||||
|
</span>
|
||||||
|
</bit-toggle>
|
||||||
|
<bit-toggle [value]="userStatusType.Invited">
|
||||||
|
{{ "invited" | i18n }}
|
||||||
|
<span bitBadge variant="info" *ngIf="dataSource.invitedUserCount as invitedCount">
|
||||||
|
{{ invitedCount }}
|
||||||
|
</span>
|
||||||
|
</bit-toggle>
|
||||||
|
<bit-toggle [value]="userStatusType.Accepted">
|
||||||
|
{{ "needsConfirmation" | i18n }}
|
||||||
|
<span bitBadge variant="info" *ngIf="dataSource.acceptedUserCount as acceptedCount">
|
||||||
|
{{ acceptedCount }}
|
||||||
|
</span>
|
||||||
|
</bit-toggle>
|
||||||
|
</bit-toggle-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!firstLoaded">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
</i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="firstLoaded">
|
||||||
|
<p *ngIf="!dataSource.filteredData.length">{{ "noMembersInList" | i18n }}</p>
|
||||||
|
<ng-container *ngIf="dataSource.filteredData.length">
|
||||||
|
<bit-callout
|
||||||
|
type="info"
|
||||||
|
title="{{ 'confirmUsers' | i18n }}"
|
||||||
|
icon="bwi-check-circle"
|
||||||
|
*ngIf="showConfirmUsers"
|
||||||
|
>
|
||||||
|
{{ "providerUsersNeedConfirmed" | i18n }}
|
||||||
|
</bit-callout>
|
||||||
|
<cdk-virtual-scroll-viewport scrollWindow [itemSize]="rowHeight" class="tw-pb-8">
|
||||||
|
<bit-table [dataSource]="dataSource">
|
||||||
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th bitCell class="tw-w-20">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bitCheckbox
|
||||||
|
class="tw-mr-1"
|
||||||
|
(change)="dataSource.checkAllFilteredUsers($any($event.target).checked)"
|
||||||
|
id="selectAll"
|
||||||
|
/>
|
||||||
|
<label class="tw-mb-0 !tw-font-bold !tw-text-muted" for="selectAll">
|
||||||
|
{{ "all" | i18n }}
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
<th bitCell bitSortable="email" default>{{ "name" | i18n }}</th>
|
||||||
|
<th bitCell bitSortable="type">{{ "role" | i18n }}</th>
|
||||||
|
<th bitCell class="tw-w-10">
|
||||||
|
<button
|
||||||
|
[bitMenuTriggerFor]="headerMenu"
|
||||||
|
type="button"
|
||||||
|
bitIconButton="bwi-ellipsis-v"
|
||||||
|
size="small"
|
||||||
|
appA11yTitle="{{ 'options' | i18n }}"
|
||||||
|
></button>
|
||||||
|
<bit-menu #headerMenu>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkReinvite()"
|
||||||
|
*ngIf="showBulkReinviteUsers"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-fw bwi-envelope" aria-hidden="true"></i>
|
||||||
|
{{ "reinviteSelected" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="bulkConfirm()"
|
||||||
|
*ngIf="showBulkConfirmUsers"
|
||||||
|
>
|
||||||
|
<span class="tw-text-success">
|
||||||
|
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
|
||||||
|
{{ "confirmSelected" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" bitMenuItem (click)="bulkRemove()">
|
||||||
|
<span class="tw-text-danger">
|
||||||
|
<i aria-hidden="true" class="bwi bwi-close"></i>
|
||||||
|
{{ "remove" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</bit-menu>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr
|
||||||
|
bitRow
|
||||||
|
*cdkVirtualFor="let user of rows$"
|
||||||
|
alignContent="middle"
|
||||||
|
[ngClass]="rowHeightClass"
|
||||||
|
>
|
||||||
|
<td bitCell (click)="dataSource.checkUser(user)">
|
||||||
|
<input type="checkbox" bitCheckbox [(ngModel)]="$any(user).checked" />
|
||||||
|
</td>
|
||||||
|
<td bitCell (click)="edit(user)" class="tw-cursor-pointer">
|
||||||
|
<div class="tw-flex tw-items-center">
|
||||||
|
<bit-avatar
|
||||||
|
size="small"
|
||||||
|
[text]="user | userName"
|
||||||
|
[id]="user.userId"
|
||||||
|
[color]="user.avatarColor"
|
||||||
|
class="tw-mr-3"
|
||||||
|
></bit-avatar>
|
||||||
|
<div class="tw-flex tw-flex-col">
|
||||||
|
<div>
|
||||||
|
<button type="button" bitLink>
|
||||||
|
{{ user.name ?? user.email }}
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
bitBadge
|
||||||
|
class="tw-text-xs"
|
||||||
|
variant="secondary"
|
||||||
|
*ngIf="user.status === userStatusType.Invited"
|
||||||
|
>
|
||||||
|
{{ "invited" | i18n }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
bitBadge
|
||||||
|
class="tw-text-xs"
|
||||||
|
variant="warning"
|
||||||
|
*ngIf="user.status === userStatusType.Accepted"
|
||||||
|
>
|
||||||
|
{{ "needsConfirmation" | i18n }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
bitBadge
|
||||||
|
class="tw-text-xs"
|
||||||
|
variant="secondary"
|
||||||
|
*ngIf="user.status === userStatusType.Revoked"
|
||||||
|
>
|
||||||
|
{{ "revoked" | i18n }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="tw-text-sm tw-text-muted" *ngIf="user.name">
|
||||||
|
{{ user.email }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-muted">
|
||||||
|
<span *ngIf="user.type === userType.ProviderAdmin">{{ "providerAdmin" | i18n }}</span>
|
||||||
|
<span *ngIf="user.type === userType.ServiceUser">{{ "serviceUser" | i18n }}</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<button
|
||||||
|
[bitMenuTriggerFor]="rowMenu"
|
||||||
|
type="button"
|
||||||
|
bitIconButton="bwi-ellipsis-v"
|
||||||
|
size="small"
|
||||||
|
appA11yTitle="{{ 'options' | i18n }}"
|
||||||
|
></button>
|
||||||
|
<bit-menu #rowMenu>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="reinvite(user)"
|
||||||
|
*ngIf="user.status === userStatusType.Invited"
|
||||||
|
>
|
||||||
|
<i aria-hidden="true" class="bwi bwi-envelope"></i>
|
||||||
|
{{ "resendInvitation" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="confirm(user)"
|
||||||
|
*ngIf="user.status === userStatusType.Accepted"
|
||||||
|
>
|
||||||
|
<span class="tw-text-success">
|
||||||
|
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
|
||||||
|
{{ "confirm" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="openEventsDialog(user)"
|
||||||
|
*ngIf="user.status === userStatusType.Confirmed"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-fw bwi-file-text" aria-hidden="true"></i>
|
||||||
|
{{ "eventLogs" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button type="button" bitMenuItem (click)="remove(user)">
|
||||||
|
<span class="tw-text-danger">
|
||||||
|
<i class="bwi bwi-fw bwi-close" aria-hidden="true"></i>
|
||||||
|
{{ "remove" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</bit-menu>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
@ -0,0 +1,243 @@
|
|||||||
|
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);
|
||||||
|
}
|
@ -15,6 +15,7 @@ import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/
|
|||||||
import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-confirm.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 { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@ -29,6 +30,9 @@ import { BulkConfirmComponent } from "./bulk/bulk-confirm.component";
|
|||||||
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
import { BulkRemoveComponent } from "./bulk/bulk-remove.component";
|
||||||
import { UserAddEditComponent } from "./user-add-edit.component";
|
import { UserAddEditComponent } from "./user-add-edit.component";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use the {@link MembersComponent} instead.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: "provider-people",
|
selector: "provider-people",
|
||||||
templateUrl: "people.component.html",
|
templateUrl: "people.component.html",
|
||||||
@ -70,6 +74,7 @@ export class PeopleComponent
|
|||||||
private providerService: ProviderService,
|
private providerService: ProviderService,
|
||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||||
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
apiService,
|
apiService,
|
||||||
@ -228,7 +233,7 @@ export class PeopleComponent
|
|||||||
users: users,
|
users: users,
|
||||||
filteredUsers: filteredUsers,
|
filteredUsers: filteredUsers,
|
||||||
request: response,
|
request: response,
|
||||||
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
successfulMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await lastValueFrom(dialogRef.closed);
|
await lastValueFrom(dialogRef.closed);
|
||||||
|
@ -10,6 +10,9 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use the {@link MembersDialogComponent} instead.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: "provider-user-add-edit",
|
selector: "provider-user-add-edit",
|
||||||
templateUrl: "user-add-edit.component.html",
|
templateUrl: "user-add-edit.component.html",
|
||||||
|
@ -13,11 +13,7 @@
|
|||||||
route="manage"
|
route="manage"
|
||||||
*ngIf="showManageTab(provider)"
|
*ngIf="showManageTab(provider)"
|
||||||
>
|
>
|
||||||
<bit-nav-item
|
<bit-nav-item [text]="'people' | i18n" route="manage/people"></bit-nav-item>
|
||||||
[text]="'people' | i18n"
|
|
||||||
route="manage/people"
|
|
||||||
*ngIf="provider.canManageUsers"
|
|
||||||
></bit-nav-item>
|
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
[text]="'eventLogs' | i18n"
|
[text]="'eventLogs' | i18n"
|
||||||
route="manage/events"
|
route="manage/events"
|
||||||
|
@ -2,8 +2,10 @@ import { NgModule } from "@angular/core";
|
|||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
import { AuthGuard } from "@bitwarden/angular/auth/guards";
|
import { AuthGuard } from "@bitwarden/angular/auth/guards";
|
||||||
|
import { featureFlaggedRoute } from "@bitwarden/angular/platform/utils/feature-flagged-route";
|
||||||
import { AnonLayoutWrapperComponent } from "@bitwarden/auth/angular";
|
import { AnonLayoutWrapperComponent } from "@bitwarden/auth/angular";
|
||||||
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ProvidersComponent } from "@bitwarden/web-vault/app/admin-console/providers/providers.component";
|
import { ProvidersComponent } from "@bitwarden/web-vault/app/admin-console/providers/providers.component";
|
||||||
import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/frontend-layout.component";
|
import { FrontendLayoutComponent } from "@bitwarden/web-vault/app/layouts/frontend-layout.component";
|
||||||
import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component";
|
import { UserLayoutComponent } from "@bitwarden/web-vault/app/layouts/user-layout.component";
|
||||||
@ -20,6 +22,7 @@ import { CreateOrganizationComponent } from "./clients/create-organization.compo
|
|||||||
import { providerPermissionsGuard } from "./guards/provider-permissions.guard";
|
import { providerPermissionsGuard } from "./guards/provider-permissions.guard";
|
||||||
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
||||||
import { EventsComponent } from "./manage/events.component";
|
import { EventsComponent } from "./manage/events.component";
|
||||||
|
import { MembersComponent } from "./manage/members.component";
|
||||||
import { PeopleComponent } from "./manage/people.component";
|
import { PeopleComponent } from "./manage/people.component";
|
||||||
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||||
import { AccountComponent } from "./settings/account.component";
|
import { AccountComponent } from "./settings/account.component";
|
||||||
@ -95,16 +98,20 @@ const routes: Routes = [
|
|||||||
pathMatch: "full",
|
pathMatch: "full",
|
||||||
redirectTo: "people",
|
redirectTo: "people",
|
||||||
},
|
},
|
||||||
{
|
...featureFlaggedRoute({
|
||||||
path: "people",
|
defaultComponent: PeopleComponent,
|
||||||
component: PeopleComponent,
|
flaggedComponent: MembersComponent,
|
||||||
canActivate: [
|
featureFlag: FeatureFlag.AC2828_ProviderPortalMembersPage,
|
||||||
providerPermissionsGuard((provider: Provider) => provider.canManageUsers),
|
routeOptions: {
|
||||||
],
|
path: "people",
|
||||||
data: {
|
canActivate: [
|
||||||
titleId: "people",
|
providerPermissionsGuard((provider: Provider) => provider.canManageUsers),
|
||||||
|
],
|
||||||
|
data: {
|
||||||
|
titleId: "people",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
{
|
{
|
||||||
path: "events",
|
path: "events",
|
||||||
component: EventsComponent,
|
component: EventsComponent,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ScrollingModule } from "@angular/cdk/scrolling";
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
@ -28,7 +29,11 @@ import { CreateOrganizationComponent } from "./clients/create-organization.compo
|
|||||||
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
import { AcceptProviderComponent } from "./manage/accept-provider.component";
|
||||||
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
|
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
|
||||||
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
|
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
|
||||||
|
import { AddEditMemberDialogComponent } from "./manage/dialogs/add-edit-member-dialog.component";
|
||||||
|
import { BulkConfirmDialogComponent } from "./manage/dialogs/bulk-confirm-dialog.component";
|
||||||
|
import { BulkRemoveDialogComponent } from "./manage/dialogs/bulk-remove-dialog.component";
|
||||||
import { EventsComponent } from "./manage/events.component";
|
import { EventsComponent } from "./manage/events.component";
|
||||||
|
import { MembersComponent } from "./manage/members.component";
|
||||||
import { PeopleComponent } from "./manage/people.component";
|
import { PeopleComponent } from "./manage/people.component";
|
||||||
import { UserAddEditComponent } from "./manage/user-add-edit.component";
|
import { UserAddEditComponent } from "./manage/user-add-edit.component";
|
||||||
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
import { ProvidersLayoutComponent } from "./providers-layout.component";
|
||||||
@ -51,20 +56,25 @@ import { SetupComponent } from "./setup/setup.component";
|
|||||||
PaymentMethodWarningsModule,
|
PaymentMethodWarningsModule,
|
||||||
TaxInfoComponent,
|
TaxInfoComponent,
|
||||||
DangerZoneComponent,
|
DangerZoneComponent,
|
||||||
|
ScrollingModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptProviderComponent,
|
AcceptProviderComponent,
|
||||||
AccountComponent,
|
AccountComponent,
|
||||||
AddOrganizationComponent,
|
AddOrganizationComponent,
|
||||||
BulkConfirmComponent,
|
BulkConfirmComponent,
|
||||||
|
BulkConfirmDialogComponent,
|
||||||
BulkRemoveComponent,
|
BulkRemoveComponent,
|
||||||
|
BulkRemoveDialogComponent,
|
||||||
ClientsComponent,
|
ClientsComponent,
|
||||||
CreateOrganizationComponent,
|
CreateOrganizationComponent,
|
||||||
EventsComponent,
|
EventsComponent,
|
||||||
PeopleComponent,
|
PeopleComponent,
|
||||||
|
MembersComponent,
|
||||||
SetupComponent,
|
SetupComponent,
|
||||||
SetupProviderComponent,
|
SetupProviderComponent,
|
||||||
UserAddEditComponent,
|
UserAddEditComponent,
|
||||||
|
AddEditMemberDialogComponent,
|
||||||
CreateClientDialogComponent,
|
CreateClientDialogComponent,
|
||||||
NoClientsComponent,
|
NoClientsComponent,
|
||||||
ManageClientsComponent,
|
ManageClientsComponent,
|
||||||
|
@ -23,6 +23,7 @@ export enum FeatureFlag {
|
|||||||
GroupsComponentRefactor = "groups-component-refactor",
|
GroupsComponentRefactor = "groups-component-refactor",
|
||||||
ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner",
|
ProviderClientVaultPrivacyBanner = "ac-2833-provider-client-vault-privacy-banner",
|
||||||
VaultBulkManagementAction = "vault-bulk-management-action",
|
VaultBulkManagementAction = "vault-bulk-management-action",
|
||||||
|
AC2828_ProviderPortalMembersPage = "AC-2828_provider-portal-members-page",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AllowedFeatureFlagTypes = boolean | number | string;
|
export type AllowedFeatureFlagTypes = boolean | number | string;
|
||||||
@ -56,6 +57,7 @@ export const DefaultFeatureFlagValue = {
|
|||||||
[FeatureFlag.GroupsComponentRefactor]: FALSE,
|
[FeatureFlag.GroupsComponentRefactor]: FALSE,
|
||||||
[FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE,
|
[FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE,
|
||||||
[FeatureFlag.VaultBulkManagementAction]: FALSE,
|
[FeatureFlag.VaultBulkManagementAction]: FALSE,
|
||||||
|
[FeatureFlag.AC2828_ProviderPortalMembersPage]: FALSE,
|
||||||
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
||||||
|
|
||||||
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
||||||
|
Loading…
Reference in New Issue
Block a user