1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 22:03:36 +00:00

[PM-3538] Migrate AddOrganizationComponent Web (#6275)

Migrate add organization in provider portal to use component library.
This commit is contained in:
Oscar Hinton
2023-10-16 15:43:38 +02:00
committed by GitHub
parent ee2f2e1fb1
commit 2dc94ede97
6 changed files with 84 additions and 130 deletions

View File

@@ -1,47 +1,25 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="addTitle"> <bit-dialog [loading]="loading">
<div class="modal-dialog modal-dialog-scrollable" role="document"> <span bitDialogTitle>{{ "addExistingOrganization" | i18n }}</span>
<div class="modal-content"> <ng-container bitDialogContent>
<div class="modal-header"> <bit-table>
<h2 class="modal-title" id="addTitle"> <ng-template body>
{{ "addExistingOrganization" | i18n }} <tr bitRow *ngFor="let o of data.organizations">
</h2> <td bitCell width="30">
<button
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</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>
<ng-container *ngIf="!loading">
<table class="table table-hover table-list">
<tr *ngFor="let o of organizations">
<td width="30">
<bit-avatar [text]="o.name" [id]="o.id" size="small"></bit-avatar> <bit-avatar [text]="o.name" [id]="o.id" size="small"></bit-avatar>
</td> </td>
<td> <td bitCell>
{{ o.name }} {{ o.name }}
</td> </td>
<td> <td bitCell>
<button <button type="button" bitButton [bitAction]="add(o)" class="tw-float-right">Add</button>
type="button"
class="btn btn-outline-secondary pull-right"
(click)="add(o)"
[disabled]="formPromise"
>
Add
</button>
</td> </td>
</tr> </tr>
</table> </ng-template>
</bit-table>
</ng-container> </ng-container>
</div> <ng-container bitDialogFooter>
</div> <button type="button" bitButton bitDialogClose>
</div> {{ "close" | i18n }}
</div> </button>
</ng-container>
</bit-dialog>

View File

@@ -1,4 +1,5 @@
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
@@ -10,20 +11,21 @@ import { DialogService } from "@bitwarden/components";
import { WebProviderService } from "../services/web-provider.service"; import { WebProviderService } from "../services/web-provider.service";
interface AddOrganizationDialogData {
providerId: string;
organizations: Organization[];
}
@Component({ @Component({
selector: "provider-add-organization",
templateUrl: "add-organization.component.html", templateUrl: "add-organization.component.html",
}) })
export class AddOrganizationComponent implements OnInit { export class AddOrganizationComponent implements OnInit {
@Input() providerId: string; protected provider: Provider;
@Input() organizations: Organization[]; protected loading = true;
@Output() onAddedOrganization = new EventEmitter();
provider: Provider;
formPromise: Promise<any>;
loading = true;
constructor( constructor(
private dialogRef: DialogRef,
@Inject(DIALOG_DATA) protected data: AddOrganizationDialogData,
private providerService: ProviderService, private providerService: ProviderService,
private webProviderService: WebProviderService, private webProviderService: WebProviderService,
private i18nService: I18nService, private i18nService: I18nService,
@@ -37,21 +39,17 @@ export class AddOrganizationComponent implements OnInit {
} }
async load() { async load() {
if (this.providerId == null) { if (this.data.providerId == null) {
return; return;
} }
this.provider = await this.providerService.get(this.providerId); this.provider = await this.providerService.get(this.data.providerId);
this.loading = false; this.loading = false;
} }
async add(organization: Organization) { add(organization: Organization) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises return async () => {
if (this.formPromise) {
return;
}
const confirmed = await this.dialogService.openSimpleDialog({ const confirmed = await this.dialogService.openSimpleDialog({
title: organization.name, title: organization.name,
content: { content: {
@@ -66,16 +64,13 @@ export class AddOrganizationComponent implements OnInit {
} }
try { try {
this.formPromise = this.webProviderService.addOrganizationToProvider( await this.webProviderService.addOrganizationToProvider(
this.providerId, this.data.providerId,
organization.id organization.id
); );
await this.formPromise;
} catch (e) { } catch (e) {
this.validationService.showError(e); this.validationService.showError(e);
return; return;
} finally {
this.formPromise = null;
} }
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
@@ -83,6 +78,14 @@ export class AddOrganizationComponent implements OnInit {
null, null,
this.i18nService.t("organizationJoinedProvider") this.i18nService.t("organizationJoinedProvider")
); );
this.onAddedOrganization.emit();
this.dialogRef.close(true);
};
}
static open(dialogService: DialogService, data: AddOrganizationDialogData) {
return dialogService.open<boolean, AddOrganizationDialogData>(AddOrganizationComponent, {
data,
});
} }
} }

View File

@@ -97,5 +97,3 @@
</table> </table>
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-template #add></ng-template>

View File

@@ -1,5 +1,6 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ModalService } from "@bitwarden/angular/services/modal.service";
@@ -33,8 +34,6 @@ const DisallowedPlanTypes = [
}) })
// eslint-disable-next-line rxjs-angular/prefer-takeuntil // eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ClientsComponent implements OnInit { export class ClientsComponent implements OnInit {
@ViewChild("add", { read: ViewContainerRef, static: true }) addModalRef: ViewContainerRef;
providerId: string; providerId: string;
searchText: string; searchText: string;
addableOrganizations: Organization[]; addableOrganizations: Organization[];
@@ -135,23 +134,14 @@ export class ClientsComponent implements OnInit {
} }
async addExistingOrganization() { async addExistingOrganization() {
const [modal] = await this.modalService.openViewRef( const dialogRef = AddOrganizationComponent.open(this.dialogService, {
AddOrganizationComponent, providerId: this.providerId,
this.addModalRef, organizations: this.addableOrganizations,
(comp) => {
comp.providerId = this.providerId;
comp.organizations = this.addableOrganizations;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
comp.onAddedOrganization.subscribe(async () => {
try {
await this.load();
modal.close();
} catch (e) {
this.logService.error(`Handled exception: ${e}`);
}
}); });
if (await firstValueFrom(dialogRef.closed)) {
await this.load();
} }
);
} }
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) { async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {

View File

@@ -1,9 +1,8 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { ComponentFactoryResolver, NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { SearchModule } from "@bitwarden/components"; import { SearchModule } from "@bitwarden/components";
import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing"; import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing";
import { OssModule } from "@bitwarden/web-vault/app/oss.module"; import { OssModule } from "@bitwarden/web-vault/app/oss.module";
@@ -56,11 +55,4 @@ import { SetupComponent } from "./setup/setup.component";
], ],
providers: [WebProviderService, ProviderPermissionsGuard], providers: [WebProviderService, ProviderPermissionsGuard],
}) })
export class ProvidersModule { export class ProvidersModule {}
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {
modalService.registerComponentFactoryResolver(
AddOrganizationComponent,
componentFactoryResolver
);
}
}

View File

@@ -88,13 +88,6 @@ export class ModalService {
return modalRef; return modalRef;
} }
registerComponentFactoryResolver<T>(
componentType: Type<T>,
componentFactoryResolver: ComponentFactoryResolver
): void {
this.factoryResolvers.set(componentType, componentFactoryResolver);
}
resolveComponentFactory<T>(componentType: Type<T>): ComponentFactory<T> { resolveComponentFactory<T>(componentType: Type<T>): ComponentFactory<T> {
if (this.factoryResolvers.has(componentType)) { if (this.factoryResolvers.has(componentType)) {
return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType); return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType);