web/satellite: Payment methods markup (#3054)
This commit is contained in:
parent
423d35fb3f
commit
b85addb658
@ -31,6 +31,8 @@ export default class App extends Vue {
|
||||
'sortTeamMemberByDropdownButton',
|
||||
'notificationArea',
|
||||
'successfulRegistrationPopup',
|
||||
'paymentSelectButton',
|
||||
'paymentSelect',
|
||||
];
|
||||
|
||||
private onClick(e: Event): void {
|
||||
|
@ -21,6 +21,7 @@
|
||||
</div>
|
||||
<AccountBalance/>
|
||||
<MonthlyBillingSummary/>
|
||||
<PaymentMethods />
|
||||
<DepositAndBilling/>
|
||||
</div>
|
||||
</template>
|
||||
@ -31,12 +32,14 @@ import { Component, Vue } from 'vue-property-decorator';
|
||||
import AccountBalance from '@/components/account/billing/AccountBalance.vue';
|
||||
import DepositAndBilling from '@/components/account/billing/DepositAndBilling.vue';
|
||||
import MonthlyBillingSummary from '@/components/account/billing/MonthlyBillingSummary.vue';
|
||||
import PaymentMethods from '@/components/account/billing/PaymentMethods.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
AccountBalance,
|
||||
MonthlyBillingSummary,
|
||||
DepositAndBilling,
|
||||
PaymentMethods,
|
||||
},
|
||||
})
|
||||
export default class BillingArea extends Vue {}
|
||||
|
140
web/satellite/src/components/account/billing/CardComponent.vue
Normal file
140
web/satellite/src/components/account/billing/CardComponent.vue
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="payment-methods-container__card-container">
|
||||
<div class="payment-methods-container__card-container__info-area">
|
||||
<svg width="52" height="35" viewBox="0 0 52 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.1174 7.97095H20.1162V27.7581H31.1174V7.97095Z" fill="#FF5F00"/>
|
||||
<path d="M20.8148 17.8645C20.8148 13.8442 22.7007 10.2783 25.5994 7.97095C23.4691 6.29289 20.7799 5.27905 17.8462 5.27905C10.8963 5.27905 5.27344 10.9076 5.27344 17.8645C5.27344 24.8215 10.8963 30.45 17.8462 30.45C20.7799 30.45 23.4691 29.4362 25.5994 27.7581C22.7007 25.4858 20.8148 21.8849 20.8148 17.8645Z" fill="#EB001B"/>
|
||||
<path d="M45.9603 17.8645C45.9603 24.8215 40.3375 30.45 33.3875 30.45C30.4539 30.45 27.7647 29.4362 25.6343 27.7581C28.5679 25.4508 30.4189 21.8849 30.4189 17.8645C30.4189 13.8442 28.533 10.2783 25.6343 7.97095C27.7647 6.29289 30.4539 5.27905 33.3875 5.27905C40.3375 5.27905 45.9603 10.9425 45.9603 17.8645Z" fill="#F79E1B"/>
|
||||
</svg>
|
||||
<div class="payment-methods-container__card-container__info-area__info-container">
|
||||
<h1 class="bold">**** **** **** {{1111}}</h1>
|
||||
</div>
|
||||
<div class="payment-methods-container__card-container__info-area__expire-container">
|
||||
<h2 class="medium">Expires</h2>
|
||||
<h1 class="bold">{{1}}/{{1111}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="payment-methods-container__card-container__button-area">
|
||||
<div class="payment-methods-container__card-container__default-button" v-if="false">
|
||||
<p class="payment-methods-container__card-container__default-button__label">Default</p>
|
||||
</div>
|
||||
<div class="payment-methods-container__card-container__dots-container">
|
||||
<svg width="12" height="4" viewBox="0 0 12 4" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="4" height="4" rx="2" fill="#354049"/>
|
||||
<rect x="8" width="4" height="4" rx="2" fill="#354049"/>
|
||||
</svg>
|
||||
<CardDialog />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import CardDialog from '@/components/account/billing/CardDialog.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
CardDialog,
|
||||
},
|
||||
})
|
||||
export default class CardComponent extends Vue {}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bold {
|
||||
font-family: 'font_bold';
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #61666B;
|
||||
margin-block-start: 0.5em;
|
||||
margin-block-end: 0.5em;
|
||||
}
|
||||
|
||||
.medium {
|
||||
font-family: 'font_regular';
|
||||
font-size: 16px;
|
||||
line-height: 21px;
|
||||
color: #61666B;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.payment-methods-container__card-container {
|
||||
width: calc(100% - 40px);
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
background-color: #F5F6FA;
|
||||
border-radius: 6px;
|
||||
|
||||
&__info-area {
|
||||
width: 75%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
&__card-logo {
|
||||
height: 70px;
|
||||
width: 85px;
|
||||
}
|
||||
|
||||
&__info-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: auto;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
&__expire-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: auto;
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
&__button-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__default-button {
|
||||
width: 100px;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_medium';
|
||||
font-size: 16px;
|
||||
line-height: 23px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&__dots-container {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
55
web/satellite/src/components/account/billing/CardDialog.vue
Normal file
55
web/satellite/src/components/account/billing/CardDialog.vue
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="dialog">
|
||||
<p class="label dialog__make-default">Make Default</p>
|
||||
<p class="label dialog__delete">Delete</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
@Component
|
||||
export default class CardDialog extends Vue {}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dialog {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
right: -30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
z-index: 100;
|
||||
background-image: url("../../../../static/images/payments/Dialog.svg");
|
||||
background-size: contain;
|
||||
width: 167px;
|
||||
height: 122px;
|
||||
cursor: initial;
|
||||
|
||||
&__make-default {
|
||||
color: #61666B;
|
||||
}
|
||||
|
||||
&__delete {
|
||||
color: #EB5757;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
font-family: 'font_medium';
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
height: 35%;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
</style>
|
199
web/satellite/src/components/account/billing/PaymentMethods.vue
Normal file
199
web/satellite/src/components/account/billing/PaymentMethods.vue
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div class="payment-methods-area">
|
||||
<div class="payment-methods-area__top-container">
|
||||
<h1 class="payment-methods-area__title text">Payment Methods</h1>
|
||||
<div class="payment-methods-area__button-area">
|
||||
<div class="payment-methods-area__button-area__default-buttons" v-if="isDefaultState">
|
||||
<VButton
|
||||
class="button"
|
||||
label="Add STORJ"
|
||||
width="123px"
|
||||
height="48px"
|
||||
:on-press="onAddSTORJ"/>
|
||||
<VButton
|
||||
class="button"
|
||||
label="Add Card"
|
||||
width="123px"
|
||||
height="48px"
|
||||
:on-press="onAddCard"/>
|
||||
</div>
|
||||
<div class="payment-methods-area__button-area__cancel" v-if="!isDefaultState" @click="onCancel">
|
||||
<p class="payment-methods-area__button-area__cancel__text">Cancel</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="payment-methods-area__adding-container storj" v-if="isAddingStorjState">
|
||||
<div class="storj-container">
|
||||
<p class="storj-container__label">Deposit STORJ Tokens via Coin Payments</p>
|
||||
<StorjInput class="form"/>
|
||||
</div>
|
||||
<VButton
|
||||
label="Continue to Coin Payments"
|
||||
width="251px"
|
||||
height="48px"
|
||||
:on-press="onConfirmAddSTORJ"/>
|
||||
</div>
|
||||
<div class="payment-methods-area__adding-container card" v-if="isAddingCardState">
|
||||
<p class="payment-methods-area__adding-container__label">Add Credit or Debit Card</p>
|
||||
<StripeInput />
|
||||
<VButton
|
||||
label="Add card"
|
||||
width="123px"
|
||||
height="48px"
|
||||
:on-press="onConfirmAddSTORJ"/>
|
||||
</div>
|
||||
<div class="payment-methods-area__existing-cards-container">
|
||||
<CardComponent />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import CardComponent from '@/components/account/billing/CardComponent.vue';
|
||||
import StorjInput from '@/components/account/billing/StorjInput.vue';
|
||||
import StripeInput from '@/components/account/billing/StripeInput.vue';
|
||||
import VButton from '@/components/common/VButton.vue';
|
||||
|
||||
import { PaymentMethodsBlockState } from '@/utils/constants/billingEnums';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
VButton,
|
||||
CardComponent,
|
||||
StorjInput,
|
||||
StripeInput,
|
||||
},
|
||||
})
|
||||
export default class PaymentMethods extends Vue {
|
||||
private areaState: number = PaymentMethodsBlockState.DEFAULT;
|
||||
|
||||
public get isDefaultState(): boolean {
|
||||
return this.areaState === PaymentMethodsBlockState.DEFAULT;
|
||||
}
|
||||
public get isAddingStorjState(): boolean {
|
||||
return this.areaState === PaymentMethodsBlockState.ADDING_STORJ;
|
||||
}
|
||||
public get isAddingCardState(): boolean {
|
||||
return this.areaState === PaymentMethodsBlockState.ADDING_CARD;
|
||||
}
|
||||
|
||||
public onAddSTORJ(): void {
|
||||
this.areaState = PaymentMethodsBlockState.ADDING_STORJ;
|
||||
|
||||
return;
|
||||
}
|
||||
public onAddCard(): void {
|
||||
this.areaState = PaymentMethodsBlockState.ADDING_CARD;
|
||||
|
||||
return;
|
||||
}
|
||||
public onCancel(): void {
|
||||
this.areaState = PaymentMethodsBlockState.DEFAULT;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public onConfirmAddSTORJ(): void {
|
||||
return;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.text {
|
||||
margin: 0;
|
||||
color: #354049;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
&:last-of-type {
|
||||
margin-left: 23px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #0059D0;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-methods-area {
|
||||
padding: 40px;
|
||||
margin-bottom: 47px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 8px;
|
||||
font-family: 'font_regular';
|
||||
|
||||
&__top-container {
|
||||
display: flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-family: 'font_bold';
|
||||
font-size: 32px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
&__button-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-height: 48px;
|
||||
|
||||
&__default-buttons {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__cancel {
|
||||
|
||||
&__text {
|
||||
font-family: 'font_medium';
|
||||
font-size: 16px;
|
||||
text-decoration: underline;
|
||||
color: #354049;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__adding-container {
|
||||
margin-top: 44px;
|
||||
display: flex;
|
||||
max-height: 52px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
font-family: 'font_medium';
|
||||
font-size: 21px;
|
||||
}
|
||||
}
|
||||
|
||||
&__existing-cards-container {
|
||||
position: relative;
|
||||
padding-top: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.storj-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
201
web/satellite/src/components/account/billing/StorjInput.vue
Normal file
201
web/satellite/src/components/account/billing/StorjInput.vue
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="selected-container" v-if="!isCustomAmount">
|
||||
<div id="paymentSelectButton" class="selected-container__label-container" @click="toggleSelection">
|
||||
<p class="selected-container__label-container__label">{{current.label}}</p>
|
||||
<div class="selected-container__label-container__svg">
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.372773 0.338888C0.869804 -0.112963 1.67565 -0.112963 2.17268 0.338888L7 4.72741L11.8273 0.338888C12.3243 -0.112963 13.1302 -0.112963 13.6272 0.338888C14.1243 0.790739 14.1243 1.52333 13.6272 1.97519L7 8L0.372773 1.97519C-0.124258 1.52333 -0.124258 0.790739 0.372773 0.338888Z" fill="#2683FF"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div id="paymentSelect" class="options-container" v-if="isSelectionShown">
|
||||
<div
|
||||
class="options-container__item"
|
||||
v-for="option in paymentOptions"
|
||||
:key="option.label"
|
||||
@click.prevent="select(option)">
|
||||
|
||||
<div class="options-container__item__svg" v-if="option.value === current.value">
|
||||
<svg width="15" height="13" viewBox="0 0 15 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.0928 3.02746C14.6603 2.4239 14.631 1.4746 14.0275 0.907152C13.4239 0.339699 12.4746 0.368972 11.9072 0.972536L14.0928 3.02746ZM4.53846 11L3.44613 12.028C3.72968 12.3293 4.12509 12.5001 4.53884 12.5C4.95258 12.4999 5.34791 12.3289 5.63131 12.0275L4.53846 11ZM3.09234 7.27469C2.52458 6.67141 1.57527 6.64261 0.971991 7.21036C0.36871 7.77812 0.339911 8.72743 0.907664 9.33071L3.09234 7.27469ZM11.9072 0.972536L3.44561 9.97254L5.63131 12.0275L14.0928 3.02746L11.9072 0.972536ZM5.6308 9.97199L3.09234 7.27469L0.907664 9.33071L3.44613 12.028L5.6308 9.97199Z" fill="#2683FF"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="options-container__item__label">{{option.label}}</p>
|
||||
</div>
|
||||
<div class="options-container__custom-container" @click.prevent="toggleCustomAmount">Custom Amount</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="label" v-if="isCustomAmount">
|
||||
<input class="custom-input" type="number" placeholder="Enter Amount" v-model="customAmount">
|
||||
<div class="input-svg" @click="toggleCustomAmount">
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.372773 0.338888C0.869804 -0.112963 1.67565 -0.112963 2.17268 0.338888L7 4.72741L11.8273 0.338888C12.3243 -0.112963 13.1302 -0.112963 13.6272 0.338888C14.1243 0.790739 14.1243 1.52333 13.6272 1.97519L7 8L0.372773 1.97519C-0.124258 1.52333 -0.124258 0.790739 0.372773 0.338888Z" fill="#2683FF"/>
|
||||
</svg>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { PaymentAmountOption } from '@/types/payments';
|
||||
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
|
||||
|
||||
@Component
|
||||
export default class StorjInput extends Vue {
|
||||
public paymentOptions: PaymentAmountOption[] = [
|
||||
new PaymentAmountOption(20, `US $20 (+5 Bonus)`),
|
||||
new PaymentAmountOption(5, `US $5`),
|
||||
new PaymentAmountOption(10, `US $10 (+2 Bonus)`),
|
||||
new PaymentAmountOption(100, `US $100 (+20 Bonus)`),
|
||||
new PaymentAmountOption(1000, `US $1000 (+200 Bonus)`),
|
||||
];
|
||||
|
||||
public current: PaymentAmountOption = new PaymentAmountOption(20, `US $20 (+$5 Bonus)`);
|
||||
public customAmount: number = 0;
|
||||
public isCustomAmount = false;
|
||||
|
||||
public get isSelectionShown(): boolean {
|
||||
return this.$store.state.appStateModule.appState.isPaymentSelectionShown;
|
||||
}
|
||||
|
||||
public toggleSelection(): void {
|
||||
this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_PAYMENT_SELECTION);
|
||||
}
|
||||
|
||||
public toggleCustomAmount(): void {
|
||||
this.isCustomAmount = !this.isCustomAmount;
|
||||
}
|
||||
|
||||
public select(value: PaymentAmountOption): void {
|
||||
this.current = value;
|
||||
this.toggleSelection();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-input {
|
||||
width: 200px;
|
||||
height: 48px;
|
||||
border: 1px solid #afb7c1;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
padding: 0 36px 0 20px;
|
||||
font-family: font_medium;
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
color: #354049;
|
||||
}
|
||||
|
||||
.custom-input::-webkit-inner-spin-button,
|
||||
.custom-input::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-svg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 20px;
|
||||
transform: translate(0, -50%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.selected-container {
|
||||
position: relative;
|
||||
width: 256px;
|
||||
height: 48px;
|
||||
border: 1px solid #afb7c1;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
width: calc(100% - 40px);
|
||||
height: 100%;
|
||||
|
||||
&__label {
|
||||
font-family: font_medium;
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
color: #354049;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options-container {
|
||||
width: 256px;
|
||||
position: absolute;
|
||||
height: auto;
|
||||
font-family: font_medium;
|
||||
font-size: 16px;
|
||||
line-height: 48px;
|
||||
color: #354049;
|
||||
background-color: white;
|
||||
z-index: 102;
|
||||
margin-right: 10px;
|
||||
border-radius: 12px;
|
||||
top: calc(100% + 10px);
|
||||
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
|
||||
|
||||
&__custom-container {
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
padding: 0 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #F2F2F6;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&__svg {
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #F2F2F6;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
125
web/satellite/src/components/account/billing/StripeInput.vue
Normal file
125
web/satellite/src/components/account/billing/StripeInput.vue
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<form id="payment-form">
|
||||
<div class="form-row">
|
||||
<div id="card-element">
|
||||
<!-- A Stripe Element will be inserted here. -->
|
||||
</div>
|
||||
<div id="card-errors" role="alert"></div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
|
||||
import { NOTIFICATION_ACTIONS } from '@/utils/constants/actionNames';
|
||||
|
||||
// StripeInput encapsulates Stripe add card addition logic
|
||||
@Component
|
||||
export default class StripeInput extends Vue {
|
||||
@Prop({default: () => console.error('onStripeResponse is not reinitialized')})
|
||||
private readonly onStripeResponseCallback: (result: any) => void;
|
||||
|
||||
// Stripe elements is using to create 'Add Card' form
|
||||
private cardElement: any;
|
||||
|
||||
// Stripe library
|
||||
private stripe: any;
|
||||
|
||||
public created(): void {
|
||||
this.$parent.$on('onSubmitStripeInputEvent', this.onSubmit);
|
||||
}
|
||||
|
||||
public mounted(): void {
|
||||
if (!window['Stripe']) {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Stripe library not loaded');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.stripe = window['Stripe'](process.env.VUE_APP_STRIPE_PUBLIC_KEY);
|
||||
|
||||
if (!this.stripe) {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to initialize stripe');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = this.stripe.elements();
|
||||
|
||||
if (!elements) {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to instantiate elements');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.cardElement = elements.create('card');
|
||||
|
||||
if (!this.cardElement) {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Unable to create card');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.cardElement.mount('#card-element');
|
||||
this.cardElement.addEventListener('change', function (event) {
|
||||
const displayError: HTMLElement = document.getElementById('card-errors') as HTMLElement;
|
||||
if (event.error) {
|
||||
displayError.textContent = event.error.message;
|
||||
} else {
|
||||
displayError.textContent = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async onStripeResponse(result: any) {
|
||||
if (result.token.card.funding === 'prepaid') {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, 'Prepaid cards are not supported');
|
||||
}
|
||||
|
||||
await this.onStripeResponseCallback(result);
|
||||
this.cardElement.clear();
|
||||
}
|
||||
|
||||
public beforeDestroy() {
|
||||
this.cardElement.removeEventListener('change');
|
||||
}
|
||||
|
||||
private onSubmit(): void {
|
||||
this.stripe.createToken(this.cardElement).then(this.onStripeResponse);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.StripeElement {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 13px 12px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 3px 0 #e6ebf1;
|
||||
-webkit-transition: box-shadow 150ms ease;
|
||||
transition: box-shadow 150ms ease;
|
||||
}
|
||||
|
||||
.StripeElement--focus {
|
||||
box-shadow: 0 1px 3px 0 #cfd7df;
|
||||
}
|
||||
|
||||
.StripeElement--invalid {
|
||||
border-color: #fa755a;
|
||||
}
|
||||
|
||||
.StripeElement--webkit-autofill {
|
||||
background-color: #fefde5 !important;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -84,7 +84,7 @@
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
font-family: 'font-medium';
|
||||
font-family: 'font_medium';
|
||||
}
|
||||
.cov-picker-box {
|
||||
background: #fff;
|
||||
|
@ -22,6 +22,7 @@ export const appStateModule = {
|
||||
isSuccessfulProjectCreationPopupShown: false,
|
||||
isEditProfilePopupShown: false,
|
||||
isChangePasswordPopupShown: false,
|
||||
isPaymentSelectionShown: false,
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
@ -87,6 +88,11 @@ export const appStateModule = {
|
||||
[APP_STATE_MUTATIONS.CHANGE_STATE](state: any, newFetchState: AppState): void {
|
||||
state.appState.fetchState = newFetchState;
|
||||
},
|
||||
|
||||
// Mutation changing payment selection visibility
|
||||
[APP_STATE_MUTATIONS.TOGGLE_PAYMENT_SELECTION](state: any): void {
|
||||
state.appState.isPaymentSelectionShown = !state.appState.isPaymentSelectionShown;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// Commits mutation for changing app popups and dropdowns visibility state
|
||||
@ -179,5 +185,12 @@ export const appStateModule = {
|
||||
[APP_STATE_ACTIONS.CHANGE_STATE]: function ({commit}: any, newFetchState: AppState): void {
|
||||
commit(APP_STATE_MUTATIONS.CHANGE_STATE, newFetchState);
|
||||
},
|
||||
[APP_STATE_ACTIONS.TOGGLE_PAYMENT_SELECTION]: function ({commit, state}: any): void {
|
||||
if (!state.appState.isPaymentSelectionShown) {
|
||||
commit(APP_STATE_MUTATIONS.CLOSE_ALL);
|
||||
}
|
||||
|
||||
commit(APP_STATE_MUTATIONS.TOGGLE_PAYMENT_SELECTION);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -25,6 +25,7 @@ export const APP_STATE_MUTATIONS = {
|
||||
SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP: 'SHOW_SET_DEFAULT_PAYMENT_METHOD_POPUP',
|
||||
CLOSE_ALL: 'CLOSE_ALL',
|
||||
CHANGE_STATE: 'CHANGE_STATE',
|
||||
TOGGLE_PAYMENT_SELECTION: 'TOGGLE_PAYMENT_SELECTION',
|
||||
};
|
||||
|
||||
export const PROJECT_PAYMENT_METHODS_MUTATIONS = {
|
||||
|
9
web/satellite/src/types/payments.ts
Normal file
9
web/satellite/src/types/payments.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
export class PaymentAmountOption {
|
||||
public constructor(
|
||||
public value: number,
|
||||
public label: string = '',
|
||||
) {}
|
||||
}
|
@ -19,6 +19,7 @@ export const APP_STATE_ACTIONS = {
|
||||
CLOSE_DELETE_PAYMENT_METHOD_POPUP: 'closeDeletePaymentMethodPopup',
|
||||
CLOSE_POPUPS: 'closePopups',
|
||||
CHANGE_STATE: 'changeFetchState',
|
||||
TOGGLE_PAYMENT_SELECTION: 'TOGGLE_PAYMENT_SELECTION',
|
||||
};
|
||||
|
||||
export const NOTIFICATION_ACTIONS = {
|
||||
|
8
web/satellite/src/utils/constants/billingEnums.ts
Normal file
8
web/satellite/src/utils/constants/billingEnums.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
export enum PaymentMethodsBlockState {
|
||||
DEFAULT = 1,
|
||||
ADDING_CARD,
|
||||
ADDING_STORJ,
|
||||
}
|
7
web/satellite/static/images/payments/Dialog.svg
Normal file
7
web/satellite/static/images/payments/Dialog.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="167" height="123" viewBox="0 0 167 123" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M167 116.954C167 120.268 164.314 122.954 161 122.954L6 122.954C2.68629 122.954 9.41428e-06 120.268 9.15286e-06 116.954L1.26404e-06 16.9541C1.00262e-06 13.6404 2.68631 10.9541 6.00002 10.9541L119.304 10.9543C120.013 10.9543 120.669 10.5788 121.028 9.9674L126.304 0.987351C127.077 -0.328508 128.98 -0.328509 129.753 0.987349L135.029 9.96742C135.388 10.5788 136.044 10.9543 136.753 10.9543L161 10.9544C164.314 10.9544 167 13.6406 167 16.9544L167 116.954Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M167 116.954C167 120.268 164.314 122.954 161 122.954L6 122.954C2.68629 122.954 9.41428e-06 120.268 9.15286e-06 116.954L1.26404e-06 16.9541C1.00262e-06 13.6404 2.68631 10.9541 6.00002 10.9541L119.304 10.9543C120.013 10.9543 120.669 10.5788 121.028 9.9674L126.304 0.987351C127.077 -0.328508 128.98 -0.328509 129.753 0.987349L135.029 9.96742C135.388 10.5788 136.044 10.9543 136.753 10.9543L161 10.9544C164.314 10.9544 167 13.6406 167 16.9544L167 116.954Z" fill="white"/>
|
||||
<path d="M161 122.954L161 121.954L161 122.954ZM167 116.954L168 116.954V116.954L167 116.954ZM6 122.954L6 123.954L6 122.954ZM9.15286e-06 116.954L1.00001 116.954L9.15286e-06 116.954ZM1.26404e-06 16.9541L-0.999999 16.9541L1.26404e-06 16.9541ZM6.00002 10.9541L6.00002 9.95412L6.00002 9.95412L6.00002 10.9541ZM161 10.9544L161 9.95436L161 9.95436L161 10.9544ZM167 16.9544L166 16.9544L166 16.9544L167 16.9544ZM135.029 9.96742L134.167 10.474L135.029 9.96742ZM126.304 0.987351L125.442 0.480798L126.304 0.987351ZM129.753 0.987349L130.615 0.480796L129.753 0.987349ZM119.304 10.9543L119.304 9.95429L119.304 10.9543ZM121.028 9.9674L120.166 9.46084L121.028 9.9674ZM161 123.954C164.866 123.954 168 120.82 168 116.954L166 116.954C166 119.716 163.761 121.954 161 121.954L161 123.954ZM6 123.954L161 123.954L161 121.954L6 121.954L6 123.954ZM-0.999991 116.954C-0.999991 120.82 2.13401 123.954 6 123.954L6 121.954C3.23858 121.954 1.00001 119.715 1.00001 116.954L-0.999991 116.954ZM-0.999999 16.9541L-0.999991 116.954L1.00001 116.954L1 16.9541L-0.999999 16.9541ZM6.00002 9.95412C2.13402 9.95412 -0.999999 13.0881 -0.999999 16.9541L1 16.9541C1 14.1927 3.23859 11.9541 6.00002 11.9541L6.00002 9.95412ZM119.304 9.95429L6.00002 9.95412L6.00002 11.9541L119.304 11.9543L119.304 9.95429ZM121.89 10.474L127.166 1.4939L125.442 0.480798L120.166 9.46084L121.89 10.474ZM128.891 1.4939L134.167 10.474L135.891 9.46087L130.615 0.480796L128.891 1.4939ZM161 9.95436L136.753 9.95432L136.753 11.9543L161 11.9544L161 9.95436ZM168 16.9544C168 13.0884 164.866 9.95436 161 9.95436L161 11.9544C163.761 11.9544 166 14.1929 166 16.9544L168 16.9544ZM168 116.954L168 16.9544L166 16.9544L166 116.954L168 116.954ZM134.167 10.474C134.705 11.3911 135.689 11.9543 136.753 11.9543L136.753 9.95432C136.399 9.95432 136.071 9.76657 135.891 9.46087L134.167 10.474ZM127.166 1.4939C127.553 0.835973 128.504 0.835974 128.891 1.4939L130.615 0.480796C129.455 -1.49299 126.601 -1.49299 125.442 0.480798L127.166 1.4939ZM119.304 11.9543C120.367 11.9543 121.352 11.3911 121.89 10.474L120.166 9.46084C119.986 9.76655 119.658 9.95429 119.304 9.95429L119.304 11.9543Z" fill="#C1C1C1" fill-opacity="0.3" mask="url(#path-1-inside-1)"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
Loading…
Reference in New Issue
Block a user