bitwarden-estensione-browser/apps/web/src/app/settings/organization-plans.componen...

329 lines
14 KiB
HTML

<ng-container *ngIf="loading">
<i
class="bwi bwi-spinner bwi-spin text-muted"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
<span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="createOrganization && selfHosted">
<p>{{ "uploadLicenseFileOrg" | i18n }}</p>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="form-group">
<label for="file">{{ "licenseFile" | i18n }}</label>
<input type="file" id="file" class="form-control-file" name="file" required />
<small class="form-text text-muted">{{
"licenseFileDesc" | i18n: "bitwarden_organization_license.json"
}}</small>
</div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
<span>{{ "submit" | i18n }}</span>
</button>
</form>
</ng-container>
<form
#form
[formGroup]="formGroup"
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
*ngIf="!loading && !selfHosted && this.plans"
class="tw-pt-6"
>
<app-org-info
(changedBusinessOwned)="changedOwnedBusiness()"
[formGroup]="formGroup"
[createOrganization]="createOrganization"
[isProvider]="!!providerId"
[acceptingSponsorship]="acceptingSponsorship"
></app-org-info>
<h2 class="mt-5">{{ "chooseYourPlan" | i18n }}</h2>
<div *ngFor="let selectableProduct of selectableProducts" class="form-check form-check-block">
<input
class="form-check-input"
type="radio"
name="product"
id="product{{ selectableProduct.product }}"
[value]="selectableProduct.product"
formControlName="product"
(change)="changedProduct()"
/>
<label class="form-check-label" for="product{{ selectableProduct.product }}">
{{ selectableProduct.nameLocalizationKey | i18n }}
<small class="mb-1">{{ selectableProduct.descriptionLocalizationKey | i18n: "1" }}</small>
<ng-container
*ngIf="selectableProduct.product === productTypes.Enterprise; else fullFeatureList"
>
<small>• {{ "includeAllTeamsFeatures" | i18n }}</small>
<small *ngIf="selectableProduct.hasSelfHost">• {{ "onPremHostingOptional" | i18n }}</small>
<small *ngIf="selectableProduct.hasSso">• {{ "includeSsoAuthentication" | i18n }}</small>
<small *ngIf="selectableProduct.hasPolicies"
>• {{ "includeEnterprisePolicies" | i18n }}</small
>
<small *ngIf="selectableProduct.trialPeriodDays && createOrganization"
>
{{ "xDayFreeTrial" | i18n: selectableProduct.trialPeriodDays }}
</small>
</ng-container>
<ng-template #fullFeatureList>
<small *ngIf="selectableProduct.product == productTypes.Free"
>• {{ "limitedUsers" | i18n: selectableProduct.maxUsers }}</small
>
<small *ngIf="selectableProduct.product != productTypes.Free && selectableProduct.maxUsers"
>• {{ "addShareLimitedUsers" | i18n: selectableProduct.maxUsers }}</small
>
<small *ngIf="!selectableProduct.maxUsers">• {{ "addShareUnlimitedUsers" | i18n }}</small>
<small *ngIf="selectableProduct.maxCollections"
>• {{ "limitedCollections" | i18n: selectableProduct.maxCollections }}</small
>
<small *ngIf="selectableProduct.maxAdditionalSeats"
>• {{ "addShareLimitedUsers" | i18n: selectableProduct.maxAdditionalSeats }}</small
>
<small *ngIf="!selectableProduct.maxCollections"
>• {{ "createUnlimitedCollections" | i18n }}</small
>
<small *ngIf="selectableProduct.baseStorageGb"
>• {{ "gbEncryptedFileStorage" | i18n: selectableProduct.baseStorageGb + "GB" }}</small
>
<small *ngIf="selectableProduct.hasGroups">• {{ "controlAccessWithGroups" | i18n }}</small>
<small *ngIf="selectableProduct.hasApi">• {{ "trackAuditLogs" | i18n }}</small>
<small *ngIf="selectableProduct.hasDirectory"
>• {{ "syncUsersFromDirectory" | i18n }}</small
>
<small *ngIf="selectableProduct.hasSelfHost">• {{ "onPremHostingOptional" | i18n }}</small>
<small *ngIf="selectableProduct.usersGetPremium">• {{ "usersGetPremium" | i18n }}</small>
<small *ngIf="selectableProduct.product != productTypes.Free"
>• {{ "priorityCustomerSupport" | i18n }}</small
>
<small *ngIf="selectableProduct.trialPeriodDays && createOrganization"
>
{{ "xDayFreeTrial" | i18n: selectableProduct.trialPeriodDays }}
</small>
</ng-template>
<span *ngIf="selectableProduct.product != productTypes.Free">
<ng-container *ngIf="selectableProduct.basePrice && !acceptingSponsorship">
{{ selectableProduct.basePrice / 12 | currency: "$" }} /{{ "month" | i18n }},
{{ "includesXUsers" | i18n: selectableProduct.baseSeats }}
<ng-container *ngIf="selectableProduct.hasAdditionalSeatsOption">
{{ ("additionalUsers" | i18n).toLowerCase() }}
{{ selectableProduct.seatPrice / 12 | currency: "$" }} /{{ "month" | i18n }}
</ng-container>
</ng-container>
</span>
<span *ngIf="!selectableProduct.basePrice && selectableProduct.hasAdditionalSeatsOption">
{{ "costPerUser" | i18n: (selectableProduct.seatPrice / 12 | currency: "$") }} /{{
"month" | i18n
}}
</span>
<span *ngIf="selectableProduct.product == productTypes.Free">{{ "freeForever" | i18n }}</span>
</label>
</div>
<div *ngIf="formGroup.controls['product'].value !== productTypes.Free">
<ng-container *ngIf="selectedPlan.hasAdditionalSeatsOption && !selectedPlan.baseSeats">
<h2 class="mt-5">{{ "users" | i18n }}</h2>
<div class="row">
<div class="col-6">
<label for="additionalSeats">{{ "userSeats" | i18n }}</label>
<input
id="additionalSeats"
class="form-control"
type="number"
name="additionalSeats"
formControlName="additionalSeats"
placeholder="{{ 'userSeatsDesc' | i18n }}"
required
/>
<small class="text-muted form-text">{{ "userSeatsHowManyDesc" | i18n }}</small>
</div>
</div>
</ng-container>
<h2 class="mt-5">{{ "addons" | i18n }}</h2>
<div class="row" *ngIf="selectedPlan.hasAdditionalSeatsOption && selectedPlan.baseSeats">
<div class="form-group col-6">
<label for="additionalSeats">{{ "additionalUserSeats" | i18n }}</label>
<input
id="additionalSeats"
class="form-control"
type="number"
name="additionalSeats"
formControlName="additionalSeats"
placeholder="{{ 'userSeatsDesc' | i18n }}"
/>
<small class="text-muted form-text">{{
"userSeatsAdditionalDesc"
| i18n: selectedPlan.baseSeats:(seatPriceMonthly(selectedPlan) | currency: "$")
}}</small>
</div>
</div>
<div class="row">
<div class="form-group col-6">
<label for="additionalStorage">{{ "additionalStorageGb" | i18n }}</label>
<input
id="additionalStorage"
class="form-control"
type="number"
name="additionalStorageGb"
formControlName="additionalStorage"
step="1"
placeholder="{{ 'additionalStorageGbDesc' | i18n }}"
/>
<small class="text-muted form-text">{{
"additionalStorageIntervalDesc"
| i18n
: "1 GB"
: (additionalStoragePriceMonthly(selectedPlan) | currency: "$")
: ("month" | i18n)
}}</small>
</div>
</div>
<div class="row">
<div class="form-group col-6" *ngIf="selectedPlan.hasPremiumAccessOption">
<div class="form-check">
<input
id="premiumAccess"
class="form-check-input"
type="checkbox"
name="premiumAccessAddon"
formControlName="premiumAccessAddon"
/>
<label for="premiumAccess" class="form-check-label bold">{{
"premiumAccess" | i18n
}}</label>
</div>
<small class="text-muted form-text">{{
"premiumAccessDesc" | i18n: (3.33 | currency: "$"):("month" | i18n)
}}</small>
</div>
</div>
<h2 class="spaced-header">{{ "summary" | i18n }}</h2>
<div class="form-check form-check-block" *ngFor="let selectablePlan of selectablePlans">
<input
class="form-check-input"
type="radio"
name="plan"
id="interval{{ selectablePlan.type }}"
[value]="selectablePlan.type"
formControlName="plan"
/>
<label class="form-check-label" for="interval{{ selectablePlan.type }}">
<ng-container *ngIf="selectablePlan.isAnnual">
{{ "annually" | i18n }}
<small *ngIf="selectablePlan.basePrice">
{{ "basePrice" | i18n }}: {{ selectablePlan.basePrice / 12 | currency: "$" }} &times; 12
{{ "monthAbbr" | i18n }}
=
<ng-container *ngIf="acceptingSponsorship; else notAcceptingSponsorship">
<span style="text-decoration: line-through">{{
selectablePlan.basePrice | currency: "$"
}}</span>
{{ "freeWithSponsorship" | i18n }}
</ng-container>
<ng-template #notAcceptingSponsorship>
{{ selectablePlan.basePrice | currency: "$" }}
/{{ "year" | i18n }}
</ng-template>
</small>
<small *ngIf="selectablePlan.hasAdditionalSeatsOption">
<span *ngIf="selectablePlan.baseSeats">{{ "additionalUsers" | i18n }}:</span>
<span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span>
{{ formGroup.controls["additionalSeats"].value || 0 }} &times;
{{ selectablePlan.seatPrice / 12 | currency: "$" }} &times; 12
{{ "monthAbbr" | i18n }} = {{ seatTotal(selectablePlan) | currency: "$" }} /{{
"year" | i18n
}}
</small>
<small *ngIf="selectablePlan.hasAdditionalStorageOption">
{{ "additionalStorageGb" | i18n }}:
{{ formGroup.controls["additionalStorage"].value || 0 }} &times;
{{ selectablePlan.additionalStoragePricePerGb / 12 | currency: "$" }} &times; 12
{{ "monthAbbr" | i18n }} =
{{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "year" | i18n }}
</small>
<small *ngIf="selectablePlan.hasPremiumAccessOption && premiumAccessAddon">
{{ "premiumAccess" | i18n }}:
{{ selectablePlan.premiumAccessOptionCost / 12 | currency: "$" }} &times; 12
{{ "monthAbbr" | i18n }}
=
{{ 40 | currency: "$" }}
/{{ "year" | i18n }}
</small>
</ng-container>
<ng-container *ngIf="!selectablePlan.isAnnual">
{{ "monthly" | i18n }}
<small *ngIf="selectablePlan.basePrice">
{{ "basePrice" | i18n }}: {{ selectablePlan.basePrice | currency: "$" }}
{{ "monthAbbr" | i18n }}
=
{{ selectablePlan.basePrice | currency: "$" }}
/{{ "month" | i18n }}
</small>
<small *ngIf="selectablePlan.hasAdditionalSeatsOption">
<span *ngIf="selectablePlan.baseSeats">{{ "additionalUsers" | i18n }}:</span>
<span *ngIf="!selectablePlan.baseSeats">{{ "users" | i18n }}:</span>
{{ formGroup.controls["additionalSeats"].value || 0 }} &times;
{{ selectablePlan.seatPrice | currency: "$" }} {{ "monthAbbr" | i18n }} =
{{ seatTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }}
</small>
<small *ngIf="selectablePlan.hasAdditionalStorageOption">
{{ "additionalStorageGb" | i18n }}:
{{ formGroup.controls["additionalStorage"].value || 0 }} &times;
{{ selectablePlan.additionalStoragePricePerGb | currency: "$" }}
{{ "monthAbbr" | i18n }} =
{{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }}
</small>
<small
*ngIf="
selectablePlan.hasPremiumAccessOption &&
formGroup.controls['premiumAccessAddon'].value
"
>
{{ "premiumAccess" | i18n }}:
{{ selectablePlan.premiumAccessOptionCost | currency: "$" }} {{ "monthAbbr" | i18n }} =
{{ 40 | currency: "$" }}
/{{ "month" | i18n }}
</small>
</ng-container>
</label>
</div>
<hr class="my-3" />
<h2 class="spaced-header mb-4">
{{ (createOrganization ? "paymentInformation" : "billingInformation") | i18n }}
</h2>
<small class="text-muted font-italic mb-3 d-block">
{{ paymentDesc }}
</small>
<app-payment *ngIf="createOrganization" [hideCredit]="true"></app-payment>
<app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info>
<div id="price" class="my-4">
<div class="text-muted text-sm">
{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}
<br />
<ng-container>
{{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }}
</ng-container>
</div>
<hr class="my-1 col-3 ml-0" />
<p class="text-lg">
<strong>{{ "total" | i18n }}:</strong> {{ total | currency: "USD $" }}/{{
selectedPlanInterval | i18n
}}
</p>
</div>
<ng-container *ngIf="!createOrganization">
<app-payment [showMethods]="false"></app-payment>
</ng-container>
</div>
<div *ngIf="singleOrgPolicyBlock" class="mt-4">
<app-callout [type]="'error'">{{ "singleOrgBlockCreateMessage" | i18n }}</app-callout>
</div>
<div class="mt-4">
<bit-submit-button [loading]="form.loading" [disabled]="!formGroup.valid">{{
"submit" | i18n
}}</bit-submit-button>
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" *ngIf="showCancel">
{{ "cancel" | i18n }}
</button>
</div>
</form>