web/satellite: Paid Tier add payment modal implemented

Added new PaidTier-related modal where user can add CC or STORJ Tokens.
Becomes visible on CTA click on Paid Tier banner at the top.

Change-Id: I51015e95d396e21d5c1a1728b8f753798626c09e
This commit is contained in:
Vitalii Shpital 2021-07-01 22:47:41 +03:00
parent 4d418c13c3
commit bbd3efaeed
14 changed files with 672 additions and 80 deletions

View File

@ -23,7 +23,6 @@ import { Component, Prop, Vue } from 'vue-property-decorator';
import AddCouponCodeInput from '@/components/common/AddCouponCodeInput.vue';
import HeaderlessInput from '@/components/common/HeaderlessInput.vue';
import ValidationMessage from '@/components/common/ValidationMessage.vue';
import CloseIcon from '@/../static/images/common/closeCross.svg';
import CheckIcon from '@/../static/images/common/success-check.svg';

View File

@ -0,0 +1,576 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="pm-area">
<div v-if="isAddModal" class="pm-area__add-modal">
<h1 class="pm-area__add-modal__title">Add a Payment Method</h1>
<div class="pm-area__add-modal__header">
<h2 class="pm-area__add-modal__header__sub-title">Payment Method</h2>
<div class="pm-area__add-modal__header__choices">
<p class="pm-area__add-modal__header__choices__var" :class="{active: !isAddCard}" @click.stop="setIsAddToken">
STORJ Token
</p>
<p class="pm-area__add-modal__header__choices__var left-margin" :class="{active: isAddCard}" @click.stop="setIsAddCard">
Card
</p>
</div>
</div>
<div v-if="isAddCard" class="pm-area__add-modal__card">
<StripeCardInput
class="pm-area__add-modal__card__stripe"
ref="stripeCardInput"
:on-stripe-response-callback="addCardToDB"
/>
<VButton
width="100%"
height="48px"
label="Add Credit Card"
:on-press="onAddCardClick"
/>
<p class="pm-area__add-modal__card__info">Get more storage and bandwidth by adding your credit card.</p>
<div class="pm-area__add-modal__card__info-bullet">
<CheckMarkIcon/>
<p class="pm-area__add-modal__card__info-bullet__label">3 projects</p>
</div>
<div class="pm-area__add-modal__card__info-bullet">
<CheckMarkIcon/>
<p class="pm-area__add-modal__card__info-bullet__label">25TB storage per project.</p>
</div>
<div class="pm-area__add-modal__card__info-bullet">
<CheckMarkIcon/>
<p class="pm-area__add-modal__card__info-bullet__label">100TB egress bandwidth per project.</p>
</div>
</div>
<div v-else class="pm-area__add-modal__tokens">
<p class="pm-area__add-modal__tokens__banner">
Deposit STORJ Token to your account and recieve a 10% bonus, or $10 for every $100.
</p>
<TokenDepositSelection
class="pm-area__add-modal__tokens__selection"
@onChangeTokenValue="onChangeTokenValue"
:payment-options="paymentOptions"
/>
<VButton
width="100%"
height="48px"
label="Continue to Coin Payments"
:on-press="onAddSTORJClick"
/>
<div v-if="coinPaymentsCheckoutLink" class="pm-area__add-modal__tokens__checkout-container">
<a
class="pm-area__add-modal__tokens__checkout-container__link"
:href="coinPaymentsCheckoutLink"
target="_blank"
rel="noopener noreferrer"
>
Checkout
</a>
</div>
<p class="pm-area__add-modal__tokens__note">
<b class="pm-area__add-modal__tokens__note__bold">Please Note:</b>
Your first deposit of $50 or more in STORJ Token is applied to your account after Coin Payments
verifies payment
</p>
<p class="pm-area__add-modal__tokens__info">
After depositing STORJ Tokens, please contact
<a
class="pm-area__add-modal__tokens__info__link"
:href="limitsIncreaseRequestURL"
target="_blank"
rel="noopener noreferrer"
>
Support
</a>
to assist you for accessing your higher limits!
</p>
</div>
<div class="pm-area__add-modal__security">
<LockImage/>
<p class="pm-area__add-modal__security__info">
Your information is secured with 128-bit SSL & AES-256 encryption.
</p>
</div>
<div v-if="isLoading" class="pm-area__add-modal__blur">
<VLoader
class="pm-area__add-modal__blur__loader"
width="30px"
height="30px"
/>
</div>
<div class="close-cross-container" @click="onClose">
<CloseCrossIcon />
</div>
</div>
<div v-else class="pm-area__success-modal">
<BigCheckMarkIcon class="pm-area__success-modal__icon"/>
<h2 class="pm-area__success-modal__title">Congratulations!</h2>
<h2 class="pm-area__success-modal__title">Youve just upgraded your account.</h2>
<p class="pm-area__success-modal__info">
Now you can have up to 75TB of total storage and 300TB of egress bandwidth per month. If you need more,
please
<a
class="pm-area__success-modal__info__link"
:href="limitsIncreaseRequestURL"
target="_blank"
rel="noopener noreferrer"
>
contact us
</a>
.
</p>
<div class="close-cross-container" @click="onClose">
<CloseCrossIcon />
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import StripeCardInput from '@/components/account/billing/paymentMethods/StripeCardInput.vue';
import TokenDepositSelection from '@/components/account/billing/paymentMethods/TokenDepositSelection.vue';
import VButton from '@/components/common/VButton.vue';
import VLoader from '@/components/common/VLoader.vue';
import LockImage from '@/../static/images/account/billing/lock.svg';
import CloseCrossIcon from '@/../static/images/common/closeCross.svg';
import CheckMarkIcon from '@/../static/images/common/greenRoundCheckmark.svg';
import BigCheckMarkIcon from '@/../static/images/common/greenRoundCheckmarkBig.svg';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { PaymentAmountOption } from '@/types/payments';
import { MetaUtils } from '@/utils/meta';
interface StripeForm {
onSubmit(): Promise<void>;
}
@Component({
components: {
StripeCardInput,
VButton,
CheckMarkIcon,
LockImage,
TokenDepositSelection,
VLoader,
CloseCrossIcon,
BigCheckMarkIcon,
},
})
export default class AddPaymentMethodModal extends Vue {
@Prop({default: () => false})
public readonly onClose: () => void;
private readonly DEFAULT_TOKEN_DEPOSIT_VALUE = 50; // in dollars.
private readonly MAX_TOKEN_AMOUNT = 1000000; // in dollars.
private tokenDepositValue: number = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
public isAddModal: boolean = true;
public isAddCard: boolean = true;
public isLoading: boolean = false;
public coinPaymentsCheckoutLink: string = '';
public $refs!: {
stripeCardInput: StripeCardInput & StripeForm;
};
/**
* Set of default payment options.
*/
public readonly paymentOptions: PaymentAmountOption[] = [
new PaymentAmountOption(50, `USD $50`),
new PaymentAmountOption(100, `USD $100`),
new PaymentAmountOption(200, `USD $200`),
new PaymentAmountOption(500, `USD $500`),
new PaymentAmountOption(1000, `USD $1000`),
];
/**
* Provides card information to Stripe.
*/
public async onAddCardClick(): Promise<void> {
if (this.isLoading) return;
this.isLoading = true;
await this.$refs.stripeCardInput.onSubmit();
}
/**
* Adds card after Stripe confirmation.
*
* @param token from Stripe
*/
public async addCardToDB(token: string) {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.ADD_CREDIT_CARD, token);
await this.$notify.success('Card successfully added');
// We fetch User one more time to update their Paid Tier status.
await this.$store.dispatch(USER_ACTIONS.GET);
if (this.$route.name === RouteConfig.Billing.name) {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_CREDIT_CARDS);
}
if (this.$route.name === RouteConfig.ProjectDashboard.name) {
await this.$store.dispatch(PROJECTS_ACTIONS.GET_LIMITS, this.$store.getters.selectedProject.id);
}
} catch (error) {
await this.$notify.error(error.message);
}
this.isLoading = false;
this.isAddModal = false;
}
/**
* onAddSTORJClick checks if amount is valid.
* If so processes token payment and returns state to default.
*/
public async onAddSTORJClick(): Promise<void> {
if (this.isLoading) return;
if (this.tokenDepositValue >= this.MAX_TOKEN_AMOUNT || this.tokenDepositValue === 0) {
await this.$notify.error('Deposit amount must be more than $0 and less than $1000000');
return;
}
this.isLoading = true;
try {
const tokenResponse = await this.$store.dispatch(PAYMENTS_ACTIONS.MAKE_TOKEN_DEPOSIT, this.tokenDepositValue * 100);
await this.$notify.success(`Successfully created new deposit transaction! \nAddress:${tokenResponse.address} \nAmount:${tokenResponse.amount}`);
const depositWindow = window.open(tokenResponse.link, '_blank');
if (depositWindow) {
depositWindow.focus();
}
this.coinPaymentsCheckoutLink = tokenResponse.link;
if (this.$route.name === RouteConfig.Billing.name) {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PAYMENTS_HISTORY);
}
} catch (error) {
await this.$notify.error(error.message);
}
this.tokenDepositValue = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
this.isLoading = false;
}
/**
* Sets modal state to add STORJ tokens.
*/
public setIsAddToken(): void {
this.isAddCard = false;
}
/**
* Sets modal state to add credit card.
*/
public setIsAddCard(): void {
this.isAddCard = true;
}
/**
* Event for changing token deposit value.
*/
public onChangeTokenValue(value: number): void {
this.tokenDepositValue = value;
}
/**
* Returns project limits increase request url from config.
*/
public get limitsIncreaseRequestURL(): string {
return MetaUtils.getMetaContent('project-limits-increase-request-url');
}
}
</script>
<style scoped lang="scss">
.pm-area {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 1000;
background: rgba(27, 37, 51, 0.75);
display: flex;
align-items: center;
justify-content: center;
font-family: 'font_regular', sans-serif;
&__add-modal {
background: #fff;
border-radius: 8px;
width: 660px;
position: relative;
padding: 45px;
&__title {
width: 100%;
text-align: center;
font-family: 'font_bold', sans-serif;
font-size: 24px;
line-height: 29px;
color: #1b2533;
margin-bottom: 40px;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
&__sub-title {
font-family: 'font_medium', sans-serif;
font-size: 18px;
line-height: 22px;
color: #000;
}
&__choices {
display: flex;
align-items: center;
&__var {
font-family: 'font_medium', sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 18px;
color: #a9b5c1;
padding: 0 10px 5px 10px;
cursor: pointer;
border-bottom: 3px solid #fff;
}
}
}
&__card {
padding-bottom: 20px;
&__stripe {
margin: 20px 0;
}
&__info {
margin: 50px 0 30px 0;
font-size: 16px;
line-height: 26px;
color: #000;
}
&__info-bullet {
display: flex;
align-items: center;
margin-bottom: 20px;
&__label {
margin-left: 12px;
font-weight: 600;
font-size: 14px;
line-height: 20px;
letter-spacing: 0.473506px;
color: #000;
}
}
}
&__tokens {
padding-bottom: 30px;
&__banner {
font-size: 14px;
line-height: 20px;
color: #384761;
padding: 20px 30px;
background: #edf4fe;
border-radius: 8px;
margin-bottom: 25px;
}
&__selection {
margin-bottom: 25px;
}
&__checkout-container {
display: flex;
justify-content: center;
margin-top: 25px;
&__link {
font-size: 16px;
line-height: 20px;
color: #2683ff;
}
}
&__note {
font-size: 14px;
line-height: 20px;
color: #7b8eab;
margin: 25px 0;
&__bold {
font-family: 'font_medium', sans-serif;
margin-right: 3px;
}
}
&__info {
font-size: 16px;
line-height: 26px;
color: #000;
&__link {
font-family: 'font_medium', sans-serif;
text-decoration: underline !important;
text-underline-position: under;
&:visited {
color: #000;
}
}
}
}
&__security {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #cef0e3;
padding: 15px 0;
border-radius: 0 0 8px 8px;
&__info {
font-weight: 500;
font-size: 15px;
line-height: 18px;
color: #1a9666;
margin-left: 12px;
}
}
&__blur {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 8px;
z-index: 1;
background-color: rgba(245, 246, 250, 0.5);
&__loader {
position: absolute;
top: 45px;
left: calc(-50% + 55px);
}
}
}
&__success-modal {
background: #fff;
border-radius: 8px;
width: 660px;
position: relative;
padding: 45px 45px 80px 45px;
display: flex;
flex-direction: column;
align-items: center;
&__icon {
margin-bottom: 15px;
}
&__title {
font-family: 'font_bold', sans-serif;
font-size: 28px;
line-height: 42px;
text-align: center;
color: #000;
margin-top: 15px;
}
&__info {
margin-top: 40px;
font-size: 16px;
line-height: 28px;
text-align: center;
color: #000;
max-width: 380px;
&__link {
font-family: 'font_medium', sans-serif;
text-decoration: underline !important;
text-underline-position: under;
&:visited {
color: #000;
}
}
}
}
}
.close-cross-container {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 30px;
top: 30px;
height: 24px;
width: 24px;
cursor: pointer;
&:hover .close-cross-svg-path {
fill: #2683ff;
}
}
.left-margin {
margin-left: 20px;
}
.active {
color: #0068dc;
border-color: #0068dc;
}
/deep/ .selected-container {
width: calc(100% - 2px);
}
/deep/ .custom-input {
width: calc(100% - 68px);
}
/deep/ .options-container,
/deep/ .payment-selection-blur {
width: 100%;
}
@media screen and (max-height: 700px) {
.pm-area {
padding: 200px 0 20px 0;
overflow-y: scroll;
}
}
</style>

View File

@ -19,13 +19,13 @@
</p>
<p class="pt-bar__functional">
Upload up to 75TB.
<b class="pt-bar__info__bold upgrade">Upgrade now.</b>
<b class="pt-bar__info__bold upgrade" @click.stop="openAddPMModal">Upgrade now.</b>
</p>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue } from 'vue-property-decorator';
import VLoader from '@/components/common/VLoader.vue';
@ -40,6 +40,9 @@ import { Size } from '@/utils/bytesSize';
},
})
export default class PaidTierBar extends Vue {
@Prop({default: () => false})
public readonly openAddPMModal: () => void;
/**
* Mounted lifecycle hook after initial render.
* Fetches total limits.

View File

@ -8,6 +8,7 @@
<TokenDepositSelection
class="add-storj-area__selection-container__form"
@onChangeTokenValue="onChangeTokenValue"
:payment-options="paymentOptions"
/>
</div>
<div class="add-storj-area__submit-area">
@ -36,6 +37,7 @@ import TokenDepositSelection from '@/components/account/billing/paymentMethods/T
import VButton from '@/components/common/VButton.vue';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PaymentAmountOption } from '@/types/payments';
const {
MAKE_TOKEN_DEPOSIT,
@ -57,8 +59,19 @@ export default class AddStorjForm extends Vue {
public readonly isLoading: boolean;
/**
* onConfirmAddSTORJ checks if amount is valid and if so process token.
* payment and return state to default
* Set of default payment options.
*/
public paymentOptions: PaymentAmountOption[] = [
new PaymentAmountOption(10, `USD $10`),
new PaymentAmountOption(20, `USD $20`),
new PaymentAmountOption(50, `USD $50`),
new PaymentAmountOption(100, `USD $100`),
new PaymentAmountOption(1000, `USD $1000`),
];
/**
* onConfirmAddSTORJ checks if amount is valid.
* If so processes token payment and returns state to default.
*/
public async onConfirmAddSTORJ(): Promise<void> {
this.$emit('toggleIsLoading');

View File

@ -430,6 +430,6 @@ export default class PaymentMethods extends Vue {
}
.pm-loader {
margin-top: 40px;
margin-top: 60px;
}
</style>

View File

@ -31,13 +31,12 @@ import {PaymentsHistoryItemType} from "@/types/payments";
</label>
<div
class="options-container"
:class="{ 'top-expand': isExpandingTop }"
v-if="isSelectionShown"
v-click-outside="close"
>
<div
class="options-container__item"
v-for="option in options"
v-for="option in paymentOptions"
:key="option.label"
@click.prevent.stop="select(option)"
>
@ -62,46 +61,34 @@ import {PaymentsHistoryItemType} from "@/types/payments";
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { Component, Prop, Vue } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import { PaymentAmountOption, PaymentsHistoryItem } from '@/types/payments';
import { PaymentAmountOption } from '@/types/payments';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
@Component
export default class TokenDepositSelection extends Vue {
/**
* Set of default payment options.
*/
public paymentOptions: PaymentAmountOption[] = [
new PaymentAmountOption(10, `USD $10`),
new PaymentAmountOption(20, `USD $20`),
new PaymentAmountOption(50, `USD $50`),
new PaymentAmountOption(100, `USD $100`),
new PaymentAmountOption(1000, `USD $1000`),
];
/**
* Set of payment options for the first ever transaction.
*/
public initialPaymentOptions: PaymentAmountOption[] = [
new PaymentAmountOption(10, `USD $10`),
new PaymentAmountOption(20, `USD $20`),
new PaymentAmountOption(50, `USD $50`),
new PaymentAmountOption(100, `USD $100`),
new PaymentAmountOption(200, `USD $200`),
];
@Prop({default: () => []})
public readonly paymentOptions: PaymentAmountOption[];
/**
* current selected payment option from default ones.
*/
public current: PaymentAmountOption = this.paymentOptions[0];
public current: PaymentAmountOption;
public customAmount: string = '';
/**
* Indicates if custom amount selection state is active.
*/
public isCustomAmount = false;
/**
* Lifecycle hook before initial render.
* Sets initial deposit amount.
*/
public beforeMount(): void {
this.current = this.paymentOptions[0];
}
/**
* Indicates if concrete payment option is currently selected.
*/
@ -109,28 +96,6 @@ export default class TokenDepositSelection extends Vue {
return (option.value === this.current.value) && !this.isCustomAmount;
}
/**
* Indicates if dropdown expands top.
*/
public get isExpandingTop(): boolean {
const hasNoTransactionsOrDepositBonuses: boolean =
!this.$store.state.paymentsModule.paymentsHistory.some((item: PaymentsHistoryItem) => item.isTransactionOrDeposit(),
);
return hasNoTransactionsOrDepositBonuses && !this.isOnboardingTour;
}
/**
* Returns payment options depending on user having his own project.
*/
public get options(): PaymentAmountOption[] {
if (this.$store.getters.projectsCount === 0 && this.noCreditCards) {
return this.initialPaymentOptions;
}
return this.paymentOptions;
}
/**
* isSelectionShown flag that indicate is token amount selection shown.
*/
@ -187,20 +152,6 @@ export default class TokenDepositSelection extends Vue {
this.$emit('onChangeTokenValue', option.value);
this.close();
}
/**
* Indicates if user has no credit cards.
*/
private get noCreditCards(): boolean {
return this.$store.state.paymentsModule.creditCards.length === 0;
}
/**
* Indicates if app state is in onboarding tour state.
*/
private get isOnboardingTour(): boolean {
return this.$route.path.includes(RouteConfig.OnboardingTour.path);
}
}
</script>
@ -357,9 +308,4 @@ export default class TokenDepositSelection extends Vue {
top: 0;
left: 0;
}
.top-expand {
top: -290px;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.25);
}
</style>

View File

@ -61,9 +61,11 @@ export default class ProjectDashboard extends Vue {
const FIRST_PAGE = 1;
try {
if (!this.isPaidTierStatus) {
await this.$store.commit(PAYMENTS_MUTATIONS.TOGGLE_PAID_TIER_BANNER_TO_LOADING);
await this.$store.dispatch(PROJECTS_ACTIONS.GET_TOTAL_LIMITS);
await this.$store.commit(PAYMENTS_MUTATIONS.TOGGLE_PAID_TIER_BANNER_TO_LOADED);
}
await this.$store.dispatch(BUCKET_ACTIONS.FETCH, FIRST_PAGE);
@ -92,6 +94,13 @@ export default class ProjectDashboard extends Vue {
public get projectLimitsIncreaseRequestURL(): string {
return MetaUtils.getMetaContent('project-limits-increase-request-url');
}
/**
* Returns user's paid tier status from store.
*/
private get isPaidTierStatus(): boolean {
return this.$store.state.usersModule.paidTier;
}
}
</script>

View File

@ -137,6 +137,11 @@ export const router = new Router({
},
component: DashboardArea,
children: [
{
path: RouteConfig.Root.path,
name: 'default',
component: ProjectDashboard,
},
{
path: RouteConfig.Account.path,
name: RouteConfig.Account.name,
@ -396,6 +401,12 @@ router.beforeEach(async (to, from, next) => {
return;
}
if (to.name === 'default') {
next(RouteConfig.ProjectDashboard.path);
return;
}
next();
});

View File

@ -29,6 +29,7 @@ export const PAYMENTS_MUTATIONS = {
SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT: 'SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT',
TOGGLE_PAID_TIER_BANNER_TO_LOADING: 'TOGGLE_PAID_TIER_BANNER_TO_LOADING',
TOGGLE_PAID_TIER_BANNER_TO_LOADED: 'TOGGLE_PAID_TIER_BANNER_TO_LOADED',
TOGGLE_IS_ADD_PM_MODAL_SHOWN: 'TOGGLE_IS_ADD_PM_MODAL_SHOWN',
};
export const PAYMENTS_ACTIONS = {
@ -61,6 +62,7 @@ const {
SET_PRICE_SUMMARY_FOR_SELECTED_PROJECT,
TOGGLE_PAID_TIER_BANNER_TO_LOADING,
TOGGLE_PAID_TIER_BANNER_TO_LOADED,
TOGGLE_IS_ADD_PM_MODAL_SHOWN,
} = PAYMENTS_MUTATIONS;
const {
@ -92,6 +94,7 @@ export class PaymentsState {
public startDate: Date = new Date();
public endDate: Date = new Date();
public isPaidTierBarLoading: boolean = true;
public isAddPMModalShown: boolean = false;
}
/**
@ -176,6 +179,9 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
[TOGGLE_PAID_TIER_BANNER_TO_LOADED](state: PaymentsState): void {
state.isPaidTierBarLoading = false;
},
[TOGGLE_IS_ADD_PM_MODAL_SHOWN](state: PaymentsState): void {
state.isAddPMModalShown = !state.isAddPMModalShown;
},
[CLEAR](state: PaymentsState) {
state.balance = new AccountBalance();
state.paymentsHistory = [];

View File

@ -15,7 +15,7 @@
</p>
</div>
<div v-if="!isLoading" class="dashboard__wrap">
<PaidTierBar v-if="!isPaidTierStatus && !isOnboardingTour"/>
<PaidTierBar v-if="!isPaidTierStatus && !isOnboardingTour" :open-add-p-m-modal="togglePMModal"/>
<DashboardHeader/>
<div class="dashboard__wrap__main-area">
<NavigationArea class="regular-navigation"/>
@ -24,12 +24,14 @@
</div>
</div>
</div>
<AddPaymentMethodModal v-if="isAddPMModal" :on-close="togglePMModal"/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import AddPaymentMethodModal from '@/components/account/billing/paidTier/AddPaymentMethodModal.vue';
import PaidTierBar from '@/components/account/billing/paidTier/PaidTierBar.vue';
import DashboardHeader from '@/components/header/HeaderArea.vue';
import NavigationArea from '@/components/navigation/NavigationArea.vue';
@ -39,7 +41,7 @@ import LoaderImage from '@/../static/images/common/loader.svg';
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
import { RouteConfig } from '@/router';
import { ACCESS_GRANTS_ACTIONS } from '@/store/modules/accessGrants';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PAYMENTS_ACTIONS, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { USER_ACTIONS } from '@/store/modules/users';
import { Project } from '@/types/projects';
@ -59,6 +61,7 @@ const {
DashboardHeader,
LoaderImage,
PaidTierBar,
AddPaymentMethodModal,
},
})
export default class DashboardArea extends Vue {
@ -126,6 +129,20 @@ export default class DashboardArea extends Vue {
await this.$store.dispatch(APP_STATE_ACTIONS.CHANGE_STATE, AppState.LOADED);
}
/**
* Opens add payment method modal.
*/
public togglePMModal(): void {
this.$store.commit(PAYMENTS_MUTATIONS.TOGGLE_IS_ADD_PM_MODAL_SHOWN);
}
/**
* Indicates if add payment method modal is shown.
*/
public get isAddPMModal(): boolean {
return this.$store.state.paymentsModule.isAddPMModalShown;
}
/**
* Returns user's paid tier status from store.
*/

View File

@ -0,0 +1,5 @@
<svg width="23" height="21" viewBox="0 0 23 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="10.4954" cy="10.3125" rx="10" ry="10" fill="#00D459"/>
<path d="M5.19919 11.0717C4.70867 10.5812 4.70867 9.78595 5.19919 9.29544C5.6897 8.80493 6.48498 8.80493 6.97549 9.29544L9.936 12.2559C10.4265 12.7465 10.4265 13.5417 9.936 14.0323C9.44549 14.5228 8.65021 14.5228 8.1597 14.0323L5.19919 11.0717Z" fill="white"/>
<path d="M9.936 14.0323C9.44549 14.5228 8.65021 14.5228 8.1597 14.0323C7.66918 13.5417 7.66918 12.7465 8.1597 12.2559L13.4886 6.92704C13.9791 6.43652 14.7744 6.43652 15.2649 6.92703C15.7554 7.41755 15.7554 8.21283 15.2649 8.70334L9.936 14.0323Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@ -0,0 +1,5 @@
<svg width="62" height="58" viewBox="0 0 58 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="29.7271" cy="29" rx="29" ry="29" fill="#00D459"/>
<path d="M14.3686 31.1928C12.9461 29.7703 12.9461 27.464 14.3686 26.0415C15.7911 24.6191 18.0974 24.6191 19.5199 26.0415L28.1054 34.627C29.5279 36.0495 29.5279 38.3558 28.1054 39.7783C26.6829 41.2008 24.3766 41.2008 22.9541 39.7783L14.3686 31.1928Z" fill="white"/>
<path d="M28.1054 39.7783C26.6829 41.2008 24.3766 41.2008 22.9541 39.7783C21.5316 38.3558 21.5316 36.0495 22.9541 34.627L38.4079 19.1732C39.8304 17.7507 42.1367 17.7507 43.5592 19.1732C44.9817 20.5957 44.9817 22.902 43.5592 24.3245L28.1054 39.7783Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 708 B

View File

@ -29,7 +29,7 @@ exports[`AddStorjForm renders correctly after continue To Coin Payments click 1`
<div class="add-storj-area">
<div class="add-storj-area__selection-container">
<p class="add-storj-area__selection-container__label">Deposit STORJ Tokens via Coin Payments</p>
<tokendepositselection-stub class="add-storj-area__selection-container__form"></tokendepositselection-stub>
<tokendepositselection-stub paymentoptions="[object Object],[object Object],[object Object],[object Object],[object Object]" class="add-storj-area__selection-container__form"></tokendepositselection-stub>
</div>
<div class="add-storj-area__submit-area"><img src="@/../static/images/account/billing/loading.gif" alt="loading gif" class="loading-image">
<vbutton-stub label="Continue to Coin Payments" width="251px" height="48px" isdisabled="true" onpress="function () { [native code] }" class="confirm-add-storj-button"></vbutton-stub>

View File

@ -14,6 +14,7 @@ exports[`Dashboard renders correctly when data is loaded 1`] = `
</div>
</div>
</div>
<!---->
</div>
`;
@ -24,5 +25,6 @@ exports[`Dashboard renders correctly when data is loading 1`] = `
</div>
<!---->
<!---->
<!---->
</div>
`;