1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +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 <bit-avatar [text]="o.name" [id]="o.id" size="small"></bit-avatar>
type="button" </td>
class="close" <td bitCell>
data-dismiss="modal" {{ o.name }}
appA11yTitle="{{ 'close' | i18n }}" </td>
> <td bitCell>
<span aria-hidden="true">&times;</span> <button type="button" bitButton [bitAction]="add(o)" class="tw-float-right">Add</button>
</button> </td>
</div> </tr>
<div class="modal-body"> </ng-template>
<div class="card-body text-center" *ngIf="loading"> </bit-table>
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i> </ng-container>
{{ "loading" | i18n }} <ng-container bitDialogFooter>
</div> <button type="button" bitButton bitDialogClose>
<ng-container *ngIf="!loading"> {{ "close" | i18n }}
<table class="table table-hover table-list"> </button>
<tr *ngFor="let o of organizations"> </ng-container>
<td width="30"> </bit-dialog>
<bit-avatar [text]="o.name" [id]="o.id" size="small"></bit-avatar>
</td>
<td>
{{ o.name }}
</td>
<td>
<button
type="button"
class="btn btn-outline-secondary pull-right"
(click)="add(o)"
[disabled]="formPromise"
>
Add
</button>
</td>
</tr>
</table>
</ng-container>
</div>
</div>
</div>
</div>

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,52 +39,53 @@ 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) { const confirmed = await this.dialogService.openSimpleDialog({
return; title: organization.name,
} content: {
key: "addOrganizationConfirmation",
placeholders: [organization.name, this.provider.name],
},
type: "warning",
});
const confirmed = await this.dialogService.openSimpleDialog({ if (!confirmed) {
title: organization.name, return false;
content: { }
key: "addOrganizationConfirmation",
placeholders: [organization.name, this.provider.name],
},
type: "warning",
});
if (!confirmed) { try {
return false; await this.webProviderService.addOrganizationToProvider(
} this.data.providerId,
organization.id
);
} catch (e) {
this.validationService.showError(e);
return;
}
try { this.platformUtilsService.showToast(
this.formPromise = this.webProviderService.addOrganizationToProvider( "success",
this.providerId, null,
organization.id this.i18nService.t("organizationJoinedProvider")
); );
await this.formPromise;
} catch (e) {
this.validationService.showError(e);
return;
} finally {
this.formPromise = null;
}
this.platformUtilsService.showToast( this.dialogRef.close(true);
"success", };
null, }
this.i18nService.t("organizationJoinedProvider")
); static open(dialogService: DialogService, data: AddOrganizationDialogData) {
this.onAddedOrganization.emit(); 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; if (await firstValueFrom(dialogRef.closed)) {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe await this.load();
comp.onAddedOrganization.subscribe(async () => { }
try {
await this.load();
modal.close();
} catch (e) {
this.logService.error(`Handled exception: ${e}`);
}
});
}
);
} }
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);