mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-21 11:35:34 +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:
parent
e516eec200
commit
cbf7c292f3
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +1,26 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||
import { combineLatest, firstValueFrom, from } from "rxjs";
|
||||
import { concatMap, switchMap, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
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 { 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 { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
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 { 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 { DialogService } from "@bitwarden/components";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { WebProviderService } from "../services/web-provider.service";
|
||||
|
||||
import { AddOrganizationComponent } from "./add-organization.component";
|
||||
import { BaseClientsComponent } from "./base-clients.component";
|
||||
|
||||
const DisallowedPlanTypes = [
|
||||
PlanType.Free,
|
||||
@ -36,90 +33,76 @@ const DisallowedPlanTypes = [
|
||||
@Component({
|
||||
templateUrl: "clients.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class ClientsComponent implements OnInit {
|
||||
export class ClientsComponent extends BaseClientsComponent {
|
||||
providerId: string;
|
||||
addableOrganizations: Organization[];
|
||||
loading = true;
|
||||
manageOrganizations = false;
|
||||
showAddExisting = false;
|
||||
|
||||
clients: ProviderOrganizationOrganizationDetailsResponse[];
|
||||
pagedClients: ProviderOrganizationOrganizationDetailsResponse[];
|
||||
|
||||
protected didScroll = false;
|
||||
protected pageSize = 100;
|
||||
protected actionPromise: Promise<unknown>;
|
||||
private pagedClientsCount = 0;
|
||||
|
||||
protected enableConsolidatedBilling$ = this.configService.getFeatureFlag$(
|
||||
protected consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.EnableConsolidatedBilling,
|
||||
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(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private providerService: ProviderService,
|
||||
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 organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
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() {
|
||||
const enableConsolidatedBilling = await firstValueFrom(this.enableConsolidatedBilling$);
|
||||
|
||||
if (enableConsolidatedBilling) {
|
||||
await this.router.navigate(["../manage-client-organizations"], { relativeTo: this.route });
|
||||
} else {
|
||||
this.route.parent.params
|
||||
.pipe(
|
||||
switchMap((params) => {
|
||||
this.providerId = params.providerId;
|
||||
return from(this.load());
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((qParams) => {
|
||||
this.searchText = qParams.search;
|
||||
});
|
||||
|
||||
this._searchText$
|
||||
.pipe(
|
||||
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((isSearchable) => {
|
||||
this.isSearching = isSearchable;
|
||||
});
|
||||
}
|
||||
ngOnInit() {
|
||||
this.activatedRoute.parent.params
|
||||
.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(["../manage-client-organizations"], {
|
||||
relativeTo: this.activatedRoute,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
return from(this.load());
|
||||
}
|
||||
}),
|
||||
);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -141,37 +124,6 @@ export class ClientsComponent implements OnInit {
|
||||
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() {
|
||||
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
|
||||
providerId: this.providerId,
|
||||
@ -182,33 +134,4 @@ export class ClientsComponent implements OnInit {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,12 @@
|
||||
<bit-nav-item
|
||||
icon="bwi-bank"
|
||||
[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-group icon="bwi-sliders" [text]="'manage' | i18n" route="manage" *ngIf="showManageTab">
|
||||
<bit-nav-item
|
||||
|
@ -4,6 +4,7 @@ import { ActivatedRoute, RouterModule } from "@angular/router";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -83,4 +84,6 @@ export class ProvidersLayoutComponent {
|
||||
return "manage/events";
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly ProviderStatusType = ProviderStatusType;
|
||||
}
|
||||
|
@ -11,14 +11,7 @@
|
||||
<bit-label>
|
||||
{{ "assignedSeats" | i18n }}
|
||||
</bit-label>
|
||||
<input
|
||||
id="assignedSeats"
|
||||
type="number"
|
||||
appAutoFocus
|
||||
bitInput
|
||||
required
|
||||
[(ngModel)]="assignedSeats"
|
||||
/>
|
||||
<input id="assignedSeats" type="number" bitInput required [(ngModel)]="assignedSeats" />
|
||||
</bit-form-field>
|
||||
<ng-container *ngIf="remainingOpenSeats > 0">
|
||||
<p>
|
||||
|
@ -1,21 +1,23 @@
|
||||
import { SelectionModel } from "@angular/cdk/collections";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { BehaviorSubject, firstValueFrom, from, lastValueFrom, Subject } from "rxjs";
|
||||
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { combineLatest, firstValueFrom, from, lastValueFrom } from "rxjs";
|
||||
import { concatMap, switchMap, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.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 { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.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 {
|
||||
@ -27,127 +29,91 @@ import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-o
|
||||
@Component({
|
||||
templateUrl: "manage-client-organizations.component.html",
|
||||
})
|
||||
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
||||
export class ManageClientOrganizationsComponent extends BaseClientsComponent {
|
||||
providerId: string;
|
||||
provider: Provider;
|
||||
|
||||
loading = true;
|
||||
manageOrganizations = false;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
private _searchText$ = new BehaviorSubject<string>("");
|
||||
private isSearching: boolean = false;
|
||||
private consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.EnableConsolidatedBilling,
|
||||
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[];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private providerService: ProviderService,
|
||||
private apiService: ApiService,
|
||||
private searchService: SearchService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private validationService: ValidationService,
|
||||
private webProviderService: WebProviderService,
|
||||
private dialogService: DialogService,
|
||||
private billingApiService: BillingApiService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.parent.params.subscribe(async (params) => {
|
||||
this.providerId = params.providerId;
|
||||
|
||||
await this.load();
|
||||
|
||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
this.searchText = qParams.search;
|
||||
});
|
||||
});
|
||||
|
||||
this._searchText$
|
||||
.pipe(
|
||||
switchMap((searchText) => from(this.searchService.isSearchable(searchText))),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe((isSearchable) => {
|
||||
this.isSearching = isSearchable;
|
||||
});
|
||||
private configService: ConfigService,
|
||||
private providerService: ProviderService,
|
||||
private router: Router,
|
||||
activatedRoute: ActivatedRoute,
|
||||
dialogService: DialogService,
|
||||
i18nService: I18nService,
|
||||
searchService: SearchService,
|
||||
toastService: ToastService,
|
||||
validationService: ValidationService,
|
||||
webProviderService: WebProviderService,
|
||||
) {
|
||||
super(
|
||||
activatedRoute,
|
||||
dialogService,
|
||||
i18nService,
|
||||
searchService,
|
||||
toastService,
|
||||
validationService,
|
||||
webProviderService,
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
ngOnInit() {
|
||||
this.activatedRoute.parent.params
|
||||
.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() {
|
||||
const clientsResponse = await this.apiService.getProviderClients(this.providerId);
|
||||
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;
|
||||
this.clients = (await this.apiService.getProviderClients(this.providerId)).data;
|
||||
|
||||
const plansResponse = await this.billingApiService.getPlans();
|
||||
this.plans = plansResponse.data;
|
||||
this.dataSource.data = this.clients;
|
||||
|
||||
this.plans = (await this.billingApiService.getPlans()).data;
|
||||
|
||||
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) {
|
||||
if (organization == null) {
|
||||
return;
|
||||
@ -161,35 +127,6 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
||||
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 () => {
|
||||
const reference = openCreateClientOrganizationDialog(this.dialogService, {
|
||||
data: {
|
||||
|
@ -7,3 +7,4 @@ export * from "./provider-type.enum";
|
||||
export * from "./provider-user-status-type.enum";
|
||||
export * from "./provider-user-type.enum";
|
||||
export * from "./scim-provider-type.enum";
|
||||
export * from "./provider-status-type.enum";
|
||||
|
@ -0,0 +1,5 @@
|
||||
export enum ProviderStatusType {
|
||||
Pending = 0,
|
||||
Created = 1,
|
||||
Billable = 2,
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { ProfileProviderResponse } from "../response/profile-provider.response";
|
||||
|
||||
export class ProviderData {
|
||||
@ -9,6 +9,7 @@ export class ProviderData {
|
||||
enabled: boolean;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
|
||||
constructor(response: ProfileProviderResponse) {
|
||||
this.id = response.id;
|
||||
@ -18,5 +19,6 @@ export class ProviderData {
|
||||
this.enabled = response.enabled;
|
||||
this.userId = response.userId;
|
||||
this.useEvents = response.useEvents;
|
||||
this.providerStatus = response.providerStatus;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { ProviderData } from "../data/provider.data";
|
||||
|
||||
export class Provider {
|
||||
@ -9,6 +9,7 @@ export class Provider {
|
||||
enabled: boolean;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
|
||||
constructor(obj?: ProviderData) {
|
||||
if (obj == null) {
|
||||
@ -22,6 +23,7 @@ export class Provider {
|
||||
this.enabled = obj.enabled;
|
||||
this.userId = obj.userId;
|
||||
this.useEvents = obj.useEvents;
|
||||
this.providerStatus = obj.providerStatus;
|
||||
}
|
||||
|
||||
get canAccess() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../../enums";
|
||||
import { PermissionsApi } from "../api/permissions.api";
|
||||
|
||||
export class ProfileProviderResponse extends BaseResponse {
|
||||
@ -12,6 +12,7 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||
permissions: PermissionsApi;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
providerStatus: ProviderStatusType;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@ -24,5 +25,6 @@ export class ProfileProviderResponse extends BaseResponse {
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.useEvents = this.getResponseProperty("UseEvents");
|
||||
this.providerStatus = this.getResponseProperty("ProviderStatus");
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from ".
|
||||
import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { ProviderUserStatusType, ProviderUserType } from "../enums";
|
||||
import { ProviderStatusType, ProviderUserStatusType, ProviderUserType } from "../enums";
|
||||
import { ProviderData } from "../models/data/provider.data";
|
||||
import { Provider } from "../models/domain/provider";
|
||||
|
||||
@ -64,6 +64,7 @@ describe("PROVIDERS key definition", () => {
|
||||
enabled: true,
|
||||
userId: "string",
|
||||
useEvents: true,
|
||||
providerStatus: ProviderStatusType.Pending,
|
||||
},
|
||||
};
|
||||
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult)));
|
||||
|
Loading…
Reference in New Issue
Block a user