mirror of
https://github.com/bitwarden/browser
synced 2026-01-09 12:03:33 +00:00
[AC-1486] Feature: SM Billing Round 1 (#5747)
* [AC-1423] Update organization subscription cloud page (#5614) * [AC-1423] Add ProgressModule to shared.module.ts * [AC-1423] Update cloud subscription page styles - Remove bootstrap styles - Use CL components where applicable - Use CL typography directives - Update heading levels to prepare for new SM sections * [AC-1423] Add usePasswordManager boolean to organization domain * [AC-1423] Introduce BitwardenProductType enum * [AC-1423] Update Organization subscription line items - Add product type prefix - Indent addon services like additional storage and service accounts - Show line items for free plans * [AC-1420] Add Secrets Manager subscribe component (#5617) * [AC-1418] Add secrets manager manage subscription component (#5661) * add additional properties (#5743) * Allow autoscale limits to be removed, update naming (#5781) * [AC-1488] Store Organization.SmServiceAccounts as total not additional (#5784) * Allow autoscale limits to be removed, update naming * Display additional service accounts only * [AC-1531] Fix SM subscribe component not showing in free org billing tab (#5848) --------- Co-authored-by: Shane Melton <smelton@bitwarden.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Thomas Rittson <trittson@bitwarden.com> Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Co-authored-by: Rui Tome <rtome@bitwarden.com>
This commit is contained in:
@@ -9,8 +9,11 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { BitwardenProductType, PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
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";
|
||||
@@ -19,6 +22,7 @@ import {
|
||||
BillingSyncApiKeyComponent,
|
||||
BillingSyncApiModalData,
|
||||
} from "./billing-sync-api-key.component";
|
||||
import { SecretsManagerSubscriptionOptions } from "./secrets-manager/sm-adjust-subscription.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-subscription-cloud",
|
||||
@@ -26,6 +30,7 @@ import {
|
||||
})
|
||||
export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy {
|
||||
sub: OrganizationSubscriptionResponse;
|
||||
lineItems: BillingSubscriptionItemResponse[] = [];
|
||||
organizationId: string;
|
||||
userOrg: Organization;
|
||||
showChangePlan = false;
|
||||
@@ -33,6 +38,9 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
adjustStorageAdd = true;
|
||||
showAdjustStorage = false;
|
||||
hasBillingSyncToken: boolean;
|
||||
showAdjustSecretsManager = false;
|
||||
|
||||
showSecretsManagerSubscribe = false;
|
||||
|
||||
firstLoaded = false;
|
||||
loading: boolean;
|
||||
@@ -48,7 +56,8 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
private organizationService: OrganizationService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private route: ActivatedRoute,
|
||||
private dialogService: DialogServiceAbstraction
|
||||
private dialogService: DialogServiceAbstraction,
|
||||
private configService: ConfigServiceAbstraction
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -68,6 +77,17 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
productName(product: BitwardenProductType) {
|
||||
switch (product) {
|
||||
case BitwardenProductType.PasswordManager:
|
||||
return this.i18nService.t("passwordManager");
|
||||
case BitwardenProductType.SecretsManager:
|
||||
return this.i18nService.t("secretsManager");
|
||||
default:
|
||||
return this.i18nService.t("passwordManager");
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
@@ -81,6 +101,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
this.userOrg = this.organizationService.get(this.organizationId);
|
||||
if (this.userOrg.canViewSubscription) {
|
||||
this.sub = await this.organizationApiService.getSubscription(this.organizationId);
|
||||
this.lineItems = this.sub?.subscription?.items?.sort(sortSubscriptionItems) ?? [];
|
||||
}
|
||||
|
||||
const apiKeyResponse = await this.organizationApiService.getApiKeyInformation(
|
||||
@@ -90,7 +111,28 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
(i) => i.keyType === OrganizationApiKeyType.BillingSync
|
||||
);
|
||||
|
||||
this.showSecretsManagerSubscribe =
|
||||
this.userOrg.canEditSubscription &&
|
||||
!this.userOrg.useSecretsManager &&
|
||||
!this.subscription?.cancelled &&
|
||||
!this.subscriptionMarkedForCancel;
|
||||
|
||||
this.showAdjustSecretsManager =
|
||||
this.userOrg.canEditSubscription &&
|
||||
this.userOrg.useSecretsManager &&
|
||||
this.subscription != null &&
|
||||
this.sub.secretsManagerPlan?.hasAdditionalSeatsOption &&
|
||||
!this.subscription.cancelled &&
|
||||
!this.subscriptionMarkedForCancel;
|
||||
|
||||
this.loading = false;
|
||||
|
||||
// Remove the remaining lines when the sm-ga-billing flag is deleted
|
||||
const smBillingEnabled = await this.configService.getFeatureFlagBool(
|
||||
FeatureFlag.SecretsManagerBilling
|
||||
);
|
||||
this.showSecretsManagerSubscribe = this.showSecretsManagerSubscribe && smBillingEnabled;
|
||||
this.showAdjustSecretsManager = this.showAdjustSecretsManager && smBillingEnabled;
|
||||
}
|
||||
|
||||
get subscription() {
|
||||
@@ -138,6 +180,20 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
return this.sub.seats;
|
||||
}
|
||||
|
||||
get smOptions(): SecretsManagerSubscriptionOptions {
|
||||
return {
|
||||
seatCount: this.sub.smSeats,
|
||||
maxAutoscaleSeats: this.sub.maxAutoscaleSmSeats,
|
||||
seatPrice: this.sub.secretsManagerPlan.seatPrice,
|
||||
maxAutoscaleServiceAccounts: this.sub.maxAutoscaleSmServiceAccounts,
|
||||
additionalServiceAccounts:
|
||||
this.sub.smServiceAccounts - this.sub.secretsManagerPlan.baseServiceAccount,
|
||||
interval: this.sub.secretsManagerPlan.isAnnual ? "year" : "month",
|
||||
additionalServiceAccountPrice: this.sub.secretsManagerPlan.additionalPricePerServiceAccount,
|
||||
baseServiceAccountCount: this.sub.secretsManagerPlan.baseServiceAccount,
|
||||
};
|
||||
}
|
||||
|
||||
get maxAutoscaleSeats() {
|
||||
return this.sub.maxAutoscaleSeats;
|
||||
}
|
||||
@@ -332,3 +388,23 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
return this.subscription == null && this.sub.planType === PlanType.Free && !this.showChangePlan;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to sort subscription items by product type and then by addon status
|
||||
*/
|
||||
function sortSubscriptionItems(
|
||||
a: BillingSubscriptionItemResponse,
|
||||
b: BillingSubscriptionItemResponse
|
||||
) {
|
||||
if (a.bitwardenProduct == b.bitwardenProduct) {
|
||||
if (a.addonSubscriptionItem == b.addonSubscriptionItem) {
|
||||
return 0;
|
||||
}
|
||||
// sort addon items to the bottom
|
||||
if (a.addonSubscriptionItem) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return a.bitwardenProduct - b.bitwardenProduct;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user