mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
PM-5019 Migrate Adjust Payment component (#8383)
* PM-5019 Migrated Adjust Payment component * PM-5019 Migrated Adjust Payment dialog component * PM-5019 Removing type any * PM-5019 Addressed review comments * PM-5019 Included deleted line space
This commit is contained in:
parent
d6f2965367
commit
f5198e86fd
@ -0,0 +1,25 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-dialog
|
||||
dialogSize="large"
|
||||
[title]="(currentType != null ? 'changePaymentMethod' : 'addPaymentMethod') | i18n"
|
||||
>
|
||||
<ng-container bitDialogContent>
|
||||
<app-payment [hideBank]="!organizationId" [hideCredit]="true"></app-payment>
|
||||
<app-tax-info (onCountryChanged)="changeCountry()"></app-tax-info>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||
{{ "submit" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
bitFormButton
|
||||
buttonType="secondary"
|
||||
[bitDialogClose]="DialogResult.Cancelled"
|
||||
>
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-dialog>
|
||||
</form>
|
@ -0,0 +1,110 @@
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, Inject, ViewChild } from "@angular/core";
|
||||
import { FormGroup } from "@angular/forms";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { PaymentComponent } from "./payment.component";
|
||||
import { TaxInfoComponent } from "./tax-info.component";
|
||||
|
||||
export interface AdjustPaymentDialogData {
|
||||
organizationId: string;
|
||||
currentType: PaymentMethodType;
|
||||
}
|
||||
|
||||
export enum AdjustPaymentDialogResult {
|
||||
Adjusted = "adjusted",
|
||||
Cancelled = "cancelled",
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: "adjust-payment-dialog.component.html",
|
||||
})
|
||||
export class AdjustPaymentDialogComponent {
|
||||
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
||||
@ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent;
|
||||
|
||||
organizationId: string;
|
||||
currentType: PaymentMethodType;
|
||||
paymentMethodType = PaymentMethodType;
|
||||
|
||||
protected DialogResult = AdjustPaymentDialogResult;
|
||||
protected formGroup = new FormGroup({});
|
||||
|
||||
constructor(
|
||||
private dialogRef: DialogRef,
|
||||
@Inject(DIALOG_DATA) protected data: AdjustPaymentDialogData,
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private paymentMethodWarningService: PaymentMethodWarningService,
|
||||
) {
|
||||
this.organizationId = data.organizationId;
|
||||
this.currentType = data.currentType;
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
const request = new PaymentRequest();
|
||||
const response = this.paymentComponent.createPaymentToken().then((result) => {
|
||||
request.paymentToken = result[0];
|
||||
request.paymentMethodType = result[1];
|
||||
request.postalCode = this.taxInfoComponent.taxInfo.postalCode;
|
||||
request.country = this.taxInfoComponent.taxInfo.country;
|
||||
if (this.organizationId == null) {
|
||||
return this.apiService.postAccountPayment(request);
|
||||
} else {
|
||||
request.taxId = this.taxInfoComponent.taxInfo.taxId;
|
||||
request.state = this.taxInfoComponent.taxInfo.state;
|
||||
request.line1 = this.taxInfoComponent.taxInfo.line1;
|
||||
request.line2 = this.taxInfoComponent.taxInfo.line2;
|
||||
request.city = this.taxInfoComponent.taxInfo.city;
|
||||
request.state = this.taxInfoComponent.taxInfo.state;
|
||||
return this.organizationApiService.updatePayment(this.organizationId, request);
|
||||
}
|
||||
});
|
||||
await response;
|
||||
if (this.organizationId) {
|
||||
await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId);
|
||||
}
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("updatedPaymentMethod"),
|
||||
);
|
||||
this.dialogRef.close(AdjustPaymentDialogResult.Adjusted);
|
||||
};
|
||||
|
||||
changeCountry() {
|
||||
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||
this.paymentComponent.hideBank = !this.organizationId;
|
||||
} else {
|
||||
this.paymentComponent.hideBank = true;
|
||||
if (this.paymentComponent.method === PaymentMethodType.BankAccount) {
|
||||
this.paymentComponent.method = PaymentMethodType.Card;
|
||||
this.paymentComponent.changeMethod();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strongly typed helper to open a AdjustPaymentDialog
|
||||
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||
* @param config Configuration for the dialog
|
||||
*/
|
||||
export function openAdjustPaymentDialog(
|
||||
dialogService: DialogService,
|
||||
config: DialogConfig<AdjustPaymentDialogData>,
|
||||
) {
|
||||
return dialogService.open<AdjustPaymentDialogResult>(AdjustPaymentDialogComponent, config);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<form #form class="card" (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
||||
<div class="card-body">
|
||||
<button type="button" class="close" appA11yTitle="{{ 'cancel' | i18n }}" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 class="card-body-header">
|
||||
{{ (currentType != null ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
||||
</h3>
|
||||
<app-payment [hideBank]="!organizationId" [hideCredit]="true"></app-payment>
|
||||
<app-tax-info (onCountryChanged)="changeCountry()"></app-tax-info>
|
||||
<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>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="cancel()">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
@ -1,90 +0,0 @@
|
||||
import { Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
import { PaymentComponent } from "./payment.component";
|
||||
import { TaxInfoComponent } from "./tax-info.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-adjust-payment",
|
||||
templateUrl: "adjust-payment.component.html",
|
||||
})
|
||||
export class AdjustPaymentComponent {
|
||||
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
||||
@ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent;
|
||||
|
||||
@Input() currentType?: PaymentMethodType;
|
||||
@Input() organizationId: string;
|
||||
@Output() onAdjusted = new EventEmitter();
|
||||
@Output() onCanceled = new EventEmitter();
|
||||
|
||||
paymentMethodType = PaymentMethodType;
|
||||
formPromise: Promise<void>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private paymentMethodWarningService: PaymentMethodWarningService,
|
||||
) {}
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
const request = new PaymentRequest();
|
||||
this.formPromise = this.paymentComponent.createPaymentToken().then((result) => {
|
||||
request.paymentToken = result[0];
|
||||
request.paymentMethodType = result[1];
|
||||
request.postalCode = this.taxInfoComponent.taxInfo.postalCode;
|
||||
request.country = this.taxInfoComponent.taxInfo.country;
|
||||
if (this.organizationId == null) {
|
||||
return this.apiService.postAccountPayment(request);
|
||||
} else {
|
||||
request.taxId = this.taxInfoComponent.taxInfo.taxId;
|
||||
request.state = this.taxInfoComponent.taxInfo.state;
|
||||
request.line1 = this.taxInfoComponent.taxInfo.line1;
|
||||
request.line2 = this.taxInfoComponent.taxInfo.line2;
|
||||
request.city = this.taxInfoComponent.taxInfo.city;
|
||||
request.state = this.taxInfoComponent.taxInfo.state;
|
||||
return this.organizationApiService.updatePayment(this.organizationId, request);
|
||||
}
|
||||
});
|
||||
await this.formPromise;
|
||||
if (this.organizationId) {
|
||||
await this.paymentMethodWarningService.removeSubscriptionRisk(this.organizationId);
|
||||
}
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("updatedPaymentMethod"),
|
||||
);
|
||||
this.onAdjusted.emit();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.onCanceled.emit();
|
||||
}
|
||||
|
||||
changeCountry() {
|
||||
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||
this.paymentComponent.hideBank = !this.organizationId;
|
||||
} else {
|
||||
this.paymentComponent.hideBank = true;
|
||||
if (this.paymentComponent.method === PaymentMethodType.BankAccount) {
|
||||
this.paymentComponent.method = PaymentMethodType.Card;
|
||||
this.paymentComponent.changeMethod();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import { HeaderModule } from "../../layouts/header/header.module";
|
||||
import { SharedModule } from "../../shared";
|
||||
|
||||
import { AddCreditComponent } from "./add-credit.component";
|
||||
import { AdjustPaymentComponent } from "./adjust-payment.component";
|
||||
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.component";
|
||||
import { AdjustStorageComponent } from "./adjust-storage.component";
|
||||
import { BillingHistoryComponent } from "./billing-history.component";
|
||||
import { OffboardingSurveyComponent } from "./offboarding-survey.component";
|
||||
@ -18,7 +18,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
|
||||
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
||||
declarations: [
|
||||
AddCreditComponent,
|
||||
AdjustPaymentComponent,
|
||||
AdjustPaymentDialogComponent,
|
||||
AdjustStorageComponent,
|
||||
BillingHistoryComponent,
|
||||
PaymentMethodComponent,
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<bit-container>
|
||||
<div class="tabbed-header" *ngIf="!organizationId">
|
||||
<!-- TODO: Organization and individual should use different "page" components -->
|
||||
<!--TODO: Organization and individual should use different "page" components -->
|
||||
<h1>{{ "paymentMethod" | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
@ -102,23 +102,9 @@
|
||||
{{ paymentSource.description }}
|
||||
</p>
|
||||
</ng-container>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
(click)="changePayment()"
|
||||
*ngIf="!showAdjustPayment"
|
||||
>
|
||||
<button type="button" bitButton buttonType="secondary" [bitAction]="changePayment">
|
||||
{{ (paymentSource ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
|
||||
</button>
|
||||
<app-adjust-payment
|
||||
[organizationId]="organizationId"
|
||||
[currentType]="paymentSource != null ? paymentSource.type : null"
|
||||
(onAdjusted)="closePayment(true)"
|
||||
(onCanceled)="closePayment(false)"
|
||||
*ngIf="showAdjustPayment"
|
||||
>
|
||||
</app-adjust-payment>
|
||||
<p *ngIf="isUnpaid">{{ "paymentChargedWithUnpaidSubscription" | i18n }}</p>
|
||||
<ng-container *ngIf="forOrganization">
|
||||
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
@ -14,6 +15,10 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import {
|
||||
AdjustPaymentDialogResult,
|
||||
openAdjustPaymentDialog,
|
||||
} from "./adjust-payment-dialog.component";
|
||||
import { TaxInfoComponent } from "./tax-info.component";
|
||||
|
||||
@Component({
|
||||
@ -25,7 +30,6 @@ export class PaymentMethodComponent implements OnInit {
|
||||
|
||||
loading = false;
|
||||
firstLoaded = false;
|
||||
showAdjustPayment = false;
|
||||
showAddCredit = false;
|
||||
billing: BillingPaymentResponse;
|
||||
org: OrganizationSubscriptionResponse;
|
||||
@ -120,18 +124,18 @@ export class PaymentMethodComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
changePayment() {
|
||||
this.showAdjustPayment = true;
|
||||
}
|
||||
|
||||
closePayment(load: boolean) {
|
||||
this.showAdjustPayment = false;
|
||||
if (load) {
|
||||
// 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
|
||||
this.load();
|
||||
}
|
||||
changePayment = async () => {
|
||||
const dialogRef = openAdjustPaymentDialog(this.dialogService, {
|
||||
data: {
|
||||
organizationId: this.organizationId,
|
||||
currentType: this.paymentSource !== null ? this.paymentSource.type : null,
|
||||
},
|
||||
});
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
if (result === AdjustPaymentDialogResult.Adjusted) {
|
||||
await this.load();
|
||||
}
|
||||
};
|
||||
|
||||
async verifyBank() {
|
||||
if (this.loading || !this.forOrganization) {
|
||||
|
Loading…
Reference in New Issue
Block a user