web/satellite: deposit and billing history splitted to be shown separately

WHAT:
1. Deposit & Billing history view was divided to be shown separately as Deposit History and Billing History
2. Datepicker was removed from billing page

WHY:
billing UX enhancements

Change-Id: Ie183849ef0965169997674ce37b71db38a562fc2
This commit is contained in:
VitaliiShpital 2020-05-21 20:01:56 +03:00 committed by Vitalii Shpital
parent 1c30efd3a1
commit 47a766328f
45 changed files with 934 additions and 754 deletions

View File

@ -315,7 +315,7 @@ func (paymentService PaymentsService) BillingHistory(ctx context.Context) (billi
Description: coupon.Description,
Amount: coupon.Amount,
Remaining: remaining,
Status: "Added to balance",
Status: "Added as Free Credits",
Link: "",
Start: coupon.Created,
Type: Coupon,

View File

@ -4,9 +4,9 @@
import { ErrorUnauthorized } from '@/api/errors/ErrorUnauthorized';
import {
AccountBalance,
BillingHistoryItem,
CreditCard,
PaymentsApi,
PaymentsHistoryItem,
ProjectUsageAndCharges,
TokenDeposit,
} from '@/types/payments';
@ -194,12 +194,12 @@ export class PaymentsHttpApi implements PaymentsApi {
}
/**
* Returns a list of invoices, transactions and all others billing history items for payment account.
* Returns a list of invoices, transactions and all others payments history items for payment account.
*
* @returns list of billing history items
* @returns list of payments history items
* @throws Error
*/
public async billingHistory(): Promise<BillingHistoryItem[]> {
public async paymentsHistory(): Promise<PaymentsHistoryItem[]> {
const path = `${this.ROOT_PATH}/billing-history`;
const response = await this.client.get(path);
@ -210,11 +210,11 @@ export class PaymentsHttpApi implements PaymentsApi {
throw new Error('can not list billing history');
}
const billingHistoryItems = await response.json();
const paymentsHistoryItems = await response.json();
if (billingHistoryItems) {
return billingHistoryItems.map(item =>
new BillingHistoryItem(
if (paymentsHistoryItems) {
return paymentsHistoryItems.map(item =>
new PaymentsHistoryItem(
item.id,
item.description,
item.amount,

View File

@ -27,36 +27,19 @@
STORJ Balance: {{ balance.coins | centsToDollars }}
</span>
</div>
<div class="account-billing-area__title-area__options-area" v-if="userHasOwnProject">
<div class="account-billing-area__title-area__options-area__option active" @click.prevent="onCurrentPeriodClick">
<span class="account-billing-area__title-area__options-area__option__label">Current Billing Period</span>
</div>
<div class="account-billing-area__title-area__options-area__option" @click.prevent="onPreviousPeriodClick">
<span class="account-billing-area__title-area__options-area__option__label">Previous Billing Period</span>
</div>
<div class="account-billing-area__title-area__options-area__option datepicker" @click.prevent.self="onCustomDateClick">
<VDatepicker
ref="datePicker"
:date="startTime"
@change="getDates"
/>
<DatePickerIcon
class="account-billing-area__title-area__options-area__option__image"
@click.prevent="onCustomDateClick"
/>
</div>
</div>
<PeriodSelection v-if="userHasOwnProject"/>
</div>
<EstimatedCostsAndCredits v-if="isSummaryVisible"/>
<PaymentMethods/>
<DepositAndBilling/>
<SmallDepositHistory/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import DepositAndBilling from '@/components/account/billing/billingHistory/DepositAndBilling.vue';
import PeriodSelection from '@/components/account/billing/depositAndBillingHistory/PeriodSelection.vue';
import SmallDepositHistory from '@/components/account/billing/depositAndBillingHistory/SmallDepositHistory.vue';
import EstimatedCostsAndCredits from '@/components/account/billing/estimatedCostsAndCredits/EstimatedCostsAndCredits.vue';
import PaymentMethods from '@/components/account/billing/paymentMethods/PaymentMethods.vue';
import VDatepicker from '@/components/common/VDatePicker.vue';
@ -65,32 +48,17 @@ import DatePickerIcon from '@/../static/images/account/billing/datePicker.svg';
import LowBalanceIcon from '@/../static/images/account/billing/lowBalance.svg';
import NegativeBalanceIcon from '@/../static/images/account/billing/negativeBalance.svg';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { AccountBalance, DateRange } from '@/types/payments';
import { AccountBalance } from '@/types/payments';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { ProjectOwning } from '@/utils/projectOwning';
/**
* Exposes empty time for DatePicker.
*/
class StartTime {
public time = null;
}
/**
* Exposes VDatepicker's showCheck method.
*/
declare interface ShowCheck {
showCheck(): void;
}
@Component({
components: {
PeriodSelection,
SmallDepositHistory,
EstimatedCostsAndCredits,
DepositAndBilling,
PaymentMethods,
VDatepicker,
DatePickerIcon,
@ -105,7 +73,7 @@ export default class BillingArea extends Vue {
*/
public async beforeMount(): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_BILLING_HISTORY);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PAYMENTS_HISTORY);
if (this.$store.getters.canUserCreateFirstProject && !this.userHasOwnProject) {
await this.$store.dispatch(APP_STATE_ACTIONS.SHOW_CREATE_PROJECT_BUTTON);
await this.$store.dispatch(APP_STATE_ACTIONS.TOGGLE_NEW_PROJ);
@ -127,56 +95,6 @@ export default class BillingArea extends Vue {
*/
private readonly CRITICAL_AMOUNT: number = 1000;
/**
* Holds start and end dates.
*/
private readonly dateRange: DateRange;
/**
* Holds empty start time for DatePicker.
*/
public readonly startTime: StartTime = new StartTime();
public constructor() {
super();
const currentDate = new Date();
const previousDate = new Date();
previousDate.setUTCMonth(currentDate.getUTCMonth() - 1);
this.dateRange = {
startDate: previousDate,
endDate: currentDate,
};
}
/**
* Lifecycle hook before changing location.
* Returns component state to default.
* @param to
* @param from
* @param next
*/
public async beforeRouteLeave(to, from, next): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP);
} catch (error) {
await this.$notify.error(error.message);
}
const buttons = [...(document as HTMLDocument).querySelectorAll('.account-billing-area__title-area__options-area__option')];
buttons.forEach(option => {
option.classList.remove('active');
});
buttons[0].classList.add('active');
next();
}
public $refs!: {
datePicker: VDatepicker & ShowCheck;
};
/**
* Returns account balance from store.
*/
@ -227,116 +145,6 @@ export default class BillingArea extends Vue {
public get userHasOwnProject(): boolean {
return new ProjectOwning(this.$store).userHasOwnProject();
}
/**
* Sets billing state to current billing period.
* @param event holds click event.
*/
public async onCurrentPeriodClick(event: any): Promise<void> {
this.onButtonClickAction(event);
try {
this.$segment.track(SegmentEvent.REPORT_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
start_date: this.dateRange.startDate,
end_date: this.dateRange.endDate,
});
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP);
} catch (error) {
await this.$notify.error(`Unable to fetch project charges. ${error.message}`);
}
}
/**
* Sets billing state to previous billing period.
* @param event holds click event.
*/
public async onPreviousPeriodClick(event: any): Promise<void> {
this.onButtonClickAction(event);
try {
this.$segment.track(SegmentEvent.REPORT_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
start_date: this.dateRange.startDate,
end_date: this.dateRange.endDate,
});
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP);
} catch (error) {
await this.$notify.error(`Unable to fetch project charges. ${error.message}`);
}
}
/**
* Sets billing state to custom billing period.
* @param event holds click event.
*/
public onCustomDateClick(event: any): void {
this.$refs.datePicker.showCheck();
this.onButtonClickAction(event);
this.$segment.track(SegmentEvent.REPORT_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
start_date: this.dateRange.startDate,
end_date: this.dateRange.endDate,
});
}
/**
* Callback for VDatePicker.
* @param datesArray selected date range.
*/
public async getDates(datesArray: Date[]): Promise<void> {
const firstDate = new Date(datesArray[0]);
const secondDate = new Date(datesArray[1]);
const isInverted = firstDate > secondDate;
const startDate = isInverted ? secondDate : firstDate;
const endDate = isInverted ? firstDate : secondDate;
const dateRange: DateRange = new DateRange(startDate, endDate);
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES, dateRange);
} catch (error) {
await this.$notify.error(`Unable to fetch project charges. ${error.message}`);
}
}
/**
* Changes buttons styling depends on selected status.
* @param event holds click event
*/
private onButtonClickAction(event: any): void {
let eventTarget = event.target;
if (eventTarget.children.length === 0) {
eventTarget = eventTarget.parentNode;
}
if (eventTarget.classList.contains('active')) {
return;
}
this.changeActiveClass(eventTarget);
}
/**
* Adds event target active class.
* @param target holds event target
*/
private changeActiveClass(target: any): void {
this.removeActiveClass();
target.classList.add('active');
}
/**
* Removes active class from all the event targets.
*/
private removeActiveClass(): void {
const buttons = [...(document as any).querySelectorAll('.account-billing-area__title-area__options-area__option')];
buttons.forEach(option => {
option.classList.remove('active');
});
}
}
</script>
@ -367,44 +175,6 @@ export default class BillingArea extends Vue {
color: #768394;
}
}
&__options-area {
display: flex;
align-items: center;
&__option {
display: flex;
align-items: center;
justify-content: center;
padding: 15px 20px;
background-color: #fff;
border-radius: 6px;
margin-left: 16px;
cursor: pointer;
&__label {
font-family: 'font_medium', sans-serif;
font-size: 14px;
line-height: 14px;
color: #384b65;
}
&.active {
background-color: #2683ff;
.account-billing-area__title-area__options-area__option__label {
color: #fff;
}
.account-billing-area__title-area__options-area__option__image {
.date-picker-svg-path {
fill: #fff !important;
}
}
}
}
}
}
&__notification-container {
@ -441,13 +211,4 @@ export default class BillingArea extends Vue {
.custom-position {
margin: 30px 0 20px 0;
}
.datepicker {
padding: 12px;
}
/deep/ .datepickbox {
max-height: 0;
max-width: 0;
}
</style>

View File

@ -1,157 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="billing-history-area">
<div class="billing-history-area__title-area" @click="onBackToAccountClick">
<div class="billing-history-area__title-area__back-button">
<BackImage/>
</div>
<p class="billing-history-area__title-area__title">Back to Account</p>
</div>
<div class="billing-history-area__content">
<h1 class="billing-history-area__content__title">Billing History</h1>
<SortingHeader/>
<BillingItem
v-for="item in billingHistoryItems"
:billing-item="item"
:key="item.id"
/>
</div>
<!-- <VPagination-->
<!-- v-if="totalPageCount > 1"-->
<!-- class="pagination-area"-->
<!-- :total-page-count="totalPageCount"-->
<!-- :on-page-click-callback="onPageClick"-->
<!-- />-->
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import BillingItem from '@/components/account/billing/billingHistory/BillingItem.vue';
import SortingHeader from '@/components/account/billing/billingHistory/SortingHeader.vue';
import VPagination from '@/components/common/VPagination.vue';
import BackImage from '@/../static/images/account/billing/back.svg';
import { RouteConfig } from '@/router';
import { BillingHistoryItem } from '@/types/payments';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
@Component({
components: {
BillingItem,
SortingHeader,
VPagination,
BackImage,
},
})
export default class BillingHistory extends Vue {
/**
* Lifecycle hook after initial render.
*/
public mounted(): void {
this.$segment.track(SegmentEvent.BILLING_HISTORY_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
invoice_count: this.$store.state.paymentsModule.billingHistory.length,
});
}
/**
* Returns list of billing history listings.
*/
public get billingHistoryItems(): BillingHistoryItem[] {
return this.$store.state.paymentsModule.billingHistory;
}
/**
* Replaces location to root billing route.
*/
public onBackToAccountClick(): void {
this.$router.push(RouteConfig.Billing.path);
}
}
</script>
<style scoped lang="scss">
p,
h1 {
margin: 0;
}
.billing-history-area {
margin-top: 83px;
padding: 0 0 80px 0;
background-color: #f5f6fa;
font-family: 'font_regular', sans-serif;
&__title-area {
display: flex;
align-items: center;
cursor: pointer;
width: 184px;
margin-bottom: 27px;
&__back-button {
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
width: 40px;
height: 40px;
border-radius: 78px;
margin-right: 11px;
}
&__title {
font-family: 'font_medium', sans-serif;
font-weight: 500;
font-size: 16px;
line-height: 21px;
color: #354049;
white-space: nowrap;
}
&:hover {
.billing-history-area__title-area__back-button {
background-color: #2683ff;
.back-button-svg-path {
fill: #fff;
}
}
}
}
&__content {
background-color: #fff;
padding: 32px 44px 34px 36px;
border-radius: 8px;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #384b65;
margin-bottom: 32px;
}
}
}
::-webkit-scrollbar,
::-webkit-scrollbar-track,
::-webkit-scrollbar-thumb {
width: 0;
}
@media (max-height: 1000px) and (max-width: 1230px) {
.billing-history-area {
overflow-y: scroll;
height: 65vh;
}
}
</style>

View File

@ -0,0 +1,150 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="history-area">
<div class="history-area__back-area" @click="onBackToBillingClick">
<BackImage/>
<p class="history-area__back-area__title">Back to Billing</p>
</div>
<h1 class="history-area__title" v-if="isBillingHistory">Billing History</h1>
<h1 class="history-area__title" v-else>Deposit History</h1>
<div class="history-area__content" v-if="historyItems.length > 0">
<SortingHeader/>
<PaymentsItem
v-for="item in historyItems"
:billing-item="item"
:key="item.id"
/>
</div>
<h2 class="history-area__empty-state" v-else>No Items Yet</h2>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import PaymentsItem from '@/components/account/billing/depositAndBillingHistory/PaymentsItem.vue';
import SortingHeader from '@/components/account/billing/depositAndBillingHistory/SortingHeader.vue';
import BackImage from '@/../static/images/account/billing/back.svg';
import { RouteConfig } from '@/router';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
@Component({
components: {
PaymentsItem,
SortingHeader,
BackImage,
},
})
export default class DetailedHistory extends Vue {
/**
* Returns list of history items depending on route name.
*/
public get historyItems(): PaymentsHistoryItem[] {
if (this.isBillingHistory) {
return this.$store.state.paymentsModule.paymentsHistory.filter((item: PaymentsHistoryItem) => {
return item.type === PaymentsHistoryItemType.Invoice || item.type === PaymentsHistoryItemType.Charge;
});
}
return this.$store.state.paymentsModule.paymentsHistory.filter((item: PaymentsHistoryItem) => {
return item.type === PaymentsHistoryItemType.Transaction || item.type === PaymentsHistoryItemType.Coupon;
});
}
/**
* Indicates if current route is billing history page.
*/
public get isBillingHistory(): boolean {
return this.$route.name === RouteConfig.BillingHistory.name;
}
/**
* Replaces location to root billing route.
*/
public onBackToBillingClick(): void {
this.$router.push(RouteConfig.Billing.path);
}
}
</script>
<style scoped lang="scss">
p,
h1 {
margin: 0;
}
.history-area {
margin-top: 27px;
padding: 0 0 80px 0;
background-color: #f5f6fa;
font-family: 'font_regular', sans-serif;
&__back-area {
display: flex;
align-items: center;
cursor: pointer;
width: 184px;
margin-bottom: 32px;
&__title {
font-family: 'font_medium', sans-serif;
font-weight: 500;
font-size: 16px;
line-height: 21px;
color: #768394;
white-space: nowrap;
margin-left: 15px;
}
&:hover {
.history-area__back-area__title {
color: #2683ff;
}
.back-button-svg-path {
fill: #2683ff;
}
}
}
&__title {
font-family: 'font_bold', sans-serif;
font-size: 22px;
line-height: 27px;
color: #384b65;
margin-bottom: 20px;
}
&__content {
background-color: #fff;
padding: 30px 40px 0 40px;
border-radius: 8px;
}
&__empty-state {
font-size: 40px;
line-height: 46px;
text-align: center;
margin-top: 200px;
}
}
::-webkit-scrollbar,
::-webkit-scrollbar-track,
::-webkit-scrollbar-thumb {
width: 0;
}
@media (max-height: 1000px) and (max-width: 1230px) {
.history-area {
overflow-y: scroll;
height: 65vh;
}
}
</style>

View File

@ -18,10 +18,10 @@
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import { BillingHistoryItemStatus, BillingHistoryItemType } from '@/types/payments';
import { PaymentsHistoryItemStatus, PaymentsHistoryItemType } from '@/types/payments';
@Component
export default class BillingHistoryDate extends Vue {
export default class PaymentsHistoryItemDate extends Vue {
/**
* expiration date.
*/
@ -33,9 +33,9 @@ export default class BillingHistoryDate extends Vue {
@Prop({default: () => new Date()})
private readonly start: Date;
@Prop({default: 0})
private readonly type: BillingHistoryItemType;
private readonly type: PaymentsHistoryItemType;
@Prop({default: ''})
private readonly status: BillingHistoryItemStatus;
private readonly status: PaymentsHistoryItemStatus;
private readonly expirationTimeInSeconds: number;
private nowInSeconds = Math.trunc(new Date().getTime() / 1000);
@ -52,7 +52,7 @@ export default class BillingHistoryDate extends Vue {
this.expirationTimeInSeconds = Math.trunc(new Date(this.expiration).getTime() / 1000);
this.isExpired = (this.expirationTimeInSeconds - this.nowInSeconds) < 0;
if (this.type === BillingHistoryItemType.Transaction) {
if (this.type === PaymentsHistoryItemType.Transaction) {
this.isExpired = this.isTransactionCompleted;
}
@ -91,7 +91,7 @@ export default class BillingHistoryDate extends Vue {
* Indicates if transaction status is completed, paid or cancelled.
*/
private get isTransactionCompleted(): boolean {
return this.status !== BillingHistoryItemStatus.Pending;
return this.status !== PaymentsHistoryItemStatus.Pending;
}
/**

View File

@ -3,8 +3,8 @@
<template>
<div class="container">
<BillingHistoryItemDate
class="container__item"
<PaymentsHistoryItemDate
class="container__item date"
:start="billingItem.start"
:expiration="billingItem.end"
:type="billingItem.type"
@ -33,18 +33,18 @@
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import BillingHistoryItemDate from '@/components/account/billing/billingHistory/BillingHistoryItemDate.vue';
import PaymentsHistoryItemDate from '@/components/account/billing/depositAndBillingHistory/PaymentsHistoryItemDate.vue';
import { BillingHistoryItem } from '@/types/payments';
import { PaymentsHistoryItem } from '@/types/payments';
@Component({
components: {
BillingHistoryItemDate,
PaymentsHistoryItemDate,
},
})
export default class BillingItem extends Vue {
@Prop({default: () => new BillingHistoryItem()})
private readonly billingItem: BillingHistoryItem;
export default class PaymentsItem extends Vue {
@Prop({default: () => new PaymentsHistoryItem()})
private readonly billingItem: PaymentsHistoryItem;
}
</script>
@ -60,26 +60,31 @@ export default class BillingItem extends Vue {
.container {
display: flex;
padding: 0 30px;
align-items: center;
width: calc(100% - 60px);
border-top: 1px solid rgba(169, 181, 193, 0.3);
width: 100%;
border-top: 1px solid #c7cdd2;
&__item {
min-width: 25%;
min-width: 20%;
font-family: 'font_medium', sans-serif;
font-size: 16px;
text-align: left;
color: #61666b;
color: #768394;
margin: 30px 0;
}
}
.date {
font-family: 'font_bold', sans-serif;
margin: 0;
}
.description {
min-width: 31%;
}
.status {
min-width: 12%;
min-width: 17%;
}
.amount {

View File

@ -0,0 +1,247 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="period-selection" @click.stop="toggle">
<div class="period-selection__current-choice">
<div class="period-selection__current-choice__label-area">
<DatePickerIcon/>
<span class="period-selection__current-choice__label-area__label">{{ currentOption }}</span>
</div>
<ExpandIcon v-if="!isDropdownShown"/>
<HideIcon v-else/>
</div>
<div class="period-selection__dropdown" v-if="isDropdownShown" v-click-outside="close">
<div
class="period-selection__dropdown__item"
v-for="(option, index) in periodOptions"
:key="index"
@click.prevent.stop="select(option)"
>
<SelectedIcon v-if="isOptionSelected(option)" class="selected-image"/>
<span class="period-selection__dropdown__item__label">{{ option }}</span>
</div>
<div @click="redirect" class="period-selection__dropdown__link-container">
<span class="period-selection__dropdown__link-container__link">Billing History</span>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import DatePickerIcon from '@/../static/images/account/billing/datePicker.svg';
import SelectedIcon from '@/../static/images/account/billing/selected.svg';
import ExpandIcon from '@/../static/images/common/BlueExpand.svg';
import HideIcon from '@/../static/images/common/BlueHide.svg';
import { RouteConfig } from '@/router';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
@Component({
components: {
DatePickerIcon,
HideIcon,
ExpandIcon,
SelectedIcon,
},
})
export default class PeriodSelection extends Vue {
public readonly periodOptions: string[] = [
'Current Billing Period',
'Previous Billing Period',
];
public currentOption: string = this.periodOptions[0];
public isDropdownShown: boolean = false;
/**
* Lifecycle hook before changing location.
* Returns component state to default.
* @param to
* @param from
* @param next
*/
public async beforeRouteLeave(to, from, next): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP);
} catch (error) {
await this.$notify.error(error.message);
}
next();
}
/**
* Returns start date of billing period from store.
*/
public get startDate(): Date {
return this.$store.state.paymentsModule.startDate;
}
/**
* Returns end date of billing period from store.
*/
public get endDate(): Date {
return this.$store.state.paymentsModule.endDate;
}
/**
* Indicates if option is selected.
* @param option - option string.
*/
public isOptionSelected(option: string): boolean {
return option === this.currentOption;
}
/**
* Holds logic for select option click.
* @param option - option string.
*/
public async select(option: string): Promise<void> {
if (option === this.periodOptions[0]) {
await this.onCurrentPeriodClick();
}
if (option === this.periodOptions[1]) {
await this.onPreviousPeriodClick();
}
this.currentOption = option;
this.close();
}
/**
* Closes dropdown.
*/
public close(): void {
this.isDropdownShown = false;
}
/**
* Toggles dropdown visibility.
*/
public toggle(): void {
this.isDropdownShown = !this.isDropdownShown;
}
/**
* Holds logic to redirect user to billing history page.
*/
public redirect(): void {
this.$router.push(RouteConfig.Account.with(RouteConfig.BillingHistory).path);
}
/**
* Sets billing state to previous billing period.
*/
private async onPreviousPeriodClick(): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP);
this.$segment.track(SegmentEvent.REPORT_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
start_date: this.startDate,
end_date: this.endDate,
});
} catch (error) {
await this.$notify.error(`Unable to fetch project charges. ${error.message}`);
}
}
/**
* Sets billing state to current billing period.
*/
private async onCurrentPeriodClick(): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP);
this.$segment.track(SegmentEvent.REPORT_VIEWED, {
project_id: this.$store.getters.selectedProject.id,
start_date: this.startDate,
end_date: this.endDate,
});
} catch (error) {
await this.$notify.error(`Unable to fetch project charges. ${error.message}`);
}
}
}
</script>
<style scoped lang="scss">
.period-selection {
padding: 15px;
width: 260px;
background-color: #fff;
position: relative;
font-family: 'font_regular', sans-serif;
border-radius: 6px;
cursor: pointer;
&__current-choice {
display: flex;
align-items: center;
justify-content: space-between;
&__label-area {
display: flex;
align-items: center;
&__label {
font-family: 'font_medium', sans-serif;
font-size: 16px;
line-height: 16px;
margin-left: 15px;
}
}
}
&__dropdown {
z-index: 120;
position: absolute;
left: 0;
top: 55px;
background-color: #fff;
border-radius: 6px;
border: 1px solid #c5cbdb;
box-shadow: 0 8px 34px rgba(161, 173, 185, 0.41);
width: 290px;
&__item {
padding: 15px;
&__label {
font-size: 14px;
line-height: 19px;
color: #494949;
}
&:hover {
background-color: #f5f5f7;
}
}
&__link-container {
width: calc(100% - 30px);
height: 50px;
padding: 0 15px;
display: flex;
align-items: center;
&:hover {
background-color: #f5f5f7;
}
&__link {
font-size: 14px;
line-height: 19px;
color: #7e8b9c;
}
}
}
}
.selected-image {
margin-right: 10px;
}
</style>

View File

@ -2,14 +2,14 @@
// See LICENSE for copying information.
<template>
<div class="deposit-and-billing-area" v-if="billingHistoryItems.length > 0">
<div class="deposit-and-billing-area__header">
<h1 class="deposit-and-billing-area__header__title">Deposit & Billing History</h1>
<div class="deposit-area" v-if="depositHistoryItems.length > 0">
<div class="deposit-area__header">
<h1 class="deposit-area__header__title">Deposit History</h1>
<div class="button" @click="onViewAllClick">View All</div>
</div>
<SortingHeader/>
<BillingItem
v-for="item in billingHistoryItems"
<PaymentsItem
v-for="item in depositHistoryItems"
:billing-item="item"
:key="item.id"
/>
@ -19,31 +19,33 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import BillingItem from '@/components/account/billing/billingHistory/BillingItem.vue';
import SortingHeader from '@/components/account/billing/billingHistory/SortingHeader.vue';
import PaymentsItem from '@/components/account/billing/depositAndBillingHistory/PaymentsItem.vue';
import SortingHeader from '@/components/account/billing/depositAndBillingHistory/SortingHeader.vue';
import { RouteConfig } from '@/router';
import { BillingHistoryItem } from '@/types/payments';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
@Component({
components: {
BillingItem,
PaymentsItem,
SortingHeader,
},
})
export default class DepositAndBilling extends Vue {
export default class SmallDepositHistory extends Vue {
/**
* Changes location to billing history route.
* Changes location to deposit history route.
*/
public onViewAllClick(): void {
this.$router.push(RouteConfig.Account.with(RouteConfig.BillingHistory).path);
this.$router.push(RouteConfig.Account.with(RouteConfig.DepositHistory).path);
}
/**
* Returns first 3 of billing history items.
* Returns first 3 of deposit history items.
*/
public get billingHistoryItems(): BillingHistoryItem[] {
return this.$store.state.paymentsModule.billingHistory.slice(0, 3);
public get depositHistoryItems(): PaymentsHistoryItem[] {
return this.$store.state.paymentsModule.paymentsHistory.filter((item: PaymentsHistoryItem) => {
return item.type === PaymentsHistoryItemType.Transaction || item.type === PaymentsHistoryItemType.Coupon;
}).slice(0, 3);
}
}
</script>
@ -55,9 +57,9 @@ export default class DepositAndBilling extends Vue {
color: #354049;
}
.deposit-and-billing-area {
.deposit-area {
margin-bottom: 32px;
padding: 40px;
padding: 40px 40px 10px 40px;
background-color: #fff;
border-radius: 8px;
font-family: 'font_regular', sans-serif;
@ -96,14 +98,14 @@ export default class DepositAndBilling extends Vue {
@media screen and (max-height: 850px) {
.deposit-and-billing-area {
.deposit-area {
margin-bottom: 50px;
}
}
@media screen and (max-height: 650px) {
.deposit-and-billing-area {
.deposit-area {
margin-bottom: 75px;
}
}

View File

@ -4,19 +4,19 @@
<template>
<div class="sort-header-container">
<div class="sort-header-container__item date">
<p class="sort-header-container__item__name">Date</p>
<p class="sort-header-container__item__name">DATE</p>
</div>
<div class="sort-header-container__item description">
<p class="sort-header-container__item__name">Description</p>
<p class="sort-header-container__item__name">DESCRIPTION</p>
</div>
<div class="sort-header-container__item status">
<p class="sort-header-container__item__name">Status</p>
<p class="sort-header-container__item__name">STATUS</p>
</div>
<div class="sort-header-container__item amount">
<p class="sort-header-container__item__name">Amount</p>
<p class="sort-header-container__item__name">AMOUNT</p>
</div>
<div class="sort-header-container__item download">
<p class="sort-header-container__item__name">Download</p>
<p class="sort-header-container__item__name">DOWNLOAD</p>
</div>
</div>
</template>
@ -31,19 +31,19 @@ export default class SortingHeader extends Vue {}
<style scoped lang="scss">
.sort-header-container {
display: flex;
background-color: rgba(245, 246, 250, 0.5);
width: calc(100% - 60px);
padding: 0 30px;
width: 100%;
padding-bottom: 32px;
&__item {
text-align: left;
min-width: 25%;
min-width: 20%;
&__name {
font-family: 'font_medium', sans-serif;
font-size: 16px;
line-height: 10px;
color: #afb7c1;
font-size: 14px;
line-height: 19px;
color: #adadad;
margin: 0;
}
}
}
@ -53,7 +53,7 @@ export default class SortingHeader extends Vue {}
}
.status {
min-width: 12%;
min-width: 17%;
}
.amount {

View File

@ -26,7 +26,6 @@ import { ProjectOwning } from '@/utils/projectOwning';
const {
ADD_CREDIT_CARD,
GET_CREDIT_CARDS,
GET_BILLING_HISTORY,
GET_BALANCE,
} = PAYMENTS_ACTIONS;

View File

@ -41,7 +41,7 @@ import { ProjectOwning } from '@/utils/projectOwning';
const {
MAKE_TOKEN_DEPOSIT,
GET_BILLING_HISTORY,
GET_PAYMENTS_HISTORY,
} = PAYMENTS_ACTIONS;
@Component({
@ -85,7 +85,7 @@ export default class AddStorjForm extends Vue {
this.tokenDepositValue = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
try {
await this.$store.dispatch(GET_BILLING_HISTORY);
await this.$store.dispatch(GET_PAYMENTS_HISTORY);
} catch (error) {
await this.$notify.error(error.message);
this.$emit('toggleIsLoading');

View File

@ -101,7 +101,6 @@ import { PaymentMethodsBlockState } from '@/utils/constants/billingEnums';
const {
GET_CREDIT_CARDS,
GET_BILLING_HISTORY,
GET_BALANCE,
} = PAYMENTS_ACTIONS;

View File

@ -113,7 +113,7 @@ export default class TokenDepositSelection extends Vue {
* Indicates if dropdown expands top.
*/
public get isExpandingTop(): boolean {
return this.$store.state.paymentsModule.billingHistory.length === 0;
return this.$store.state.paymentsModule.paymentsHistory.length === 0;
}
/**

View File

@ -27,11 +27,11 @@
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HideIcon from '@/../static/images/common/ArrowHide.svg';
import ExpandIcon from '@/../static/images/common/BlackArrowExpand.svg';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import HideIcon from '../../../static/images/common/ArrowHide.svg';
import AccountDropdown from './AccountDropdown.vue';
@Component({

View File

@ -146,7 +146,7 @@ export default class CreateProjectStep extends Vue {
}
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_BILLING_HISTORY);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PAYMENTS_HISTORY);
} catch (error) {
await this.$notify.error(`Unable to get billing history. ${error.message}`);
}

View File

@ -41,7 +41,6 @@ import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
const {
ADD_CREDIT_CARD,
GET_CREDIT_CARDS,
GET_BILLING_HISTORY,
GET_BALANCE,
} = PAYMENTS_ACTIONS;

View File

@ -40,7 +40,7 @@ import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
const {
MAKE_TOKEN_DEPOSIT,
GET_BILLING_HISTORY,
GET_PAYMENTS_HISTORY,
} = PAYMENTS_ACTIONS;
@Component({
@ -100,7 +100,7 @@ export default class PayingStep extends Vue {
this.tokenDepositValue = this.DEFAULT_TOKEN_DEPOSIT_VALUE;
try {
await this.$store.dispatch(GET_BILLING_HISTORY);
await this.$store.dispatch(GET_PAYMENTS_HISTORY);
} catch (error) {
await this.$notify.error(error.message);
}

View File

@ -163,7 +163,7 @@ export default class NewProjectPopup extends Vue {
}
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_BILLING_HISTORY);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PAYMENTS_HISTORY);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_BALANCE);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP);
await this.$store.dispatch(PROJECTS_ACTIONS.GET_LIMITS, this.createdProjectId);

View File

@ -6,7 +6,7 @@ import Router, { RouteRecord } from 'vue-router';
import AccountArea from '@/components/account/AccountArea.vue';
import AccountBilling from '@/components/account/billing/BillingArea.vue';
import BillingHistory from '@/components/account/billing/billingHistory/BillingHistory.vue';
import DetailedHistory from '@/components/account/billing/depositAndBillingHistory/DetailedHistory.vue';
import SettingsArea from '@/components/account/SettingsArea.vue';
import ApiKeysArea from '@/components/apiKeys/ApiKeysArea.vue';
import Page404 from '@/components/errors/Page404.vue';
@ -42,6 +42,7 @@ export abstract class RouteConfig {
public static Settings = new NavigationLink('settings', 'Settings');
public static Billing = new NavigationLink('billing', 'Billing');
public static BillingHistory = new NavigationLink('billing-history', 'Billing History');
public static DepositHistory = new NavigationLink('deposit-history', 'Deposit History');
// TODO: disabled until implementation
// public static Referral = new NavigationLink('referral', 'Referral');
@ -55,6 +56,7 @@ export const notProjectRelatedRoutes = [
RouteConfig.ForgotPassword.name,
RouteConfig.Billing.name,
RouteConfig.BillingHistory.name,
RouteConfig.DepositHistory.name,
RouteConfig.Settings.name,
// RouteConfig.Referral.name,
];
@ -102,7 +104,12 @@ export const router = new Router({
{
path: RouteConfig.BillingHistory.path,
name: RouteConfig.BillingHistory.name,
component: BillingHistory,
component: DetailedHistory,
},
{
path: RouteConfig.DepositHistory.path,
name: RouteConfig.DepositHistory.name,
component: DetailedHistory,
},
// {
// path: RouteConfig.Referral.path,

View File

@ -4,12 +4,12 @@
import { StoreModule } from '@/store';
import {
AccountBalance,
BillingHistoryItem,
BillingHistoryItemStatus,
BillingHistoryItemType,
CreditCard,
DateRange,
PaymentsApi,
PaymentsHistoryItem,
PaymentsHistoryItemStatus,
PaymentsHistoryItemType,
ProjectUsageAndCharges,
TokenDeposit,
} from '@/types/payments';
@ -21,7 +21,7 @@ export const PAYMENTS_MUTATIONS = {
CLEAR: 'CLEAR_PAYMENT_INFO',
UPDATE_CARDS_SELECTION: 'UPDATE_CARDS_SELECTION',
UPDATE_CARDS_DEFAULT: 'UPDATE_CARDS_DEFAULT',
SET_BILLING_HISTORY: 'SET_BILLING_HISTORY',
SET_PAYMENTS_HISTORY: 'SET_PAYMENTS_HISTORY',
SET_PROJECT_USAGE_AND_CHARGES: 'SET_PROJECT_USAGE_AND_CHARGES',
SET_CURRENT_ROLLUP_PRICE: 'SET_CURRENT_ROLLUP_PRICE',
SET_PREVIOUS_ROLLUP_PRICE: 'SET_PREVIOUS_ROLLUP_PRICE',
@ -38,7 +38,7 @@ export const PAYMENTS_ACTIONS = {
CLEAR_CARDS_SELECTION: 'clearCardsSelection',
MAKE_CARD_DEFAULT: 'makeCardDefault',
REMOVE_CARD: 'removeCard',
GET_BILLING_HISTORY: 'getBillingHistory',
GET_PAYMENTS_HISTORY: 'getPaymentsHistory',
MAKE_TOKEN_DEPOSIT: 'makeTokenDeposit',
GET_PROJECT_USAGE_AND_CHARGES: 'getProjectUsageAndCharges',
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP: 'getProjectUsageAndChargesCurrentRollup',
@ -52,7 +52,7 @@ const {
CLEAR,
UPDATE_CARDS_SELECTION,
UPDATE_CARDS_DEFAULT,
SET_BILLING_HISTORY,
SET_PAYMENTS_HISTORY,
SET_PROJECT_USAGE_AND_CHARGES,
SET_PRICE_SUMMARY,
} = PAYMENTS_MUTATIONS;
@ -67,9 +67,8 @@ const {
CLEAR_PAYMENT_INFO,
MAKE_CARD_DEFAULT,
REMOVE_CARD,
GET_BILLING_HISTORY,
GET_PAYMENTS_HISTORY,
MAKE_TOKEN_DEPOSIT,
GET_PROJECT_USAGE_AND_CHARGES,
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP,
GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP,
} = PAYMENTS_ACTIONS;
@ -80,7 +79,7 @@ export class PaymentsState {
*/
public balance: AccountBalance = new AccountBalance();
public creditCards: CreditCard[] = [];
public billingHistory: BillingHistoryItem[] = [];
public paymentsHistory: PaymentsHistoryItem[] = [];
public usageAndCharges: ProjectUsageAndCharges[] = [];
public priceSummary: number = 0;
public startDate: Date = new Date();
@ -132,8 +131,8 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
return card;
});
},
[SET_BILLING_HISTORY](state: PaymentsState, billingHistory: BillingHistoryItem[]): void {
state.billingHistory = billingHistory;
[SET_PAYMENTS_HISTORY](state: PaymentsState, paymentsHistory: PaymentsHistoryItem[]): void {
state.paymentsHistory = paymentsHistory;
},
[SET_PROJECT_USAGE_AND_CHARGES](state: PaymentsState, usageAndCharges: ProjectUsageAndCharges[]): void {
state.usageAndCharges = usageAndCharges;
@ -151,7 +150,7 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
},
[CLEAR](state: PaymentsState) {
state.balance = new AccountBalance();
state.billingHistory = [];
state.paymentsHistory = [];
state.usageAndCharges = [];
state.priceSummary = 0;
state.creditCards = [];
@ -199,31 +198,14 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
[CLEAR_PAYMENT_INFO]: function({commit}: any): void {
commit(CLEAR);
},
[GET_BILLING_HISTORY]: async function({commit}: any): Promise<void> {
const billingHistory: BillingHistoryItem[] = await api.billingHistory();
[GET_PAYMENTS_HISTORY]: async function({commit}: any): Promise<void> {
const paymentsHistory: PaymentsHistoryItem[] = await api.paymentsHistory();
commit(SET_BILLING_HISTORY, billingHistory);
commit(SET_PAYMENTS_HISTORY, paymentsHistory);
},
[MAKE_TOKEN_DEPOSIT]: async function({commit}: any, amount: number): Promise<TokenDeposit> {
return await api.makeTokenDeposit(amount);
},
[GET_PROJECT_USAGE_AND_CHARGES]: async function({commit}: any, dateRange: DateRange): Promise<void> {
const now = new Date();
let beforeUTC = new Date(Date.UTC(dateRange.endDate.getUTCFullYear(), dateRange.endDate.getUTCMonth(), dateRange.endDate.getUTCDate(), 23, 59));
if (now.getUTCFullYear() === dateRange.endDate.getUTCFullYear() &&
now.getUTCMonth() === dateRange.endDate.getUTCMonth() &&
now.getUTCDate() <= dateRange.endDate.getUTCDate()) {
beforeUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes()));
}
const sinceUTC = new Date(Date.UTC(dateRange.startDate.getUTCFullYear(), dateRange.startDate.getUTCMonth(), dateRange.startDate.getUTCDate(), 0, 0));
const usageAndCharges: ProjectUsageAndCharges[] = await api.projectsUsageAndCharges(sinceUTC, beforeUTC);
commit(SET_DATE, dateRange);
commit(SET_PROJECT_USAGE_AND_CHARGES, usageAndCharges);
commit(SET_PRICE_SUMMARY, usageAndCharges);
},
[GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP]: async function({commit}: any): Promise<void> {
const now = new Date();
const endUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes()));
@ -249,23 +231,23 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
},
getters: {
canUserCreateFirstProject: (state: PaymentsState): boolean => {
return (state.billingHistory.some((billingItem: BillingHistoryItem) => {
return billingItem.amount >= 50 && billingItem.type === BillingHistoryItemType.Transaction
&& billingItem.status === BillingHistoryItemStatus.Completed;
return (state.paymentsHistory.some((paymentsItem: PaymentsHistoryItem) => {
return paymentsItem.amount >= 50 && paymentsItem.type === PaymentsHistoryItemType.Transaction
&& paymentsItem.status === PaymentsHistoryItemStatus.Completed;
}) && state.balance.sum > 0) || state.creditCards.length > 0;
},
isTransactionProcessing: (state: PaymentsState): boolean => {
return state.billingHistory.some((billingItem: BillingHistoryItem) => {
return billingItem.amount >= 50 && billingItem.type === BillingHistoryItemType.Transaction
&& (billingItem.status === BillingHistoryItemStatus.Pending
|| billingItem.status === BillingHistoryItemStatus.Paid
|| billingItem.status === BillingHistoryItemStatus.Completed);
return state.paymentsHistory.some((paymentsItem: PaymentsHistoryItem) => {
return paymentsItem.amount >= 50 && paymentsItem.type === PaymentsHistoryItemType.Transaction
&& (paymentsItem.status === PaymentsHistoryItemStatus.Pending
|| paymentsItem.status === PaymentsHistoryItemStatus.Paid
|| paymentsItem.status === PaymentsHistoryItemStatus.Completed);
}) && state.balance.sum === 0;
},
isTransactionCompleted: (state: PaymentsState): boolean => {
return (state.billingHistory.some((billingItem: BillingHistoryItem) => {
return billingItem.amount >= 50 && billingItem.type === BillingHistoryItemType.Transaction
&& billingItem.status === BillingHistoryItemStatus.Completed;
return (state.paymentsHistory.some((paymentsItem: PaymentsHistoryItem) => {
return paymentsItem.amount >= 50 && paymentsItem.type === PaymentsHistoryItemType.Transaction
&& paymentsItem.status === PaymentsHistoryItemStatus.Completed;
}) && state.balance.sum > 0);
},
},

View File

@ -55,12 +55,12 @@ export interface PaymentsApi {
makeCreditCardDefault(cardId: string): Promise<void>;
/**
* Returns a list of invoices, transactions and all others billing history items for payment account.
* Returns a list of invoices, transactions and all others payments history items for payment account.
*
* @returns list of billing history items
* @returns list of payments history items
* @throws Error
*/
billingHistory(): Promise<BillingHistoryItem[]>;
paymentsHistory(): Promise<PaymentsHistoryItem[]>;
/**
* Creates token transaction in CoinPayments
@ -103,7 +103,7 @@ export class PaymentAmountOption {
}
// BillingHistoryItem holds all public information about billing history line.
export class BillingHistoryItem {
export class PaymentsHistoryItem {
public constructor(
public readonly id: string = '',
public readonly description: string = '',
@ -113,11 +113,11 @@ export class BillingHistoryItem {
public readonly link: string = '',
public readonly start: Date = new Date(),
public readonly end: Date = new Date(),
public readonly type: BillingHistoryItemType = BillingHistoryItemType.Invoice,
public readonly type: PaymentsHistoryItemType = PaymentsHistoryItemType.Invoice,
) {}
public get quantity(): Amount {
if (this.type === BillingHistoryItemType.Transaction) {
if (this.type === PaymentsHistoryItemType.Transaction) {
return new Amount('USD $', this.amountDollars(this.amount), this.amountDollars(this.received));
}
@ -137,28 +137,30 @@ export class BillingHistoryItem {
return '';
}
const downloadLabel = this.type === BillingHistoryItemType.Transaction ? 'Checkout' : 'Invoice PDF';
const downloadLabel = this.type === PaymentsHistoryItemType.Transaction ? 'Checkout' : 'Invoice PDF';
return `<a class="download-link" target="_blank" href="${this.link}">${downloadLabel}</a>`;
}
}
/**
* BillingHistoryItemType indicates type of billing history item.
* PaymentsHistoryItemType indicates type of history item.
*/
export enum BillingHistoryItemType {
export enum PaymentsHistoryItemType {
// Invoice is a Stripe invoice billing item.
Invoice = 0,
// Transaction is a Coinpayments transaction billing item.
Transaction = 1,
// Charge is a credit card charge billing item.
Charge = 2,
// Coupon is a promotional coupon item.
Coupon = 3,
}
/**
* BillingHistoryStatusType indicates status of billing history item.
* PaymentsHistoryItemStatus indicates status of history item.
*/
export enum BillingHistoryItemStatus {
export enum PaymentsHistoryItemStatus {
/**
* Status showed if transaction successfully completed.
*/

View File

@ -61,7 +61,7 @@ const {
SETUP_ACCOUNT,
GET_BALANCE,
GET_CREDIT_CARDS,
GET_BILLING_HISTORY,
GET_PAYMENTS_HISTORY,
GET_PROJECT_USAGE_AND_CHARGES_CURRENT_ROLLUP,
GET_PROJECT_USAGE_AND_CHARGES_PREVIOUS_ROLLUP,
} = PAYMENTS_ACTIONS;
@ -119,9 +119,9 @@ export default class DashboardArea extends Vue {
}
try {
await this.$store.dispatch(GET_BILLING_HISTORY);
await this.$store.dispatch(GET_PAYMENTS_HISTORY);
} catch (error) {
await this.$notify.error(`Unable to get account billing history. ${error.message}`);
await this.$notify.error(`Unable to get account payments history. ${error.message}`);
}
try {

View File

@ -1,3 +1,4 @@
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="back-button-svg-path" fill-rule="evenodd" clip-rule="evenodd" d="M7.22572 0.300601C7.62652 0.701402 7.62652 1.35123 7.22572 1.75203L3.50406 5.47368H11.9737C12.5405 5.47368 13 5.93318 13 6.5C13 7.06682 12.5405 7.52632 11.9737 7.52632H3.50406L7.22572 11.248C7.62652 11.6488 7.62652 12.2986 7.22572 12.6994C6.82491 13.1002 6.17509 13.1002 5.77429 12.6994L0.300601 7.22571C-0.1002 6.82491 -0.1002 6.17509 0.300601 5.77428L5.77429 0.300601C6.17509 -0.1002 6.82491 -0.1002 7.22572 0.300601Z" fill="#384B65"/>
<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="back-button-svg-path" fill-rule="evenodd" clip-rule="evenodd" d="M7.66111 0.372773C8.11296 0.869804 8.11296 1.67565 7.66111 2.17268L3.27259 7L7.66111 11.8273C8.11296 12.3243 8.11296 13.1302 7.66111 13.6272C7.20926 14.1243 6.47666 14.1243 6.02481 13.6272L-3.0598e-07 7L6.02481 0.372773C6.47667 -0.124258 7.20926 -0.124258 7.66111 0.372773Z" fill="#768394"/>
</svg>

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 476 B

View File

@ -0,0 +1,4 @@
<svg width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.495399 5.75514C-0.165133 5.12956 -0.165133 4.1153 0.495399 3.48973C1.15593 2.86415 2.22687 2.86415 2.8874 3.48973L6.87406 7.26541C7.53459 7.89098 7.53459 8.90524 6.87406 9.53082C6.21353 10.1564 5.14259 10.1564 4.48206 9.53082L0.495399 5.75514Z" fill="#2683FF"/>
<path d="M6.87406 9.53082C6.21353 10.1564 5.14259 10.1564 4.48206 9.53082C3.82153 8.90524 3.82153 7.89098 4.48206 7.26541L11.6581 0.469182C12.3186 -0.156394 13.3895 -0.156394 14.0501 0.469182C14.7106 1.09476 14.7106 2.10902 14.0501 2.73459L6.87406 9.53082Z" fill="#2683FF"/>
</svg>

After

Width:  |  Height:  |  Size: 660 B

View File

@ -1,63 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import sinon from 'sinon';
import Vuex from 'vuex';
import BillingHistory from '@/components/account/billing/billingHistory/BillingHistory.vue';
import { PaymentsHttpApi } from '@/api/payments';
import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { makeProjectsModule, PROJECTS_MUTATIONS } from '@/store/modules/projects';
import { BillingHistoryItem, BillingHistoryItemType } from '@/types/payments';
import { Project } from '@/types/projects';
import { SegmentioPlugin } from '@/utils/plugins/segment';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { ProjectsApiMock } from '../../../mock/api/projects';
const localVue = createLocalVue();
const segmentioPlugin = new SegmentioPlugin();
const projectsApi = new ProjectsApiMock();
const projectsModule = makeProjectsModule(projectsApi);
const paymentsApi = new PaymentsHttpApi();
const paymentsModule = makePaymentsModule(paymentsApi);
const itemInvoice = new BillingHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Invoice);
const itemCharge = new BillingHistoryItem('testId1', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Charge);
const itemTransaction = new BillingHistoryItem('testId2', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Transaction);
const itemTransaction1 = new BillingHistoryItem('testId3', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Transaction);
const project = new Project('id', 'projectName', 'projectDescription', 'test', 'testOwnerId', false);
const clickSpy = sinon.spy();
localVue.use(Vuex);
localVue.use(segmentioPlugin);
const store = new Vuex.Store({ modules: { paymentsModule, projectsModule }});
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [itemCharge, itemInvoice, itemTransaction, itemTransaction1]);
store.commit(PROJECTS_MUTATIONS.SET_PROJECTS, [project]);
store.commit(PROJECTS_MUTATIONS.SELECT_PROJECT, project.id);
describe('BillingHistory', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(BillingHistory, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('click on back works correctly', async (): Promise<void> => {
const wrapper = shallowMount(BillingHistory, {
localVue,
store,
methods: {
onBackToAccountClick: clickSpy,
},
});
await wrapper.find('.billing-history-area__title-area').trigger('click');
expect(clickSpy.callCount).toBe(1);
});
});

View File

@ -1,61 +0,0 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import sinon from 'sinon';
import Vuex from 'vuex';
import DepositAndBilling from '@/components/account/billing/billingHistory/DepositAndBilling.vue';
import { PaymentsHttpApi } from '@/api/payments';
import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { BillingHistoryItem, BillingHistoryItemType } from '@/types/payments';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
const paymentsApi = new PaymentsHttpApi();
const paymentsModule = makePaymentsModule(paymentsApi);
const itemInvoice = new BillingHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Invoice);
const itemCharge = new BillingHistoryItem('testId1', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Charge);
const itemTransaction = new BillingHistoryItem('testId2', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Transaction);
const itemTransaction1 = new BillingHistoryItem('testId3', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Transaction);
const clickSpy = sinon.spy();
const store = new Vuex.Store({ modules: { paymentsModule }});
describe('DepositAndBilling', (): void => {
it('renders correctly without items', (): void => {
const wrapper = shallowMount(DepositAndBilling, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with items', (): void => {
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [itemCharge, itemInvoice, itemTransaction, itemTransaction1]);
const wrapper = shallowMount(DepositAndBilling, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('click on view all works correctly', async (): Promise<void> => {
const wrapper = shallowMount(DepositAndBilling, {
localVue,
store,
methods: {
onViewAllClick: clickSpy,
},
});
await wrapper.find('.button').trigger('click');
expect(clickSpy.callCount).toBe(1);
});
});

View File

@ -1,20 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BillingHistory renders correctly 1`] = `
<div class="billing-history-area">
<div class="billing-history-area__title-area">
<div class="billing-history-area__title-area__back-button">
<backimage-stub></backimage-stub>
</div>
<p class="billing-history-area__title-area__title">Back to Account</p>
</div>
<div class="billing-history-area__content">
<h1 class="billing-history-area__content__title">Billing History</h1>
<sortingheader-stub></sortingheader-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
</div>
</div>
`;

View File

@ -1,16 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DepositAndBilling renders correctly with items 1`] = `
<div class="deposit-and-billing-area">
<div class="deposit-and-billing-area__header">
<h1 class="deposit-and-billing-area__header__title">Deposit &amp; Billing History</h1>
<div class="button">View All</div>
</div>
<sortingheader-stub></sortingheader-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
<billingitem-stub billingitem="[object Object]"></billingitem-stub>
</div>
`;
exports[`DepositAndBilling renders correctly without items 1`] = ``;

View File

@ -0,0 +1,95 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import sinon from 'sinon';
import Vuex from 'vuex';
import DetailedHistory from '@/components/account/billing/depositAndBillingHistory/DetailedHistory.vue';
import { PaymentsHttpApi } from '@/api/payments';
import { router } from '@/router';
import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { makeProjectsModule, PROJECTS_MUTATIONS } from '@/store/modules/projects';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { Project } from '@/types/projects';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { ProjectsApiMock } from '../../../mock/api/projects';
const localVue = createLocalVue();
const projectsApi = new ProjectsApiMock();
const projectsModule = makeProjectsModule(projectsApi);
const paymentsApi = new PaymentsHttpApi();
const paymentsModule = makePaymentsModule(paymentsApi);
const itemInvoice = new PaymentsHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Invoice);
const itemCharge = new PaymentsHistoryItem('testId1', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Charge);
const itemTransaction = new PaymentsHistoryItem('testId2', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const itemTransaction1 = new PaymentsHistoryItem('testId3', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const project = new Project('id', 'projectName', 'projectDescription', 'test', 'testOwnerId', false);
const clickSpy = sinon.spy();
localVue.use(Vuex);
const store = new Vuex.Store({ modules: { paymentsModule, projectsModule }});
store.commit(PROJECTS_MUTATIONS.SET_PROJECTS, [project]);
store.commit(PROJECTS_MUTATIONS.SELECT_PROJECT, project.id);
describe('DetailedHistory', (): void => {
it('renders correctly without items', (): void => {
const wrapper = shallowMount(DetailedHistory, {
localVue,
store,
router,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with deposit items', (): void => {
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [itemTransaction, itemTransaction1, itemInvoice, itemCharge]);
const wrapper = shallowMount(DetailedHistory, {
localVue,
store,
router,
});
expect(wrapper).toMatchSnapshot();
});
// TODO: use when vue-test-utils $route mocking feature is implemented.
// it('renders correctly with billing items', (): void => {
// const $route = {
// path: RouteConfig.BillingHistory.path,
// };
//
// store.commit(PAYMENTS_MUTATIONS.CLEAR);
// store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [itemCharge, itemInvoice]);
//
// const wrapper = shallowMount(DetailedHistory, {
// localVue,
// store,
// router,
// mocks: {
// $route,
// },
// });
//
// expect(wrapper).toMatchSnapshot();
// });
it('click on back works correctly', async (): Promise<void> => {
const wrapper = shallowMount(DetailedHistory, {
localVue,
store,
router,
methods: {
onBackToBillingClick: clickSpy,
},
});
await wrapper.find('.history-area__back-area').trigger('click');
expect(clickSpy.callCount).toBe(1);
});
});

View File

@ -1,9 +1,9 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import BillingHistoryItemDate from '@/components/account/billing/billingHistory/BillingHistoryItemDate.vue';
import PaymentsHistoryItemDate from '@/components/account/billing/depositAndBillingHistory/PaymentsHistoryItemDate.vue';
import { BillingHistoryItemStatus, BillingHistoryItemType } from '@/types/payments';
import { PaymentsHistoryItemStatus, PaymentsHistoryItemType } from '@/types/payments';
import { createLocalVue, mount } from '@vue/test-utils';
const localVue = createLocalVue();
@ -15,17 +15,17 @@ localVue.filter('leadingZero', function (value: number): string {
return `${value}`;
});
describe('BillingHistoryItemDate', (): void => {
describe('PaymentsHistoryItemDate', (): void => {
it('renders correctly if invoice', (): void => {
const startDate = new Date(2019, 1, 1, 1, 1, 1, 1);
const expirationDate = new Date(0, 1, 1, 1, 1, 1, 1);
const wrapper = mount(BillingHistoryItemDate, {
const wrapper = mount(PaymentsHistoryItemDate, {
localVue,
propsData: {
expiration: expirationDate,
start: startDate,
type: BillingHistoryItemType.Invoice,
type: PaymentsHistoryItemType.Invoice,
},
});
@ -38,12 +38,12 @@ describe('BillingHistoryItemDate', (): void => {
const startDate = new Date(2019, 5, 5, 5, 5, 5, 5);
const expirationDate = new Date(0, 1, 1, 1, 1, 1, 1);
const wrapper = mount(BillingHistoryItemDate, {
const wrapper = mount(PaymentsHistoryItemDate, {
localVue,
propsData: {
expiration: expirationDate,
start: startDate,
type: BillingHistoryItemType.Charge,
type: PaymentsHistoryItemType.Charge,
},
});
@ -59,13 +59,13 @@ describe('BillingHistoryItemDate', (): void => {
spyOn(Date.prototype, 'getTime').and.returnValue(testTimeNow);
const wrapper = mount(BillingHistoryItemDate, {
const wrapper = mount(PaymentsHistoryItemDate, {
localVue,
propsData: {
expiration: expirationDate,
start: startDate,
type: BillingHistoryItemType.Transaction,
status: BillingHistoryItemStatus.Pending,
type: PaymentsHistoryItemType.Transaction,
status: PaymentsHistoryItemStatus.Pending,
},
});
@ -76,13 +76,13 @@ describe('BillingHistoryItemDate', (): void => {
const startDate = new Date(2019, 5, 6, 5, 5, 5, 5);
const expirationDate = new Date(2019, 5, 6, 6, 5, 5, 5);
const wrapper = mount(BillingHistoryItemDate, {
const wrapper = mount(PaymentsHistoryItemDate, {
localVue,
propsData: {
expiration: expirationDate,
start: startDate,
type: BillingHistoryItemType.Transaction,
status: BillingHistoryItemStatus.Completed,
type: PaymentsHistoryItemType.Transaction,
status: PaymentsHistoryItemStatus.Completed,
},
});

View File

@ -1,19 +1,19 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import BillingItem from '@/components/account/billing/billingHistory/BillingItem.vue';
import PaymentsItem from '@/components/account/billing/depositAndBillingHistory/PaymentsItem.vue';
import { BillingHistoryItem, BillingHistoryItemType } from '@/types/payments';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { createLocalVue, mount } from '@vue/test-utils';
const localVue = createLocalVue();
const itemInvoice = new BillingHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Invoice);
const itemCharge = new BillingHistoryItem('testId', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Charge);
const itemTransaction = new BillingHistoryItem('testId', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), BillingHistoryItemType.Transaction);
const itemInvoice = new PaymentsHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Invoice);
const itemCharge = new PaymentsHistoryItem('testId', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Charge);
const itemTransaction = new PaymentsHistoryItem('testId', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
describe('BillingItem', (): void => {
describe('PaymentsItem', (): void => {
it('renders correctly if invoice', (): void => {
const wrapper = mount(BillingItem, {
const wrapper = mount(PaymentsItem, {
localVue,
propsData: {
billingItem: itemInvoice,
@ -24,7 +24,7 @@ describe('BillingItem', (): void => {
});
it('renders correctly if charge', (): void => {
const wrapper = mount(BillingItem, {
const wrapper = mount(PaymentsItem, {
localVue,
propsData: {
billingItem: itemCharge,
@ -35,7 +35,7 @@ describe('BillingItem', (): void => {
});
it('renders correctly if transaction', (): void => {
const wrapper = mount(BillingItem, {
const wrapper = mount(PaymentsItem, {
localVue,
propsData: {
billingItem: itemTransaction,

View File

@ -0,0 +1,99 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import sinon from 'sinon';
import { VNode } from 'vue';
import { DirectiveBinding } from 'vue/types/options';
import Vuex from 'vuex';
import PeriodSelection from '@/components/account/billing/depositAndBillingHistory/PeriodSelection.vue';
import { makeProjectsModule, PROJECTS_MUTATIONS } from '@/store/modules/projects';
import { Project } from '@/types/projects';
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import { ProjectsApiMock } from '../../../mock/api/projects';
const localVue = createLocalVue();
const projectsApi = new ProjectsApiMock();
const projectsModule = makeProjectsModule(projectsApi);
const project = new Project('id', 'projectName', 'projectDescription', 'test', 'testOwnerId', false);
let clickOutsideEvent: EventListener;
localVue.directive('click-outside', {
bind: function (el: HTMLElement, binding: DirectiveBinding, vnode: VNode) {
clickOutsideEvent = function(event: Event): void {
if (el === event.target) {
return;
}
if (vnode.context) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', clickOutsideEvent);
},
unbind: function(): void {
document.body.removeEventListener('click', clickOutsideEvent);
},
});
localVue.use(Vuex);
const store = new Vuex.Store({ modules: { projectsModule }});
store.commit(PROJECTS_MUTATIONS.SET_PROJECTS, [project]);
store.commit(PROJECTS_MUTATIONS.SELECT_PROJECT, project.id);
describe('PeriodSelection', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(PeriodSelection, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with dropdown', async (): Promise<void> => {
const wrapper = mount(PeriodSelection, {
localVue,
store,
});
await wrapper.find('.period-selection').trigger('click');
expect(wrapper).toMatchSnapshot();
});
it('clicks work correctly', async (): Promise<void> => {
const currentClickSpy = sinon.spy();
const previousClickSpy = sinon.spy();
const historyClickSpy = sinon.spy();
const wrapper = mount(PeriodSelection, {
localVue,
store,
methods: {
onCurrentPeriodClick: currentClickSpy,
onPreviousPeriodClick: previousClickSpy,
redirect: historyClickSpy,
},
});
await wrapper.find('.period-selection').trigger('click');
await wrapper.findAll('.period-selection__dropdown__item').at(0).trigger('click');
expect(currentClickSpy.callCount).toBe(1);
await wrapper.find('.period-selection').trigger('click');
await wrapper.findAll('.period-selection__dropdown__item').at(1).trigger('click');
expect(previousClickSpy.callCount).toBe(1);
await wrapper.find('.period-selection').trigger('click');
await wrapper.find('.period-selection__dropdown__link-container').trigger('click');
expect(historyClickSpy.callCount).toBe(1);
});
});

View File

@ -0,0 +1,63 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import sinon from 'sinon';
import Vuex from 'vuex';
import SmallDepositHistory from '@/components/account/billing/depositAndBillingHistory/SmallDepositHistory.vue';
import { PaymentsHttpApi } from '@/api/payments';
import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { PaymentsHistoryItem, PaymentsHistoryItemType } from '@/types/payments';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
const paymentsApi = new PaymentsHttpApi();
const paymentsModule = makePaymentsModule(paymentsApi);
const itemInvoice = new PaymentsHistoryItem('testId', 'Invoice', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Invoice);
const itemCharge = new PaymentsHistoryItem('testId1', 'Charge', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Charge);
const itemTransaction = new PaymentsHistoryItem('testId2', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const itemTransaction1 = new PaymentsHistoryItem('testId3', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const itemTransaction2 = new PaymentsHistoryItem('testId4', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const itemTransaction3 = new PaymentsHistoryItem('testId5', 'Transaction', 500, 500, 'test', 'test', new Date(1), new Date(1), PaymentsHistoryItemType.Transaction);
const clickSpy = sinon.spy();
const store = new Vuex.Store({ modules: { paymentsModule }});
describe('SmallDepositHistory', (): void => {
it('renders correctly without items', (): void => {
const wrapper = shallowMount(SmallDepositHistory, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with items', (): void => {
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [itemCharge, itemInvoice, itemTransaction, itemTransaction1, itemTransaction2, itemTransaction3]);
const wrapper = shallowMount(SmallDepositHistory, {
localVue,
store,
});
expect(wrapper).toMatchSnapshot();
});
it('click on view all works correctly', async (): Promise<void> => {
const wrapper = shallowMount(SmallDepositHistory, {
localVue,
store,
methods: {
onViewAllClick: clickSpy,
},
});
await wrapper.find('.button').trigger('click');
expect(clickSpy.callCount).toBe(1);
});
});

View File

@ -1,7 +1,7 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import SortingHeader from '@/components/account/billing/billingHistory/SortingHeader.vue';
import SortingHeader from '@/components/account/billing/depositAndBillingHistory/SortingHeader.vue';
import { createLocalVue, mount } from '@vue/test-utils';

View File

@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DetailedHistory renders correctly with deposit items 1`] = `
<div class="history-area">
<div class="history-area__back-area">
<backimage-stub></backimage-stub>
<p class="history-area__back-area__title">Back to Billing</p>
</div>
<h1 class="history-area__title">Deposit History</h1>
<div class="history-area__content">
<sortingheader-stub></sortingheader-stub>
<paymentsitem-stub billingitem="[object Object]"></paymentsitem-stub>
<paymentsitem-stub billingitem="[object Object]"></paymentsitem-stub>
</div>
</div>
`;
exports[`DetailedHistory renders correctly without items 1`] = `
<div class="history-area">
<div class="history-area__back-area">
<backimage-stub></backimage-stub>
<p class="history-area__back-area__title">Back to Billing</p>
</div>
<h1 class="history-area__title">Deposit History</h1>
<h2 class="history-area__empty-state">No Items Yet</h2>
</div>
`;

View File

@ -1,24 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BillingHistoryItemDate renders correctly if charge 1`] = `
exports[`PaymentsHistoryItemDate renders correctly if charge 1`] = `
<div class="countdown-container">
<div>June 05, 2019</div>
</div>
`;
exports[`BillingHistoryItemDate renders correctly if invoice 1`] = `
exports[`PaymentsHistoryItemDate renders correctly if invoice 1`] = `
<div class="countdown-container">
<div>February 01, 2019</div>
</div>
`;
exports[`BillingHistoryItemDate renders correctly if transaction expired 1`] = `
exports[`PaymentsHistoryItemDate renders correctly if transaction expired 1`] = `
<div class="countdown-container">
<div>June 06, 2019</div>
</div>
`;
exports[`BillingHistoryItemDate renders correctly if transaction is not expired 1`] = `
exports[`PaymentsHistoryItemDate renders correctly if transaction is not expired 1`] = `
<div class="countdown-container">
<div class="row">
<p>Expires in </p>

View File

@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BillingItem renders correctly if charge 1`] = `
exports[`PaymentsItem renders correctly if charge 1`] = `
<div class="container">
<div class="countdown-container container__item">
<div class="countdown-container container__item date">
<div>January 01, 1970</div>
</div>
<p class="container__item description">Charge</p>
@ -18,9 +18,9 @@ exports[`BillingItem renders correctly if charge 1`] = `
</div>
`;
exports[`BillingItem renders correctly if invoice 1`] = `
exports[`PaymentsItem renders correctly if invoice 1`] = `
<div class="container">
<div class="countdown-container container__item">
<div class="countdown-container container__item date">
<div>January 01, 1970</div>
</div>
<p class="container__item description">Invoice</p>
@ -36,9 +36,9 @@ exports[`BillingItem renders correctly if invoice 1`] = `
</div>
`;
exports[`BillingItem renders correctly if transaction 1`] = `
exports[`PaymentsItem renders correctly if transaction 1`] = `
<div class="container">
<div class="countdown-container container__item">
<div class="countdown-container container__item date">
<div>January 01, 1970</div>
</div>
<p class="container__item description">Transaction</p>

View File

@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PeriodSelection renders correctly 1`] = `
<div class="period-selection">
<div class="period-selection__current-choice">
<div class="period-selection__current-choice__label-area">
<datepickericon-stub></datepickericon-stub> <span class="period-selection__current-choice__label-area__label">Current Billing Period</span>
</div>
<expandicon-stub></expandicon-stub>
</div>
<!---->
</div>
`;
exports[`PeriodSelection renders correctly with dropdown 1`] = `
<div class="period-selection">
<div class="period-selection__current-choice">
<div class="period-selection__current-choice__label-area"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.3213 2.28026H14.8009V1.50058C14.8009 1.10058 14.4806 0.760742 14.0611 0.760742C13.6611 0.760742 13.3213 1.08106 13.3213 1.50058V2.28026H6.66106V1.50058C6.66106 1.10058 6.34074 0.760742 5.92122 0.760742C5.5009 0.760742 5.2009 1.10058 5.2009 1.50058V2.28026H3.68058C1.92042 2.28026 0.500977 3.70058 0.500977 5.45986V16.0599C0.500977 17.82 1.9213 19.2395 3.68058 19.2395H16.3204C18.0805 19.2395 19.5 17.8191 19.5 16.0599V5.45986C19.5008 3.70048 18.0804 2.28026 16.321 2.28026H16.3213ZM3.68066 3.74042H5.20098V4.5201C5.20098 4.9201 5.5213 5.25994 5.94082 5.25994C6.36114 5.25994 6.68066 4.93962 6.68066 4.5201V3.74042H13.3603V4.5201C13.3603 4.9201 13.6806 5.25994 14.1001 5.25994C14.5001 5.25994 14.8399 4.93962 14.8399 4.5201V3.74042H16.3603C17.3001 3.74042 18.0806 4.50058 18.0806 5.46074V7.06074H1.96098V5.46074C1.96098 4.5209 2.74066 3.74042 3.68052 3.74042H3.68066ZM9.62126 14.2006H10.4009C11.0213 14.2006 11.5213 14.7006 11.5213 15.3209C11.5213 15.9413 11.0213 16.4413 10.4009 16.4413H9.62126C9.00094 16.4413 8.50094 15.9413 8.50094 15.3209C8.50094 14.7006 9.00094 14.2006 9.62126 14.2006ZM8.50094 10.8404C8.50094 10.2201 9.00094 9.7201 9.62126 9.7201L10.4009 9.72088C11.0213 9.72088 11.5213 10.2209 11.5213 10.8412C11.5213 11.4615 11.0213 11.9615 10.4009 11.9615H9.62126C9.00094 11.9607 8.50094 11.4607 8.50094 10.8404V10.8404ZM14.8407 14.2006H15.6204C16.2407 14.2006 16.7407 14.7006 16.7407 15.3209C16.7407 15.9413 16.2407 16.4413 15.6204 16.4413H14.8407C14.2204 16.4413 13.7204 15.9413 13.7204 15.3209C13.7212 14.7006 14.2212 14.2006 14.8407 14.2006ZM13.7212 10.8404C13.7212 10.2201 14.2212 9.7201 14.8415 9.7201H15.6212C16.2415 9.7201 16.7415 10.2201 16.7415 10.8404C16.7415 11.4607 16.2415 11.9607 15.6212 11.9607H14.8415C14.2212 11.9607 13.7212 11.4607 13.7212 10.8404ZM6.2806 10.8404C6.2806 11.4607 5.7806 11.9607 5.16028 11.9607H4.3806C3.76028 11.9607 3.26028 11.4607 3.26028 10.8404C3.26028 10.2201 3.76028 9.7201 4.3806 9.7201H5.16028C5.7806 9.72088 6.2806 10.2209 6.2806 10.8404ZM4.3806 14.2006H5.16028C5.7806 14.2006 6.2806 14.7006 6.2806 15.3209C6.2806 15.9413 5.7806 16.4413 5.16028 16.4413H4.3806C3.76028 16.4413 3.26028 15.9413 3.26028 15.3209C3.26106 14.7006 3.76106 14.2006 4.3806 14.2006Z" fill="#2683FF" class="date-picker-svg-path"></path>
</svg> <span class="period-selection__current-choice__label-area__label">Current Billing Period</span></div> <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="M13.6272 7.66111C13.1302 8.11296 12.3243 8.11296 11.8273 7.66111L7 3.27259L2.17268 7.66111C1.67565 8.11296 0.869804 8.11296 0.372774 7.66111C-0.124258 7.20926 -0.124258 6.47666 0.372774 6.02481L7 -6.11959e-07L13.6272 6.02481C14.1243 6.47667 14.1243 7.20926 13.6272 7.66111Z" fill="#2683FF"></path>
</svg>
</div>
<div class="period-selection__dropdown">
<div class="period-selection__dropdown__item"><svg width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg" class="selected-image">
<path d="M0.495399 5.75514C-0.165133 5.12956 -0.165133 4.1153 0.495399 3.48973C1.15593 2.86415 2.22687 2.86415 2.8874 3.48973L6.87406 7.26541C7.53459 7.89098 7.53459 8.90524 6.87406 9.53082C6.21353 10.1564 5.14259 10.1564 4.48206 9.53082L0.495399 5.75514Z" fill="#2683FF"></path>
<path d="M6.87406 9.53082C6.21353 10.1564 5.14259 10.1564 4.48206 9.53082C3.82153 8.90524 3.82153 7.89098 4.48206 7.26541L11.6581 0.469182C12.3186 -0.156394 13.3895 -0.156394 14.0501 0.469182C14.7106 1.09476 14.7106 2.10902 14.0501 2.73459L6.87406 9.53082Z" fill="#2683FF"></path>
</svg> <span class="period-selection__dropdown__item__label">Current Billing Period</span></div>
<div class="period-selection__dropdown__item">
<!----> <span class="period-selection__dropdown__item__label">Previous Billing Period</span></div>
<div class="period-selection__dropdown__link-container"><span class="period-selection__dropdown__link-container__link">Billing History</span></div>
</div>
</div>
`;

View File

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SmallDepositHistory renders correctly with items 1`] = `
<div class="deposit-area">
<div class="deposit-area__header">
<h1 class="deposit-area__header__title">Deposit History</h1>
<div class="button">View All</div>
</div>
<sortingheader-stub></sortingheader-stub>
<paymentsitem-stub billingitem="[object Object]"></paymentsitem-stub>
<paymentsitem-stub billingitem="[object Object]"></paymentsitem-stub>
<paymentsitem-stub billingitem="[object Object]"></paymentsitem-stub>
</div>
`;
exports[`SmallDepositHistory renders correctly without items 1`] = ``;

View File

@ -3,19 +3,19 @@
exports[`SortingHeader renders correctly 1`] = `
<div class="sort-header-container">
<div class="sort-header-container__item date">
<p class="sort-header-container__item__name">Date</p>
<p class="sort-header-container__item__name">DATE</p>
</div>
<div class="sort-header-container__item description">
<p class="sort-header-container__item__name">Description</p>
<p class="sort-header-container__item__name">DESCRIPTION</p>
</div>
<div class="sort-header-container__item status">
<p class="sort-header-container__item__name">Status</p>
<p class="sort-header-container__item__name">STATUS</p>
</div>
<div class="sort-header-container__item amount">
<p class="sort-header-container__item__name">Amount</p>
<p class="sort-header-container__item__name">AMOUNT</p>
</div>
<div class="sort-header-container__item download">
<p class="sort-header-container__item__name">Download</p>
<p class="sort-header-container__item__name">DOWNLOAD</p>
</div>
</div>
`;

View File

@ -11,7 +11,7 @@ import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments
import { makeProjectsModule, PROJECTS_MUTATIONS } from '@/store/modules/projects';
import { makeUsersModule, USER_MUTATIONS } from '@/store/modules/users';
import { NOTIFICATION_MUTATIONS } from '@/store/mutationConstants';
import { BillingHistoryItem, BillingHistoryItemStatus, BillingHistoryItemType } from '@/types/payments';
import { PaymentsHistoryItem, PaymentsHistoryItemStatus, PaymentsHistoryItemType } from '@/types/payments';
import { Project } from '@/types/projects';
import { User } from '@/types/users';
import { Notificator } from '@/utils/plugins/notificator';
@ -76,11 +76,11 @@ describe('AddStorjForm', () => {
it('user is able to add less than 10$ after coupon is applied', async () => {
window.open = jest.fn();
const billingTransactionItem = new BillingHistoryItem('itemId', 'test', 10, 10,
BillingHistoryItemStatus.Completed, 'test', new Date(), new Date(), BillingHistoryItemType.Transaction);
const billingTransactionItem = new PaymentsHistoryItem('itemId', 'test', 10, 10,
PaymentsHistoryItemStatus.Completed, 'test', new Date(), new Date(), PaymentsHistoryItemType.Transaction);
const project = new Project('testId', 'test', 'test', 'test', 'id', true);
store.commit(NOTIFICATION_MUTATIONS.CLEAR);
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [billingTransactionItem]);
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [billingTransactionItem]);
store.commit(PROJECTS_MUTATIONS.ADD, project);
const wrapper = mount(AddStorjForm, {
store,

View File

@ -13,10 +13,10 @@ import { makeUsersModule, USER_MUTATIONS } from '@/store/modules/users';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import {
AccountBalance,
BillingHistoryItem,
BillingHistoryItemStatus,
BillingHistoryItemType,
CreditCard,
PaymentsHistoryItem,
PaymentsHistoryItemStatus,
PaymentsHistoryItemType,
} from '@/types/payments';
import { Project } from '@/types/projects';
import { User } from '@/types/users';
@ -70,10 +70,10 @@ describe('NewProjectArea', () => {
});
it('renders correctly without projects and with completed 50$ transaction', () => {
const billingTransactionItem = new BillingHistoryItem('itemId', 'test', 50, 50,
BillingHistoryItemStatus.Completed, 'test', new Date(), new Date(), BillingHistoryItemType.Transaction);
const billingTransactionItem = new PaymentsHistoryItem('itemId', 'test', 50, 50,
PaymentsHistoryItemStatus.Completed, 'test', new Date(), new Date(), PaymentsHistoryItemType.Transaction);
store.commit(PAYMENTS_MUTATIONS.CLEAR);
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [billingTransactionItem]);
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [billingTransactionItem]);
store.commit(PAYMENTS_MUTATIONS.SET_BALANCE, new AccountBalance(0, 5000));
const wrapper = mount(NewProjectArea, {

View File

@ -3,9 +3,9 @@
import {
AccountBalance,
BillingHistoryItem,
CreditCard,
PaymentsApi,
PaymentsHistoryItem,
ProjectUsageAndCharges,
TokenDeposit,
} from '@/types/payments';
@ -42,7 +42,7 @@ export class PaymentsMock implements PaymentsApi {
throw new Error('Method not implemented');
}
billingHistory(): Promise<BillingHistoryItem[]> {
paymentsHistory(): Promise<PaymentsHistoryItem[]> {
return Promise.resolve([]);
}

View File

@ -6,7 +6,12 @@ import Vuex from 'vuex';
import AddStorjState from '@/components/onboardingTour/steps/paymentStates/AddStorjState.vue';
import { makePaymentsModule, PAYMENTS_MUTATIONS } from '@/store/modules/payments';
import { AccountBalance, BillingHistoryItem, BillingHistoryItemStatus, BillingHistoryItemType } from '@/types/payments';
import {
AccountBalance,
PaymentsHistoryItem,
PaymentsHistoryItemStatus,
PaymentsHistoryItemType,
} from '@/types/payments';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { PaymentsMock } from '../../../mock/api/payments';
@ -29,9 +34,9 @@ describe('AddStorjState.vue', () => {
});
it('renders correctly with pending transaction', (): void => {
const billingTransactionItem = new BillingHistoryItem('itemId', 'test', 50, 50,
BillingHistoryItemStatus.Pending, 'test', new Date(), new Date(), BillingHistoryItemType.Transaction);
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [billingTransactionItem]);
const billingTransactionItem = new PaymentsHistoryItem('itemId', 'test', 50, 50,
PaymentsHistoryItemStatus.Pending, 'test', new Date(), new Date(), PaymentsHistoryItemType.Transaction);
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [billingTransactionItem]);
const wrapper = shallowMount(AddStorjState, {
store,
localVue,
@ -41,9 +46,9 @@ describe('AddStorjState.vue', () => {
});
it('renders correctly with completed transaction', (): void => {
const billingTransactionItem = new BillingHistoryItem('itemId', 'test', 50, 50,
BillingHistoryItemStatus.Completed, 'test', new Date(), new Date(), BillingHistoryItemType.Transaction);
store.commit(PAYMENTS_MUTATIONS.SET_BILLING_HISTORY, [billingTransactionItem]);
const billingTransactionItem = new PaymentsHistoryItem('itemId', 'test', 50, 50,
PaymentsHistoryItemStatus.Completed, 'test', new Date(), new Date(), PaymentsHistoryItemType.Transaction);
store.commit(PAYMENTS_MUTATIONS.SET_PAYMENTS_HISTORY, [billingTransactionItem]);
store.commit(PAYMENTS_MUTATIONS.SET_BALANCE, new AccountBalance(275, 5000));
const wrapper = shallowMount(AddStorjState, {
store,