mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
[PM-5018] Migrate Add Credit component (#8321)
* Migrated Add Credit component * PM-5018 Addressed Review comments for Add Credit Component * PM-5018 Design team comments addressed * PM-5018 Latest review comments are addressed * PM-5018 Minor comments addressed
This commit is contained in:
committed by
GitHub
parent
a3d69047c7
commit
c8998d0c00
@@ -0,0 +1,61 @@
|
|||||||
|
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
|
<bit-dialog dialogSize="default" [title]="'addCredit' | i18n">
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<p bitTypography="body1">{{ "creditDelayed" | i18n }}</p>
|
||||||
|
<div class="tw-grid tw-grid-cols-2">
|
||||||
|
<bit-radio-group formControlName="method">
|
||||||
|
<bit-radio-button id="credit-method-paypal" [value]="paymentMethodType.PayPal">
|
||||||
|
<bit-label> <i class="bwi bwi-paypal"></i>PayPal</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
<bit-radio-button id="credit-method-bitcoin" [value]="paymentMethodType.BitPay">
|
||||||
|
<bit-label> <i class="bwi bwi-bitcoin"></i>Bitcoin</bit-label>
|
||||||
|
</bit-radio-button>
|
||||||
|
</bit-radio-group>
|
||||||
|
</div>
|
||||||
|
<div class="tw-grid tw-grid-cols-2">
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>{{ "amount" | i18n }}</bit-label>
|
||||||
|
<input
|
||||||
|
bitInput
|
||||||
|
type="text"
|
||||||
|
formControlName="creditAmount"
|
||||||
|
(blur)="formatAmount()"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span bitPrefix>$USD</span>
|
||||||
|
</bit-form-field>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
<form #ppButtonForm action="{{ ppButtonFormAction }}" method="post" target="_top">
|
||||||
|
<input type="hidden" name="cmd" value="_xclick" />
|
||||||
|
<input type="hidden" name="business" value="{{ ppButtonBusinessId }}" />
|
||||||
|
<input type="hidden" name="button_subtype" value="services" />
|
||||||
|
<input type="hidden" name="no_note" value="1" />
|
||||||
|
<input type="hidden" name="no_shipping" value="1" />
|
||||||
|
<input type="hidden" name="rm" value="1" />
|
||||||
|
<input type="hidden" name="return" value="{{ returnUrl }}" />
|
||||||
|
<input type="hidden" name="cancel_return" value="{{ returnUrl }}" />
|
||||||
|
<input type="hidden" name="currency_code" value="USD" />
|
||||||
|
<input type="hidden" name="image_url" value="https://bitwarden.com/images/paypal-banner.png" />
|
||||||
|
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynow_LG.gif:NonHosted" />
|
||||||
|
<input type="hidden" name="amount" value="{{ formGroup.get('creditAmount').value }}" />
|
||||||
|
<input type="hidden" name="custom" value="{{ ppButtonCustomField }}" />
|
||||||
|
<input type="hidden" name="item_name" value="Bitwarden Account Credit" />
|
||||||
|
<input type="hidden" name="item_number" value="{{ subject }}" />
|
||||||
|
</form>
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
import {
|
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||||
Component,
|
import { Component, ElementRef, Inject, OnInit, ViewChild } from "@angular/core";
|
||||||
ElementRef,
|
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||||
EventEmitter,
|
|
||||||
Input,
|
|
||||||
OnInit,
|
|
||||||
Output,
|
|
||||||
ViewChild,
|
|
||||||
} from "@angular/core";
|
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -17,6 +11,16 @@ import { BitPayInvoiceRequest } from "@bitwarden/common/billing/models/request/b
|
|||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
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";
|
||||||
|
|
||||||
|
export interface AddCreditDialogData {
|
||||||
|
organizationId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AddCreditDialogResult {
|
||||||
|
Added = "added",
|
||||||
|
Cancelled = "cancelled",
|
||||||
|
}
|
||||||
|
|
||||||
export type PayPalConfig = {
|
export type PayPalConfig = {
|
||||||
businessId?: string;
|
businessId?: string;
|
||||||
@@ -24,17 +28,9 @@ export type PayPalConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-add-credit",
|
templateUrl: "add-credit-dialog.component.html",
|
||||||
templateUrl: "add-credit.component.html",
|
|
||||||
})
|
})
|
||||||
export class AddCreditComponent implements OnInit {
|
export class AddCreditDialogComponent implements OnInit {
|
||||||
@Input() creditAmount: string;
|
|
||||||
@Input() showOptions = true;
|
|
||||||
@Input() method = PaymentMethodType.PayPal;
|
|
||||||
@Input() organizationId: string;
|
|
||||||
@Output() onAdded = new EventEmitter();
|
|
||||||
@Output() onCanceled = new EventEmitter();
|
|
||||||
|
|
||||||
@ViewChild("ppButtonForm", { read: ElementRef, static: true }) ppButtonFormRef: ElementRef;
|
@ViewChild("ppButtonForm", { read: ElementRef, static: true }) ppButtonFormRef: ElementRef;
|
||||||
|
|
||||||
paymentMethodType = PaymentMethodType;
|
paymentMethodType = PaymentMethodType;
|
||||||
@@ -44,14 +40,22 @@ export class AddCreditComponent implements OnInit {
|
|||||||
ppLoading = false;
|
ppLoading = false;
|
||||||
subject: string;
|
subject: string;
|
||||||
returnUrl: string;
|
returnUrl: string;
|
||||||
formPromise: Promise<any>;
|
organizationId: string;
|
||||||
|
|
||||||
private userId: string;
|
private userId: string;
|
||||||
private name: string;
|
private name: string;
|
||||||
private email: string;
|
private email: string;
|
||||||
private region: string;
|
private region: string;
|
||||||
|
|
||||||
|
protected DialogResult = AddCreditDialogResult;
|
||||||
|
protected formGroup = new FormGroup({
|
||||||
|
method: new FormControl(PaymentMethodType.PayPal),
|
||||||
|
creditAmount: new FormControl(null, [Validators.required]),
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private dialogRef: DialogRef,
|
||||||
|
@Inject(DIALOG_DATA) protected data: AddCreditDialogData,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
@@ -59,6 +63,7 @@ export class AddCreditComponent implements OnInit {
|
|||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
|
this.organizationId = data.organizationId;
|
||||||
const payPalConfig = process.env.PAYPAL_CONFIG as PayPalConfig;
|
const payPalConfig = process.env.PAYPAL_CONFIG as PayPalConfig;
|
||||||
this.ppButtonFormAction = payPalConfig.buttonAction;
|
this.ppButtonFormAction = payPalConfig.buttonAction;
|
||||||
this.ppButtonBusinessId = payPalConfig.businessId;
|
this.ppButtonBusinessId = payPalConfig.businessId;
|
||||||
@@ -93,7 +98,18 @@ export class AddCreditComponent implements OnInit {
|
|||||||
this.returnUrl = window.location.href;
|
this.returnUrl = window.location.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
get creditAmount() {
|
||||||
|
return this.formGroup.value.creditAmount;
|
||||||
|
}
|
||||||
|
set creditAmount(value: string) {
|
||||||
|
this.formGroup.get("creditAmount").setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get method() {
|
||||||
|
return this.formGroup.value.method;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
if (this.creditAmount == null || this.creditAmount === "") {
|
if (this.creditAmount == null || this.creditAmount === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,33 +120,20 @@ export class AddCreditComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.method === PaymentMethodType.BitPay) {
|
if (this.method === PaymentMethodType.BitPay) {
|
||||||
try {
|
const req = new BitPayInvoiceRequest();
|
||||||
const req = new BitPayInvoiceRequest();
|
req.email = this.email;
|
||||||
req.email = this.email;
|
req.name = this.name;
|
||||||
req.name = this.name;
|
req.credit = true;
|
||||||
req.credit = true;
|
req.amount = this.creditAmountNumber;
|
||||||
req.amount = this.creditAmountNumber;
|
req.organizationId = this.organizationId;
|
||||||
req.organizationId = this.organizationId;
|
req.userId = this.userId;
|
||||||
req.userId = this.userId;
|
req.returnUrl = this.returnUrl;
|
||||||
req.returnUrl = this.returnUrl;
|
const bitPayUrl: string = await this.apiService.postBitPayInvoice(req);
|
||||||
this.formPromise = this.apiService.postBitPayInvoice(req);
|
this.platformUtilsService.launchUri(bitPayUrl);
|
||||||
const bitPayUrl: string = await this.formPromise;
|
|
||||||
this.platformUtilsService.launchUri(bitPayUrl);
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
this.dialogRef.close(AddCreditDialogResult.Added);
|
||||||
this.onAdded.emit();
|
};
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.onCanceled.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
formatAmount() {
|
formatAmount() {
|
||||||
try {
|
try {
|
||||||
@@ -160,3 +163,15 @@ export class AddCreditComponent implements OnInit {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strongly typed helper to open a AddCreditDialog
|
||||||
|
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||||
|
* @param config Configuration for the dialog
|
||||||
|
*/
|
||||||
|
export function openAddCreditDialog(
|
||||||
|
dialogService: DialogService,
|
||||||
|
config: DialogConfig<AddCreditDialogData>,
|
||||||
|
) {
|
||||||
|
return dialogService.open<AddCreditDialogResult>(AddCreditDialogComponent, config);
|
||||||
|
}
|
||||||
@@ -1,80 +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">{{ "addCredit" | i18n }}</h3>
|
|
||||||
<div class="mb-4 text-lg" *ngIf="showOptions">
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="Method"
|
|
||||||
id="credit-method-paypal"
|
|
||||||
[value]="paymentMethodType.PayPal"
|
|
||||||
[(ngModel)]="method"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="credit-method-paypal">
|
|
||||||
<i class="bwi bwi-fw bwi-paypal" aria-hidden="true"></i> PayPal</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="radio"
|
|
||||||
name="Method"
|
|
||||||
id="credit-method-bitcoin"
|
|
||||||
[value]="paymentMethodType.BitPay"
|
|
||||||
[(ngModel)]="method"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="credit-method-bitcoin">
|
|
||||||
<i class="bwi bwi-fw bwi-bitcoin" aria-hidden="true"></i> Bitcoin</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4">
|
|
||||||
<label for="creditAmount">{{ "amount" | i18n }}</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-prepend"><span class="input-group-text">$USD</span></div>
|
|
||||||
<input
|
|
||||||
id="creditAmount"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
name="CreditAmount"
|
|
||||||
[(ngModel)]="creditAmount"
|
|
||||||
(blur)="formatAmount()"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">{{ "creditDelayed" | i18n }}</small>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading || ppLoading">
|
|
||||||
<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>
|
|
||||||
<form #ppButtonForm action="{{ ppButtonFormAction }}" method="post" target="_top">
|
|
||||||
<input type="hidden" name="cmd" value="_xclick" />
|
|
||||||
<input type="hidden" name="business" value="{{ ppButtonBusinessId }}" />
|
|
||||||
<input type="hidden" name="button_subtype" value="services" />
|
|
||||||
<input type="hidden" name="no_note" value="1" />
|
|
||||||
<input type="hidden" name="no_shipping" value="1" />
|
|
||||||
<input type="hidden" name="rm" value="1" />
|
|
||||||
<input type="hidden" name="return" value="{{ returnUrl }}" />
|
|
||||||
<input type="hidden" name="cancel_return" value="{{ returnUrl }}" />
|
|
||||||
<input type="hidden" name="currency_code" value="USD" />
|
|
||||||
<input type="hidden" name="image_url" value="https://bitwarden.com/images/paypal-banner.png" />
|
|
||||||
<input type="hidden" name="bn" value="PP-BuyNowBF:btn_buynow_LG.gif:NonHosted" />
|
|
||||||
<input type="hidden" name="amount" value="{{ creditAmount }}" />
|
|
||||||
<input type="hidden" name="custom" value="{{ ppButtonCustomField }}" />
|
|
||||||
<input type="hidden" name="item_name" value="Bitwarden Account Credit" />
|
|
||||||
<input type="hidden" name="item_number" value="{{ subject }}" />
|
|
||||||
</form>
|
|
||||||
@@ -3,7 +3,7 @@ import { NgModule } from "@angular/core";
|
|||||||
import { HeaderModule } from "../../layouts/header/header.module";
|
import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
|
|
||||||
import { AddCreditComponent } from "./add-credit.component";
|
import { AddCreditDialogComponent } from "./add-credit-dialog.component";
|
||||||
import { AdjustPaymentDialogComponent } from "./adjust-payment-dialog.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";
|
||||||
@@ -17,7 +17,7 @@ import { UpdateLicenseComponent } from "./update-license.component";
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
imports: [SharedModule, PaymentComponent, TaxInfoComponent, HeaderModule],
|
||||||
declarations: [
|
declarations: [
|
||||||
AddCreditComponent,
|
AddCreditDialogComponent,
|
||||||
AdjustPaymentDialogComponent,
|
AdjustPaymentDialogComponent,
|
||||||
AdjustStorageComponent,
|
AdjustStorageComponent,
|
||||||
BillingHistoryComponent,
|
BillingHistoryComponent,
|
||||||
|
|||||||
@@ -33,22 +33,9 @@
|
|||||||
<strong>{{ creditOrBalance | currency: "$" }}</strong>
|
<strong>{{ creditOrBalance | currency: "$" }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<p>{{ "creditAppliedDesc" | i18n }}</p>
|
<p>{{ "creditAppliedDesc" | i18n }}</p>
|
||||||
<button
|
<button type="button" bitButton buttonType="secondary" [bitAction]="addCredit">
|
||||||
type="button"
|
|
||||||
bitButton
|
|
||||||
buttonType="secondary"
|
|
||||||
(click)="addCredit()"
|
|
||||||
*ngIf="!showAddCredit"
|
|
||||||
>
|
|
||||||
{{ "addCredit" | i18n }}
|
{{ "addCredit" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<app-add-credit
|
|
||||||
[organizationId]="organizationId"
|
|
||||||
(onAdded)="closeAddCredit(true)"
|
|
||||||
(onCanceled)="closeAddCredit(false)"
|
|
||||||
*ngIf="showAddCredit"
|
|
||||||
>
|
|
||||||
</app-add-credit>
|
|
||||||
<h2 class="spaced-header">{{ "paymentMethod" | i18n }}</h2>
|
<h2 class="spaced-header">{{ "paymentMethod" | i18n }}</h2>
|
||||||
<p *ngIf="!paymentSource">{{ "noPaymentMethod" | i18n }}</p>
|
<p *ngIf="!paymentSource">{{ "noPaymentMethod" | i18n }}</p>
|
||||||
<ng-container *ngIf="paymentSource">
|
<ng-container *ngIf="paymentSource">
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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 { AddCreditDialogResult, openAddCreditDialog } from "./add-credit-dialog.component";
|
||||||
import {
|
import {
|
||||||
AdjustPaymentDialogResult,
|
AdjustPaymentDialogResult,
|
||||||
openAdjustPaymentDialog,
|
openAdjustPaymentDialog,
|
||||||
@@ -30,7 +31,6 @@ export class PaymentMethodComponent implements OnInit {
|
|||||||
|
|
||||||
loading = false;
|
loading = false;
|
||||||
firstLoaded = false;
|
firstLoaded = false;
|
||||||
showAddCredit = false;
|
|
||||||
billing: BillingPaymentResponse;
|
billing: BillingPaymentResponse;
|
||||||
org: OrganizationSubscriptionResponse;
|
org: OrganizationSubscriptionResponse;
|
||||||
sub: SubscriptionResponse;
|
sub: SubscriptionResponse;
|
||||||
@@ -111,18 +111,17 @@ export class PaymentMethodComponent implements OnInit {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCredit() {
|
addCredit = async () => {
|
||||||
this.showAddCredit = true;
|
const dialogRef = openAddCreditDialog(this.dialogService, {
|
||||||
}
|
data: {
|
||||||
|
organizationId: this.organizationId,
|
||||||
closeAddCredit(load: boolean) {
|
},
|
||||||
this.showAddCredit = false;
|
});
|
||||||
if (load) {
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
if (result === AddCreditDialogResult.Added) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
await this.load();
|
||||||
this.load();
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
changePayment = async () => {
|
changePayment = async () => {
|
||||||
const dialogRef = openAdjustPaymentDialog(this.dialogService, {
|
const dialogRef = openAdjustPaymentDialog(this.dialogService, {
|
||||||
|
|||||||
Reference in New Issue
Block a user