From 2eaff7a8fafd4850d25c6b0efe0166dd18cdf891 Mon Sep 17 00:00:00 2001 From: Stephon Brown Date: Wed, 28 Jan 2026 17:49:43 -0500 Subject: [PATCH] feat(billing): improves premium org upgrade dialog Adds a close button to the premium organization upgrade dialog. Updates the success toast message after upgrading to teams. Hides the formatted amount for credit discounts. Sets the change detection strategy to OnPush for improved performance. --- ...mium-org-upgrade-payment.component.spec.ts | 2 +- .../premium-org-upgrade-payment.component.ts | 2 +- .../premium-org-upgrade.component.html | 11 ++++++++ .../premium-org-upgrade.component.ts | 25 ++++++++++++++++--- .../subscription-pricing-card-details.ts | 2 +- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.spec.ts b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.spec.ts index fdfc3e7f4f7..38c60173f77 100644 --- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.spec.ts +++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.spec.ts @@ -337,7 +337,7 @@ describe("PremiumOrgUpgradePaymentComponent", () => { expect(mockToastService.showToast).toHaveBeenCalledWith({ variant: "success", - message: "organizationUpdated", + message: "plansUpdated", }); expect(completeSpy).toHaveBeenCalledWith({ status: PremiumOrgUpgradePaymentStatus.UpgradedToTeams, diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts index c7ca7a88383..380e116911c 100644 --- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts +++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade-payment/premium-org-upgrade-payment.component.ts @@ -177,7 +177,7 @@ export class PremiumOrgUpgradePaymentComponent implements OnInit, AfterViewInit type: "amount-off", value: this.estimatedInvoice().credit, translationKey: "premiumMembershipDiscount", - quantity: 1, + hideFormattedAmount: true, }, }; }); diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.html b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.html index 69d238cb521..8f8bd11a02b 100644 --- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.html +++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.html @@ -4,6 +4,17 @@ cdkTrapFocus cdkTrapFocusAutoCapture > +
+ +

diff --git a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.ts b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.ts index 5a5d21d363e..fa5112f9971 100644 --- a/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.ts +++ b/apps/web/src/app/billing/individual/upgrade/premium-org-upgrade/premium-org-upgrade.component.ts @@ -1,6 +1,13 @@ import { CdkTrapFocus } from "@angular/cdk/a11y"; import { CommonModule } from "@angular/common"; -import { Component, DestroyRef, OnInit, output, signal } from "@angular/core"; +import { + Component, + DestroyRef, + OnInit, + output, + signal, + ChangeDetectionStrategy, +} from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { combineLatest, catchError, of } from "rxjs"; @@ -16,15 +23,21 @@ import { SubscriptionCadenceIds, } from "@bitwarden/common/billing/types/subscription-pricing-tier"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { ButtonType, DialogModule, ToastService } from "@bitwarden/components"; import { PricingCardComponent } from "@bitwarden/pricing"; import { SharedModule } from "../../../../shared"; import { BillingServicesModule } from "../../../services"; +export const PremiumOrgUpgradeStatus = { + Closed: "closed", + ProceededToPayment: "proceeded-to-payment", +} as const; + +export type PremiumOrgUpgradeStatus = UnionOfValues; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: "app-premium-org-upgrade", imports: [ CommonModule, @@ -38,6 +51,9 @@ import { BillingServicesModule } from "../../../services"; }) export class PremiumOrgUpgradeComponent implements OnInit { planSelected = output(); + closeClicked = output(); + protected closedStatus = PremiumOrgUpgradeStatus.Closed; + protected readonly loading = signal(true); protected familiesCardDetails!: SubscriptionPricingCardDetails; protected teamsCardDetails!: SubscriptionPricingCardDetails; @@ -129,11 +145,13 @@ export class PremiumOrgUpgradeComponent implements OnInit { } let priceAmount: number | undefined; + let shouldShowPerUser = false; if ("annualPrice" in tier.passwordManager) { priceAmount = tier.passwordManager.annualPrice; } else if ("annualPricePerUser" in tier.passwordManager) { priceAmount = tier.passwordManager.annualPricePerUser; + shouldShowPerUser = true; } return { @@ -144,6 +162,7 @@ export class PremiumOrgUpgradeComponent implements OnInit { ? { amount: priceAmount / 12, cadence: SubscriptionCadenceIds.Monthly, + showPerUser: shouldShowPerUser, } : undefined, button: { diff --git a/libs/angular/src/billing/types/subscription-pricing-card-details.ts b/libs/angular/src/billing/types/subscription-pricing-card-details.ts index 9000b10a729..fb13706a69a 100644 --- a/libs/angular/src/billing/types/subscription-pricing-card-details.ts +++ b/libs/angular/src/billing/types/subscription-pricing-card-details.ts @@ -4,7 +4,7 @@ import { ButtonType } from "@bitwarden/components"; export type SubscriptionPricingCardDetails = { title: string; tagline: string; - price?: { amount: number; cadence: SubscriptionCadence }; + price?: { amount: number; cadence: SubscriptionCadence; showPerUser?: boolean }; button: { text: string; type: ButtonType; icon?: { type: string; position: "before" | "after" } }; features: string[]; };