mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 18:23:31 +00:00
[AC-3022]Selecting a plan makes the plan cards content to auto-adjusts (#10992)
* Resolve the recommended issue * Resolve the discount display issues * remove unused tw property * Resolve all the outstanding bugs * Fix the A11y bug * Resolve the base storage issue * Rename service account in the summary * changes for the A11y bug * Fix the improper keyboard navigation in modal * Add some additional ui changes
This commit is contained in:
@@ -7,31 +7,37 @@
|
|||||||
<p>{{ "upgradePlans" | i18n }}</p>
|
<p>{{ "upgradePlans" | i18n }}</p>
|
||||||
<div class="tw-mb-3 tw-flex tw-justify-between">
|
<div class="tw-mb-3 tw-flex tw-justify-between">
|
||||||
<span class="tw-text-lg tw-pr-1 tw-font-bold">{{ "selectAPlan" | i18n }}</span>
|
<span class="tw-text-lg tw-pr-1 tw-font-bold">{{ "selectAPlan" | i18n }}</span>
|
||||||
|
<!-- Discount Badge -->
|
||||||
<div class="tw-items-center tw-gap-2">
|
<div class="tw-items-center tw-gap-2">
|
||||||
<span
|
<span
|
||||||
|
class="tw-mr-1"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
this.discountPercentageFromSub > 0
|
this.discountPercentageFromSub > 0
|
||||||
? discountPercentageFromSub
|
? discountPercentageFromSub
|
||||||
: this.discountPercentage && selectedInterval === 1
|
: this.discountPercentage && selectedInterval === planIntervals.Annually
|
||||||
"
|
"
|
||||||
bitBadge
|
bitBadge
|
||||||
variant="success"
|
variant="success"
|
||||||
>{{
|
>{{
|
||||||
"upgradeDiscount"
|
"upgradeDiscount"
|
||||||
| i18n
|
| i18n
|
||||||
: (this.discountPercentageFromSub > 0
|
: (selectedInterval === planIntervals.Annually
|
||||||
? discountPercentageFromSub
|
? discountPercentageFromSub + this.discountPercentage
|
||||||
: this.discountPercentage)
|
: this.discountPercentageFromSub)
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
|
<!-- Plan Interval Toggle -->
|
||||||
<div class="tw-inline-block">
|
<div class="tw-inline-block">
|
||||||
<bit-toggle-group
|
<bit-toggle-group
|
||||||
[selected]="selectedInterval"
|
[selected]="selectedInterval"
|
||||||
(selectedChange)="updateInterval($event)"
|
(selectedChange)="updateInterval($event)"
|
||||||
>
|
>
|
||||||
<bit-toggle
|
<bit-toggle
|
||||||
*ngFor="let planInterval of getPlanIntervals()"
|
*ngFor="
|
||||||
|
let planInterval of getPlanIntervals();
|
||||||
|
trackBy: optimizedNgForRender;
|
||||||
|
let i = index
|
||||||
|
"
|
||||||
[value]="planInterval.value"
|
[value]="planInterval.value"
|
||||||
>
|
>
|
||||||
{{ planInterval.name }}
|
{{ planInterval.name }}
|
||||||
@@ -40,6 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Plan Selection Cards -->
|
||||||
<ng-container *ngIf="!loading && !selfHosted && this.passwordManagerPlans">
|
<ng-container *ngIf="!loading && !selfHosted && this.passwordManagerPlans">
|
||||||
<div
|
<div
|
||||||
class="tw-grid tw-grid-flow-col tw-gap-4 tw-mb-4"
|
class="tw-grid tw-grid-flow-col tw-gap-4 tw-mb-4"
|
||||||
@@ -53,23 +60,34 @@
|
|||||||
>
|
>
|
||||||
<div class="tw-relative">
|
<div class="tw-relative">
|
||||||
<div
|
<div
|
||||||
*ngIf="selectableProduct === selectedPlan"
|
*ngIf="selectableProduct.productTier === productTypes.Enterprise"
|
||||||
class="tw-bg-primary-600 tw-text-center !tw-text-contrast tw-text-sm tw-font-bold tw-py-1 group-hover:tw-bg-primary-700"
|
class="tw-bg-secondary-100 tw-text-center !tw-border-0 tw-text-sm tw-font-bold tw-py-1"
|
||||||
|
[ngClass]="{
|
||||||
|
'tw-bg-primary-700 !tw-text-contrast': selectableProduct === selectedPlan,
|
||||||
|
'tw-bg-secondary-100': !(selectableProduct === selectedPlan),
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
{{ "recommended" | i18n }}
|
{{ "recommended" | i18n }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="tw-px-2 tw-py-4"
|
class="tw-px-2 tw-pb-[4px]"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'tw-py-1': !(selectableProduct === selectedPlan),
|
'tw-py-1': !(selectableProduct === selectedPlan),
|
||||||
'tw-py-0': selectableProduct === selectedPlan,
|
'tw-py-0': selectableProduct === selectedPlan,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<h3 class="tw-text-lg tw-font-bold">
|
<h3
|
||||||
|
class="tw-text-[1.5rem] tw-mt-[6px] tw-font-bold tw-mb-0 tw-leading-[2rem] tw-flex tw-items-center"
|
||||||
|
>
|
||||||
<span class="tw-capitalize">{{
|
<span class="tw-capitalize">{{
|
||||||
selectableProduct.nameLocalizationKey | i18n
|
selectableProduct.nameLocalizationKey | i18n
|
||||||
}}</span>
|
}}</span>
|
||||||
<span bitBadge variant="secondary" *ngIf="selectableProduct === currentPlan">
|
<span
|
||||||
|
bitBadge
|
||||||
|
variant="secondary"
|
||||||
|
*ngIf="selectableProduct === currentPlan"
|
||||||
|
class="tw-ml-2 tw-align-middle"
|
||||||
|
>
|
||||||
{{ "current" | i18n }}</span
|
{{ "current" | i18n }}</span
|
||||||
>
|
>
|
||||||
</h3>
|
</h3>
|
||||||
@@ -133,10 +151,13 @@
|
|||||||
else nonEnterprisePlans
|
else nonEnterprisePlans
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<p class="tw-text-xs tw-px-2 tw-font-semibold" *ngIf="organization.useSecretsManager">
|
<p
|
||||||
|
class="tw-text-xs tw-px-2 tw-font-semibold tw-mb-1"
|
||||||
|
*ngIf="organization.useSecretsManager"
|
||||||
|
>
|
||||||
{{ "bitwardenPasswordManager" | i18n }}
|
{{ "bitwardenPasswordManager" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
<p class="tw-text-xs tw-px-2">{{ "enterprisePlanUpgradeMessage" | i18n }}</p>
|
<p class="tw-text-xs tw-px-2 tw-mb-1">{{ "enterprisePlanUpgradeMessage" | i18n }}</p>
|
||||||
|
|
||||||
<ul class="bwi-ul tw-text-xs">
|
<ul class="bwi-ul tw-text-xs">
|
||||||
<li>
|
<li>
|
||||||
@@ -157,7 +178,10 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p class="tw-text-xs tw-px-2 tw-font-semibold" *ngIf="organization.useSecretsManager">
|
<p
|
||||||
|
class="tw-text-xs tw-px-2 tw-font-semibold tw-mb-1"
|
||||||
|
*ngIf="organization.useSecretsManager"
|
||||||
|
>
|
||||||
{{ "bitwardenSecretsManager" | i18n }}
|
{{ "bitwardenSecretsManager" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
<ul class="bwi-ul tw-text-xs" *ngIf="organization.useSecretsManager">
|
<ul class="bwi-ul tw-text-xs" *ngIf="organization.useSecretsManager">
|
||||||
@@ -195,25 +219,25 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #fullFeatureList>
|
<ng-template #fullFeatureList>
|
||||||
<p
|
<p
|
||||||
class="tw-text-xs tw-px-2 tw-font-semibold"
|
class="tw-text-xs tw-px-2 tw-font-semibold tw-mb-1"
|
||||||
*ngIf="organization.useSecretsManager"
|
*ngIf="organization.useSecretsManager"
|
||||||
>
|
>
|
||||||
{{ "bitwardenPasswordManager" | i18n }}
|
{{ "bitwardenPasswordManager" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
*ngIf="selectableProduct.productTier === productTypes.Teams"
|
*ngIf="selectableProduct.productTier === productTypes.Teams"
|
||||||
class="tw-text-xs tw-px-2"
|
class="tw-text-xs tw-px-2 tw-mb-1"
|
||||||
>
|
>
|
||||||
{{ "teamsPlanUpgradeMessage" | i18n }}
|
{{ "teamsPlanUpgradeMessage" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
*ngIf="selectableProduct.productTier === productTypes.Families"
|
*ngIf="selectableProduct.productTier === productTypes.Families"
|
||||||
class="tw-text-xs tw-px-2"
|
class="tw-text-xs tw-px-2 tw-mb-1"
|
||||||
>
|
>
|
||||||
{{ "familyPlanUpgradeMessage" | i18n }}
|
{{ "familyPlanUpgradeMessage" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
<ul
|
<ul
|
||||||
class="bwi-ul tw-text-xs"
|
class="bwi-ul tw-text-xs tw-mb-1"
|
||||||
*ngIf="selectableProduct.productTier == productTypes.Families"
|
*ngIf="selectableProduct.productTier == productTypes.Families"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
@@ -247,7 +271,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p
|
<p
|
||||||
class="tw-text-xs tw-px-2 tw-font-semibold"
|
class="tw-text-xs tw-px-2 tw-font-semibold tw-mb-1"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
organization.useSecretsManager &&
|
organization.useSecretsManager &&
|
||||||
selectableProduct.productTier !== productTypes.Families
|
selectableProduct.productTier !== productTypes.Families
|
||||||
@@ -283,16 +307,16 @@
|
|||||||
<bit-callout
|
<bit-callout
|
||||||
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
||||||
type="info"
|
type="info"
|
||||||
title="INFO"
|
title="SECRETS MANAGER SUBSCRIPTION"
|
||||||
>
|
>
|
||||||
{{ "secretsManagerSubInfo" | i18n }}
|
{{ "secretsManagerSubInfo" | i18n }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
<bit-callout
|
<bit-callout
|
||||||
*ngIf="organization.useSecretsManager && isSecretsManagerTrial()"
|
*ngIf="organization.useSecretsManager && isSecretsManagerTrial()"
|
||||||
type="info"
|
type="info"
|
||||||
title="INFO"
|
title="PASSWORD MANAGER SUBSCRIPTION"
|
||||||
>
|
>
|
||||||
{{ "secretsManagerWithFreePasswordManagerInfo" | i18n }}
|
{{ "secretsManagerComplimentaryPasswordManager" | i18n }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
<br />
|
<br />
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -392,23 +416,37 @@
|
|||||||
<p
|
<p
|
||||||
class="tw-mb-0 tw-flex tw-justify-between"
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="selectedPlan.PasswordManager.hasAdditionalStorageOption && storageGb > 0"
|
||||||
selectedPlan.PasswordManager.hasAdditionalStorageOption &&
|
|
||||||
!organization.useSecretsManager &&
|
|
||||||
organization.maxStorageGb > 0
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ organization.maxStorageGb }}
|
{{ storageGb }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "additionalStorageGbMessage" | i18n }}
|
||||||
×
|
×
|
||||||
{{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }}
|
{{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }}
|
||||||
/{{ "year" | i18n }}
|
/{{ "year" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{
|
<span>{{ additionalStorageTotal(selectedPlan) | currency: "$" }}</span>
|
||||||
organization.maxStorageGb * selectedPlan.PasswordManager.additionalStoragePricePerGb
|
</p>
|
||||||
| currency: "$"
|
<!--Discount PM Annual-->
|
||||||
|
<p
|
||||||
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
|
bitTypography="body2"
|
||||||
|
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="selectedInterval == planIntervals.Annually">
|
||||||
|
<span class="tw-text-xs">
|
||||||
|
{{
|
||||||
|
"providerDiscount"
|
||||||
|
| i18n: this.discountPercentageFromSub + this.discountPercentage
|
||||||
|
| lowercase
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="tw-line-through tw-text-xs">{{
|
||||||
|
calculateTotalAppliedDiscount(
|
||||||
|
passwordManagerSeatTotal(selectedPlan) + additionalStorageTotal(selectedPlan)
|
||||||
|
) | currency: "$"
|
||||||
}}</span>
|
}}</span>
|
||||||
|
</ng-container>
|
||||||
</p>
|
</p>
|
||||||
<!-- secrets manager summary for annual -->
|
<!-- secrets manager summary for annual -->
|
||||||
<p class="tw-font-semibold tw-mt-3 tw-mb-1" *ngIf="organization.useSecretsManager">
|
<p class="tw-font-semibold tw-mt-3 tw-mb-1" *ngIf="organization.useSecretsManager">
|
||||||
@@ -459,18 +497,40 @@
|
|||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
selectedPlan?.SecretsManager?.hasAdditionalServiceAccountOption &&
|
selectedPlan?.SecretsManager?.hasAdditionalServiceAccountOption &&
|
||||||
additionalServiceAccount
|
additionalServiceAccount > 0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ additionalServiceAccount }}
|
{{ additionalServiceAccount }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "serviceAccounts" | i18n | lowercase }}
|
||||||
×
|
×
|
||||||
{{ selectedPlan?.SecretsManager?.additionalPricePerServiceAccount | currency: "$" }}
|
{{ selectedPlan?.SecretsManager?.additionalPricePerServiceAccount | currency: "$" }}
|
||||||
/{{ "month" | i18n }}
|
/{{ "month" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<!--Discount SM annual-->
|
||||||
|
<p
|
||||||
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
|
bitTypography="body2"
|
||||||
|
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="selectedInterval == planIntervals.Annually">
|
||||||
|
<span class="tw-text-xs">
|
||||||
|
{{
|
||||||
|
"providerDiscount"
|
||||||
|
| i18n: this.discountPercentageFromSub + this.discountPercentage
|
||||||
|
| lowercase
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="tw-line-through tw-text-xs">{{
|
||||||
|
calculateTotalAppliedDiscount(
|
||||||
|
additionalServiceAccountTotal(selectedPlan) +
|
||||||
|
secretsManagerSeatTotal(selectedPlan, sub.smSeats)
|
||||||
|
) | currency: "$"
|
||||||
|
}}</span>
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
<bit-hint class="col-6" *ngIf="selectedInterval == planIntervals.Monthly">
|
<bit-hint class="col-6" *ngIf="selectedInterval == planIntervals.Monthly">
|
||||||
<p class="tw-font-semibold tw-mb-1" *ngIf="organization.useSecretsManager">
|
<p class="tw-font-semibold tw-mb-1" *ngIf="organization.useSecretsManager">
|
||||||
@@ -512,24 +572,39 @@
|
|||||||
<p
|
<p
|
||||||
class="tw-mb-0 tw-flex tw-justify-between"
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="selectedPlan.PasswordManager.hasAdditionalStorageOption && storageGb > 0"
|
||||||
selectedPlan.PasswordManager.hasAdditionalStorageOption &&
|
|
||||||
!organization.useSecretsManager &&
|
|
||||||
organization.maxStorageGb > 0
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ organization.maxStorageGb }}
|
{{ storageGb }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "additionalStorageGbMessage" | i18n }}
|
||||||
×
|
×
|
||||||
{{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }}
|
{{ additionalStoragePriceMonthly(selectedPlan) | currency: "$" }}
|
||||||
/{{ "month" | i18n }}
|
/{{ "month" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{
|
<span>{{
|
||||||
organization.maxStorageGb * selectedPlan.PasswordManager.additionalStoragePricePerGb
|
storageGb * selectedPlan.PasswordManager.additionalStoragePricePerGb | currency: "$"
|
||||||
| currency: "$"
|
|
||||||
}}</span>
|
}}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<!--Discount PM Monthly-->
|
||||||
|
<p
|
||||||
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
|
bitTypography="body2"
|
||||||
|
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="selectedInterval == planIntervals.Monthly">
|
||||||
|
<span
|
||||||
|
class="tw-text-xs"
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
>
|
||||||
|
{{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
class="tw-line-through tw-text-xs"
|
||||||
|
>{{ calculateTotalAppliedDiscount(total) | currency: "$" }}</span
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
<!-- secrets manager summary for monthly -->
|
<!-- secrets manager summary for monthly -->
|
||||||
<p class="tw-font-semibold tw-mt-3 tw-mb-1" *ngIf="organization.useSecretsManager">
|
<p class="tw-font-semibold tw-mt-3 tw-mb-1" *ngIf="organization.useSecretsManager">
|
||||||
{{ "secretsManager" | i18n }}
|
{{ "secretsManager" | i18n }}
|
||||||
@@ -575,18 +650,41 @@
|
|||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
||||||
additionalServiceAccount
|
additionalServiceAccount > 0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ additionalServiceAccount }}
|
{{ additionalServiceAccount }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "serviceAccounts" | i18n | lowercase }}
|
||||||
×
|
×
|
||||||
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
||||||
/{{ "month" | i18n }}
|
/{{ "month" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<!--Discount SM Monthly-->
|
||||||
|
<p
|
||||||
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
|
bitTypography="body2"
|
||||||
|
*ngIf="organization.useSecretsManager && !isSecretsManagerTrial()"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="selectedInterval == planIntervals.Monthly">
|
||||||
|
<span
|
||||||
|
class="tw-text-xs"
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
>
|
||||||
|
{{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
class="tw-line-through tw-text-xs"
|
||||||
|
>{{
|
||||||
|
additionalServiceAccountTotal(selectedPlan) +
|
||||||
|
secretsManagerSeatTotal(selectedPlan, sub?.smSeats) | currency: "$"
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
</div>
|
</div>
|
||||||
<!-- SM + Free PM cost summary -->
|
<!-- SM + Free PM cost summary -->
|
||||||
@@ -641,18 +739,40 @@
|
|||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
||||||
additionalServiceAccount
|
additionalServiceAccount > 0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ additionalServiceAccount }}
|
{{ additionalServiceAccount }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "serviceAccounts" | i18n }}
|
||||||
×
|
×
|
||||||
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
||||||
/{{ "month" | i18n }}
|
/{{ "month" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
<span>{{ additionalServiceAccountTotal(selectedPlan) | currency: "$" }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<!--Discount SM Annual-->
|
||||||
|
<p
|
||||||
|
class="tw-mb-0 tw-flex tw-justify-between"
|
||||||
|
bitTypography="body2"
|
||||||
|
*ngIf="organization.useSecretsManager && isSecretsManagerTrial()"
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="selectedInterval == planIntervals.Annually">
|
||||||
|
<span class="tw-text-xs">
|
||||||
|
{{
|
||||||
|
"providerDiscount"
|
||||||
|
| i18n: this.discountPercentageFromSub + this.discountPercentage
|
||||||
|
| lowercase
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="tw-line-through tw-text-xs">{{
|
||||||
|
calculateTotalAppliedDiscount(
|
||||||
|
additionalServiceAccountTotal(selectedPlan) +
|
||||||
|
secretsManagerSeatTotal(selectedPlan, sub.smSeats)
|
||||||
|
) | currency: "$"
|
||||||
|
}}</span>
|
||||||
|
</ng-container>
|
||||||
|
</p>
|
||||||
<!-- password manager summary for annual -->
|
<!-- password manager summary for annual -->
|
||||||
<p class="tw-font-semibold tw-mt-3 tw-mb-0" *ngIf="organization.useSecretsManager">
|
<p class="tw-font-semibold tw-mt-3 tw-mb-0" *ngIf="organization.useSecretsManager">
|
||||||
{{ "passwordManager" | i18n }}
|
{{ "passwordManager" | i18n }}
|
||||||
@@ -663,7 +783,7 @@
|
|||||||
*ngIf="selectedPlan.PasswordManager.basePrice"
|
*ngIf="selectedPlan.PasswordManager.basePrice"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ organization.seats }}
|
{{ sub?.seats }}
|
||||||
{{ "members" | i18n }} ×
|
{{ "members" | i18n }} ×
|
||||||
{{
|
{{
|
||||||
(selectedPlan.isAnnual
|
(selectedPlan.isAnnual
|
||||||
@@ -694,7 +814,7 @@
|
|||||||
<span *ngIf="selectedPlan.PasswordManager.baseSeats"
|
<span *ngIf="selectedPlan.PasswordManager.baseSeats"
|
||||||
>{{ "additionalUsers" | i18n }}:</span
|
>{{ "additionalUsers" | i18n }}:</span
|
||||||
>
|
>
|
||||||
{{ organization.seats || 0 }}
|
{{ sub?.seats || 0 }}
|
||||||
<span *ngIf="!selectedPlan.PasswordManager.baseSeats">{{ "members" | i18n }}</span>
|
<span *ngIf="!selectedPlan.PasswordManager.baseSeats">{{ "members" | i18n }}</span>
|
||||||
×
|
×
|
||||||
{{ selectedPlan.PasswordManager.seatPrice | currency: "$" }}
|
{{ selectedPlan.PasswordManager.seatPrice | currency: "$" }}
|
||||||
@@ -756,12 +876,12 @@
|
|||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
*ngIf="
|
*ngIf="
|
||||||
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
selectedPlan.SecretsManager.hasAdditionalServiceAccountOption &&
|
||||||
additionalServiceAccount
|
additionalServiceAccount > 0
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
{{ additionalServiceAccount }}
|
{{ additionalServiceAccount }}
|
||||||
{{ "additionalStorageGbMessage" | i18n }}
|
{{ "serviceAccounts" | i18n }}
|
||||||
×
|
×
|
||||||
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
{{ selectedPlan.SecretsManager.additionalPricePerServiceAccount | currency: "$" }}
|
||||||
/{{ "month" | i18n }}
|
/{{ "month" | i18n }}
|
||||||
@@ -795,7 +915,7 @@
|
|||||||
<span *ngIf="selectedPlan.PasswordManager.baseSeats"
|
<span *ngIf="selectedPlan.PasswordManager.baseSeats"
|
||||||
>{{ "additionalUsers" | i18n }}:</span
|
>{{ "additionalUsers" | i18n }}:</span
|
||||||
>
|
>
|
||||||
{{ organization.seats }}
|
{{ sub?.seats }}
|
||||||
<span *ngIf="!selectedPlan.PasswordManager.baseSeats">{{ "members" | i18n }}</span>
|
<span *ngIf="!selectedPlan.PasswordManager.baseSeats">{{ "members" | i18n }}</span>
|
||||||
×
|
×
|
||||||
{{ selectedPlan.PasswordManager.seatPrice | currency: "$" }}
|
{{ selectedPlan.PasswordManager.seatPrice | currency: "$" }}
|
||||||
@@ -811,6 +931,46 @@
|
|||||||
</p>
|
</p>
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- discountPercentage to PM Only -->
|
||||||
|
<div
|
||||||
|
*ngIf="totalOpened && discountPercentage && !organization.useSecretsManager"
|
||||||
|
class="row"
|
||||||
|
>
|
||||||
|
<bit-hint class="col-6">
|
||||||
|
<p class="tw-mb-0 tw-flex tw-justify-between" bitTypography="body2">
|
||||||
|
<ng-container
|
||||||
|
*ngIf="
|
||||||
|
selectedInterval == planIntervals.Annually;
|
||||||
|
else MonthlyOrAnnuallyWithDiscount
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="tw-text-xs">
|
||||||
|
{{
|
||||||
|
"providerDiscount"
|
||||||
|
| i18n: this.discountPercentageFromSub + this.discountPercentage
|
||||||
|
| lowercase
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span class="tw-line-through tw-text-xs">{{
|
||||||
|
calculateTotalAppliedDiscount(total) | currency: "$"
|
||||||
|
}}</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #MonthlyOrAnnuallyWithDiscount>
|
||||||
|
<span
|
||||||
|
class="tw-text-xs"
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
>
|
||||||
|
{{ "providerDiscount" | i18n: this.discountPercentageFromSub | lowercase }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
[style.display]="discountPercentageFromSub > 0 ? 'block' : 'none'"
|
||||||
|
class="tw-line-through tw-text-xs"
|
||||||
|
>{{ calculateTotalAppliedDiscount(total) | currency: "$" }}</span
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
</p>
|
||||||
|
</bit-hint>
|
||||||
|
</div>
|
||||||
<div *ngIf="totalOpened" id="price" class="row tw-mt-4">
|
<div *ngIf="totalOpened" id="price" class="row tw-mt-4">
|
||||||
<bit-hint class="col-6">
|
<bit-hint class="col-6">
|
||||||
<p
|
<p
|
||||||
@@ -821,7 +981,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ total | currency: "USD" : "$" }}
|
{{ total | currency: "USD" : "$" }}
|
||||||
<span class="tw-text-xs tw-font-light"> / {{ selectedPlanInterval | i18n }}</span>
|
<span class="tw-text-xs tw-font-semibold">
|
||||||
|
/ {{ selectedPlanInterval | i18n }}</span
|
||||||
|
>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
|
|||||||
@@ -246,27 +246,28 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
selected: false,
|
selected: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
this.discountPercentageFromSub = this.sub?.customerDiscount?.percentOff;
|
this.discountPercentageFromSub = this.isSecretsManagerTrial()
|
||||||
|
? 0
|
||||||
|
: (this.sub?.customerDiscount?.percentOff ?? 0);
|
||||||
|
|
||||||
this.setInitialPlanSelection();
|
this.setInitialPlanSelection();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setInitialPlanSelection() {
|
setInitialPlanSelection() {
|
||||||
if (
|
|
||||||
this.organization.useSecretsManager &&
|
|
||||||
this.currentPlan.productTier == ProductTierType.Free
|
|
||||||
) {
|
|
||||||
this.selectPlan(this.getPlanByType(ProductTierType.Teams));
|
|
||||||
} else {
|
|
||||||
this.selectPlan(this.getPlanByType(ProductTierType.Enterprise));
|
this.selectPlan(this.getPlanByType(ProductTierType.Enterprise));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
getPlanByType(productTier: ProductTierType) {
|
getPlanByType(productTier: ProductTierType) {
|
||||||
return this.selectableProducts.find((product) => product.productTier === productTier);
|
return this.selectableProducts.find((product) => product.productTier === productTier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretsManagerTrialDiscount() {
|
||||||
|
return this.sub?.customerDiscount?.appliesTo?.includes("sm-standalone")
|
||||||
|
? this.discountPercentage
|
||||||
|
: this.discountPercentageFromSub + this.discountPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
isSecretsManagerTrial(): boolean {
|
isSecretsManagerTrial(): boolean {
|
||||||
return (
|
return (
|
||||||
this.sub?.subscription?.items?.some((item) =>
|
this.sub?.subscription?.items?.some((item) =>
|
||||||
@@ -276,15 +277,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
planTypeChanged() {
|
planTypeChanged() {
|
||||||
if (
|
|
||||||
this.organization.useSecretsManager &&
|
|
||||||
this.currentPlan.productTier == ProductTierType.Free
|
|
||||||
) {
|
|
||||||
this.selectPlan(this.getPlanByType(ProductTierType.Teams));
|
|
||||||
} else {
|
|
||||||
this.selectPlan(this.getPlanByType(ProductTierType.Enterprise));
|
this.selectPlan(this.getPlanByType(ProductTierType.Enterprise));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
updateInterval(event: number) {
|
updateInterval(event: number) {
|
||||||
this.selectedInterval = event;
|
this.selectedInterval = event;
|
||||||
@@ -304,6 +298,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optimizedNgForRender(index: number) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
protected getPlanCardContainerClasses(plan: PlanResponse, index: number) {
|
protected getPlanCardContainerClasses(plan: PlanResponse, index: number) {
|
||||||
let cardState: PlanCardState;
|
let cardState: PlanCardState;
|
||||||
|
|
||||||
@@ -370,6 +368,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (plan === this.currentPlan) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.selectedPlan = plan;
|
this.selectedPlan = plan;
|
||||||
this.formGroup.patchValue({ productTier: plan.productTier });
|
this.formGroup.patchValue({ productTier: plan.productTier });
|
||||||
}
|
}
|
||||||
@@ -463,6 +465,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get storageGb() {
|
||||||
|
return this.sub?.maxStorageGb - 1;
|
||||||
|
}
|
||||||
|
|
||||||
passwordManagerSeatTotal(plan: PlanResponse): number {
|
passwordManagerSeatTotal(plan: PlanResponse): number {
|
||||||
if (!plan.PasswordManager.hasAdditionalSeatsOption || this.isSecretsManagerTrial()) {
|
if (!plan.PasswordManager.hasAdditionalSeatsOption || this.isSecretsManagerTrial()) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -486,8 +492,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
plan.PasswordManager.additionalStoragePricePerGb *
|
plan.PasswordManager.additionalStoragePricePerGb * Math.abs(this.sub?.maxStorageGb - 1 || 0)
|
||||||
Math.abs(this.organization.maxStorageGb || 0)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +504,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
additionalServiceAccountTotal(plan: PlanResponse): number {
|
additionalServiceAccountTotal(plan: PlanResponse): number {
|
||||||
if (!plan.SecretsManager.hasAdditionalServiceAccountOption || this.additionalServiceAccount) {
|
if (
|
||||||
|
!plan.SecretsManager.hasAdditionalServiceAccountOption ||
|
||||||
|
this.additionalServiceAccount == 0
|
||||||
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,7 +549,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
if (this.selectedPlan.productTier === ProductTierType.Families) {
|
if (this.selectedPlan.productTier === ProductTierType.Families) {
|
||||||
return this.selectedPlan.PasswordManager.baseSeats;
|
return this.selectedPlan.PasswordManager.baseSeats;
|
||||||
}
|
}
|
||||||
return this.organization.seats;
|
return this.sub?.seats;
|
||||||
}
|
}
|
||||||
|
|
||||||
get total() {
|
get total() {
|
||||||
@@ -565,7 +573,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get additionalServiceAccount() {
|
get additionalServiceAccount() {
|
||||||
const baseServiceAccount = this.selectedPlan.SecretsManager?.baseServiceAccount || 0;
|
const baseServiceAccount = this.currentPlan.SecretsManager?.baseServiceAccount || 0;
|
||||||
const usedServiceAccounts = this.sub?.smServiceAccounts || 0;
|
const usedServiceAccounts = this.sub?.smServiceAccounts || 0;
|
||||||
|
|
||||||
const additionalServiceAccounts = baseServiceAccount - usedServiceAccounts;
|
const additionalServiceAccounts = baseServiceAccount - usedServiceAccounts;
|
||||||
@@ -652,7 +660,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
if (!this.acceptingSponsorship && !this.isInTrialFlow) {
|
if (!this.acceptingSponsorship && !this.isInTrialFlow) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.router.navigate(["/organizations/" + orgId]);
|
this.router.navigate(["/organizations/" + orgId + "/members"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isInTrialFlow) {
|
if (this.isInTrialFlow) {
|
||||||
@@ -676,11 +684,11 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
private async updateOrganization() {
|
private async updateOrganization() {
|
||||||
const request = new OrganizationUpgradeRequest();
|
const request = new OrganizationUpgradeRequest();
|
||||||
if (this.selectedPlan.productTier !== ProductTierType.Families) {
|
if (this.selectedPlan.productTier !== ProductTierType.Families) {
|
||||||
request.additionalSeats = this.organization.seats;
|
request.additionalSeats = this.sub?.seats;
|
||||||
}
|
}
|
||||||
if (this.organization.maxStorageGb > this.selectedPlan.PasswordManager.baseStorageGb) {
|
if (this.sub?.maxStorageGb > this.selectedPlan.PasswordManager.baseStorageGb) {
|
||||||
request.additionalStorageGb =
|
request.additionalStorageGb =
|
||||||
this.organization.maxStorageGb - this.selectedPlan.PasswordManager.baseStorageGb;
|
this.sub?.maxStorageGb - this.selectedPlan.PasswordManager.baseStorageGb;
|
||||||
}
|
}
|
||||||
request.premiumAccessAddon =
|
request.premiumAccessAddon =
|
||||||
this.selectedPlan.PasswordManager.hasPremiumAccessOption &&
|
this.selectedPlan.PasswordManager.hasPremiumAccessOption &&
|
||||||
@@ -768,6 +776,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
request.additionalSmSeats = this.organization.seats;
|
request.additionalSmSeats = this.organization.seats;
|
||||||
} else {
|
} else {
|
||||||
request.additionalSmSeats = this.sub?.smSeats;
|
request.additionalSmSeats = this.sub?.smSeats;
|
||||||
|
request.additionalServiceAccounts = this.additionalServiceAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,6 +821,16 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
|||||||
this.totalOpened = !this.totalOpened;
|
this.totalOpened = !this.totalOpened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateTotalAppliedDiscount(total: number) {
|
||||||
|
const discountPercent =
|
||||||
|
this.selectedInterval == PlanInterval.Annually
|
||||||
|
? this.discountPercentage + this.discountPercentageFromSub
|
||||||
|
: this.discountPercentageFromSub;
|
||||||
|
|
||||||
|
const discountedTotal = total / (1 - discountPercent / 100);
|
||||||
|
return discountedTotal;
|
||||||
|
}
|
||||||
|
|
||||||
get paymentSourceClasses() {
|
get paymentSourceClasses() {
|
||||||
if (this.billing.paymentSource == null) {
|
if (this.billing.paymentSource == null) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -69,14 +69,25 @@
|
|||||||
></app-subscription-status>
|
></app-subscription-status>
|
||||||
<ng-container *ngIf="userOrg.canEditSubscription">
|
<ng-container *ngIf="userOrg.canEditSubscription">
|
||||||
<div class="tw-flex-col">
|
<div class="tw-flex-col">
|
||||||
<strong class="tw-block tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300">{{
|
<strong class="tw-block tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300"
|
||||||
"details" | i18n
|
>{{ "details" | i18n
|
||||||
}}</strong>
|
}}<span
|
||||||
|
class="tw-ml-3"
|
||||||
|
*ngIf="customerDiscount?.percentOff > 0 && !isSecretsManagerTrial()"
|
||||||
|
bitBadge
|
||||||
|
variant="success"
|
||||||
|
>{{ "providerDiscount" | i18n: customerDiscount?.percentOff }}</span
|
||||||
|
></strong
|
||||||
|
>
|
||||||
<bit-table>
|
<bit-table>
|
||||||
<ng-template body>
|
<ng-template body>
|
||||||
<ng-container *ngIf="subscription">
|
<ng-container *ngIf="subscription">
|
||||||
<tr bitRow *ngFor="let i of subscriptionLineItems">
|
<tr bitRow *ngFor="let i of subscriptionLineItems">
|
||||||
<td bitCell [ngClass]="{ 'tw-pl-20': i.addonSubscriptionItem }">
|
<td
|
||||||
|
bitCell
|
||||||
|
[ngClass]="{ 'tw-pl-20': i.addonSubscriptionItem }"
|
||||||
|
class="tw-align-middle"
|
||||||
|
>
|
||||||
<span *ngIf="!i.addonSubscriptionItem">{{ i.productName | i18n }} -</span>
|
<span *ngIf="!i.addonSubscriptionItem">{{ i.productName | i18n }} -</span>
|
||||||
{{ i.name }} {{ i.quantity > 1 ? "×" + i.quantity : "" }} @
|
{{ i.name }} {{ i.quantity > 1 ? "×" + i.quantity : "" }} @
|
||||||
{{ i.amount | currency: "$" }}
|
{{ i.amount | currency: "$" }}
|
||||||
@@ -91,7 +102,19 @@
|
|||||||
{{ "freeForOneYear" | i18n }}
|
{{ "freeForOneYear" | i18n }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #calculateElse>
|
<ng-template #calculateElse>
|
||||||
|
<div class="tw-flex tw-flex-col">
|
||||||
|
<span>
|
||||||
{{ i.quantity * i.amount | currency: "$" }} /{{ i.interval | i18n }}
|
{{ i.quantity * i.amount | currency: "$" }} /{{ i.interval | i18n }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
*ngIf="customerDiscount?.percentOff && !isSecretsManagerTrial()"
|
||||||
|
class="tw-line-through !tw-text-muted"
|
||||||
|
>{{
|
||||||
|
calculateTotalAppliedDiscount(i.quantity * i.amount) | currency: "$"
|
||||||
|
}}
|
||||||
|
/ {{ "year" | i18n }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -112,7 +135,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="userOrg.canEditSubscription">
|
<ng-container *ngIf="userOrg.canEditSubscription">
|
||||||
<div class="tw-mt-7">
|
<div class="tw-mt-5">
|
||||||
<button
|
<button
|
||||||
bitButton
|
bitButton
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
|
|||||||
@@ -430,6 +430,14 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSecretsManagerTrial(): boolean {
|
||||||
|
return (
|
||||||
|
this.sub?.subscription?.items?.some((item) =>
|
||||||
|
this.sub?.customerDiscount?.appliesTo?.includes(item.productId),
|
||||||
|
) ?? false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
closeChangePlan() {
|
closeChangePlan() {
|
||||||
this.showChangePlan = false;
|
this.showChangePlan = false;
|
||||||
}
|
}
|
||||||
@@ -464,6 +472,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculateTotalAppliedDiscount(total: number) {
|
||||||
|
const discountedTotal = total / (1 - this.customerDiscount?.percentOff / 100);
|
||||||
|
return discountedTotal;
|
||||||
|
}
|
||||||
|
|
||||||
adjustStorage = (add: boolean) => {
|
adjustStorage = (add: boolean) => {
|
||||||
return async () => {
|
return async () => {
|
||||||
const deprecateStripeSourcesAPI = await firstValueFrom(this.deprecateStripeSourcesAPI$);
|
const deprecateStripeSourcesAPI = await firstValueFrom(this.deprecateStripeSourcesAPI$);
|
||||||
|
|||||||
@@ -9076,8 +9076,8 @@
|
|||||||
"bitwardenPasswordManager": {
|
"bitwardenPasswordManager": {
|
||||||
"message": "Bitwarden Password Manager"
|
"message": "Bitwarden Password Manager"
|
||||||
},
|
},
|
||||||
"secretsManagerWithFreePasswordManagerInfo": {
|
"secretsManagerComplimentaryPasswordManager": {
|
||||||
"message": "Your complementary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
|
"message": "Your complimentary one year Password Manager subscription will upgrade to the selected plan. You will not be charged until the complimentary period is over."
|
||||||
},
|
},
|
||||||
"fileSavedToDevice": {
|
"fileSavedToDevice": {
|
||||||
"message": "File saved to device. Manage from your device downloads."
|
"message": "File saved to device. Manage from your device downloads."
|
||||||
|
|||||||
Reference in New Issue
Block a user