diff --git a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts index 26d0c43ff8f..cdccaaab8ab 100644 --- a/apps/web/src/app/billing/individual/individual-billing-routing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing-routing.module.ts @@ -2,15 +2,15 @@ import { inject, NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { map } from "rxjs"; -import { componentRouteSwap } from "@bitwarden/angular/utils/component-route-swap"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { AccountPaymentDetailsComponent } from "@bitwarden/web-vault/app/billing/individual/payment-details/account-payment-details.component"; +import { SelfHostedPremiumComponent } from "@bitwarden/web-vault/app/billing/individual/premium/self-hosted-premium.component"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; -import { PremiumVNextComponent } from "./premium/premium-vnext.component"; -import { PremiumComponent } from "./premium/premium.component"; +import { CloudHostedPremiumVNextComponent } from "./premium/cloud-hosted-premium-vnext.component"; +import { CloudHostedPremiumComponent } from "./premium/cloud-hosted-premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -26,22 +26,55 @@ const routes: Routes = [ component: UserSubscriptionComponent, data: { titleId: "premiumMembership" }, }, - ...componentRouteSwap( - PremiumComponent, - PremiumVNextComponent, - () => { - const configService = inject(ConfigService); - const platformUtilsService = inject(PlatformUtilsService); + /** + * Three-Route Matching Strategy for /premium: + * + * Routes are evaluated in order using canMatch guards. The first route that matches will be selected. + * + * 1. Self-Hosted Environment → SelfHostedPremiumComponent + * - Matches when platformUtilsService.isSelfHost() === true + * + * 2. Cloud-Hosted + Feature Flag Enabled → CloudHostedPremiumVNextComponent + * - Only evaluated if Route 1 doesn't match (not self-hosted) + * - Matches when PM24033PremiumUpgradeNewDesign feature flag === true + * + * 3. Cloud-Hosted + Feature Flag Disabled → CloudHostedPremiumComponent (Fallback) + * - No canMatch guard, so this always matches as the fallback route + * - Used when neither Route 1 nor Route 2 match + */ + // Route 1: Self-Hosted -> SelfHostedPremiumComponent + { + path: "premium", + component: SelfHostedPremiumComponent, + data: { titleId: "goPremium" }, + canMatch: [ + () => { + const platformUtilsService = inject(PlatformUtilsService); + return platformUtilsService.isSelfHost(); + }, + ], + }, + // Route 2: Cloud Hosted + FF -> CloudHostedPremiumVNextComponent + { + path: "premium", + component: CloudHostedPremiumVNextComponent, + data: { titleId: "goPremium" }, + canMatch: [ + () => { + const configService = inject(ConfigService); - return configService - .getFeatureFlag$(FeatureFlag.PM24033PremiumUpgradeNewDesign) - .pipe(map((flagValue) => flagValue === true && !platformUtilsService.isSelfHost())); - }, - { - data: { titleId: "goPremium" }, - path: "premium", - }, - ), + return configService + .getFeatureFlag$(FeatureFlag.PM24033PremiumUpgradeNewDesign) + .pipe(map((flagValue) => flagValue === true)); + }, + ], + }, + // Route 3: Cloud Hosted + FF Disabled -> CloudHostedPremiumComponent (Fallback) + { + path: "premium", + component: CloudHostedPremiumComponent, + data: { titleId: "goPremium" }, + }, { path: "payment-details", component: AccountPaymentDetailsComponent, diff --git a/apps/web/src/app/billing/individual/individual-billing.module.ts b/apps/web/src/app/billing/individual/individual-billing.module.ts index 56c40002f1d..200df5d9f07 100644 --- a/apps/web/src/app/billing/individual/individual-billing.module.ts +++ b/apps/web/src/app/billing/individual/individual-billing.module.ts @@ -11,7 +11,7 @@ import { BillingSharedModule } from "../shared"; import { BillingHistoryViewComponent } from "./billing-history-view.component"; import { IndividualBillingRoutingModule } from "./individual-billing-routing.module"; -import { PremiumComponent } from "./premium/premium.component"; +import { CloudHostedPremiumComponent } from "./premium/cloud-hosted-premium.component"; import { SubscriptionComponent } from "./subscription.component"; import { UserSubscriptionComponent } from "./user-subscription.component"; @@ -28,7 +28,7 @@ import { UserSubscriptionComponent } from "./user-subscription.component"; SubscriptionComponent, BillingHistoryViewComponent, UserSubscriptionComponent, - PremiumComponent, + CloudHostedPremiumComponent, ], }) export class IndividualBillingModule {} diff --git a/apps/web/src/app/billing/individual/premium/premium-vnext.component.html b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.html similarity index 97% rename from apps/web/src/app/billing/individual/premium/premium-vnext.component.html rename to apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.html index ee2bef9baa3..6b168901b2e 100644 --- a/apps/web/src/app/billing/individual/premium/premium-vnext.component.html +++ b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.html @@ -7,7 +7,7 @@ -
diff --git a/apps/web/src/app/billing/individual/premium/premium-vnext.component.ts b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.ts
similarity index 94%
rename from apps/web/src/app/billing/individual/premium/premium-vnext.component.ts
rename to apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.ts
index 334e84d1451..9fb34a6ccf0 100644
--- a/apps/web/src/app/billing/individual/premium/premium-vnext.component.ts
+++ b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium-vnext.component.ts
@@ -21,7 +21,6 @@ import {
PersonalSubscriptionPricingTier,
PersonalSubscriptionPricingTierIds,
} from "@bitwarden/common/billing/types/subscription-pricing-tier";
-import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import {
BadgeModule,
@@ -52,7 +51,7 @@ const RouteParamValues = {
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
- templateUrl: "./premium-vnext.component.html",
+ templateUrl: "./cloud-hosted-premium-vnext.component.html",
standalone: true,
imports: [
CommonModule,
@@ -64,7 +63,7 @@ const RouteParamValues = {
PricingCardComponent,
],
})
-export class PremiumVNextComponent {
+export class CloudHostedPremiumVNextComponent {
protected hasPremiumFromAnyOrganization$: Observable
+
{{
"premiumPriceWithFamilyPlan"
| i18n: (premiumPrice$ | async | currency: "$") : familyPlanMaxUserCount
@@ -65,24 +65,9 @@
{{ "bitwardenFamiliesPlan" | i18n }}
{{ "goPremium" | i18n }}
+ {{ "goPremium" | i18n }}