mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 19:53:43 +00:00
org billing settings setup
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
<form #form class="card" (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="canChange">
|
||||
<div class="card-body">
|
||||
<h3 class="card-body-header">{{(currentType != null ? 'changePaymentMethod' : 'addPaymentMethod') | i18n}}</h3>
|
||||
<app-payment [showOptions]="currentType == null"></app-payment>
|
||||
<app-payment [showOptions]="!organizationId || currentType == null" [hidePaypal]="true" [hideBank]="!organizationId"></app-payment>
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<span>{{'submit' | i18n}}</span>
|
||||
|
||||
@@ -26,7 +26,7 @@ export class AdjustPaymentComponent {
|
||||
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
|
||||
|
||||
@Input() currentType?: PaymentMethodType;
|
||||
@Input() user = true;
|
||||
@Input() organizationId: string;
|
||||
@Output() onAdjusted = new EventEmitter();
|
||||
@Output() onCanceled = new EventEmitter();
|
||||
|
||||
@@ -41,8 +41,10 @@ export class AdjustPaymentComponent {
|
||||
const request = new PaymentRequest();
|
||||
this.formPromise = this.paymentComponent.createPaymentToken().then((token) => {
|
||||
request.paymentToken = token;
|
||||
if (this.user) {
|
||||
if (this.organizationId == null) {
|
||||
return this.apiService.postAccountPayment(request);
|
||||
} else {
|
||||
return this.apiService.postOrganizationPayment(this.organizationId, request);
|
||||
}
|
||||
});
|
||||
await this.formPromise;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { StorageRequest } from 'jslib/models/request/storageRequest';
|
||||
export class AdjustStorageComponent {
|
||||
@Input() storageGbPrice = 0;
|
||||
@Input() add = true;
|
||||
@Input() user = true;
|
||||
@Input() organizationId: string;
|
||||
@Input() interval = 'year';
|
||||
@Output() onAdjusted = new EventEmitter<number>();
|
||||
@Output() onCanceled = new EventEmitter();
|
||||
@@ -39,8 +39,10 @@ export class AdjustStorageComponent {
|
||||
request.storageGbAdjustment *= -1;
|
||||
}
|
||||
|
||||
if (this.user) {
|
||||
if (this.organizationId == null) {
|
||||
this.formPromise = this.apiService.postAccountStorage(request);
|
||||
} else {
|
||||
this.formPromise = this.apiService.postOrganizationStorage(this.organizationId, request);
|
||||
}
|
||||
await this.formPromise;
|
||||
this.analytics.eventTrack.next({ action: this.add ? 'Added Storage' : 'Removed Storage' });
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
</div>
|
||||
<small class="text-muted font-italic">{{'paymentChargedWithTrial' | i18n : (interval | i18n) }}</small>
|
||||
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
|
||||
<app-payment></app-payment>
|
||||
<app-payment [hidePaypal]="true"></app-payment>
|
||||
</ng-container>
|
||||
<div [ngClass]="{'mt-4': plans[plan].noPayment}">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick [disabled]="form.loading">
|
||||
|
||||
@@ -32,7 +32,7 @@ export class DeleteAccountComponent {
|
||||
const request = new PasswordVerificationRequest();
|
||||
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
|
||||
try {
|
||||
this.formPromise = this.apiService.postDeleteAccount(request);
|
||||
this.formPromise = this.apiService.deleteAccount(request);
|
||||
await this.formPromise;
|
||||
this.analytics.eventTrack.next({ action: 'Deleted Account' });
|
||||
this.toasterService.popAsync('success', this.i18nService.t('accountDeleted'),
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
<label class="form-check-label" for="method-card">
|
||||
<i class="fa fa-fw fa-credit-card"></i> {{'creditCard' | i18n}}</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<div class="form-check form-check-inline mr-4" *ngIf="!hideBank">
|
||||
<input class="form-check-input" type="radio" name="Method" id="method-bank" value="bank" [(ngModel)]="method" (change)="changeMethod()">
|
||||
<label class="form-check-label" for="method-bank">
|
||||
<i class="fa fa-fw fa-university"></i> {{'bankAccount' | i18n}}</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline" *ngIf="!hidePaypal">
|
||||
<input class="form-check-input" type="radio" name="Method" id="method-paypal" value="paypal" [(ngModel)]="method" (change)="changeMethod()">
|
||||
<label class="form-check-label" for="method-paypal">
|
||||
<i class="fa fa-fw fa-paypal"></i> PayPal</label>
|
||||
@@ -309,3 +314,31 @@
|
||||
<small class="text-muted">{{'paypalClickSubmit' | i18n}}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="method === 'bank'">
|
||||
<app-callout type="warning" title="{{'verifyBankAccount' | i18n}}">
|
||||
{{'verifyBankAccountInitialDesc' | i18n}} {{'verifyBankAccountFailureWarning' | i18n}}
|
||||
</app-callout>
|
||||
<div class="row">
|
||||
<div class="form-group col-6">
|
||||
<label for="routing_number">{{'routingNumber' | i18n}}</label>
|
||||
<input id="routing_number" class="form-control" type="text" name="routing_number" [(ngModel)]="bank.routing_number" required>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="account_number">{{'accountNumber' | i18n}}</label>
|
||||
<input id="account_number" class="form-control" type="text" name="account_number" [(ngModel)]="bank.account_number" required>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="account_holder_name">{{'accountHolderName' | i18n}}</label>
|
||||
<input id="account_holder_name" class="form-control" type="text" name="account_holder_name" [(ngModel)]="bank.account_holder_name"
|
||||
required>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="account_holder_type">{{'bankAccountType' | i18n}}</label>
|
||||
<select id="account_holder_type" class="form-control" name="account_holder_type" [(ngModel)]="bank.account_holder_type" required>
|
||||
<option value="">-- {{'select' | i18n}} --</option>
|
||||
<option value="company">{{'bankAccountTypeCompany' | i18n}}</option>
|
||||
<option value="individual">{{'bankAccountTypeIndividual' | i18n}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -20,8 +20,10 @@ const Keys = {
|
||||
})
|
||||
export class PaymentComponent implements OnInit {
|
||||
@Input() showOptions = true;
|
||||
@Input() method = 'card';
|
||||
@Input() hideBank = false;
|
||||
@Input() hidePaypal = false;
|
||||
|
||||
method = 'card';
|
||||
card: any = {
|
||||
number: null,
|
||||
exp_month: null,
|
||||
@@ -29,6 +31,14 @@ export class PaymentComponent implements OnInit {
|
||||
address_country: '',
|
||||
address_zip: null,
|
||||
};
|
||||
bank: any = {
|
||||
routing_number: null,
|
||||
account_number: null,
|
||||
account_holder_name: null,
|
||||
account_holder_type: '',
|
||||
currency: 'USD',
|
||||
country: 'US',
|
||||
};
|
||||
cardExpMonthOptions: any[];
|
||||
cardExpYearOptions: any[];
|
||||
|
||||
@@ -68,24 +78,32 @@ export class PaymentComponent implements OnInit {
|
||||
{ name: '-- ' + i18nService.t('select') + ' --', value: null },
|
||||
];
|
||||
const year = (new Date()).getFullYear();
|
||||
for (let i = year; i < (year + 10); i++) {
|
||||
for (let i = year; i < (year + 15); i++) {
|
||||
this.cardExpYearOptions.push({ name: i.toString(), value: i.toString().slice(-2) });
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.showOptions) {
|
||||
this.hidePaypal = this.method !== 'paypal';
|
||||
this.hideBank = this.method !== 'bank';
|
||||
}
|
||||
window.document.head.appendChild(this.stripeScript);
|
||||
window.document.head.appendChild(this.btScript);
|
||||
if (!this.hidePaypal) {
|
||||
window.document.head.appendChild(this.btScript);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
window.document.head.removeChild(this.stripeScript);
|
||||
window.document.head.removeChild(this.btScript);
|
||||
Array.from(window.document.querySelectorAll('iframe')).forEach((el) => {
|
||||
if (el.src != null && el.src.indexOf('stripe') > -1) {
|
||||
window.document.body.removeChild(el);
|
||||
}
|
||||
});
|
||||
if (!this.hidePaypal) {
|
||||
window.document.head.removeChild(this.btScript);
|
||||
}
|
||||
}
|
||||
|
||||
changeMethod() {
|
||||
@@ -127,7 +145,7 @@ export class PaymentComponent implements OnInit {
|
||||
}).catch((err: any) => {
|
||||
reject(err.message);
|
||||
});
|
||||
} else {
|
||||
} else if (this.method === 'card') {
|
||||
(window as any).Stripe.card.createToken(this.card, (status: number, response: any) => {
|
||||
if (status === 200 && response.id != null) {
|
||||
resolve(response.id);
|
||||
@@ -137,6 +155,16 @@ export class PaymentComponent implements OnInit {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
} else if (this.method === 'bank') {
|
||||
(window as any).Stripe.bankAccount.createToken(this.bank, (status: number, response: any) => {
|
||||
if (status === 200 && response.id != null) {
|
||||
resolve(response.id);
|
||||
} else if (response.error != null) {
|
||||
reject(response.error.message);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
<small class="text-muted font-italic">{{'paymentChargedAnnually' | i18n}}</small>
|
||||
<h2 class="spaced-header mb-4">{{'paymentInformation' | i18n}}</h2>
|
||||
<app-payment></app-payment>
|
||||
<app-payment [hideBank]="true"></app-payment>
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<span>{{'submit' | i18n}}</span>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="col-8" *ngIf="subscription">
|
||||
<strong class="d-block mb-1">{{'details' | i18n}}</strong>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
@@ -82,22 +82,22 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
<h2 class="spaced-header">{{'storage' | i18n}}</h2>
|
||||
<p>{{'subscriptionStorage' | i18n : billing.maxStorageGb : billing.storageName || '0 MB'}}</p>
|
||||
<p>{{'subscriptionStorage' | i18n : billing.maxStorageGb || 0 : billing.storageName || '0 MB'}}</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" [ngStyle]="{width: storagePercentage + '%' }" [attr.aria-valuenow]="storagePercentage"
|
||||
<div class="progress-bar bg-success" role="progressbar" [ngStyle]="{width: storageProgressWidth + '%' }" [attr.aria-valuenow]="storagePercentage"
|
||||
aria-valuemin="0" aria-valuemax="100">{{(storagePercentage / 100) | percent}}</div>
|
||||
</div>
|
||||
<ng-container *ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel && paymentSource">
|
||||
<div class="mt-3">
|
||||
<ng-container *ngIf="!showAdjustStorage">
|
||||
<div class="d-flex" *ngIf="!showAdjustStorage">
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="adjustStorage(true)">
|
||||
{{'addStorage' | i18n}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="adjustStorage(false)">
|
||||
<button type="button" class="ml-1 btn btn-outline-secondary" (click)="adjustStorage(false)">
|
||||
{{'removeStorage' | i18n}}
|
||||
</button>
|
||||
</ng-container>
|
||||
<app-adjust-storage [storageGbPrice]="4" [add]="adjustStorageAdd" [user]="true" (onAdjusted)="closeStorage(true)" (onCanceled)="closeStorage(false)"
|
||||
</div>
|
||||
<app-adjust-storage [storageGbPrice]="4" [add]="adjustStorageAdd" (onAdjusted)="closeStorage(true)" (onCanceled)="closeStorage(false)"
|
||||
*ngIf="showAdjustStorage"></app-adjust-storage>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -112,8 +112,8 @@
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="changePayment()" *ngIf="!showAdjustPayment">
|
||||
{{(paymentSource ? 'changePaymentMethod' : 'addPaymentMethod') | i18n}}
|
||||
</button>
|
||||
<app-adjust-payment [currentType]="paymentSource != null ? paymentSource.type : null" [user]="true" (onAdjusted)="closePayment(true)"
|
||||
(onCanceled)="closePayment(false)" *ngIf="showAdjustPayment">
|
||||
<app-adjust-payment [currentType]="paymentSource != null ? paymentSource.type : null" (onAdjusted)="closePayment(true)" (onCanceled)="closePayment(false)"
|
||||
*ngIf="showAdjustPayment">
|
||||
</app-adjust-payment>
|
||||
<h2 class="spaced-header">{{'charges' | i18n}}</h2>
|
||||
<p *ngIf="!charges || !charges.length">{{'noCharges' | i18n}}</p>
|
||||
|
||||
@@ -170,6 +170,11 @@ export class UserBillingComponent implements OnInit {
|
||||
}
|
||||
|
||||
get storagePercentage() {
|
||||
return this.billing != null ? +(100 * (this.billing.storageGb / this.billing.maxStorageGb)).toFixed(2) : 0;
|
||||
return this.billing != null && this.billing.maxStorageGb ?
|
||||
+(100 * (this.billing.storageGb / this.billing.maxStorageGb)).toFixed(2) : 0;
|
||||
}
|
||||
|
||||
get storageProgressWidth() {
|
||||
return this.storagePercentage < 5 ? 5 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user