mirror of
https://github.com/bitwarden/browser.git
synced 2024-08-26 23:09:46 +02:00
Added create-client-organization.component (#8767)
This commit is contained in:
parent
cbaf3462c1
commit
1e0ad09757
|
@ -7958,5 +7958,26 @@
|
|||
},
|
||||
"errorAssigningTargetFolder": {
|
||||
"message": "Error assigning target folder."
|
||||
},
|
||||
"createNewClientToManageAsProvider": {
|
||||
"message": "Create a new client organization to manage as a Provider. Additional seats will be reflected in the next billing cycle."
|
||||
},
|
||||
"selectAPlan": {
|
||||
"message": "Select a plan"
|
||||
},
|
||||
"thirtyFivePercentDiscount": {
|
||||
"message": "35% Discount"
|
||||
},
|
||||
"monthPerMember": {
|
||||
"message": "month per member"
|
||||
},
|
||||
"seats": {
|
||||
"message": "Seats"
|
||||
},
|
||||
"addOrganization": {
|
||||
"message": "Add organization"
|
||||
},
|
||||
"createdNewClient": {
|
||||
"message": "Successfully created new client"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ const routes: Routes = [
|
|||
{
|
||||
path: "manage-client-organizations",
|
||||
component: ManageClientOrganizationsComponent,
|
||||
data: { titleId: "manage-client-organizations" },
|
||||
data: { titleId: "clients" },
|
||||
},
|
||||
{
|
||||
path: "manage",
|
||||
|
|
|
@ -4,13 +4,16 @@ import { FormsModule } from "@angular/forms";
|
|||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { SearchModule } from "@bitwarden/components";
|
||||
import { DangerZoneComponent } from "@bitwarden/web-vault/app/auth/settings/account/danger-zone.component";
|
||||
import { OrganizationPlansComponent, TaxInfoComponent } from "@bitwarden/web-vault/app/billing";
|
||||
import { PaymentMethodWarningsModule } from "@bitwarden/web-vault/app/billing/shared";
|
||||
import { OssModule } from "@bitwarden/web-vault/app/oss.module";
|
||||
|
||||
import { DangerZoneComponent } from "../../../../../../apps/web/src/app/auth/settings/account/danger-zone.component";
|
||||
import { ManageClientOrganizationSubscriptionComponent } from "../../billing/providers/clients/manage-client-organization-subscription.component";
|
||||
import { ManageClientOrganizationsComponent } from "../../billing/providers/clients/manage-client-organizations.component";
|
||||
import {
|
||||
CreateClientOrganizationComponent,
|
||||
ManageClientOrganizationSubscriptionComponent,
|
||||
ManageClientOrganizationsComponent,
|
||||
} from "../../billing/providers/clients";
|
||||
|
||||
import { AddOrganizationComponent } from "./clients/add-organization.component";
|
||||
import { ClientsComponent } from "./clients/clients.component";
|
||||
|
@ -56,6 +59,7 @@ import { SetupComponent } from "./setup/setup.component";
|
|||
SetupComponent,
|
||||
SetupProviderComponent,
|
||||
UserAddEditComponent,
|
||||
CreateClientOrganizationComponent,
|
||||
ManageClientOrganizationsComponent,
|
||||
ManageClientOrganizationSubscriptionComponent,
|
||||
],
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||
import { ProviderAddOrganizationRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-add-organization.request";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billilng-api.service.abstraction";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { OrgKey } from "@bitwarden/common/types/key";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
@Injectable()
|
||||
|
@ -11,6 +18,9 @@ export class WebProviderService {
|
|||
private cryptoService: CryptoService,
|
||||
private syncService: SyncService,
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
private encryptService: EncryptService,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async addOrganizationToProvider(providerId: string, organizationId: string) {
|
||||
|
@ -28,6 +38,46 @@ export class WebProviderService {
|
|||
return response;
|
||||
}
|
||||
|
||||
async createClientOrganization(
|
||||
providerId: string,
|
||||
name: string,
|
||||
ownerEmail: string,
|
||||
planType: PlanType,
|
||||
seats: number,
|
||||
): Promise<void> {
|
||||
const organizationKey = (await this.cryptoService.makeOrgKey<OrgKey>())[1];
|
||||
|
||||
const [publicKey, encryptedPrivateKey] = await this.cryptoService.makeKeyPair(organizationKey);
|
||||
|
||||
const encryptedCollectionName = await this.encryptService.encrypt(
|
||||
this.i18nService.t("defaultCollection"),
|
||||
organizationKey,
|
||||
);
|
||||
|
||||
const providerKey = await this.cryptoService.getProviderKey(providerId);
|
||||
|
||||
const encryptedProviderKey = await this.encryptService.encrypt(
|
||||
organizationKey.key,
|
||||
providerKey,
|
||||
);
|
||||
|
||||
const request = new CreateClientOrganizationRequest();
|
||||
request.name = name;
|
||||
request.ownerEmail = ownerEmail;
|
||||
request.planType = planType;
|
||||
request.seats = seats;
|
||||
|
||||
request.key = encryptedProviderKey.encryptedString;
|
||||
request.keyPair = new OrganizationKeysRequest(publicKey, encryptedPrivateKey.encryptedString);
|
||||
request.collectionName = encryptedCollectionName.encryptedString;
|
||||
|
||||
await this.billingApiService.createClientOrganization(providerId, request);
|
||||
|
||||
await this.apiService.refreshIdentityToken();
|
||||
|
||||
await this.syncService.fullSync(true);
|
||||
}
|
||||
|
||||
async detachOrganization(providerId: string, organizationId: string): Promise<any> {
|
||||
await this.apiService.deleteProviderOrganization(providerId, organizationId);
|
||||
await this.syncService.fullSync(true);
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog dialogSize="large">
|
||||
<span bitDialogTitle class="tw-font-semibold">
|
||||
{{ "newClientOrganization" | i18n }}
|
||||
</span>
|
||||
<div bitDialogContent>
|
||||
<p>{{ "createNewClientToManageAsProvider" | i18n }}</p>
|
||||
<div class="tw-mb-3">
|
||||
<span class="tw-text-lg tw-pr-1">{{ "selectAPlan" | i18n }}</span>
|
||||
<span bitBadge variant="success">{{ "thirtyFivePercentDiscount" | i18n }}</span>
|
||||
</div>
|
||||
<ng-container>
|
||||
<div class="tw-grid tw-grid-flow-col tw-grid-cols-2 tw-gap-4 tw-mb-4">
|
||||
<div
|
||||
*ngFor="let planCard of planCards"
|
||||
[ngClass]="getPlanCardContainerClasses(planCard.selected)"
|
||||
(click)="selectPlan(planCard.name)"
|
||||
>
|
||||
<div class="tw-relative">
|
||||
<div
|
||||
*ngIf="planCard.selected"
|
||||
class="tw-bg-primary-600 tw-text-center !tw-text-contrast tw-text-sm tw-font-bold tw-py-1 group-hover:tw-bg-primary-700"
|
||||
>
|
||||
{{ "selected" | i18n }}
|
||||
</div>
|
||||
<div class="tw-p-5" [ngClass]="{ 'tw-pt-12': !planCard.selected }">
|
||||
<h3 class="tw-text-2xl tw-font-bold tw-uppercase">{{ planCard.name }}</h3>
|
||||
<span class="tw-text-2xl tw-font-semibold">{{
|
||||
planCard.cost | currency: "$"
|
||||
}}</span>
|
||||
<span class="tw-text-sm tw-font-bold">/{{ "monthPerMember" | i18n }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div class="tw-grid tw-grid-flow-col tw-grid-cols-2 tw-gap-4">
|
||||
<bit-form-field>
|
||||
<bit-label>
|
||||
{{ "organizationName" | i18n }}
|
||||
</bit-label>
|
||||
<input type="text" bitInput formControlName="organizationName" />
|
||||
</bit-form-field>
|
||||
<bit-form-field>
|
||||
<bit-label>
|
||||
{{ "clientOwnerEmail" | i18n }}
|
||||
</bit-label>
|
||||
<input type="text" bitInput formControlName="clientOwnerEmail" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-grid tw-grid-flow-col tw-grid-cols-2 tw-gap-4">
|
||||
<bit-form-field>
|
||||
<bit-label>
|
||||
{{ "seats" | i18n }}
|
||||
</bit-label>
|
||||
<input type="text" bitInput formControlName="seats" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container bitDialogFooter>
|
||||
<button bitButton bitFormButton buttonType="primary" type="submit">
|
||||
{{ "addOrganization" | i18n }}
|
||||
</button>
|
||||
<button bitButton buttonType="secondary" type="button" [bitDialogClose]="ResultType.Closed">
|
||||
{{ "close" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
|
@ -0,0 +1,142 @@
|
|||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, Inject, OnInit } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
|
||||
|
||||
type CreateClientOrganizationParams = {
|
||||
providerId: string;
|
||||
plans: PlanResponse[];
|
||||
};
|
||||
|
||||
export enum CreateClientOrganizationResultType {
|
||||
Closed = "closed",
|
||||
Submitted = "submitted",
|
||||
}
|
||||
|
||||
export const openCreateClientOrganizationDialog = (
|
||||
dialogService: DialogService,
|
||||
dialogConfig: DialogConfig<CreateClientOrganizationParams>,
|
||||
) =>
|
||||
dialogService.open<CreateClientOrganizationResultType, CreateClientOrganizationParams>(
|
||||
CreateClientOrganizationComponent,
|
||||
dialogConfig,
|
||||
);
|
||||
|
||||
type PlanCard = {
|
||||
name: string;
|
||||
cost: number;
|
||||
type: PlanType;
|
||||
selected: boolean;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-create-client-organization",
|
||||
templateUrl: "./create-client-organization.component.html",
|
||||
})
|
||||
export class CreateClientOrganizationComponent implements OnInit {
|
||||
protected ResultType = CreateClientOrganizationResultType;
|
||||
protected formGroup = this.formBuilder.group({
|
||||
clientOwnerEmail: ["", [Validators.required, Validators.email]],
|
||||
organizationName: ["", Validators.required],
|
||||
seats: [null, [Validators.required, Validators.min(1)]],
|
||||
});
|
||||
protected planCards: PlanCard[];
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) private dialogParams: CreateClientOrganizationParams,
|
||||
private dialogRef: DialogRef<CreateClientOrganizationResultType>,
|
||||
private formBuilder: FormBuilder,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private webProviderService: WebProviderService,
|
||||
) {}
|
||||
|
||||
protected getPlanCardContainerClasses(selected: boolean) {
|
||||
switch (selected) {
|
||||
case true: {
|
||||
return [
|
||||
"tw-group",
|
||||
"tw-cursor-pointer",
|
||||
"tw-block",
|
||||
"tw-rounded",
|
||||
"tw-border",
|
||||
"tw-border-solid",
|
||||
"tw-border-primary-600",
|
||||
"hover:tw-border-primary-700",
|
||||
"focus:tw-border-2",
|
||||
"focus:tw-border-primary-700",
|
||||
"focus:tw-rounded-lg",
|
||||
];
|
||||
}
|
||||
case false: {
|
||||
return [
|
||||
"tw-cursor-pointer",
|
||||
"tw-block",
|
||||
"tw-rounded",
|
||||
"tw-border",
|
||||
"tw-border-solid",
|
||||
"tw-border-secondary-300",
|
||||
"hover:tw-border-text-main",
|
||||
"focus:tw-border-2",
|
||||
"focus:tw-border-primary-700",
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
const teamsPlan = this.dialogParams.plans.find((plan) => plan.type === PlanType.TeamsMonthly);
|
||||
const enterprisePlan = this.dialogParams.plans.find(
|
||||
(plan) => plan.type === PlanType.EnterpriseMonthly,
|
||||
);
|
||||
|
||||
this.planCards = [
|
||||
{
|
||||
name: this.i18nService.t("planNameTeams"),
|
||||
cost: teamsPlan.PasswordManager.seatPrice * 0.65, // 35% off for MSPs,
|
||||
type: teamsPlan.type,
|
||||
selected: true,
|
||||
},
|
||||
{
|
||||
name: this.i18nService.t("planNameEnterprise"),
|
||||
cost: enterprisePlan.PasswordManager.seatPrice * 0.65, // 35% off for MSPs,
|
||||
type: enterprisePlan.type,
|
||||
selected: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
protected selectPlan(name: string) {
|
||||
this.planCards.find((planCard) => planCard.name === name).selected = true;
|
||||
this.planCards.find((planCard) => planCard.name !== name).selected = false;
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPlanCard = this.planCards.find((planCard) => planCard.selected);
|
||||
|
||||
await this.webProviderService.createClientOrganization(
|
||||
this.dialogParams.providerId,
|
||||
this.formGroup.value.organizationName,
|
||||
this.formGroup.value.clientOwnerEmail,
|
||||
selectedPlanCard.type,
|
||||
this.formGroup.value.seats,
|
||||
);
|
||||
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("createdNewClient"));
|
||||
|
||||
this.dialogRef.close(this.ResultType.Submitted);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./create-client-organization.component";
|
||||
export * from "./manage-client-organizations.component";
|
||||
export * from "./manage-client-organization-subscription.component";
|
|
@ -3,7 +3,7 @@ import { Component, Inject, OnInit } from "@angular/core";
|
|||
|
||||
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 { ProviderSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/provider-subscription-update.request";
|
||||
import { UpdateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/update-client-organization.request";
|
||||
import { Plans } from "@bitwarden/common/billing/models/response/provider-subscription-response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
@ -45,7 +45,7 @@ export class ManageClientOrganizationSubscriptionComponent implements OnInit {
|
|||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
const response = await this.billingApiService.getProviderClientSubscriptions(this.providerId);
|
||||
const response = await this.billingApiService.getProviderSubscription(this.providerId);
|
||||
this.AdditionalSeatPurchased = this.getPurchasedSeatsByPlan(this.planName, response.plans);
|
||||
const seatMinimum = this.getProviderSeatMinimumByPlan(this.planName, response.plans);
|
||||
const assignedByPlan = this.getAssignedByPlan(this.planName, response.plans);
|
||||
|
@ -69,10 +69,10 @@ export class ManageClientOrganizationSubscriptionComponent implements OnInit {
|
|||
return;
|
||||
}
|
||||
|
||||
const request = new ProviderSubscriptionUpdateRequest();
|
||||
const request = new UpdateClientOrganizationRequest();
|
||||
request.assignedSeats = assignedSeats;
|
||||
|
||||
await this.billingApiService.putProviderClientSubscriptions(
|
||||
await this.billingApiService.updateClientOrganization(
|
||||
this.providerId,
|
||||
this.providerOrganizationId,
|
||||
request,
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<app-header>
|
||||
<bit-search [placeholder]="'search' | i18n" [(ngModel)]="searchText"></bit-search>
|
||||
<a bitButton routerLink="create" *ngIf="manageOrganizations" buttonType="primary">
|
||||
<a
|
||||
type="button"
|
||||
bitButton
|
||||
*ngIf="manageOrganizations"
|
||||
buttonType="primary"
|
||||
(click)="createClientOrganization()"
|
||||
>
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "addNewOrganization" | i18n }}
|
||||
</a>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { SelectionModel } from "@angular/cdk/collections";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { BehaviorSubject, Subject, firstValueFrom, from } from "rxjs";
|
||||
import { BehaviorSubject, firstValueFrom, from, lastValueFrom, Subject } from "rxjs";
|
||||
import { first, switchMap, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
|
@ -9,6 +9,8 @@ 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 { 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 { 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";
|
||||
|
@ -16,6 +18,10 @@ import { DialogService, TableDataSource } from "@bitwarden/components";
|
|||
|
||||
import { WebProviderService } from "../../../admin-console/providers/services/web-provider.service";
|
||||
|
||||
import {
|
||||
CreateClientOrganizationResultType,
|
||||
openCreateClientOrganizationDialog,
|
||||
} from "./create-client-organization.component";
|
||||
import { ManageClientOrganizationSubscriptionComponent } from "./manage-client-organization-subscription.component";
|
||||
|
||||
@Component({
|
||||
|
@ -52,6 +58,7 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
|||
private pagedClientsCount = 0;
|
||||
selection = new SelectionModel<string>(true, []);
|
||||
protected dataSource = new TableDataSource<ProviderOrganizationOrganizationDetailsResponse>();
|
||||
protected plans: PlanResponse[];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -63,6 +70,7 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
|||
private validationService: ValidationService,
|
||||
private webProviderService: WebProviderService,
|
||||
private dialogService: DialogService,
|
||||
private billingApiService: BillingApiService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -94,12 +102,16 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
async load() {
|
||||
const response = await this.apiService.getProviderClients(this.providerId);
|
||||
this.clients = response.data != null && response.data.length > 0 ? response.data : [];
|
||||
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;
|
||||
|
||||
const plansResponse = await this.billingApiService.getPlans();
|
||||
this.plans = plansResponse.data;
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
|
@ -177,4 +189,21 @@ export class ManageClientOrganizationsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
createClientOrganization = async () => {
|
||||
const reference = openCreateClientOrganizationDialog(this.dialogService, {
|
||||
data: {
|
||||
providerId: this.providerId,
|
||||
plans: this.plans,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await lastValueFrom(reference.closed);
|
||||
|
||||
if (result === CreateClientOrganizationResultType.Closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.load();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1051,10 +1051,12 @@ const safeProviders: SafeProvider[] = [
|
|||
provide: OrganizationBillingServiceAbstraction,
|
||||
useClass: OrganizationBillingService,
|
||||
deps: [
|
||||
ApiServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
EncryptService,
|
||||
I18nServiceAbstraction,
|
||||
OrganizationApiServiceAbstraction,
|
||||
SyncServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
||||
import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
|
||||
import { ProviderSubscriptionUpdateRequest } from "../models/request/provider-subscription-update.request";
|
||||
import { PlanResponse } from "../../billing/models/response/plan.response";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
|
||||
import { UpdateClientOrganizationRequest } from "../models/request/update-client-organization.request";
|
||||
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";
|
||||
|
||||
export abstract class BillingApiServiceAbstraction {
|
||||
|
@ -9,11 +12,16 @@ export abstract class BillingApiServiceAbstraction {
|
|||
request: SubscriptionCancellationRequest,
|
||||
) => Promise<void>;
|
||||
cancelPremiumUserSubscription: (request: SubscriptionCancellationRequest) => Promise<void>;
|
||||
createClientOrganization: (
|
||||
providerId: string,
|
||||
request: CreateClientOrganizationRequest,
|
||||
) => Promise<void>;
|
||||
getBillingStatus: (id: string) => Promise<OrganizationBillingStatusResponse>;
|
||||
getProviderClientSubscriptions: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
||||
putProviderClientSubscriptions: (
|
||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||
getProviderSubscription: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
||||
updateClientOrganization: (
|
||||
providerId: string,
|
||||
organizationId: string,
|
||||
request: ProviderSubscriptionUpdateRequest,
|
||||
request: UpdateClientOrganizationRequest,
|
||||
) => Promise<any>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { OrganizationKeysRequest } from "../../../admin-console/models/request/organization-keys.request";
|
||||
import { PlanType } from "../../../billing/enums";
|
||||
|
||||
export class CreateClientOrganizationRequest {
|
||||
name: string;
|
||||
ownerEmail: string;
|
||||
planType: PlanType;
|
||||
seats: number;
|
||||
key: string;
|
||||
keyPair: OrganizationKeysRequest;
|
||||
collectionName: string;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export class ProviderSubscriptionUpdateRequest {
|
||||
assignedSeats: number;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export class UpdateClientOrganizationRequest {
|
||||
assignedSeats: number;
|
||||
}
|
|
@ -2,7 +2,10 @@ import { ApiService } from "../../abstractions/api.service";
|
|||
import { BillingApiServiceAbstraction } from "../../billing/abstractions/billilng-api.service.abstraction";
|
||||
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
||||
import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
|
||||
import { ProviderSubscriptionUpdateRequest } from "../models/request/provider-subscription-update.request";
|
||||
import { PlanResponse } from "../../billing/models/response/plan.response";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
|
||||
import { UpdateClientOrganizationRequest } from "../models/request/update-client-organization.request";
|
||||
import { ProviderSubscriptionResponse } from "../models/response/provider-subscription-response";
|
||||
|
||||
export class BillingApiService implements BillingApiServiceAbstraction {
|
||||
|
@ -25,6 +28,19 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
|||
return this.apiService.send("POST", "/accounts/cancel", request, true, false);
|
||||
}
|
||||
|
||||
createClientOrganization(
|
||||
providerId: string,
|
||||
request: CreateClientOrganizationRequest,
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/providers/" + providerId + "/clients",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
async getBillingStatus(id: string): Promise<OrganizationBillingStatusResponse> {
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
|
@ -37,7 +53,12 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
|||
return new OrganizationBillingStatusResponse(r);
|
||||
}
|
||||
|
||||
async getProviderClientSubscriptions(providerId: string): Promise<ProviderSubscriptionResponse> {
|
||||
async getPlans(): Promise<ListResponse<PlanResponse>> {
|
||||
const r = await this.apiService.send("GET", "/plans", null, false, true);
|
||||
return new ListResponse(r, PlanResponse);
|
||||
}
|
||||
|
||||
async getProviderSubscription(providerId: string): Promise<ProviderSubscriptionResponse> {
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
"/providers/" + providerId + "/billing/subscription",
|
||||
|
@ -48,14 +69,14 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
|||
return new ProviderSubscriptionResponse(r);
|
||||
}
|
||||
|
||||
async putProviderClientSubscriptions(
|
||||
async updateClientOrganization(
|
||||
providerId: string,
|
||||
organizationId: string,
|
||||
request: ProviderSubscriptionUpdateRequest,
|
||||
request: UpdateClientOrganizationRequest,
|
||||
): Promise<any> {
|
||||
return await this.apiService.send(
|
||||
"PUT",
|
||||
"/providers/" + providerId + "/organizations/" + organizationId,
|
||||
"/providers/" + providerId + "/clients/" + organizationId,
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
|
||||
import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request";
|
||||
|
@ -7,6 +8,7 @@ import { EncryptService } from "../../platform/abstractions/encrypt.service";
|
|||
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||
import { EncString } from "../../platform/models/domain/enc-string";
|
||||
import { OrgKey } from "../../types/key";
|
||||
import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction";
|
||||
import {
|
||||
OrganizationBillingServiceAbstraction,
|
||||
OrganizationInformation,
|
||||
|
@ -25,10 +27,12 @@ interface OrganizationKeys {
|
|||
|
||||
export class OrganizationBillingService implements OrganizationBillingServiceAbstraction {
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private cryptoService: CryptoService,
|
||||
private encryptService: EncryptService,
|
||||
private i18nService: I18nService,
|
||||
private organizationApiService: OrganizationApiService,
|
||||
private syncService: SyncService,
|
||||
) {}
|
||||
|
||||
async purchaseSubscription(subscription: SubscriptionInformation): Promise<OrganizationResponse> {
|
||||
|
@ -44,7 +48,13 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
|||
|
||||
this.setPaymentInformation(request, subscription.payment);
|
||||
|
||||
return await this.organizationApiService.create(request);
|
||||
const response = await this.organizationApiService.create(request);
|
||||
|
||||
await this.apiService.refreshIdentityToken();
|
||||
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async startFree(subscription: SubscriptionInformation): Promise<OrganizationResponse> {
|
||||
|
@ -58,7 +68,13 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
|||
|
||||
this.setPlanInformation(request, subscription.plan);
|
||||
|
||||
return await this.organizationApiService.create(request);
|
||||
const response = await this.organizationApiService.create(request);
|
||||
|
||||
await this.apiService.refreshIdentityToken();
|
||||
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private async makeOrganizationKeys(): Promise<OrganizationKeys> {
|
||||
|
|
Loading…
Reference in New Issue
Block a user