1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-05 01:53:55 +00:00
Files
browser/apps/web/src/app/billing/individual/premium/self-hosted-premium.component.ts
cyprain-okeke bcbf013cd9 [PM 27122] Individual subscription page for self-hosted customers (#17517)
* implement the self-host subscription changes

* Correct few ui changes

* Update to h1

* PR review changes

* Changes for the async cancel

* Resolve the two bug issues

* implement the review comments

* Resolve the Active issue

* Fix the space issues

* Remove the tabs for billing and payment

* revert the self-host changes

* Fix the subtitle issue
2025-12-17 21:13:18 +01:00

153 lines
5.0 KiB
TypeScript

import { CommonModule } from "@angular/common";
import { Component, DestroyRef, inject } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { firstValueFrom, lastValueFrom, map, Observable, of, switchMap } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
BadgeModule,
DialogService,
LinkModule,
SectionComponent,
ToastService,
TypographyModule,
} from "@bitwarden/components";
import { PricingCardComponent } from "@bitwarden/pricing";
import { I18nPipe } from "@bitwarden/ui-common";
import { UpdateLicenseDialogComponent } from "../../shared/update-license-dialog.component";
import { UpdateLicenseDialogResult } from "../../shared/update-license-types";
// 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: "./self-hosted-premium.component.html",
standalone: true,
imports: [
CommonModule,
SectionComponent,
BadgeModule,
TypographyModule,
LinkModule,
I18nPipe,
PricingCardComponent,
],
})
export class SelfHostedPremiumComponent {
protected cloudPremiumPageUrl$ = this.environmentService.cloudWebVaultUrl$.pipe(
map((url) => `${url}/#/settings/subscription/premium`),
);
protected cloudFamiliesPageUrl$ = this.environmentService.cloudWebVaultUrl$.pipe(
map((url) => `${url}/#/settings/subscription/premium`),
);
protected hasPremiumFromAnyOrganization$: Observable<boolean> =
this.accountService.activeAccount$.pipe(
switchMap((account) =>
account
? this.billingAccountProfileStateService.hasPremiumFromAnyOrganization$(account.id)
: of(false),
),
);
protected hasPremiumPersonally$: Observable<boolean> = this.accountService.activeAccount$.pipe(
switchMap((account) =>
account
? this.billingAccountProfileStateService.hasPremiumPersonally$(account.id)
: of(false),
),
);
protected shouldShowUpgradeView$: Observable<boolean> = this.hasPremiumPersonally$.pipe(
map((hasPremium) => !hasPremium),
);
protected premiumFeatures = [
this.i18nService.t("builtInAuthenticator"),
this.i18nService.t("secureFileStorage"),
this.i18nService.t("emergencyAccess"),
this.i18nService.t("breachMonitoring"),
this.i18nService.t("andMoreFeatures"),
];
protected familiesFeatures = [
this.i18nService.t("premiumAccounts"),
this.i18nService.t("familiesUnlimitedSharing"),
this.i18nService.t("familiesUnlimitedCollections"),
this.i18nService.t("familiesSharedStorage"),
];
private destroyRef = inject(DestroyRef);
constructor(
private accountService: AccountService,
private activatedRoute: ActivatedRoute,
private billingAccountProfileStateService: BillingAccountProfileStateService,
private dialogService: DialogService,
private environmentService: EnvironmentService,
private i18nService: I18nService,
private router: Router,
private toastService: ToastService,
) {
// Redirect premium users to subscription page
this.hasPremiumPersonally$
.pipe(
takeUntilDestroyed(this.destroyRef),
switchMap((hasPremiumPersonally) => {
if (hasPremiumPersonally) {
return this.navigateToSubscription();
}
return of(true);
}),
)
.subscribe();
}
protected openUploadLicenseDialog = async () => {
const dialogRef = UpdateLicenseDialogComponent.open(this.dialogService);
const result = await lastValueFrom(dialogRef.closed);
if (result === UpdateLicenseDialogResult.Updated) {
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("premiumUpdated"),
});
await this.navigateToSubscription();
}
};
protected navigateToSubscription = async (): Promise<boolean> =>
this.router.navigate(["../user-subscription"], { relativeTo: this.activatedRoute });
protected onPremiumUpgradeClick = async () => {
const url = await firstValueFrom(this.cloudPremiumPageUrl$);
if (!url) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("cloudUrlNotConfigured"),
});
return;
}
window.open(url, "_blank", "noopener,noreferrer");
};
protected onFamiliesUpgradeClick = async () => {
const url = await firstValueFrom(this.cloudFamiliesPageUrl$);
if (!url) {
this.toastService.showToast({
variant: "error",
title: "",
message: this.i18nService.t("cloudUrlNotConfigured"),
});
return;
}
window.open(url, "_blank", "noopener,noreferrer");
};
}