mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
[PM 22968] Enforce UI Restrictions When MSP/BUP Is Disabled (#15752)
* Suspended client change and tooltips * Add the warning Icon properly * Add feature flag to the changes * Reverted it, should only show for admins * Add changes to disable AddOrganizationButton * change the default value * Refactor the code to reactive * Apply the requested changes * Remove the providerId
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
|
@let isAdminOrServiceUser = isAdminOrServiceUser$ | async;
|
||||||
<app-header>
|
<app-header>
|
||||||
<bit-search
|
<bit-search
|
||||||
class="tw-grow"
|
class="tw-grow"
|
||||||
[formControl]="searchControl"
|
[formControl]="searchControl"
|
||||||
[placeholder]="'search' | i18n"
|
[placeholder]="'search' | i18n"
|
||||||
></bit-search>
|
></bit-search>
|
||||||
<a bitButton routerLink="create" *ngIf="manageOrganizations" buttonType="primary">
|
<a bitButton routerLink="create" *ngIf="isAdminOrServiceUser" buttonType="primary">
|
||||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||||
{{ "newClient" | i18n }}
|
{{ "newClient" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
bitButton
|
bitButton
|
||||||
(click)="addExistingOrganization()"
|
(click)="addExistingOrganization()"
|
||||||
*ngIf="manageOrganizations && showAddExisting"
|
*ngIf="isAdminOrServiceUser && showAddExisting"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||||
{{ "addExistingOrganization" | i18n }}
|
{{ "addExistingOrganization" | i18n }}
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
<span>{{ row.plan }}</span>
|
<span>{{ row.plan }}</span>
|
||||||
<div appListDropdown>
|
<div appListDropdown>
|
||||||
<button
|
<button
|
||||||
*ngIf="manageOrganizations"
|
*ngIf="isAdminOrServiceUser"
|
||||||
[bitMenuTriggerFor]="removeMenu"
|
[bitMenuTriggerFor]="removeMenu"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Component } from "@angular/core";
|
|||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormControl } from "@angular/forms";
|
import { FormControl } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router, RouterModule } from "@angular/router";
|
import { ActivatedRoute, Router, RouterModule } from "@angular/router";
|
||||||
import { firstValueFrom, from, map } from "rxjs";
|
import { firstValueFrom, from, map, Observable, switchMap } from "rxjs";
|
||||||
import { debounceTime, first, switchMap } from "rxjs/operators";
|
import { debounceTime, first } from "rxjs/operators";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -54,15 +54,29 @@ const DisallowedPlanTypes = [
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ClientsComponent {
|
export class ClientsComponent {
|
||||||
providerId: string = "";
|
|
||||||
addableOrganizations: Organization[] = [];
|
addableOrganizations: Organization[] = [];
|
||||||
loading = true;
|
loading = true;
|
||||||
manageOrganizations = false;
|
|
||||||
showAddExisting = false;
|
showAddExisting = false;
|
||||||
dataSource: TableDataSource<ProviderOrganizationOrganizationDetailsResponse> =
|
dataSource: TableDataSource<ProviderOrganizationOrganizationDetailsResponse> =
|
||||||
new TableDataSource();
|
new TableDataSource();
|
||||||
protected searchControl = new FormControl("", { nonNullable: true });
|
protected searchControl = new FormControl("", { nonNullable: true });
|
||||||
|
|
||||||
|
protected providerId$: Observable<string> =
|
||||||
|
this.activatedRoute.parent?.params.pipe(map((params) => params.providerId as string)) ??
|
||||||
|
new Observable();
|
||||||
|
|
||||||
|
protected provider$ = this.providerId$.pipe(
|
||||||
|
switchMap((providerId) => this.providerService.get$(providerId)),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected isAdminOrServiceUser$ = this.provider$.pipe(
|
||||||
|
map(
|
||||||
|
(provider) =>
|
||||||
|
provider?.type === ProviderUserType.ProviderAdmin ||
|
||||||
|
provider?.type === ProviderUserType.ServiceUser,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private providerService: ProviderService,
|
private providerService: ProviderService,
|
||||||
@@ -81,24 +95,17 @@ export class ClientsComponent {
|
|||||||
this.searchControl.setValue(queryParams.search);
|
this.searchControl.setValue(queryParams.search);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activatedRoute.parent?.params
|
this.provider$
|
||||||
?.pipe(
|
.pipe(
|
||||||
switchMap((params) => {
|
map((provider) => {
|
||||||
this.providerId = params.providerId;
|
if (provider?.providerStatus === ProviderStatusType.Billable) {
|
||||||
return this.providerService.get$(this.providerId).pipe(
|
return from(
|
||||||
map((provider) => provider?.providerStatus === ProviderStatusType.Billable),
|
this.router.navigate(["../manage-client-organizations"], {
|
||||||
map((isBillable) => {
|
relativeTo: this.activatedRoute,
|
||||||
if (isBillable) {
|
}),
|
||||||
return from(
|
);
|
||||||
this.router.navigate(["../manage-client-organizations"], {
|
}
|
||||||
relativeTo: this.activatedRoute,
|
return from(this.load());
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return from(this.load());
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(),
|
takeUntilDestroyed(),
|
||||||
)
|
)
|
||||||
@@ -124,7 +131,8 @@ export class ClientsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.webProviderService.detachOrganization(this.providerId, organization.id);
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
|
await this.webProviderService.detachOrganization(providerId, organization.id);
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: "",
|
title: "",
|
||||||
@@ -137,12 +145,11 @@ export class ClientsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
const response = await this.apiService.getProviderClients(this.providerId);
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
|
const response = await this.apiService.getProviderClients(providerId);
|
||||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
const clients = response.data != null && response.data.length > 0 ? response.data : [];
|
const clients = response.data != null && response.data.length > 0 ? response.data : [];
|
||||||
this.dataSource.data = clients;
|
this.dataSource.data = clients;
|
||||||
this.manageOrganizations =
|
|
||||||
(await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin;
|
|
||||||
const candidateOrgs = (
|
const candidateOrgs = (
|
||||||
await firstValueFrom(this.organizationService.organizations$(userId))
|
await firstValueFrom(this.organizationService.organizations$(userId))
|
||||||
).filter((o) => o.isOwner && o.providerId == null);
|
).filter((o) => o.isOwner && o.providerId == null);
|
||||||
@@ -158,8 +165,9 @@ export class ClientsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addExistingOrganization() {
|
async addExistingOrganization() {
|
||||||
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
|
const dialogRef = AddOrganizationComponent.open(this.dialogService, {
|
||||||
providerId: this.providerId,
|
providerId: providerId,
|
||||||
organizations: this.addableOrganizations,
|
organizations: this.addableOrganizations,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,15 @@
|
|||||||
icon="bwi-provider"
|
icon="bwi-provider"
|
||||||
[text]="clientsTranslationKey$ | async | i18n"
|
[text]="clientsTranslationKey$ | async | i18n"
|
||||||
[route]="(isBillable | async) ? 'manage-client-organizations' : 'clients'"
|
[route]="(isBillable | async) ? 'manage-client-organizations' : 'clients'"
|
||||||
></bit-nav-item>
|
>
|
||||||
|
<i
|
||||||
|
*ngIf="!provider.enabled && (providerPortalTakeover$ | async)"
|
||||||
|
slot="end"
|
||||||
|
class="bwi bwi-exclamation-triangle tw-text-danger"
|
||||||
|
title="{{ 'providerIsDisabled' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</bit-nav-item>
|
||||||
<bit-nav-group
|
<bit-nav-group
|
||||||
icon="bwi-sliders"
|
icon="bwi-sliders"
|
||||||
[text]="'manage' | i18n"
|
[text]="'manage' | i18n"
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
protected clientsTranslationKey$: Observable<string>;
|
protected clientsTranslationKey$: Observable<string>;
|
||||||
protected managePaymentDetailsOutsideCheckout$: Observable<boolean>;
|
protected managePaymentDetailsOutsideCheckout$: Observable<boolean>;
|
||||||
|
protected providerPortalTakeover$: Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -94,6 +95,10 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy {
|
|||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
this.providerPortalTakeover$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.PM21821_ProviderPortalTakeover,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@let isSuspensionActive = suspensionActive$ | async;
|
||||||
|
@let provider = provider$ | async;
|
||||||
<app-header [title]="pageTitle">
|
<app-header [title]="pageTitle">
|
||||||
<bit-search [placeholder]="'search' | i18n" [formControl]="searchControl"></bit-search>
|
<bit-search [placeholder]="'search' | i18n" [formControl]="searchControl"></bit-search>
|
||||||
<button
|
<button
|
||||||
@@ -5,17 +7,31 @@
|
|||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
type="button"
|
type="button"
|
||||||
[bitMenuTriggerFor]="clientMenu"
|
[bitMenuTriggerFor]="clientMenu"
|
||||||
|
[disabled]="isSuspensionActive"
|
||||||
|
[title]="isSuspensionActive ? ('providerIsDisabled' | i18n) : ''"
|
||||||
appA11yTitle="{{ 'add' | i18n }}"
|
appA11yTitle="{{ 'add' | i18n }}"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||||
{{ "add" | i18n }}
|
{{ "add" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<bit-menu #clientMenu>
|
<bit-menu #clientMenu>
|
||||||
<button type="button" bitMenuItem (click)="createClient()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
[disabled]="isSuspensionActive"
|
||||||
|
[title]="isSuspensionActive ? ('providerIsDisabled' | i18n) : ''"
|
||||||
|
(click)="createClient()"
|
||||||
|
>
|
||||||
<i aria-hidden="true" class="bwi bwi-business"></i>
|
<i aria-hidden="true" class="bwi bwi-business"></i>
|
||||||
{{ newClientButtonLabel }}
|
{{ newClientButtonLabel }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitMenuItem (click)="addExistingOrganization()">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
[disabled]="isSuspensionActive"
|
||||||
|
[title]="isSuspensionActive ? ('providerIsDisabled' | i18n) : ''"
|
||||||
|
(click)="addExistingOrganization()"
|
||||||
|
>
|
||||||
<i aria-hidden="true" class="bwi bwi-filter"></i>
|
<i aria-hidden="true" class="bwi bwi-filter"></i>
|
||||||
{{ "existingOrganization" | i18n }}
|
{{ "existingOrganization" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
@@ -73,26 +89,41 @@
|
|||||||
appA11yTitle="{{ 'options' | i18n }}"
|
appA11yTitle="{{ 'options' | i18n }}"
|
||||||
></button>
|
></button>
|
||||||
<bit-menu #rowMenu>
|
<bit-menu #rowMenu>
|
||||||
<button type="button" bitMenuItem (click)="manageClientName(row)">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
[disabled]="isSuspensionActive"
|
||||||
|
[title]="isSuspensionActive ? ('providerIsDisabled' | i18n) : ''"
|
||||||
|
(click)="manageClientName(row)"
|
||||||
|
>
|
||||||
<i aria-hidden="true" class="bwi bwi-pencil-square"></i>
|
<i aria-hidden="true" class="bwi bwi-pencil-square"></i>
|
||||||
{{ "updateName" | i18n }}
|
{{ "updateName" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitMenuItem (click)="manageClientSubscription(row)">
|
<button
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
[disabled]="isSuspensionActive"
|
||||||
|
[title]="isSuspensionActive ? ('providerIsDisabled' | i18n) : ''"
|
||||||
|
(click)="manageClientSubscription(row)"
|
||||||
|
>
|
||||||
<i aria-hidden="true" class="bwi bwi-family"></i>
|
<i aria-hidden="true" class="bwi bwi-family"></i>
|
||||||
{{ "manageSubscription" | i18n }}
|
{{ "manageSubscription" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="isProviderAdmin" type="button" bitMenuItem (click)="remove(row)">
|
@if (provider?.type === ProviderUserType.ProviderAdmin) {
|
||||||
<span class="tw-text-danger">
|
<button type="button" bitMenuItem (click)="remove(row)">
|
||||||
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "unlinkOrganization" | i18n }}
|
<span class="tw-text-danger">
|
||||||
</span>
|
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "unlinkOrganization" | i18n }}
|
||||||
</button>
|
</span>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</bit-menu>
|
</bit-menu>
|
||||||
</td>
|
</td>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</bit-table-scroll>
|
</bit-table-scroll>
|
||||||
<div *ngIf="dataSource.data.length === 0" class="tw-mt-10">
|
<div *ngIf="dataSource.data.length === 0" class="tw-mt-10">
|
||||||
<app-no-clients
|
<app-no-clients
|
||||||
[showAddOrganizationButton]="isProviderAdmin"
|
[showAddOrganizationButton]="provider?.type === ProviderUserType.ProviderAdmin"
|
||||||
|
[disableAddOrganizationButton]="isSuspensionActive"
|
||||||
(addNewOrganizationClicked)="createClient()"
|
(addNewOrganizationClicked)="createClient()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,8 +2,16 @@ import { Component } from "@angular/core";
|
|||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormControl } from "@angular/forms";
|
import { FormControl } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { firstValueFrom, from, lastValueFrom, map } from "rxjs";
|
import {
|
||||||
import { debounceTime, first, switchMap } from "rxjs/operators";
|
firstValueFrom,
|
||||||
|
from,
|
||||||
|
lastValueFrom,
|
||||||
|
map,
|
||||||
|
combineLatest,
|
||||||
|
switchMap,
|
||||||
|
Observable,
|
||||||
|
} from "rxjs";
|
||||||
|
import { debounceTime, first } from "rxjs/operators";
|
||||||
|
|
||||||
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +23,8 @@ 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 } from "@bitwarden/common/billing/abstractions";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import {
|
import {
|
||||||
@@ -61,20 +71,49 @@ import { ReplacePipe } from "./replace.pipe";
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ManageClientsComponent {
|
export class ManageClientsComponent {
|
||||||
providerId: string = "";
|
|
||||||
provider: Provider | undefined;
|
|
||||||
loading = true;
|
loading = true;
|
||||||
isProviderAdmin = false;
|
|
||||||
dataSource: TableDataSource<ProviderOrganizationOrganizationDetailsResponse> =
|
dataSource: TableDataSource<ProviderOrganizationOrganizationDetailsResponse> =
|
||||||
new TableDataSource();
|
new TableDataSource();
|
||||||
|
|
||||||
protected searchControl = new FormControl("", { nonNullable: true });
|
protected searchControl = new FormControl("", { nonNullable: true });
|
||||||
protected plans: PlanResponse[] = [];
|
protected plans: PlanResponse[] = [];
|
||||||
|
protected ProviderUserType = ProviderUserType;
|
||||||
|
|
||||||
pageTitle = this.i18nService.t("clients");
|
pageTitle = this.i18nService.t("clients");
|
||||||
clientColumnHeader = this.i18nService.t("client");
|
clientColumnHeader = this.i18nService.t("client");
|
||||||
newClientButtonLabel = this.i18nService.t("newClient");
|
newClientButtonLabel = this.i18nService.t("newClient");
|
||||||
|
|
||||||
|
protected providerId$: Observable<string> =
|
||||||
|
this.activatedRoute.parent?.params.pipe(map((params) => params.providerId as string)) ??
|
||||||
|
new Observable();
|
||||||
|
|
||||||
|
protected provider$ = this.providerId$.pipe(
|
||||||
|
switchMap((providerId) => this.providerService.get$(providerId)),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected isAdminOrServiceUser$ = this.provider$.pipe(
|
||||||
|
map(
|
||||||
|
(provider) =>
|
||||||
|
provider?.type === ProviderUserType.ProviderAdmin ||
|
||||||
|
provider?.type === ProviderUserType.ServiceUser,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
protected providerPortalTakeover$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.PM21821_ProviderPortalTakeover,
|
||||||
|
);
|
||||||
|
|
||||||
|
protected suspensionActive$ = combineLatest([
|
||||||
|
this.isAdminOrServiceUser$,
|
||||||
|
this.providerPortalTakeover$,
|
||||||
|
this.provider$.pipe(map((provider) => provider?.enabled ?? false)),
|
||||||
|
]).pipe(
|
||||||
|
map(
|
||||||
|
([isAdminOrServiceUser, portalTakeoverEnabled, providerEnabled]) =>
|
||||||
|
isAdminOrServiceUser && portalTakeoverEnabled && !providerEnabled,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
private providerService: ProviderService,
|
private providerService: ProviderService,
|
||||||
@@ -86,29 +125,23 @@ export class ManageClientsComponent {
|
|||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
private webProviderService: WebProviderService,
|
private webProviderService: WebProviderService,
|
||||||
private billingNotificationService: BillingNotificationService,
|
private billingNotificationService: BillingNotificationService,
|
||||||
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => {
|
this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => {
|
||||||
this.searchControl.setValue(queryParams.search);
|
this.searchControl.setValue(queryParams.search);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.activatedRoute.parent?.params
|
this.provider$
|
||||||
?.pipe(
|
.pipe(
|
||||||
switchMap((params) => {
|
map((provider: Provider) => {
|
||||||
this.providerId = params.providerId;
|
if (provider?.providerStatus !== ProviderStatusType.Billable) {
|
||||||
return this.providerService.get$(this.providerId).pipe(
|
return from(
|
||||||
map((provider: Provider) => provider?.providerStatus === ProviderStatusType.Billable),
|
this.router.navigate(["../clients"], {
|
||||||
map((isBillable) => {
|
relativeTo: this.activatedRoute,
|
||||||
if (!isBillable) {
|
}),
|
||||||
return from(
|
);
|
||||||
this.router.navigate(["../clients"], {
|
}
|
||||||
relativeTo: this.activatedRoute,
|
return from(this.load());
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return from(this.load());
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
takeUntilDestroyed(),
|
takeUntilDestroyed(),
|
||||||
)
|
)
|
||||||
@@ -124,15 +157,15 @@ export class ManageClientsComponent {
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
try {
|
try {
|
||||||
this.provider = await firstValueFrom(this.providerService.get$(this.providerId));
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
if (this.provider?.providerType === ProviderType.BusinessUnit) {
|
const provider = await firstValueFrom(this.providerService.get$(providerId));
|
||||||
|
if (provider?.providerType === ProviderType.BusinessUnit) {
|
||||||
this.pageTitle = this.i18nService.t("businessUnits");
|
this.pageTitle = this.i18nService.t("businessUnits");
|
||||||
this.clientColumnHeader = this.i18nService.t("businessUnit");
|
this.clientColumnHeader = this.i18nService.t("businessUnit");
|
||||||
this.newClientButtonLabel = this.i18nService.t("newBusinessUnit");
|
this.newClientButtonLabel = this.i18nService.t("newBusinessUnit");
|
||||||
}
|
}
|
||||||
this.isProviderAdmin = this.provider?.type === ProviderUserType.ProviderAdmin;
|
|
||||||
this.dataSource.data = (
|
this.dataSource.data = (
|
||||||
await this.billingApiService.getProviderClientOrganizations(this.providerId)
|
await this.billingApiService.getProviderClientOrganizations(providerId)
|
||||||
).data;
|
).data;
|
||||||
this.plans = (await this.billingApiService.getPlans()).data;
|
this.plans = (await this.billingApiService.getPlans()).data;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@@ -142,10 +175,11 @@ export class ManageClientsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addExistingOrganization = async () => {
|
addExistingOrganization = async () => {
|
||||||
if (this.provider) {
|
const provider = await firstValueFrom(this.provider$);
|
||||||
|
if (provider) {
|
||||||
const reference = AddExistingOrganizationDialogComponent.open(this.dialogService, {
|
const reference = AddExistingOrganizationDialogComponent.open(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
provider: this.provider,
|
provider: provider,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -158,9 +192,10 @@ export class ManageClientsComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
createClient = async () => {
|
createClient = async () => {
|
||||||
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
const reference = openCreateClientDialog(this.dialogService, {
|
const reference = openCreateClientDialog(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
providerId: this.providerId,
|
providerId: providerId,
|
||||||
plans: this.plans,
|
plans: this.plans,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -173,9 +208,10 @@ export class ManageClientsComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
manageClientName = async (organization: ProviderOrganizationOrganizationDetailsResponse) => {
|
manageClientName = async (organization: ProviderOrganizationOrganizationDetailsResponse) => {
|
||||||
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
const dialogRef = openManageClientNameDialog(this.dialogService, {
|
const dialogRef = openManageClientNameDialog(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
providerId: this.providerId,
|
providerId: providerId,
|
||||||
organization: {
|
organization: {
|
||||||
id: organization.id,
|
id: organization.id,
|
||||||
name: organization.organizationName,
|
name: organization.organizationName,
|
||||||
@@ -194,10 +230,11 @@ export class ManageClientsComponent {
|
|||||||
manageClientSubscription = async (
|
manageClientSubscription = async (
|
||||||
organization: ProviderOrganizationOrganizationDetailsResponse,
|
organization: ProviderOrganizationOrganizationDetailsResponse,
|
||||||
) => {
|
) => {
|
||||||
|
const provider = await firstValueFrom(this.provider$);
|
||||||
const dialogRef = openManageClientSubscriptionDialog(this.dialogService, {
|
const dialogRef = openManageClientSubscriptionDialog(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
organization,
|
organization,
|
||||||
provider: this.provider!,
|
provider: provider!,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,7 +257,8 @@ export class ManageClientsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.webProviderService.detachOrganization(this.providerId, organization.id);
|
const providerId = await firstValueFrom(this.providerId$);
|
||||||
|
await this.webProviderService.detachOrganization(providerId, organization.id);
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: "",
|
title: "",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const gearIcon = svgIcon`
|
|||||||
<p class="tw-mt-4">{{ "noClients" | i18n }}</p>
|
<p class="tw-mt-4">{{ "noClients" | i18n }}</p>
|
||||||
<a
|
<a
|
||||||
*ngIf="showAddOrganizationButton"
|
*ngIf="showAddOrganizationButton"
|
||||||
|
[disabled]="disableAddOrganizationButton"
|
||||||
type="button"
|
type="button"
|
||||||
bitButton
|
bitButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
@@ -43,6 +44,7 @@ const gearIcon = svgIcon`
|
|||||||
export class NoClientsComponent {
|
export class NoClientsComponent {
|
||||||
icon = gearIcon;
|
icon = gearIcon;
|
||||||
@Input() showAddOrganizationButton = true;
|
@Input() showAddOrganizationButton = true;
|
||||||
|
@Input() disableAddOrganizationButton = false;
|
||||||
@Output() addNewOrganizationClicked = new EventEmitter();
|
@Output() addNewOrganizationClicked = new EventEmitter();
|
||||||
|
|
||||||
addNewOrganization = () => this.addNewOrganizationClicked.emit();
|
addNewOrganization = () => this.addNewOrganizationClicked.emit();
|
||||||
|
|||||||
Reference in New Issue
Block a user