web/satellite: slight refactoring of new billing screen

New billing screen implementation is a total disaster and needs much bigger refactoring effort.
Tried to rework overall components to look like figma designs.
Fixed applying of coupon code flow.

Issue:
https://github.com/storj/storj/issues/5103

Change-Id: I42663208a003ec0abc390366a0e52c21a62751e1
This commit is contained in:
Vitalii 2022-09-18 19:14:47 +03:00 committed by Storj Robot
parent 0bfaadcc6c
commit f83652ee1d
7 changed files with 116 additions and 288 deletions

View File

@ -42,7 +42,7 @@
</div>
</div>
</div>
<AddCoupon2
<AddCouponCode2
v-if="showCreateCode"
@toggleMethod="toggleCreateModal"
/>
@ -53,18 +53,17 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { Coupon } from '@/types/payments';
import AddCoupon2 from '@/components/account/billing/coupons/AddCouponCode2.vue';
import AddCouponCode2 from '@/components/account/billing/coupons/AddCouponCode2.vue';
import VLoader from '@/components/common/VLoader.vue';
// @vue/component
@Component({
components: {
VLoader,
AddCoupon2,
AddCouponCode2,
},
})
export default class Coupons extends Vue {
@ -85,13 +84,6 @@ export default class Coupons extends Vue {
}
}
/**
* Opens Add Coupon modal.
*/
public onCreateClick(): void {
this.$router.push(RouteConfig.Billing.with(RouteConfig.AddCouponCode).path);
}
/**
* Opens Add Coupon modal.
*/

View File

@ -139,6 +139,7 @@ export default class CardComponent extends Vue {
margin: 0 7px 0 0;
white-space: nowrap;
color: #354049;
align-self: end;
&:hover {
background-color: #2683ff;
@ -186,8 +187,8 @@ export default class CardComponent extends Vue {
border-radius: 6px;
display: grid;
grid-template-columns: 4fr 2fr;
grid-template-rows: 1fr 0fr auto 0fr;
height: 126px;
grid-template-rows: 1fr 0fr 1fr 1fr;
height: 100%;
&__function-buttons {
grid-column: 1;
@ -265,11 +266,10 @@ export default class CardComponent extends Vue {
grid-column: 2;
width: 58px;
height: 24px;
left: 950px;
top: 250px;
background: #e6edf7;
border: 1px solid #d7e8ff;
border-radius: 4px;
justify-self: end;
}
&__default-text {

View File

@ -49,7 +49,7 @@
</div>
<div class="payments-area__container__new-payments">
<div v-if="!isAddingPayment" class="payments-area__container__new-payments__text-area">
<span class="payments-area__container__new-payments__text-area__plus-icon">+&nbsp;</span>
<span>+&nbsp;</span>
<span
class="payments-area__container__new-payments__text-area__text"
@click="addPaymentMethodHandler"
@ -187,7 +187,7 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { CreditCard , PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { CreditCard, PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { USER_ACTIONS } from '@/store/modules/users';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { RouteConfig } from '@/router';
@ -219,7 +219,7 @@ interface StripeForm {
onSubmit(): Promise<void>;
}
interface CardEdited {
id?: number,
id?: string,
isDefault?: boolean
}
const {
@ -266,7 +266,7 @@ export default class PaymentMethods extends Vue {
*/
public showTransactions = false;
public showAddFunds = false;
public mostRecentTransaction: Record<string, unknown>[] = [];
public mostRecentTransaction: PaymentsHistoryItem[] = [];
public paginationLocation: {start: number, end: number} = { start: paginationStartNumber, end: paginationEndNumber };
public tokenHistory: {amount: number, start: Date, status: string,}[] = [];
public displayedHistory: Record<string, unknown>[] = [];
@ -315,11 +315,7 @@ export default class PaymentMethods extends Vue {
this.transactionCount = tokenArray.length;
this.displayedHistory = tokenArray.slice(0,10);
this.tokensAreLoaded = true;
if (this.transactionCount > 0){
this.showAddFunds = false;
} else {
this.showAddFunds = true;
}
this.showAddFunds = this.transactionCount <= 0;
}
public toggleShowAddFunds(): void {
@ -363,8 +359,7 @@ export default class PaymentMethods extends Vue {
}
this.isRemovePaymentMethodsModalOpen = false;
}
else {
} else {
this.$notify.error('You cannot delete the default payment method.');
}
}
@ -430,8 +425,11 @@ export default class PaymentMethods extends Vue {
this.isAddingPayment = true;
}
public removePaymentMethodHandler(creditCard) {
this.cardBeingEdited = creditCard;
public removePaymentMethodHandler(creditCard: CreditCard) {
this.cardBeingEdited = {
id: creditCard.id,
isDefault: creditCard.isDefault,
};
this.isRemovePaymentMethodsModalOpen = true;
}
@ -530,8 +528,9 @@ $flex: flex;
$align: center;
:deep(.loader) {
width: auto;
padding: 63px 114px;
width: 40px;
height: 40px;
padding: 81.5px 154px;
}
.divider {
@ -656,7 +655,9 @@ $align: center;
.close-add-payment {
position: absolute;
margin-left: 208px;
right: 0;
top: 0;
cursor: pointer;
}
.card-icon {
@ -708,6 +709,7 @@ $align: center;
text-align: $align;
letter-spacing: -0.02em;
color: #1b2533;
align-self: center;
}
&__header-subtext {
@ -735,7 +737,7 @@ $align: center;
&__container {
display: grid;
grid-template-columns: auto;
grid-template-rows: 1fr 30px 30px auto auto;
grid-template-rows: 1fr 60px 30px auto auto;
width: 400px;
background: #f5f6fa;
border-radius: 6px;
@ -838,50 +840,49 @@ $align: center;
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: space-between;
&__cards {
width: 227px;
height: 126px;
padding: 20px;
width: 348px;
height: 203px;
padding: 24px;
box-sizing: border-box;
background: #fff;
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
border-radius: 10px;
margin: 0 10px 10px 0;
}
&__new-payments {
width: 227px;
height: 126px;
padding: 18px;
display: grid !important;
grid-template-columns: 6fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
width: 348px;
height: 203px;
padding: 24px;
box-sizing: border-box;
border: 2px dashed #929fb1;
border-radius: 10px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
&__text-area {
display: flex;
align-items: center;
justify-content: center;
&__plus-icon {
color: #0149ff;
font-family: sans-serif;
font-size: 24px;
}
font-size: 16px;
font-family: 'font_regular', sans-serif;
color: #0149ff;
cursor: pointer;
&__text {
color: #0149ff;
font-family: sans-serif;
font-size: 16px;
text-decoration: underline;
text-underline-position: under;
}
}
&__add-card {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}

View File

@ -16,7 +16,7 @@
<h2 class="add-coupon__body-wrapper__title">Apply Coupon Code</h2>
<p class="add-coupon__body-wrapper__text">If you have a coupon active, it will automatically be replaced.</p>
</div>
<AddCouponCodeInput2 />
<AddCouponCodeInput2 @close="onCloseClick" />
</div>
</div>
</div>
@ -38,7 +38,7 @@ import CouponIcon from '@/../static/images/account/billing/greenCoupon.svg';
CouponIcon,
},
})
export default class AddCouponCode extends Vue {
export default class AddCouponCode2 extends Vue {
@Prop({ default: false })
protected readonly success: boolean;

View File

@ -8,24 +8,11 @@
<StorjLarge />
</div>
</div>
<v-loader v-if="!pageLoaded" class="token-loader" />
<div v-else class="token__add-funds">
<div class="token__add-funds">
<h3 class="token__add-funds__title">
STORJ Token
</h3>
<div class="token__add-funds__button-container">
<p class="token__add-funds__support-info">To deposit STORJ token and request higher limits, please contact <a target="_blank" rel="noopener noreferrer" href="https://supportdcs.storj.io/hc/en-us/requests/new?ticket_form_id=360000683212">Support</a></p>
<VButton
v-if="totalCount > 0"
class="token__add-funds__button"
label="Back"
is-transparent="true"
width="50px"
height="30px"
font-size="11px"
:on-press="toggleShowAddFunds"
/>
</div>
<p class="token__add-funds__support-info">To deposit STORJ token and request higher limits, please contact <a target="_blank" rel="noopener noreferrer" href="https://supportdcs.storj.io/hc/en-us/requests/new?ticket_form_id=360000683212">Support</a></p>
</div>
</div>
</template>
@ -33,124 +20,17 @@
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PaymentAmountOption } from '@/types/payments';
import VButton from '@/components/common/VButton.vue';
import VLoader from '@/components/common/VLoader.vue';
import StorjLarge from '@/../static/images/billing/storj-icon-large.svg';
const {
MAKE_TOKEN_DEPOSIT,
GET_PAYMENTS_HISTORY,
} = PAYMENTS_ACTIONS;
// @vue/component
@Component({
components: {
StorjLarge,
VButton,
VLoader,
},
})
export default class AddTokenCard extends Vue {
@Prop({ default: 0 })
private readonly totalCount: number;
private readonly DEFAULT_TOKEN_DEPOSIT_VALUE = 10; // in dollars.
private readonly MAX_TOKEN_AMOUNT = 1000000; // in dollars.
private tokenDepositValue: number = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
private pageLoaded = true;
public toggleShowAddFunds(): void {
this.$emit('toggleShowAddFunds');
}
/**
* 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> {
if (!this.isDepositValueValid) return;
try {
this.pageLoaded = false;
const tokenResponse = await this.$store.dispatch(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.pageLoaded = true;
}
this.$emit('fetchHistory');
} catch (error) {
await this.$notify.error(error.message);
this.$emit('toggleIsLoading');
this.pageLoaded = true;
}
this.tokenDepositValue = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
try {
await this.$store.dispatch(GET_PAYMENTS_HISTORY);
} catch (error) {
await this.$notify.error(error.message);
this.$emit('toggleIsLoading');
}
}
/**
* Event for changing token deposit value.
*/
public onChangeTokenValue(value: number): void {
this.tokenDepositValue = value;
}
/**
* Indicates if deposit value is valid.
*/
private get isDepositValueValid(): boolean {
switch (true) {
case (this.tokenDepositValue < this.DEFAULT_TOKEN_DEPOSIT_VALUE || this.tokenDepositValue >= this.MAX_TOKEN_AMOUNT) && !this.userHasOwnProject:
this.$notify.error(`First deposit amount must be more than $10 and less than $${this.MAX_TOKEN_AMOUNT}`);
this.setDefault();
return false;
case this.tokenDepositValue >= this.MAX_TOKEN_AMOUNT || this.tokenDepositValue === 0:
this.$notify.error(`Deposit amount must be more than $0 and less than $${this.MAX_TOKEN_AMOUNT}`);
this.setDefault();
return false;
default:
return true;
}
}
/**
* Sets adding payment method state to default.
*/
private setDefault(): void {
this.tokenDepositValue = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
}
/**
* Indicates if user has own project.
*/
private get userHasOwnProject(): boolean {
return this.$store.getters.projectsCount > 0;
}
}
</script>
@ -163,12 +43,14 @@ export default class AddTokenCard extends Vue {
.token {
border-radius: 10px;
width: 227px;
height: 126px;
padding: 20px;
width: 348px;
height: 203px;
box-sizing: border-box;
padding: 24px;
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
background: #fff;
position: relative;
font-family: 'font_regular', sans-serif;
&__large-icon-container {
position: absolute;
@ -190,43 +72,22 @@ export default class AddTokenCard extends Vue {
&__add-funds {
display: flex;
flex-direction: column;
z-index: 5;
position: relative;
height: 100%;
width: 100%;
&__title {
font-family: sans-serif;
}
&__label {
font-family: sans-serif;
color: #56606d;
font-size: 11px;
margin-top: 5px;
}
&__dropdown {
margin-top: 10px;
}
&__button-container {
margin-top: 10px;
display: flex;
justify-content: space-between;
font-family: 'font_bold', sans-serif;
}
&__support-info {
font-family: sans-serif;
font-weight: 600;
font-size: 14px;
line-height: 20px;
color: #000;
position: relative;
top: 14px;
z-index: 1;
a {
color: #0149ff;
text-decoration: underline !important;
}
}
}

View File

@ -47,35 +47,20 @@
:on-press="toggleShowAddFunds"
/>
</div>
<div
v-if="showAddFunds"
class="token__add-funds"
>
<h3
class="token__add-funds__title"
>
<div v-else class="token__add-funds">
<h3 class="token__add-funds__title">
STORJ Token
</h3>
<p class="token__add-funds__label">Deposit STORJ Tokens via Coin Payments:</p>
<div class="token__add-funds__button-container">
<VButton
class="token__add-funds__button"
label="Continue to CoinPayments"
width="150px"
height="30px"
font-size="11px"
:on-press="onConfirmAddSTORJ"
/>
<VButton
class="token__add-funds__button"
label="Back"
is-transparent="true"
width="50px"
height="30px"
font-size="11px"
:on-press="toggleShowAddFunds"
/>
</div>
<p class="token__add-funds__support-info">To deposit STORJ token and request higher limits, please contact <a target="_blank" rel="noopener noreferrer" href="https://supportdcs.storj.io/hc/en-us/requests/new?ticket_form_id=360000683212">Support</a></p>
<VButton
label="Back"
width="100px"
height="30px"
is-transparent="true"
font-size="13px"
class="token__base__transaction-button"
:on-press="toggleShowAddFunds"
/>
</div>
</div>
</template>
@ -102,8 +87,10 @@ export default class BalanceTokenCard extends Vue {
@Prop({ default: () => new PaymentsHistoryItem() })
private readonly billingItem: PaymentsHistoryItem;
private showAddFunds = false;
public toggleShowAddFunds(): void {
this.$emit('toggleShowAddFunds');
this.showAddFunds = !this.showAddFunds;
}
public toggleTransactionsTable(): void {
@ -127,13 +114,14 @@ export default class BalanceTokenCard extends Vue {
.token {
border-radius: 10px;
width: 227px;
height: 126px;
margin: 0 10px 10px 0;
padding: 20px;
width: 348px;
height: 203px;
box-sizing: border-box;
padding: 24px;
box-shadow: 0 0 20px rgb(0 0 0 / 4%);
background: #fff;
position: relative;
font-family: 'font_regular', sans-serif;
&__large-icon-container {
position: absolute;
@ -305,6 +293,29 @@ export default class BalanceTokenCard extends Vue {
grid-row: 4;
z-index: 3;
}
}
&__add-funds {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
width: 100%;
&__title {
font-family: 'font_bold', sans-serif;
}
&__support-info {
font-size: 14px;
line-height: 20px;
color: #000;
z-index: 1;
a {
color: #0149ff;
text-decoration: underline !important;
}
}
}
}
</style>

View File

@ -8,16 +8,11 @@
Coupon Code:
</p>
<VInput
:label="inputLabel"
placeholder="Enter Coupon Code"
height="52px"
:with-icon="false"
@setData="setCouponCode"
/>
<CheckIcon
v-if="isCodeValid"
class="add-coupon__check"
/>
</div>
<ValidationMessage
class="add-coupon__valid-message"
@ -27,13 +22,13 @@
:show-message="showValidationMessage"
/>
<VButton
v-if="!isSignupView"
class="add-coupon__apply-button"
label="Apply Coupon Code"
width="150px"
height="44px"
font-size="14px"
:on-press="couponCheck"
:on-press="applyCouponCode"
:is-disabled="isLoading"
/>
</div>
</template>
@ -41,48 +36,26 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import { PaymentsHttpApi } from '@/api/payments';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import VInput from '@/components/common/VInput.vue';
import ValidationMessage from '@/components/common/ValidationMessage.vue';
import VButton from '@/components/common/VButton.vue';
import CheckIcon from '@/../static/images/common/validCheck.svg';
// @vue/component
@Component({
components: {
VButton,
VInput,
CheckIcon,
ValidationMessage,
},
})
export default class AddCouponCodeInput extends Vue {
export default class AddCouponCodeInput2 extends Vue {
private showValidationMessage = false;
private errorMessage = '';
private isCodeValid = false;
private errorMessage = '';
private couponCode = '';
private readonly payments: PaymentsHttpApi = new PaymentsHttpApi();
/**
* Signup view requires some unque styling and element text.
*/
public get isSignupView(): boolean {
return this.$route.name === RouteConfig.Register.name;
}
/**
* Returns label for input if in signup view
* Depending on view.
*/
public get inputLabel(): string | void {
return this.isSignupView ? 'Add Coupon' : '';
}
private isLoading = false;
public setCouponCode(value: string): void {
this.couponCode = value;
@ -92,30 +65,20 @@ export default class AddCouponCodeInput extends Vue {
* Check if coupon code is valid
*/
public async applyCouponCode(): Promise<void> {
if (this.isLoading) return;
this.isLoading = true;
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.APPLY_COUPON_CODE, this.couponCode);
}
catch (error) {
this.errorMessage = error.message;
this.isCodeValid = false;
this.showValidationMessage = true;
return;
}
this.isCodeValid = true;
this.showValidationMessage = true;
}
/**
* Check if user has a coupon code applied to their account before applying
*/
public async couponCheck(): Promise<void> {
try {
this.applyCouponCode();
await this.$notify.success('Coupon Added!');
this.$emit('close');
} catch (error) {
this.errorMessage = error.message;
this.isCodeValid = false;
this.showValidationMessage = true;
return;
} finally {
this.isLoading = false;
}
}
}