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">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title" id="bulkTitle">
|
||||
{{ "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>
|
||||
<bit-dialog dialogSize="large" [title]="'bulkConfirmStatus' | i18n">
|
||||
<ng-container bitDialogContent>
|
||||
<div class="tw-text-center" *ngIf="loading">
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
{{ "loading" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<bit-table *ngIf="!loading">
|
||||
<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 {
|
||||
OrganizationUserStatusType,
|
||||
ProviderUserStatusType,
|
||||
} 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 {
|
||||
id: string;
|
||||
|
@ -19,11 +29,63 @@ type BulkStatusEntry = {
|
|||
message: string;
|
||||
};
|
||||
|
||||
type BulkStatusDialogData = {
|
||||
users: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||
filteredUsers: Array<OrganizationUserView | ProviderUserUserDetailsResponse>;
|
||||
request: Promise<ListResponse<OrganizationUserBulkResponse | ProviderUserBulkResponse>>;
|
||||
successfullMessage: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-bulk-status",
|
||||
templateUrl: "bulk-status.component.html",
|
||||
})
|
||||
export class BulkStatusComponent {
|
||||
export class BulkStatusComponent implements OnInit {
|
||||
users: BulkStatusEntry[];
|
||||
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 { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||
import { OrganizationUserConfirmRequest } from "@bitwarden/common/admin-console/abstractions/organization-user/requests";
|
||||
import {
|
||||
OrganizationUserBulkResponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
} from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||
import { 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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
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 { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.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.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.showBulkStatus(
|
||||
users,
|
||||
filteredUsers,
|
||||
response,
|
||||
this.i18nService.t("bulkReinviteMessage"),
|
||||
);
|
||||
|
||||
// Bulk Status component open
|
||||
const dialogRef = BulkStatusComponent.open(this.dialogService, {
|
||||
data: {
|
||||
users: users,
|
||||
filteredUsers: filteredUsers,
|
||||
request: response,
|
||||
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||
},
|
||||
});
|
||||
await lastValueFrom(dialogRef.closed);
|
||||
} catch (e) {
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
|
@ -665,59 +666,6 @@ export class PeopleComponent extends BasePeopleComponent<OrganizationUserView> {
|
|||
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) {
|
||||
return this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
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 { 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 { 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 { 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);
|
||||
// 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
|
||||
this.showBulkStatus(
|
||||
users,
|
||||
filteredUsers,
|
||||
response,
|
||||
this.i18nService.t("bulkReinviteMessage"),
|
||||
);
|
||||
|
||||
// Bulk Status component open
|
||||
const dialogRef = BulkStatusComponent.open(this.dialogService, {
|
||||
data: {
|
||||
users: users,
|
||||
filteredUsers: filteredUsers,
|
||||
request: response,
|
||||
successfullMessage: this.i18nService.t("bulkReinviteMessage"),
|
||||
},
|
||||
});
|
||||
await lastValueFrom(dialogRef.closed);
|
||||
} catch (e) {
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
|
@ -251,56 +256,4 @@ export class PeopleComponent extends BasePeopleComponent<ProviderUserUserDetails
|
|||
await modal.onClosedPromise();
|
||||
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