[PM 5007] migrate premium component (#8387)
* premium component migration * premium component migration * premium component migration * premium component migration
This commit is contained in:
parent
1180c60e91
commit
30ef66139e
|
@ -1,129 +1,143 @@
|
||||||
<div *ngIf="selfHosted" class="page-header">
|
<bit-section>
|
||||||
<h1>{{ "subscription" | i18n }}</h1>
|
<h2 *ngIf="!selfHosted" bitTypography="h2">{{ "goPremium" | i18n }}</h2>
|
||||||
</div>
|
<bit-callout
|
||||||
<div *ngIf="!selfHosted" class="tabbed-header">
|
type="info"
|
||||||
<h1>{{ "goPremium" | i18n }}</h1>
|
*ngIf="canAccessPremium$ | async"
|
||||||
</div>
|
title="{{ 'youHavePremiumAccess' | i18n }}"
|
||||||
<bit-callout
|
icon="bwi bwi-star-f"
|
||||||
type="info"
|
|
||||||
*ngIf="canAccessPremium$ | async"
|
|
||||||
title="{{ 'youHavePremiumAccess' | i18n }}"
|
|
||||||
icon="bwi bwi-star-f"
|
|
||||||
>
|
|
||||||
{{ "alreadyPremiumFromOrg" | i18n }}
|
|
||||||
</bit-callout>
|
|
||||||
<bit-callout type="success">
|
|
||||||
<p>{{ "premiumUpgradeUnlockFeatures" | i18n }}</p>
|
|
||||||
<ul class="bwi-ul">
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpStorage" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpTwoStepOptions" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpEmergency" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpReports" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpTotp" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpSupport" | i18n }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<i class="bwi bwi-check text-success bwi-li" aria-hidden="true"></i>
|
|
||||||
{{ "premiumSignUpFuture" | i18n }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p class="text-lg" [ngClass]="{ 'mb-0': !selfHosted }">
|
|
||||||
{{
|
|
||||||
"premiumPriceWithFamilyPlan" | i18n: (premiumPrice | currency: "$") : familyPlanMaxUserCount
|
|
||||||
}}
|
|
||||||
<a routerLink="/create-organization" [queryParams]="{ plan: 'families' }">{{
|
|
||||||
"bitwardenFamiliesPlan" | i18n
|
|
||||||
}}</a>
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
bitButton
|
|
||||||
href="{{ this.cloudWebVaultUrl }}/#/settings/subscription/premium"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
buttonType="secondary"
|
|
||||||
*ngIf="selfHosted"
|
|
||||||
>
|
>
|
||||||
{{ "purchasePremium" | i18n }}
|
{{ "alreadyPremiumFromOrg" | i18n }}
|
||||||
</a>
|
</bit-callout>
|
||||||
</bit-callout>
|
<bit-callout type="success">
|
||||||
<ng-container *ngIf="selfHosted">
|
<p>{{ "premiumUpgradeUnlockFeatures" | i18n }}</p>
|
||||||
<p>{{ "uploadLicenseFilePremium" | i18n }}</p>
|
<ul class="bwi-ul">
|
||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
<li>
|
||||||
<div class="form-group">
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
<label for="file">{{ "licenseFile" | i18n }}</label>
|
{{ "premiumSignUpStorage" | i18n }}
|
||||||
<input type="file" id="file" class="form-control-file" name="file" required />
|
</li>
|
||||||
<small class="form-text text-muted">{{
|
<li>
|
||||||
"licenseFileDesc" | i18n: "bitwarden_premium_license.json"
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
}}</small>
|
{{ "premiumSignUpTwoStepOptions" | i18n }}
|
||||||
</div>
|
</li>
|
||||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
<li>
|
||||||
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
|
{{ "premiumSignUpEmergency" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
|
{{ "premiumSignUpReports" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
|
{{ "premiumSignUpTotp" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
|
{{ "premiumSignUpSupport" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="bwi bwi-check tw-text-success bwi-li" aria-hidden="true"></i>
|
||||||
|
{{ "premiumSignUpFuture" | i18n }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p bitTypography="body1" [ngClass]="{ 'tw-mb-0': !selfHosted }">
|
||||||
|
{{
|
||||||
|
"premiumPriceWithFamilyPlan" | i18n: (premiumPrice | currency: "$") : familyPlanMaxUserCount
|
||||||
|
}}
|
||||||
|
<a
|
||||||
|
bitLink
|
||||||
|
linkType="primary"
|
||||||
|
routerLink="/create-organization"
|
||||||
|
[queryParams]="{ plan: 'families' }"
|
||||||
|
>{{ "bitwardenFamiliesPlan" | i18n }}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
bitButton
|
||||||
|
href="{{ this.cloudWebVaultUrl }}/#/settings/subscription/premium"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
buttonType="secondary"
|
||||||
|
*ngIf="selfHosted"
|
||||||
|
>
|
||||||
|
{{ "purchasePremium" | i18n }}
|
||||||
|
</a>
|
||||||
|
</bit-callout>
|
||||||
|
</bit-section>
|
||||||
|
<bit-section *ngIf="selfHosted">
|
||||||
|
<p bitTypography="body1">{{ "uploadLicenseFilePremium" | i18n }}</p>
|
||||||
|
<form [formGroup]="licenseForm" [bitSubmit]="submit">
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>{{ "licenseFile" | i18n }}</bit-label>
|
||||||
|
<div>
|
||||||
|
<button bitButton type="button" buttonType="secondary" (click)="fileSelector.click()">
|
||||||
|
{{ "chooseFile" | i18n }}
|
||||||
|
</button>
|
||||||
|
{{ this.licenseFile ? this.licenseFile.name : ("noFileChosen" | i18n) }}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
bitInput
|
||||||
|
#fileSelector
|
||||||
|
type="file"
|
||||||
|
formControlName="file"
|
||||||
|
(change)="setSelectedFile($event)"
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
<bit-hint>{{ "licenseFileDesc" | i18n: "bitwarden_premium_license.json" }}</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
|
<button type="submit" buttonType="primary" bitButton bitFormButton>
|
||||||
{{ "submit" | i18n }}
|
{{ "submit" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</ng-container>
|
</bit-section>
|
||||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="!selfHosted">
|
<form [formGroup]="addonForm" [bitSubmit]="submit" *ngIf="!selfHosted">
|
||||||
<h2 class="mt-5">{{ "addons" | i18n }}</h2>
|
<bit-section>
|
||||||
<div class="row">
|
<h2 bitTypography="h2">{{ "addons" | i18n }}</h2>
|
||||||
<div class="form-group col-6">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
<label for="additionalStorage">{{ "additionalStorageGb" | i18n }}</label>
|
<bit-form-field class="tw-col-span-6">
|
||||||
<input
|
<bit-label>{{ "additionalStorageGb" | i18n }}</bit-label>
|
||||||
id="additionalStorage"
|
<input
|
||||||
class="form-control"
|
bitInput
|
||||||
type="number"
|
formControlName="additionalStorage"
|
||||||
name="AdditionalStorageGb"
|
type="number"
|
||||||
[(ngModel)]="additionalStorage"
|
step="1"
|
||||||
min="0"
|
placeholder="{{ 'additionalStorageGbDesc' | i18n }}"
|
||||||
max="99"
|
/>
|
||||||
step="1"
|
<bit-hint>{{
|
||||||
placeholder="{{ 'additionalStorageGbDesc' | i18n }}"
|
"additionalStorageIntervalDesc"
|
||||||
/>
|
| i18n: "1 GB" : (storageGbPrice | currency: "$") : ("year" | i18n)
|
||||||
<small class="text-muted form-text">{{
|
}}</bit-hint>
|
||||||
"additionalStorageIntervalDesc"
|
</bit-form-field>
|
||||||
| i18n: "1 GB" : (storageGbPrice | currency: "$") : ("year" | i18n)
|
|
||||||
}}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</bit-section>
|
||||||
<h2 class="spaced-header">{{ "summary" | i18n }}</h2>
|
<bit-section>
|
||||||
{{ "premiumMembership" | i18n }}: {{ premiumPrice | currency: "$" }} <br />
|
<h2 bitTypography="h2">{{ "summary" | i18n }}</h2>
|
||||||
{{ "additionalStorageGb" | i18n }}: {{ additionalStorage || 0 }} GB ×
|
{{ "premiumMembership" | i18n }}: {{ premiumPrice | currency: "$" }} <br />
|
||||||
{{ storageGbPrice | currency: "$" }} =
|
{{ "additionalStorageGb" | i18n }}: {{ additionalStorage || 0 }} GB ×
|
||||||
{{ additionalStorageTotal | currency: "$" }}
|
{{ storageGbPrice | currency: "$" }} =
|
||||||
<hr class="my-3" />
|
{{ additionalStorageTotal | currency: "$" }}
|
||||||
<h2 class="spaced-header mb-4">{{ "paymentInformation" | i18n }}</h2>
|
<hr class="tw-my-3" />
|
||||||
<app-payment [hideBank]="true"></app-payment>
|
</bit-section>
|
||||||
<app-tax-info></app-tax-info>
|
<bit-section>
|
||||||
<div id="price" class="my-4">
|
<h3 bitTypography="h2">{{ "paymentInformation" | i18n }}</h3>
|
||||||
<div class="text-muted text-sm">
|
<app-payment [hideBank]="true"></app-payment>
|
||||||
{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}
|
<app-tax-info></app-tax-info>
|
||||||
<br />
|
<div id="price" class="tw-my-4">
|
||||||
<ng-container>
|
<div class="tw-text-muted tw-text-sm">
|
||||||
{{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }}
|
{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}
|
||||||
</ng-container>
|
<br />
|
||||||
|
<ng-container>
|
||||||
|
{{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }}
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<hr class="tw-my-1 tw-w-1/4 tw-ml-0" />
|
||||||
|
<p bitTypography="body1">
|
||||||
|
<strong>{{ "total" | i18n }}:</strong> {{ total | currency: "USD $" }}/{{ "year" | i18n }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr class="my-1 col-3 ml-0" />
|
<p bitTypography="body2">{{ "paymentChargedAnnually" | i18n }}</p>
|
||||||
<p class="text-lg">
|
<button type="submit" bitButton bitFormButton>
|
||||||
<strong>{{ "total" | i18n }}:</strong> {{ total | currency: "USD $" }}/{{ "year" | i18n }}
|
{{ "submit" | i18n }}
|
||||||
</p>
|
</button>
|
||||||
</div>
|
</bit-section>
|
||||||
<small class="text-muted font-italic">{{ "paymentChargedAnnually" | i18n }}</small>
|
|
||||||
<button type="submit" bitButton [loading]="form.loading">
|
|
||||||
{{ "submit" | i18n }}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom, Observable } from "rxjs";
|
import { firstValueFrom, Observable } from "rxjs";
|
||||||
|
|
||||||
|
@ -7,7 +8,6 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
|
@ -26,11 +26,16 @@ export class PremiumComponent implements OnInit {
|
||||||
premiumPrice = 10;
|
premiumPrice = 10;
|
||||||
familyPlanMaxUserCount = 6;
|
familyPlanMaxUserCount = 6;
|
||||||
storageGbPrice = 4;
|
storageGbPrice = 4;
|
||||||
additionalStorage = 0;
|
|
||||||
cloudWebVaultUrl: string;
|
cloudWebVaultUrl: string;
|
||||||
|
licenseFile: File = null;
|
||||||
|
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
|
protected licenseForm = new FormGroup({
|
||||||
|
file: new FormControl(null, [Validators.required]),
|
||||||
|
});
|
||||||
|
protected addonForm = new FormGroup({
|
||||||
|
additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]),
|
||||||
|
});
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
@ -39,14 +44,17 @@ export class PremiumComponent implements OnInit {
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private messagingService: MessagingService,
|
private messagingService: MessagingService,
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private logService: LogService,
|
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
) {
|
) {
|
||||||
this.selfHosted = platformUtilsService.isSelfHost();
|
this.selfHosted = platformUtilsService.isSelfHost();
|
||||||
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
|
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
|
||||||
}
|
}
|
||||||
|
protected setSelectedFile(event: Event) {
|
||||||
|
const fileInputEl = <HTMLInputElement>event.target;
|
||||||
|
const file: File = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null;
|
||||||
|
this.licenseFile = file;
|
||||||
|
}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||||
if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) {
|
if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) {
|
||||||
|
@ -56,13 +64,11 @@ export class PremiumComponent implements OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
submit = async () => {
|
||||||
async submit() {
|
this.licenseForm.markAllAsTouched();
|
||||||
let files: FileList = null;
|
this.addonForm.markAllAsTouched();
|
||||||
if (this.selfHosted) {
|
if (this.selfHosted) {
|
||||||
const fileEl = document.getElementById("file") as HTMLInputElement;
|
if (this.licenseFile == null) {
|
||||||
files = fileEl.files;
|
|
||||||
if (files == null || files.length === 0) {
|
|
||||||
this.platformUtilsService.showToast(
|
this.platformUtilsService.showToast(
|
||||||
"error",
|
"error",
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("errorOccurred"),
|
||||||
|
@ -72,53 +78,48 @@ export class PremiumComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (this.selfHosted) {
|
||||||
if (this.selfHosted) {
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
if (!this.tokenService.getEmailVerified()) {
|
||||||
if (!this.tokenService.getEmailVerified()) {
|
this.platformUtilsService.showToast(
|
||||||
this.platformUtilsService.showToast(
|
"error",
|
||||||
"error",
|
this.i18nService.t("errorOccurred"),
|
||||||
this.i18nService.t("errorOccurred"),
|
this.i18nService.t("verifyEmailFirst"),
|
||||||
this.i18nService.t("verifyEmailFirst"),
|
);
|
||||||
);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.append("license", files[0]);
|
|
||||||
this.formPromise = this.apiService.postAccountLicense(fd).then(() => {
|
|
||||||
return this.finalizePremium();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.formPromise = this.paymentComponent
|
|
||||||
.createPaymentToken()
|
|
||||||
.then((result) => {
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.append("paymentMethodType", result[1].toString());
|
|
||||||
if (result[0] != null) {
|
|
||||||
fd.append("paymentToken", result[0]);
|
|
||||||
}
|
|
||||||
fd.append("additionalStorageGb", (this.additionalStorage || 0).toString());
|
|
||||||
fd.append("country", this.taxInfoComponent.taxInfo.country);
|
|
||||||
fd.append("postalCode", this.taxInfoComponent.taxInfo.postalCode);
|
|
||||||
return this.apiService.postPremium(fd);
|
|
||||||
})
|
|
||||||
.then((paymentResponse) => {
|
|
||||||
if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) {
|
|
||||||
return this.paymentComponent.handleStripeCardPayment(
|
|
||||||
paymentResponse.paymentIntentClientSecret,
|
|
||||||
() => this.finalizePremium(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return this.finalizePremium();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
await this.formPromise;
|
|
||||||
} catch (e) {
|
const fd = new FormData();
|
||||||
this.logService.error(e);
|
fd.append("license", this.licenseFile);
|
||||||
|
await this.apiService.postAccountLicense(fd).then(() => {
|
||||||
|
return this.finalizePremium();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.paymentComponent
|
||||||
|
.createPaymentToken()
|
||||||
|
.then((result) => {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("paymentMethodType", result[1].toString());
|
||||||
|
if (result[0] != null) {
|
||||||
|
fd.append("paymentToken", result[0]);
|
||||||
|
}
|
||||||
|
fd.append("additionalStorageGb", (this.additionalStorage || 0).toString());
|
||||||
|
fd.append("country", this.taxInfoComponent.taxInfo.country);
|
||||||
|
fd.append("postalCode", this.taxInfoComponent.taxInfo.postalCode);
|
||||||
|
return this.apiService.postPremium(fd);
|
||||||
|
})
|
||||||
|
.then((paymentResponse) => {
|
||||||
|
if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) {
|
||||||
|
return this.paymentComponent.handleStripeCardPayment(
|
||||||
|
paymentResponse.paymentIntentClientSecret,
|
||||||
|
() => this.finalizePremium(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.finalizePremium();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async finalizePremium() {
|
async finalizePremium() {
|
||||||
await this.apiService.refreshIdentityToken();
|
await this.apiService.refreshIdentityToken();
|
||||||
|
@ -127,6 +128,9 @@ export class PremiumComponent implements OnInit {
|
||||||
await this.router.navigate(["/settings/subscription/user-subscription"]);
|
await this.router.navigate(["/settings/subscription/user-subscription"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get additionalStorage(): number {
|
||||||
|
return this.addonForm.get("additionalStorage").value;
|
||||||
|
}
|
||||||
get additionalStorageTotal(): number {
|
get additionalStorageTotal(): number {
|
||||||
return this.storageGbPrice * Math.abs(this.additionalStorage || 0);
|
return this.storageGbPrice * Math.abs(this.additionalStorage || 0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue