1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-25 12:15:18 +01:00

[AC-2485] Add redirects to clients components based on FF and provider status (#8839)

* Add provider clients redirects based on FF and provider status

* Fixing broken test
This commit is contained in:
Alex Morask 2024-04-25 15:27:06 -04:00 committed by GitHub
parent e516eec200
commit cbf7c292f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 287 additions and 283 deletions

View File

@ -0,0 +1,130 @@
import { SelectionModel } from "@angular/cdk/collections";
import { Directive, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { BehaviorSubject, from, Subject, switchMap } from "rxjs";
import { first, takeUntil } from "rxjs/operators";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogService, TableDataSource, ToastService } from "@bitwarden/components";
import { WebProviderService } from "../services/web-provider.service";
@Directive()
export abstract class BaseClientsComponent implements OnInit, OnDestroy {
protected destroy$ = new Subject<void>();
private searchText$ = new BehaviorSubject<string>("");
get searchText() {
return this.searchText$.value;
}
set searchText(value: string) {
this.searchText$.next(value);
this.selection.clear();
this.dataSource.filter = value;
}
private searching = false;
protected scrolled = false;
protected pageSize = 100;
private pagedClientsCount = 0;
protected selection = new SelectionModel<string>(true, []);
protected clients: ProviderOrganizationOrganizationDetailsResponse[];
protected pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
protected dataSource = new TableDataSource<ProviderOrganizationOrganizationDetailsResponse>();
abstract providerId: string;
protected constructor(
protected activatedRoute: ActivatedRoute,
protected dialogService: DialogService,
private i18nService: I18nService,
private searchService: SearchService,
private toastService: ToastService,
private validationService: ValidationService,
private webProviderService: WebProviderService,
) {}
abstract load(): Promise<void>;
ngOnInit() {
this.activatedRoute.queryParams
.pipe(first(), takeUntil(this.destroy$))
.subscribe((queryParams) => {
this.searchText = queryParams.search;
});
this.searchText$
.pipe(
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
takeUntil(this.destroy$),
)
.subscribe((isSearchable) => {
this.searching = isSearchable;
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
isPaging() {
if (this.searching && this.scrolled) {
this.resetPaging();
}
return !this.searching && this.clients && this.clients.length > this.pageSize;
}
resetPaging() {
this.pagedClients = [];
this.loadMore();
}
loadMore() {
if (!this.clients || this.clients.length <= this.pageSize) {
return;
}
const pagedLength = this.pagedClients.length;
let pagedSize = this.pageSize;
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
pagedSize = this.pagedClientsCount;
}
if (this.clients.length > pagedLength) {
this.pagedClients = this.pagedClients.concat(
this.clients.slice(pagedLength, pagedLength + pagedSize),
);
}
this.pagedClientsCount = this.pagedClients.length;
this.scrolled = this.pagedClients.length > this.pageSize;
}
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
const confirmed = await this.dialogService.openSimpleDialog({
title: organization.organizationName,
content: { key: "detachOrganizationConfirmation" },
type: "warning",
});
if (!confirmed) {
return;
}
try {
await this.webProviderService.detachOrganization(this.providerId, organization.id);
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("detachedOrganization", organization.organizationName),
});
await this.load();
} catch (e) {
this.validationService.showError(e);
}
}
}

View File

@ -1,29 +1,26 @@
import { Component, OnInit } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs"; import { combineLatest, firstValueFrom, from } from "rxjs";
import { first, switchMap, takeUntil } from "rxjs/operators"; import { concatMap, switchMap, takeUntil } from "rxjs/operators";
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 { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { PlanType } from "@bitwarden/common/billing/enums"; import { PlanType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { WebProviderService } from "../services/web-provider.service"; import { WebProviderService } from "../services/web-provider.service";
import { AddOrganizationComponent } from "./add-organization.component"; import { AddOrganizationComponent } from "./add-organization.component";
import { BaseClientsComponent } from "./base-clients.component";
const DisallowedPlanTypes = [ const DisallowedPlanTypes = [
PlanType.Free, PlanType.Free,
@ -36,90 +33,76 @@ const DisallowedPlanTypes = [
@Component({ @Component({
templateUrl: "clients.component.html", templateUrl: "clients.component.html",
}) })
// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ClientsComponent extends BaseClientsComponent {
export class ClientsComponent implements OnInit {
providerId: string; providerId: string;
addableOrganizations: Organization[]; addableOrganizations: Organization[];
loading = true; loading = true;
manageOrganizations = false; manageOrganizations = false;
showAddExisting = false; showAddExisting = false;
clients: ProviderOrganizationOrganizationDetailsResponse[]; protected consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
protected didScroll = false;
protected pageSize = 100;
protected actionPromise: Promise<unknown>;
private pagedClientsCount = 0;
protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$(
FeatureFlag.EnableConsolidatedBilling, FeatureFlag.EnableConsolidatedBilling,
false, false,
); );
private destroy$ = new Subject<void>();
private _searchText$ = new BehaviorSubject<string>("");
private isSearching: boolean = false;
get searchText() {
return this._searchText$.value;
}
set searchText(value: string) {
this._searchText$.next(value);
}
constructor( constructor(
private route: ActivatedRoute,
private router: Router, private router: Router,
private providerService: ProviderService, private providerService: ProviderService,
private apiService: ApiService, private apiService: ApiService,
private searchService: SearchService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private validationService: ValidationService,
private webProviderService: WebProviderService,
private logService: LogService,
private modalService: ModalService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction,
private dialogService: DialogService,
private configService: ConfigService, private configService: ConfigService,
) {} activatedRoute: ActivatedRoute,
dialogService: DialogService,
i18nService: I18nService,
searchService: SearchService,
toastService: ToastService,
validationService: ValidationService,
webProviderService: WebProviderService,
) {
super(
activatedRoute,
dialogService,
i18nService,
searchService,
toastService,
validationService,
webProviderService,
);
}
async ngOnInit() { ngOnInit() {
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$); this.activatedRoute.parent.params
.pipe(
if (enableConsolidatedBilling) { switchMap((params) => {
await this.router.navigate(["../manage-client-organizations"], { relativeTo: this.route }); this.providerId = params.providerId;
} else { return combineLatest([
this.route.parent.params this.providerService.get(this.providerId),
.pipe( this.consolidatedBillingEnabled$,
switchMap((params) => { ]).pipe(
this.providerId = params.providerId; concatMap(([provider, consolidatedBillingEnabled]) => {
return from(this.load()); if (
}), consolidatedBillingEnabled &&
takeUntil(this.destroy$), provider.providerStatus === ProviderStatusType.Billable
) ) {
.subscribe(); return from(
this.router.navigate(["../manage-client-organizations"], {
this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((qParams) => { relativeTo: this.activatedRoute,
this.searchText = qParams.search; }),
}); );
} else {
this._searchText$ return from(this.load());
.pipe( }
switchMap((searchText) => from(this.searchService.isSearchable(searchText))), }),
takeUntil(this.destroy$), );
) }),
.subscribe((isSearchable) => { takeUntil(this.destroy$),
this.isSearching = isSearchable; )
}); .subscribe();
}
} }
ngOnDestroy() { ngOnDestroy() {
this.destroy$.next(); super.ngOnDestroy();
this.destroy$.complete();
} }
async load() { async load() {
@ -141,37 +124,6 @@ export class ClientsComponent implements OnInit {
this.loading = false; this.loading = false;
} }
isPaging() {
const searching = this.isSearching;
if (searching && this.didScroll) {
this.resetPaging();
}
return !searching && this.clients && this.clients.length > this.pageSize;
}
resetPaging() {
this.pagedClients = [];
this.loadMore();
}
loadMore() {
if (!this.clients || this.clients.length <= this.pageSize) {
return;
}
const pagedLength = this.pagedClients.length;
let pagedSize = this.pageSize;
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
pagedSize = this.pagedClientsCount;
}
if (this.clients.length > pagedLength) {
this.pagedClients = this.pagedClients.concat(
this.clients.slice(pagedLength, pagedLength + pagedSize),
);
}
this.pagedClientsCount = this.pagedClients.length;
this.didScroll = this.pagedClients.length > this.pageSize;
}
async addExistingOrganization() { async addExistingOrganization() {
const dialogRef = AddOrganizationComponent.open(this.dialogService, { const dialogRef = AddOrganizationComponent.open(this.dialogService, {
providerId: this.providerId, providerId: this.providerId,
@ -182,33 +134,4 @@ export class ClientsComponent implements OnInit {
await this.load(); await this.load();
} }
} }
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
const confirmed = await this.dialogService.openSimpleDialog({
title: organization.organizationName,
content: { key: "detachOrganizationConfirmation" },
type: "warning",
});
if (!confirmed) {
return false;
}
this.actionPromise = this.webProviderService.detachOrganization(
this.providerId,
organization.id,
);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("detachedOrganization", organization.organizationName),
);
await this.load();
} catch (e) {
this.validationService.showError(e);
}
this.actionPromise = null;
}
} }

View File

@ -7,7 +7,12 @@
<bit-nav-item <bit-nav-item
icon="bwi-bank" icon="bwi-bank"
[text]="'clients' | i18n" [text]="'clients' | i18n"
[route]="(enableConsolidatedBilling$ | async) ? 'manage-client-organizations' : 'clients'" [route]="
(enableConsolidatedBilling$ | async) &&
provider.providerStatus === ProviderStatusType.Billable
? 'manage-client-organizations'
: 'clients'
"
></bit-nav-item> ></bit-nav-item>
<bit-nav-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab"> <bit-nav-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab">
<bit-nav-item <bit-nav-item

View File

@ -4,6 +4,7 @@ import { ActivatedRoute, RouterModule } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { ProviderStatusType } from "@bitwarden/common/admin-console/enums";
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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@ -83,4 +84,6 @@ export class ProvidersLayoutComponent {
return "manage/events"; return "manage/events";
} }
} }
protected readonly ProviderStatusType = ProviderStatusType;
} }

View File

@ -11,14 +11,7 @@
<bit-label> <bit-label>
{{ "assignedSeats" | i18n }} {{ "assignedSeats" | i18n }}
</bit-label> </bit-label>
<input <input id="assignedSeats" type="number" bitInput required [(ngModel)]="assignedSeats" />
id="assignedSeats"
type="number"
appAutoFocus
bitInput
required
[(ngModel)]="assignedSeats"
/>
</bit-form-field> </bit-form-field>
<ng-container *ngIf="remainingOpenSeats > 0"> <ng-container *ngIf="remainingOpenSeats > 0">
<p> <p>

View File

@ -1,21 +1,23 @@
import { SelectionModel } from "@angular/cdk/collections"; import { Component } from "@angular/core";
import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router";
import { ActivatedRoute } from "@angular/router"; import { combineLatest, firstValueFrom, from, lastValueFrom } from "rxjs";
import { BehaviorSubject, firstValueFrom, from, lastValueFrom, Subject } from "rxjs"; import { concatMap, switchMap, takeUntil } from "rxjs/operators";
import { first, switchMap, takeUntil } from "rxjs/operators";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction"; import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogService, TableDataSource } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { BaseClientsComponent } from "../../../admin-console/providers/clients/base-clients.component";
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service"; import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
import { import {
@ -27,127 +29,91 @@ import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-o
@Component({ @Component({
templateUrl: "manage-client-organizations.component.html", templateUrl: "manage-client-organizations.component.html",
}) })
export class ManageClientOrganizationsComponent extends BaseClientsComponent {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
providerId: string; providerId: string;
provider: Provider;
loading = true; loading = true;
manageOrganizations = false; manageOrganizations = false;
private destroy$ = new Subject<void>(); private consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
private _searchText$ = new BehaviorSubject<string>(""); FeatureFlag.EnableConsolidatedBilling,
private isSearching: boolean = false; false,
);
get searchText() {
return this._searchText$.value;
}
set searchText(search: string) {
this._searchText$.value;
this.selection.clear();
this.dataSource.filter = search;
}
clients: ProviderOrganizationOrganizationDetailsResponse[];
pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
protected didScroll = false;
protected pageSize = 100;
protected actionPromise: Promise<unknown>;
private pagedClientsCount = 0;
selection = new SelectionModel<string>(true, []);
protected dataSource = new TableDataSource<ProviderOrganizationOrganizationDetailsResponse>();
protected plans: PlanResponse[]; protected plans: PlanResponse[];
constructor( constructor(
private route: ActivatedRoute,
private providerService: ProviderService,
private apiService: ApiService, private apiService: ApiService,
private searchService: SearchService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private validationService: ValidationService,
private webProviderService: WebProviderService,
private dialogService: DialogService,
private billingApiService: BillingApiService, private billingApiService: BillingApiService,
) {} private configService: ConfigService,
private providerService: ProviderService,
async ngOnInit() { private router: Router,
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe activatedRoute: ActivatedRoute,
this.route.parent.params.subscribe(async (params) => { dialogService: DialogService,
this.providerId = params.providerId; i18nService: I18nService,
searchService: SearchService,
await this.load(); toastService: ToastService,
validationService: ValidationService,
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ webProviderService: WebProviderService,
this.route.queryParams.pipe(first()).subscribe(async (qParams) => { ) {
this.searchText = qParams.search; super(
}); activatedRoute,
}); dialogService,
i18nService,
this._searchText$ searchService,
.pipe( toastService,
switchMap((searchText) => from(this.searchService.isSearchable(searchText))), validationService,
takeUntil(this.destroy$), webProviderService,
) );
.subscribe((isSearchable) => {
this.isSearching = isSearchable;
});
} }
ngOnDestroy(): void { ngOnInit() {
this.destroy$.next(); this.activatedRoute.parent.params
this.destroy$.complete(); .pipe(
switchMap((params) => {
this.providerId = params.providerId;
return combineLatest([
this.providerService.get(this.providerId),
this.consolidatedBillingEnabled$,
]).pipe(
concatMap(([provider, consolidatedBillingEnabled]) => {
if (
!consolidatedBillingEnabled ||
provider.providerStatus !== ProviderStatusType.Billable
) {
return from(
this.router.navigate(["../clients"], {
relativeTo: this.activatedRoute,
}),
);
} else {
this.provider = provider;
this.manageOrganizations = this.provider.type === ProviderUserType.ProviderAdmin;
return from(this.load());
}
}),
);
}),
takeUntil(this.destroy$),
)
.subscribe();
}
ngOnDestroy() {
super.ngOnDestroy();
} }
async load() { async load() {
const clientsResponse = await this.apiService.getProviderClients(this.providerId); this.clients = (await this.apiService.getProviderClients(this.providerId)).data;
this.clients =
clientsResponse.data != null && clientsResponse.data.length > 0 ? clientsResponse.data : [];
this.dataSource.data = this.clients;
this.manageOrganizations =
(await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin;
const plansResponse = await this.billingApiService.getPlans(); this.dataSource.data = this.clients;
this.plans = plansResponse.data;
this.plans = (await this.billingApiService.getPlans()).data;
this.loading = false; this.loading = false;
} }
isPaging() {
const searching = this.isSearching;
if (searching && this.didScroll) {
// 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.resetPaging();
}
return !searching && this.clients && this.clients.length > this.pageSize;
}
async resetPaging() {
this.pagedClients = [];
this.loadMore();
}
loadMore() {
if (!this.clients || this.clients.length <= this.pageSize) {
return;
}
const pagedLength = this.pagedClients.length;
let pagedSize = this.pageSize;
if (pagedLength === 0 && this.pagedClientsCount > this.pageSize) {
pagedSize = this.pagedClientsCount;
}
if (this.clients.length > pagedLength) {
this.pagedClients = this.pagedClients.concat(
this.clients.slice(pagedLength, pagedLength + pagedSize),
);
}
this.pagedClientsCount = this.pagedClients.length;
this.didScroll = this.pagedClients.length > this.pageSize;
}
async manageSubscription(organization: ProviderOrganizationOrganizationDetailsResponse) { async manageSubscription(organization: ProviderOrganizationOrganizationDetailsResponse) {
if (organization == null) { if (organization == null) {
return; return;
@ -161,35 +127,6 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
await this.load(); await this.load();
} }
async remove(organization: ProviderOrganizationOrganizationDetailsResponse) {
const confirmed = await this.dialogService.openSimpleDialog({
title: organization.organizationName,
content: { key: "detachOrganizationConfirmation" },
type: "warning",
});
if (!confirmed) {
return false;
}
this.actionPromise = this.webProviderService.detachOrganization(
this.providerId,
organization.id,
);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("detachedOrganization", organization.organizationName),
);
await this.load();
} catch (e) {
this.validationService.showError(e);
}
this.actionPromise = null;
}
createClientOrganization = async () => { createClientOrganization = async () => {
const reference = openCreateClientOrganizationDialog(this.dialogService, { const reference = openCreateClientOrganizationDialog(this.dialogService, {
data: { data: {

View File

@ -7,3 +7,4 @@ export * from "./provider-type.enum";
export * from "./provider-user-status-type.enum"; export * from "./provider-user-status-type.enum";
export * from "./provider-user-type.enum"; export * from "./provider-user-type.enum";
export * from "./scim-provider-type.enum"; export * from "./scim-provider-type.enum";
export * from "./provider-status-type.enum";

View File

@ -0,0 +1,5 @@
export enum ProviderStatusType {
Pending = 0,
Created = 1,
Billable = 2,
}

View File

@ -1,4 +1,4 @@
import { ProviderUserStatusType, ProviderUserType } from "../../enums"; import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
import { ProfileProviderResponse } from "../response/profile-provider.response"; import { ProfileProviderResponse } from "../response/profile-provider.response";
export class ProviderData { export class ProviderData {
@ -9,6 +9,7 @@ export class ProviderData {
enabled: boolean; enabled: boolean;
userId: string; userId: string;
useEvents: boolean; useEvents: boolean;
providerStatus: ProviderStatusType;
constructor(response: ProfileProviderResponse) { constructor(response: ProfileProviderResponse) {
this.id = response.id; this.id = response.id;
@ -18,5 +19,6 @@ export class ProviderData {
this.enabled = response.enabled; this.enabled = response.enabled;
this.userId = response.userId; this.userId = response.userId;
this.useEvents = response.useEvents; this.useEvents = response.useEvents;
this.providerStatus = response.providerStatus;
} }
} }

View File

@ -1,4 +1,4 @@
import { ProviderUserStatusType, ProviderUserType } from "../../enums"; import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
import { ProviderData } from "../data/provider.data"; import { ProviderData } from "../data/provider.data";
export class Provider { export class Provider {
@ -9,6 +9,7 @@ export class Provider {
enabled: boolean; enabled: boolean;
userId: string; userId: string;
useEvents: boolean; useEvents: boolean;
providerStatus: ProviderStatusType;
constructor(obj?: ProviderData) { constructor(obj?: ProviderData) {
if (obj == null) { if (obj == null) {
@ -22,6 +23,7 @@ export class Provider {
this.enabled = obj.enabled; this.enabled = obj.enabled;
this.userId = obj.userId; this.userId = obj.userId;
this.useEvents = obj.useEvents; this.useEvents = obj.useEvents;
this.providerStatus = obj.providerStatus;
} }
get canAccess() { get canAccess() {

View File

@ -1,5 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response"; import { BaseResponse } from "../../../models/response/base.response";
import { ProviderUserStatusType, ProviderUserType } from "../../enums"; import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
import { PermissionsApi } from "../api/permissions.api"; import { PermissionsApi } from "../api/permissions.api";
export class ProfileProviderResponse extends BaseResponse { export class ProfileProviderResponse extends BaseResponse {
@ -12,6 +12,7 @@ export class ProfileProviderResponse extends BaseResponse {
permissions: PermissionsApi; permissions: PermissionsApi;
userId: string; userId: string;
useEvents: boolean; useEvents: boolean;
providerStatus: ProviderStatusType;
constructor(response: any) { constructor(response: any) {
super(response); super(response);
@ -24,5 +25,6 @@ export class ProfileProviderResponse extends BaseResponse {
this.permissions = new PermissionsApi(this.getResponseProperty("permissions")); this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
this.userId = this.getResponseProperty("UserId"); this.userId = this.getResponseProperty("UserId");
this.useEvents = this.getResponseProperty("UseEvents"); this.useEvents = this.getResponseProperty("UseEvents");
this.providerStatus = this.getResponseProperty("ProviderStatus");
} }
} }

View File

@ -2,7 +2,7 @@ import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from ".
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state"; import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
import { Utils } from "../../platform/misc/utils"; import { Utils } from "../../platform/misc/utils";
import { UserId } from "../../types/guid"; import { UserId } from "../../types/guid";
import { ProviderUserStatusType, ProviderUserType } from "../enums"; import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../enums";
import { ProviderData } from "../models/data/provider.data"; import { ProviderData } from "../models/data/provider.data";
import { Provider } from "../models/domain/provider"; import { Provider } from "../models/domain/provider";
@ -64,6 +64,7 @@ describe("PROVIDERS key definition", () => {
enabled: true, enabled: true,
userId: "string", userId: "string",
useEvents: true, useEvents: true,
providerStatus: ProviderStatusType.Pending,
}, },
}; };
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult))); const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));