1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-22 16:29:09 +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:
KiruthigaManivannan 2024-04-17 01:08:19 +05:30 committed by GitHub
parent d6f2965367
commit f5198e86fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 155 additions and 139 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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">&times;</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>

View File

@ -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();
}
}
}
}

View File

@ -4,7 +4,7 @@ import { HeaderModule } from "../../layouts/header/header.module";
import { SharedModule } from "../../shared"; import { SharedModule } from "../../shared";
import { AddCreditComponent } from "./add-credit.component"; 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 { AdjustStorageComponent } from "./adjust-storage.component";
import { BillingHistoryComponent } from "./billing-history.component"; import { BillingHistoryComponent } from "./billing-history.component";
import { OffboardingSurveyComponent } from "./offboarding-survey.component"; import { OffboardingSurveyComponent } from "./offboarding-survey.component";
@ -18,7 +18,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule], imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
declarations: [ declarations: [
AddCreditComponent, AddCreditComponent,
AdjustPaymentComponent, AdjustPaymentDialogComponent,
AdjustStorageComponent, AdjustStorageComponent,
BillingHistoryComponent, BillingHistoryComponent,
PaymentMethodComponent, PaymentMethodComponent,

View File

@ -15,7 +15,7 @@
<bit-container> <bit-container>
<div class="tabbed-header" *ngIf="!organizationId"> <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> <h1>{{ "paymentMethod" | i18n }}</h1>
</div> </div>
@ -102,23 +102,9 @@
{{ paymentSource.description }} {{ paymentSource.description }}
</p> </p>
</ng-container> </ng-container>
<button <button type="button" bitButton buttonType="secondary" [bitAction]="changePayment">
type="button"
bitButton
buttonType="secondary"
(click)="changePayment()"
*ngIf="!showAdjustPayment"
>
{{ (paymentSource ? "changePaymentMethod" : "addPaymentMethod") | i18n }} {{ (paymentSource ? "changePaymentMethod" : "addPaymentMethod") | i18n }}
</button> </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> <p *ngIf="isUnpaid">{{ "paymentChargedWithUnpaidSubscription" | i18n }}</p>
<ng-container *ngIf="forOrganization"> <ng-container *ngIf="forOrganization">
<h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2> <h2 class="spaced-header">{{ "taxInformation" | i18n }}</h2>

View File

@ -1,6 +1,7 @@
import { Component, OnInit, ViewChild } from "@angular/core"; import { Component, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { FormBuilder, FormControl, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { lastValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; 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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
import {
AdjustPaymentDialogResult,
openAdjustPaymentDialog,
} from "./adjust-payment-dialog.component";
import { TaxInfoComponent } from "./tax-info.component"; import { TaxInfoComponent } from "./tax-info.component";
@Component({ @Component({
@ -25,7 +30,6 @@ export class PaymentMethodComponent implements OnInit {
loading = false; loading = false;
firstLoaded = false; firstLoaded = false;
showAdjustPayment = false;
showAddCredit = false; showAddCredit = false;
billing: BillingPaymentResponse; billing: BillingPaymentResponse;
org: OrganizationSubscriptionResponse; org: OrganizationSubscriptionResponse;
@ -120,18 +124,18 @@ export class PaymentMethodComponent implements OnInit {
} }
} }
changePayment() { changePayment = async () => {
this.showAdjustPayment = true; const dialogRef = openAdjustPaymentDialog(this.dialogService, {
} data: {
organizationId: this.organizationId,
closePayment(load: boolean) { currentType: this.paymentSource !== null ? this.paymentSource.type : null,
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. const result = await lastValueFrom(dialogRef.closed);
// eslint-disable-next-line @typescript-eslint/no-floating-promises if (result === AdjustPaymentDialogResult.Adjusted) {
this.load(); await this.load();
}
} }
};
async verifyBank() { async verifyBank() {
if (this.loading || !this.forOrganization) { if (this.loading || !this.forOrganization) {