PM-2053 Update Bulk Status dialog (#8928)
* PM-2053 Update Bulk Status Dialog * PM-2053 Upadate bulk status dialog * PM-2053 Updated type issues in Bulk status dialog
This commit is contained in:
parent
dd53a1c5ce
commit
92b70d983d
|
@ -1,57 +1,38 @@
|
||||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="bulkTitle">
|
<bit-dialog dialogSize="large" [title]="'bulkConfirmStatus' | i18n">
|
||||||
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
|
<ng-container bitDialogContent>
|
||||||
<div class="modal-content">
|
<div class="tw-text-center" *ngIf="loading">
|
||||||
<div class="modal-header">
|
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||||
<h1 class="modal-title" id="bulkTitle">
|
{{ "loading" | i18n }}
|
||||||
{{ "bulkConfirmStatus" | i18n }}
|
|
||||||
</h1>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
appA11yTitle="{{ 'close' | i18n }}"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="card-body text-center" *ngIf="loading">
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
|
||||||
{{ "loading" | i18n }}
|
|
||||||
</div>
|
|
||||||
<table class="table table-hover table-list" *ngIf="!loading">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="2">{{ "user" | i18n }}</th>
|
|
||||||
<th>{{ "status" | i18n }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tr *ngFor="let item of users">
|
|
||||||
<td width="30">
|
|
||||||
<bit-avatar
|
|
||||||
[text]="item.user | userName"
|
|
||||||
[id]="item.user.id"
|
|
||||||
size="small"
|
|
||||||
></bit-avatar>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.user.email }}
|
|
||||||
<small class="text-muted d-block" *ngIf="item.user.name">{{ item.user.name }}</small>
|
|
||||||
</td>
|
|
||||||
<td class="text-danger" *ngIf="item.error">
|
|
||||||
{{ item.message }}
|
|
||||||
</td>
|
|
||||||
<td *ngIf="!item.error">
|
|
||||||
{{ item.message }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
|
||||||
{{ "close" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<bit-table *ngIf="!loading">
|
||||||
</div>
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" bitCell>{{ "user" | i18n }}</th>
|
||||||
|
<th bitCell>{{ "status" | i18n }}</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body>
|
||||||
|
<tr bitRow *ngFor="let item of users">
|
||||||
|
<td width="30" bitCell>
|
||||||
|
<bit-avatar [text]="item.user | userName" [id]="item.user.id" size="small"></bit-avatar>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
{{ item.user.email }}
|
||||||
|
<small class="text-muted d-block" *ngIf="item.user.name">{{ item.user.name }}</small>
|
||||||
|
</td>
|
||||||
|
<td class="tw-text-danger" *ngIf="item.error" bitCell>
|
||||||
|
{{ item.message }}
|
||||||
|
</td>
|
||||||
|
<td *ngIf="!item.error" bitCell>
|
||||||
|
{{ item.message }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button bitButton type="button" buttonType="secondary" bitDialogClose>
|
||||||
|
{{ "close" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { Component } from "@angular/core";
|
import { DIALOG_DATA, DialogConfig } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { OrganizationUserBulkResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
import {
|
import {
|
||||||
OrganizationUserStatusType,
|
OrganizationUserStatusType,
|
||||||
ProviderUserStatusType,
|
ProviderUserStatusType,
|
||||||
} from "@bitwarden/common/admin-console/enums";
|
} from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response";
|
||||||
|
import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response";
|
||||||
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { OrganizationUserView } from "../../../core/views/organization-user.view";
|
||||||
|
|
||||||
export interface BulkUserDetails {
|
export interface BulkUserDetails {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -19,11 +29,63 @@ type BulkStatusEntry = {
|
||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type BulkStatusDialogData = {
|
||||||
|
users: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||||
|
filteredUsers: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||||
|
request: Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>>;
|
||||||
|
successfullMessage: string;
|
||||||
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-bulk-status",
|
selector: "app-bulk-status",
|
||||||
templateUrl: "bulk-status.component.html",
|
templateUrl: "bulk-status.component.html",
|
||||||
})
|
})
|
||||||
export class BulkStatusComponent {
|
export class BulkStatusComponent implements OnInit {
|
||||||
users: BulkStatusEntry[];
|
users: BulkStatusEntry[];
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(DIALOG_DATA) protected data: BulkStatusDialogData,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private logService: LogService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.loading = true;
|
||||||
|
await this.showBulkStatus(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async showBulkStatus(data: BulkStatusDialogData) {
|
||||||
|
try {
|
||||||
|
const response = await data.request;
|
||||||
|
const keyedErrors: any = response.data
|
||||||
|
.filter((r) => r.error !== "")
|
||||||
|
.reduce((a, x) => ({ ...a, [x.id]: x.error }), {});
|
||||||
|
const keyedFilteredUsers: any = data.filteredUsers.reduce(
|
||||||
|
(a, x) => ({ ...a, [x.id]: x }),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.users = data.users.map((user) => {
|
||||||
|
let message = keyedErrors[user.id] ?? data.successfullMessage;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
||||||
|
message = this.i18nService.t("bulkFilteredMessage");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: user,
|
||||||
|
error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
|
||||||
|
message: message,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static open(dialogService: DialogService, config: DialogConfig<BulkStatusDialogData>) {
|
||||||
|
return dialogService.open(BulkStatusComponent, config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||||
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 { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||||
import { OrganizationUserConfirmRequest } from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
import { OrganizationUserConfirmRequest } from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
||||||
import {
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
OrganizationUserBulkResponse,
|
|
||||||
OrganizationUserUserDetailsResponse,
|
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
|
||||||
import { PolicyApiServiceAbstraction as PolicyApiService } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
import { PolicyApiServiceAbstraction as PolicyApiService } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import {
|
import {
|
||||||
|
@ -39,7 +36,6 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||||
import { ProductType } from "@bitwarden/common/enums";
|
import { ProductType } from "@bitwarden/common/enums";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
|
||||||
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";
|
||||||
|
@ -538,12 +534,17 @@ export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
|
||||||
);
|
);
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.showBulkStatus(
|
|
||||||
users,
|
// Bulk Status component open
|
||||||
filteredUsers,
|
const dialogRef = BulkStatusComponent.open(this.dialogService, {
|
||||||
response,
|
data: {
|
||||||
this.i18nService.t("bulkReinviteMessage"),
|
users: users,
|
||||||
);
|
filteredUsers: filteredUsers,
|
||||||
|
request: response,
|
||||||
|
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await lastValueFrom(dialogRef.closed);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.validationService.showError(e);
|
this.validationService.showError(e);
|
||||||
}
|
}
|
||||||
|
@ -665,59 +666,6 @@ export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async showBulkStatus(
|
|
||||||
users: OrganizationUserView[],
|
|
||||||
filteredUsers: OrganizationUserView[],
|
|
||||||
request: Promise<ListResponse<OrganizationUserBulkResponse>>,
|
|
||||||
successfullMessage: string,
|
|
||||||
) {
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
|
||||||
BulkStatusComponent,
|
|
||||||
this.bulkStatusModalRef,
|
|
||||||
(comp) => {
|
|
||||||
comp.loading = true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Workaround to handle closing the modal shortly after it has been opened
|
|
||||||
let close = false;
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
modal.onShown.subscribe(() => {
|
|
||||||
if (close) {
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await request;
|
|
||||||
|
|
||||||
if (modal) {
|
|
||||||
const keyedErrors: any = response.data
|
|
||||||
.filter((r) => r.error !== "")
|
|
||||||
.reduce((a, x) => ({ ...a, [x.id]: x.error }), {});
|
|
||||||
const keyedFilteredUsers: any = filteredUsers.reduce((a, x) => ({ ...a, [x.id]: x }), {});
|
|
||||||
|
|
||||||
childComponent.users = users.map((user) => {
|
|
||||||
let message = keyedErrors[user.id] ?? successfullMessage;
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
|
||||||
message = this.i18nService.t("bulkFilteredMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
user: user,
|
|
||||||
error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
|
|
||||||
message: message,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
childComponent.loading = false;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
close = true;
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
||||||
return this.dialogService.openSimpleDialog({
|
return this.dialogService.openSimpleDialog({
|
||||||
title: {
|
title: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { lastValueFrom } from "rxjs";
|
||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||||
|
@ -12,7 +13,6 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr
|
||||||
import { ProviderUserStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
import { ProviderUserStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
|
||||||
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 { 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 { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response";
|
|
||||||
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 { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
|
@ -222,12 +222,17 @@ export class PeopleComponent extends BasePeopleComponent<ProviderUserUserDetails
|
||||||
const response = this.apiService.postManyProviderUserReinvite(this.providerId, request);
|
const response = this.apiService.postManyProviderUserReinvite(this.providerId, request);
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.showBulkStatus(
|
|
||||||
users,
|
// Bulk Status component open
|
||||||
filteredUsers,
|
const dialogRef = BulkStatusComponent.open(this.dialogService, {
|
||||||
response,
|
data: {
|
||||||
this.i18nService.t("bulkReinviteMessage"),
|
users: users,
|
||||||
);
|
filteredUsers: filteredUsers,
|
||||||
|
request: response,
|
||||||
|
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await lastValueFrom(dialogRef.closed);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.validationService.showError(e);
|
this.validationService.showError(e);
|
||||||
}
|
}
|
||||||
|
@ -251,56 +256,4 @@ export class PeopleComponent extends BasePeopleComponent<ProviderUserUserDetails
|
||||||
await modal.onClosedPromise();
|
await modal.onClosedPromise();
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async showBulkStatus(
|
|
||||||
users: ProviderUserUserDetailsResponse[],
|
|
||||||
filteredUsers: ProviderUserUserDetailsResponse[],
|
|
||||||
request: Promise<ListResponse<ProviderUserBulkResponse>>,
|
|
||||||
successfullMessage: string,
|
|
||||||
) {
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
|
||||||
BulkStatusComponent,
|
|
||||||
this.bulkStatusModalRef,
|
|
||||||
(comp) => {
|
|
||||||
comp.loading = true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Workaround to handle closing the modal shortly after it has been opened
|
|
||||||
let close = false;
|
|
||||||
modal.onShown.subscribe(() => {
|
|
||||||
if (close) {
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await request;
|
|
||||||
|
|
||||||
if (modal) {
|
|
||||||
const keyedErrors: any = response.data
|
|
||||||
.filter((r) => r.error !== "")
|
|
||||||
.reduce((a, x) => ({ ...a, [x.id]: x.error }), {});
|
|
||||||
const keyedFilteredUsers: any = filteredUsers.reduce((a, x) => ({ ...a, [x.id]: x }), {});
|
|
||||||
|
|
||||||
childComponent.users = users.map((user) => {
|
|
||||||
let message = keyedErrors[user.id] ?? successfullMessage;
|
|
||||||
// eslint-disable-next-line
|
|
||||||
if (!keyedFilteredUsers.hasOwnProperty(user.id)) {
|
|
||||||
message = this.i18nService.t("bulkFilteredMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
user: user,
|
|
||||||
error: keyedErrors.hasOwnProperty(user.id), // eslint-disable-line
|
|
||||||
message: message,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
childComponent.loading = false;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
close = true;
|
|
||||||
modal.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue