1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 22:03:36 +00:00

[PM-13470] Allow creating clients for multi-org providers (#11890)

This commit is contained in:
Jonas Hendrickx
2024-11-11 07:57:09 +01:00
committed by GitHub
parent e91741b146
commit 2bbe4d2cba
7 changed files with 54 additions and 24 deletions

View File

@@ -9017,6 +9017,12 @@
"providerPlan": { "providerPlan": {
"message": "Managed Service Provider" "message": "Managed Service Provider"
}, },
"managedServiceProvider": {
"message": "Managed service provider"
},
"multiOrganizationEnterprise": {
"message": "Multi-organization enterprise"
},
"orgSeats": { "orgSeats": {
"message": "Organization Seats" "message": "Organization Seats"
}, },

View File

@@ -12,7 +12,7 @@
}}</span> }}</span>
</div> </div>
<ng-container> <ng-container>
<div class="tw-grid tw-grid-flow-col tw-grid-cols-2 tw-gap-4 tw-mb-4"> <div class="tw-grid tw-grid-flow-col tw-auto-cols-[minmax(0,_2fr)] tw-gap-4 tw-mb-4">
<div <div
*ngFor="let planCard of planCards" *ngFor="let planCard of planCards"
[ngClass]="getPlanCardContainerClasses(planCard.selected)" [ngClass]="getPlanCardContainerClasses(planCard.selected)"

View File

@@ -3,7 +3,7 @@ import { Component, Inject, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms"; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { PlanType } from "@bitwarden/common/billing/enums"; import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response"; import { ProviderPlanResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -103,30 +103,35 @@ export class CreateClientDialogComponent implements OnInit {
this.providerPlans = response?.plans ?? []; this.providerPlans = response?.plans ?? [];
const teamsPlan = this.dialogParams.plans.find((plan) => plan.type === PlanType.TeamsMonthly);
const enterprisePlan = this.dialogParams.plans.find(
(plan) => plan.type === PlanType.EnterpriseMonthly,
);
this.discountPercentage = response.discountPercentage; this.discountPercentage = response.discountPercentage;
const discountFactor = this.discountPercentage ? (100 - this.discountPercentage) / 100 : 1; const discountFactor = this.discountPercentage ? (100 - this.discountPercentage) / 100 : 1;
this.planCards = [ this.planCards = [];
{
name: this.i18nService.t("planNameTeams"), for (let i = 0; i < this.providerPlans.length; i++) {
cost: teamsPlan.PasswordManager.providerPortalSeatPrice * discountFactor, const providerPlan = this.providerPlans[i];
type: teamsPlan.type, const plan = this.dialogParams.plans.find((plan) => plan.type === providerPlan.type);
plan: teamsPlan,
selected: true, let planName: string;
}, switch (plan.productTier) {
{ case ProductTierType.Teams: {
name: this.i18nService.t("planNameEnterprise"), planName = this.i18nService.t("planNameTeams");
cost: enterprisePlan.PasswordManager.providerPortalSeatPrice * discountFactor, break;
type: enterprisePlan.type, }
plan: enterprisePlan, case ProductTierType.Enterprise: {
selected: false, planName = this.i18nService.t("planNameEnterprise");
}, break;
]; }
}
this.planCards.push({
name: planName,
cost: plan.PasswordManager.providerPortalSeatPrice * discountFactor,
type: plan.type,
plan: plan,
selected: i === 0,
});
}
this.loading = false; this.loading = false;
} }

View File

@@ -4,7 +4,7 @@
</bit-callout> </bit-callout>
<dl class="tw-grid tw-grid-flow-col tw-grid-rows-2"> <dl class="tw-grid tw-grid-flow-col tw-grid-rows-2">
<dt>{{ "billingPlan" | i18n }}</dt> <dt>{{ "billingPlan" | i18n }}</dt>
<dd>{{ "providerPlan" | i18n }}</dd> <dd>{{ plan | i18n }}</dd>
<ng-container *ngIf="data.status && data.date"> <ng-container *ngIf="data.status && data.date">
<dt>{{ data.status.label }}</dt> <dt>{{ data.status.label }}</dt>
<dd> <dd>

View File

@@ -1,6 +1,7 @@
import { DatePipe } from "@angular/common"; import { DatePipe } from "@angular/common";
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { ProviderType } from "@bitwarden/common/admin-console/enums";
import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response"; import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -32,6 +33,15 @@ export class ProviderSubscriptionStatusComponent {
private i18nService: I18nService, private i18nService: I18nService,
) {} ) {}
get plan(): string {
switch (this.subscription.providerType) {
case ProviderType.Msp:
return "managedServiceProvider";
case ProviderType.MultiOrganizationEnterprise:
return "multiOrganizationEnterprise";
}
}
get status(): string { get status(): string {
if (this.subscription.cancelAt && this.subscription.status === "active") { if (this.subscription.cancelAt && this.subscription.status === "active") {
return "pending_cancellation"; return "pending_cancellation";

View File

@@ -1,4 +1,5 @@
export enum ProviderType { export enum ProviderType {
Msp = 0, Msp = 0,
Reseller = 1, Reseller = 1,
MultiOrganizationEnterprise = 2,
} }

View File

@@ -1,3 +1,5 @@
import { ProviderType } from "@bitwarden/common/admin-console/enums";
import { PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response"; import { SubscriptionSuspensionResponse } from "@bitwarden/common/billing/models/response/subscription-suspension.response";
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
@@ -13,6 +15,7 @@ export class ProviderSubscriptionResponse extends BaseResponse {
taxInformation?: TaxInfoResponse; taxInformation?: TaxInfoResponse;
cancelAt?: string; cancelAt?: string;
suspension?: SubscriptionSuspensionResponse; suspension?: SubscriptionSuspensionResponse;
providerType: ProviderType;
constructor(response: any) { constructor(response: any) {
super(response); super(response);
@@ -34,6 +37,7 @@ export class ProviderSubscriptionResponse extends BaseResponse {
if (suspension != null) { if (suspension != null) {
this.suspension = new SubscriptionSuspensionResponse(suspension); this.suspension = new SubscriptionSuspensionResponse(suspension);
} }
this.providerType = this.getResponseProperty("providerType");
} }
} }
@@ -44,6 +48,8 @@ export class ProviderPlanResponse extends BaseResponse {
purchasedSeats: number; purchasedSeats: number;
cost: number; cost: number;
cadence: string; cadence: string;
type: PlanType;
productTier: ProductTierType;
constructor(response: any) { constructor(response: any) {
super(response); super(response);
@@ -53,5 +59,7 @@ export class ProviderPlanResponse extends BaseResponse {
this.purchasedSeats = this.getResponseProperty("PurchasedSeats"); this.purchasedSeats = this.getResponseProperty("PurchasedSeats");
this.cost = this.getResponseProperty("Cost"); this.cost = this.getResponseProperty("Cost");
this.cadence = this.getResponseProperty("Cadence"); this.cadence = this.getResponseProperty("Cadence");
this.type = this.getResponseProperty("Type");
this.productTier = this.getResponseProperty("ProductTier");
} }
} }