web/satellite: billing periods logic implemented on billing page

Change-Id: Ia762f50e0b700fe17258efcd255c16d635edd003
This commit is contained in:
VitaliiShpital 2020-03-13 20:14:41 +02:00 committed by Vitalii Shpital
parent 5dc1b7db90
commit dedb87a6dd
19 changed files with 562 additions and 273 deletions

View File

@ -3,7 +3,28 @@
<template> <template>
<div class="account-billing-area"> <div class="account-billing-area">
<h1 class="account-billing-area__title">Billing</h1> <div class="account-billing-area__title-area">
<h1 class="account-billing-area__title-area__title">Billing</h1>
<div class="account-billing-area__title-area__options-area" v-if="areBillingPeriodsVisible">
<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>
</div>
<div class="account-billing-area__notification-container" v-if="hasNoCreditCard"> <div class="account-billing-area__notification-container" v-if="hasNoCreditCard">
<div class="account-billing-area__notification-container__negative-balance" v-if="isBalanceNegative"> <div class="account-billing-area__notification-container__negative-balance" v-if="isBalanceNegative">
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -20,10 +41,7 @@
<p class="account-billing-area__notification-container__low-balance__text">Your account balance is running low. Please add STORJ Tokens or a debit/credit card to prevent data loss.</p> <p class="account-billing-area__notification-container__low-balance__text">Your account balance is running low. Please add STORJ Tokens or a debit/credit card to prevent data loss.</p>
</div> </div>
</div> </div>
<div v-if="areBalanceAndSummaryVisible"> <EstimatedCostsAndCredits v-if="isSummaryVisible"/>
<AccountBalance/>
<MonthlyBillingSummary/>
</div>
<PaymentMethods/> <PaymentMethods/>
<DepositAndBilling/> <DepositAndBilling/>
</div> </div>
@ -32,28 +50,54 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import AccountBalance from '@/components/account/billing/balance/AccountBalance.vue';
import DepositAndBilling from '@/components/account/billing/billingHistory/DepositAndBilling.vue'; import DepositAndBilling from '@/components/account/billing/billingHistory/DepositAndBilling.vue';
import MonthlyBillingSummary from '@/components/account/billing/monthlySummary/MonthlyBillingSummary.vue'; import EstimatedCostsAndCredits from '@/components/account/billing/estimatedCostsAndCredits/EstimatedCostsAndCredits.vue';
import PaymentMethods from '@/components/account/billing/paymentMethods/PaymentMethods.vue'; import PaymentMethods from '@/components/account/billing/paymentMethods/PaymentMethods.vue';
import VDatepicker from '@/components/common/VDatePicker.vue';
import DatePickerIcon from '@/../static/images/project/datePicker.svg';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { PROJECTS_ACTIONS } from '@/store/modules/projects'; import { PROJECTS_ACTIONS } from '@/store/modules/projects';
import { DateRange } from '@/types/usage';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { ProjectOwning } from '@/utils/projectOwning'; 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({ @Component({
components: { components: {
AccountBalance, EstimatedCostsAndCredits,
MonthlyBillingSummary,
DepositAndBilling, DepositAndBilling,
PaymentMethods, PaymentMethods,
VDatepicker,
DatePickerIcon,
}, },
}) })
export default class BillingArea extends Vue { export default class BillingArea extends Vue {
/** /**
* Mounted lifecycle hook after initial render. * Mounted lifecycle hook after initial render.
* Fetches project limits. * Fetches project limits and current usage rollup.
*/ */
public async mounted(): Promise<void> { public async mounted(): Promise<void> {
try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_CHARGES_CURRENT_ROLLUP);
} catch (error) {
await this.$notify.error(error.message);
}
if (!this.$store.getters.selectedProject.id) { if (!this.$store.getters.selectedProject.id) {
return; return;
} }
@ -72,9 +116,59 @@ export default class BillingArea extends Vue {
private readonly CRITICAL_AMOUNT: number = 1000; private readonly CRITICAL_AMOUNT: number = 1000;
/** /**
* Indicates if balance and summary components are visible. * Holds start and end dates.
*/ */
public get areBalanceAndSummaryVisible(): boolean { 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_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;
};
/**
* Indicates if isEstimatedCostsAndCredits component is visible.
*/
public get isSummaryVisible(): boolean {
const isBalancePositive: boolean = this.$store.state.paymentsModule.balance > 0; const isBalancePositive: boolean = this.$store.state.paymentsModule.balance > 0;
return isBalancePositive || new ProjectOwning(this.$store).userHasOwnProject(); return isBalancePositive || new ProjectOwning(this.$store).userHasOwnProject();
@ -100,6 +194,123 @@ export default class BillingArea extends Vue {
public get isBalanceLow(): boolean { public get isBalanceLow(): boolean {
return this.$store.state.paymentsModule.balance > 0 && this.$store.state.paymentsModule.balance < this.CRITICAL_AMOUNT; return this.$store.state.paymentsModule.balance > 0 && this.$store.state.paymentsModule.balance < this.CRITICAL_AMOUNT;
} }
/**
* Indicates if billing periods logic is visible.
*/
public get areBillingPeriodsVisible(): 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_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_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_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> </script>
@ -107,12 +318,56 @@ export default class BillingArea extends Vue {
.account-billing-area { .account-billing-area {
padding-bottom: 55px; padding-bottom: 55px;
&__title { &__title-area {
font-family: 'font_bold', sans-serif; display: flex;
font-size: 32px; align-items: center;
line-height: 39px; justify-content: space-between;
color: #384b65; margin: 60px 0 35px 0;
margin: 60px 0 0 0;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 39px;
color: #384b65;
}
&__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 { &__notification-container {
@ -145,4 +400,13 @@ export default class BillingArea extends Vue {
} }
} }
} }
.datepicker {
padding: 12px;
}
/deep/ .datepickbox {
max-height: 0;
max-width: 0;
}
</style> </style>

View File

@ -1,160 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="account-balance-area">
<div class="account-balance-area__title-area">
<h1 class="account-balance-area__title-area__title">Account Balance</h1>
<VInfo
class="account-balance-area__title-area__info-button"
bold-text="Prepaid STORJ token amount and any additional credits.">
<svg class="account-balance-area__title-area__info-button__image" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect class="account-balance-svg-rect" x="0.5" y="0.5" width="19" height="19" rx="9.5" stroke="#AFB7C1"/>
<path class="account-balance-svg-path" d="M7 7.25177C7.00959 6.23527 7.28777 5.44177 7.83453 4.87129C8.38129 4.29043 9.1199 4 10.0504 4C10.952 4 11.6667 4.22819 12.1942 4.68458C12.7314 5.14097 13 5.79444 13 6.64498C13 7.03913 12.9376 7.38661 12.8129 7.68741C12.6882 7.98821 12.5396 8.24234 12.3669 8.44979C12.1942 8.65724 11.9592 8.90099 11.6619 9.18105C11.2686 9.54408 10.9712 9.876 10.7698 10.1768C10.5779 10.4672 10.482 10.8303 10.482 11.2659H9.04317C9.04317 10.851 9.10072 10.488 9.21583 10.1768C9.33094 9.86563 9.46523 9.6115 9.61871 9.41443C9.78177 9.20698 10.0024 8.96841 10.2806 8.69873C10.6067 8.37718 10.8465 8.09712 11 7.85856C11.1535 7.61999 11.2302 7.31919 11.2302 6.95615C11.2302 6.55163 11.1103 6.25082 10.8705 6.05375C10.6403 5.8463 10.3141 5.74257 9.89209 5.74257C9.45084 5.74257 9.10552 5.87223 8.85611 6.13154C8.60671 6.38048 8.47242 6.75389 8.45324 7.25177H7ZM9.73381 12.7595C10.0216 12.7595 10.2566 12.8633 10.4388 13.0707C10.6307 13.2782 10.7266 13.5427 10.7266 13.8642C10.7266 14.1961 10.6307 14.471 10.4388 14.6888C10.2566 14.8963 10.0216 15 9.73381 15C9.45564 15 9.22062 14.8911 9.02878 14.6733C8.84652 14.4554 8.7554 14.1858 8.7554 13.8642C8.7554 13.5427 8.84652 13.2782 9.02878 13.0707C9.22062 12.8633 9.45564 12.7595 9.73381 12.7595Z" fill="#354049"/>
</svg>
</VInfo>
</div>
<div class="account-balance-area__balance-area">
<span class="account-balance-area__balance-area__balance">
Balance
<b
class="account-balance-area__balance-area__balance__bold-text"
:style="{color: balanceColor}"
>
{{ balance | centsToDollars}}
</b>
</span>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VInfo from '@/components/common/VInfo.vue';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { Project } from '@/types/projects';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { ProjectOwning } from '@/utils/projectOwning';
@Component({
components: {
VInfo,
},
})
export default class AccountBalance extends Vue {
/**
* Lifecycle hook after initial render where balance is fetched.
*/
public mounted() {
try {
this.$store.dispatch(PAYMENTS_ACTIONS.GET_BALANCE);
} catch (error) {
this.$notify.error(error.message);
}
if (this.balance > 0 && !new ProjectOwning(this.$store).userHasOwnProject()) {
this.$store.dispatch(APP_STATE_ACTIONS.SHOW_CREATE_PROJECT_BUTTON);
}
}
/**
* Returns balance from store in cents.
*/
public get balance(): number {
return this.$store.state.paymentsModule.balance;
}
/**
* Returns balance color red if balance below zero and clack if not.
*/
public get balanceColor(): string {
return this.$store.state.paymentsModule.balance < 0 ? '#FF0000' : '#000';
}
}
</script>
<style scoped lang="scss">
h1,
span {
margin: 0;
color: #354049;
}
.account-balance-area {
display: flex;
align-items: center;
justify-content: space-between;
padding: 40px;
margin: 32px 0;
background-color: #fff;
border-radius: 8px;
font-family: 'font_regular', sans-serif;
&__title-area {
display: flex;
align-items: center;
&__title {
font-family: 'font_bold', sans-serif;
font-size: 32px;
line-height: 48px;
margin-right: 13px;
}
&__info-button {
max-height: 20px;
cursor: pointer;
margin-right: 10px;
&:hover {
.account-balance-svg-path {
fill: #fff;
}
.account-balance-svg-rect {
fill: #2683ff;
}
}
}
}
&__balance-area {
display: flex;
align-items: center;
&__balance {
font-size: 18px;
color: rgba(53, 64, 73, 0.5);
&__bold-text {
color: #354049;
}
}
}
}
/deep/ .info__message-box {
background-image: url('../../../../../static/images/account/billing/MessageBox.png');
background-repeat: no-repeat;
min-height: 80px;
min-width: 195px;
top: 110%;
left: -200%;
padding: 0 20px 12px 20px;
&__text {
text-align: left;
font-size: 13px;
line-height: 17px;
margin-top: 20px;
&__bold-text {
font-family: 'font_medium', sans-serif;
color: #354049;
}
}
}
</style>

View File

@ -82,8 +82,8 @@ export default class DepositAndBilling extends Vue {
font-family: 'font_bold', sans-serif; font-family: 'font_bold', sans-serif;
&__title { &__title {
font-size: 32px; font-size: 28px;
line-height: 48px; line-height: 42px;
} }
.button { .button {

View File

@ -3,26 +3,20 @@
<template> <template>
<div class="current-month-area"> <div class="current-month-area">
<div class="current-month-area__header"> <div class="current-month-area__title-area">
<div class="current-month-area__header__month-info"> <h1 class="current-month-area__title-area__title">Estimated Costs for This Billing Period</h1>
<h1 class="current-month-area__header__month-info__title">Current Month</h1> <span class="current-month-area__title-area__costs">{{ chargesSummary | centsToDollars }}</span>
<h2 class="current-month-area__header__month-info__title-info">{{ currentPeriod }}</h2>
</div>
</div> </div>
<div class="current-month-area__content"> <div class="current-month-area__content">
<h2 class="current-month-area__content__title">DETAILED SUMMARY</h2> <h2 class="current-month-area__content__title">DETAILS</h2>
<div class="current-month-area__content__usage-charges" @click="toggleUsageChargesPopup"> <div class="current-month-area__content__usage-charges" @click="toggleUsageChargesPopup">
<div class="current-month-area__content__usage-charges__head"> <div class="current-month-area__content__usage-charges__head">
<div class="current-month-area__content__usage-charges__head__name"> <div class="current-month-area__content__usage-charges__head__name-area">
<div class="current-month-area__content__usage-charges__head__name__image-container" v-if="usageCharges.length > 0"> <div class="current-month-area__content__usage-charges__head__name-area__image-container" v-if="usageCharges.length > 0">
<svg class="current-month-area__content__usage-charges__head__name__image-container__image" v-if="!areUsageChargesShown" width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <ArrowRightIcon v-if="!areUsageChargesShown"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"/> <ArrowDownIcon v-if="areUsageChargesShown"/>
</svg>
<svg class="current-month-area__content__usage-charges__head__name__image-container__image" v-if="areUsageChargesShown" width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.372773 0.338888C0.869804 -0.112963 1.67565 -0.112963 2.17268 0.338888L7 4.72741L11.8273 0.338888C12.3243 -0.112963 13.1302 -0.112963 13.6272 0.338888C14.1243 0.790739 14.1243 1.52333 13.6272 1.97519L7 8L0.372773 1.97519C-0.124258 1.52333 -0.124258 0.790739 0.372773 0.338888Z" fill="#2683FF"/>
</svg>
</div> </div>
<span class="current-month-area__content__usage-charges__head__name__title">Usage Charges</span> <span class="current-month-area__content__usage-charges__head__name-area__title">Usage Charges</span>
</div> </div>
<span>Estimated total <span class="summary">{{ chargesSummary | centsToDollars }}</span></span> <span>Estimated total <span class="summary">{{ chargesSummary | centsToDollars }}</span></span>
</div> </div>
@ -35,6 +29,17 @@
/> />
</div> </div>
</div> </div>
<div class="current-month-area__content__credits-area">
<div class="current-month-area__content__credits-area__title-area">
<span class="current-month-area__content__credits-area__title-area__title">Earned Credits</span>
</div>
<span
:style="{ color: balanceColor }"
class="current-month-area__content__credits-area__balance"
>
{{ balance | centsToDollars }}
</span>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -42,28 +47,40 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import UsageChargeItem from '@/components/account/billing/monthlySummary/UsageChargeItem.vue'; import UsageChargeItem from '@/components/account/billing/estimatedCostsAndCredits/UsageChargeItem.vue';
import VButton from '@/components/common/VButton.vue'; import VButton from '@/components/common/VButton.vue';
import ArrowRightIcon from '@/../static/images/common/BlueArrowRight.svg';
import ArrowDownIcon from '@/../static/images/common/BlueExpand.svg';
import { PAYMENTS_ACTIONS } from '@/store/modules/payments'; import { PAYMENTS_ACTIONS } from '@/store/modules/payments';
import { ProjectCharge } from '@/types/payments'; import { ProjectCharge } from '@/types/payments';
import { APP_STATE_ACTIONS } from '@/utils/constants/actionNames';
import { ProjectOwning } from '@/utils/projectOwning';
@Component({ @Component({
components: { components: {
VButton, VButton,
UsageChargeItem, UsageChargeItem,
ArrowRightIcon,
ArrowDownIcon,
}, },
}) })
export default class MonthlyBillingSummary extends Vue { export default class EstimatedCostsAndCredits extends Vue {
/** /**
* Lifecycle hook after initial render. * Lifecycle hook after initial render.
* Fetches current project usage rollup. * Fetches current project usage rollup.
*/ */
public async mounted(): Promise<void> { public async mounted(): Promise<void> {
try { try {
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_BALANCE);
await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_CHARGES_CURRENT_ROLLUP); await this.$store.dispatch(PAYMENTS_ACTIONS.GET_PROJECT_CHARGES_CURRENT_ROLLUP);
} catch (error) { } catch (error) {
await this.$notify.error(`Unable to fetch project usage. ${error.message}`); await this.$notify.error(error.message);
}
if (this.balance > 0 && !new ProjectOwning(this.$store).userHasOwnProject()) {
await this.$store.dispatch(APP_STATE_ACTIONS.SHOW_CREATE_PROJECT_BUTTON);
} }
} }
@ -79,23 +96,6 @@ export default class MonthlyBillingSummary extends Vue {
return this.$store.state.paymentsModule.charges; return this.$store.state.paymentsModule.charges;
} }
/**
* String representation of current billing period dates range.
*/
public get currentPeriod(): string {
const months: string[] = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const now: Date = new Date();
const monthNumber = now.getMonth();
const date = now.getDate();
const year = now.getFullYear();
if (date === 1) {
return `${months[monthNumber]} 1 ${year}`;
}
return `${months[monthNumber]} 1 - ${date} ${year}`;
}
/** /**
* chargesSummary returns summary of all projects. * chargesSummary returns summary of all projects.
*/ */
@ -109,6 +109,20 @@ export default class MonthlyBillingSummary extends Vue {
return usageItemSummaries.reduce((accumulator, current) => accumulator + current); return usageItemSummaries.reduce((accumulator, current) => accumulator + current);
} }
/**
* Returns balance from store in cents.
*/
public get balance(): number {
return this.$store.state.paymentsModule.balance;
}
/**
* Returns balance color red if balance below zero and clack if not.
*/
public get balanceColor(): string {
return this.$store.state.paymentsModule.balance < 0 ? '#FF0000' : '#000';
}
/** /**
* toggleUsageChargesPopup is used to open/close area with list of project charges. * toggleUsageChargesPopup is used to open/close area with list of project charges.
*/ */
@ -138,38 +152,19 @@ export default class MonthlyBillingSummary extends Vue {
border-radius: 8px; border-radius: 8px;
font-family: 'font_regular', sans-serif; font-family: 'font_regular', sans-serif;
&__header { &__title-area {
display: flex; display: flex;
align-items: center;
justify-content: space-between; justify-content: space-between;
align-items: center;
padding-bottom: 40px;
border-bottom: 1px solid rgba(169, 181, 193, 0.3);
&__month-info { &__title,
&__costs {
&__title { font-size: 28px;
font-family: 'font_bold', sans-serif; line-height: 42px;
font-size: 32px; font-family: 'font_bold', sans-serif;
line-height: 48px; color: #354049;
}
&__title-info {
font-size: 18px;
user-select: text;
}
}
&__usage-info {
display: flex;
align-items: center;
&__data {
margin-right: 27px;
color: rgba(53, 64, 73, 0.5);
font-size: 18px;
&__bold-text {
color: #354049;
}
}
} }
} }
@ -177,9 +172,11 @@ export default class MonthlyBillingSummary extends Vue {
margin-top: 20px; margin-top: 20px;
&__title { &__title {
font-size: 14px; font-size: 16px;
line-height: 21px; line-height: 23px;
color: #afb7c1; letter-spacing: 0.04em;
text-transform: uppercase;
color: #919191;
} }
&__usage-charges { &__usage-charges {
@ -195,7 +192,7 @@ export default class MonthlyBillingSummary extends Vue {
align-items: center; align-items: center;
padding: 20px; padding: 20px;
&__name { &__name-area {
display: flex; display: flex;
align-items: center; align-items: center;
@ -219,6 +216,28 @@ export default class MonthlyBillingSummary extends Vue {
padding: 0 20px; padding: 0 20px;
} }
} }
&__credits-area {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
width: calc(100% - 40px);
background-color: #f5f6fa;
border-radius: 12px;
margin-top: 20px;
&__title-area {
display: flex;
align-items: center;
&__title {
font-size: 16px;
line-height: 21px;
color: #354049;
}
}
}
} }
} }

View File

@ -3,8 +3,8 @@
<template> <template>
<div class="usage-charge-item-container"> <div class="usage-charge-item-container">
<div class="usage-charge-item-container__summary" @click="toggleDetailedInfo"> <div class="usage-charge-item-container__summary" @click.self="toggleDetailedInfo">
<div class="usage-charge-item-container__summary__name-container"> <div class="usage-charge-item-container__summary__name-container" @click="toggleDetailedInfo">
<svg class="usage-charge-item-container__summary__name-container__expand-image" v-if="!isDetailedInfoShown" width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="usage-charge-item-container__summary__name-container__expand-image" v-if="!isDetailedInfoShown" width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"/>
</svg> </svg>
@ -13,6 +13,7 @@
</svg> </svg>
<span>{{ projectName }}</span> <span>{{ projectName }}</span>
</div> </div>
<div class="usage-charge-item-container__summary__report-link" @click="onReportClick">Advanced Report -></div>
</div> </div>
<div class="usage-charge-item-container__detailed-info-container" v-if="isDetailedInfoShown"> <div class="usage-charge-item-container__detailed-info-container" v-if="isDetailedInfoShown">
<div class="usage-charge-item-container__detailed-info-container__info-header"> <div class="usage-charge-item-container__detailed-info-container__info-header">
@ -54,6 +55,8 @@ import { Component, Prop, Vue } from 'vue-property-decorator';
import { ProjectCharge } from '@/types/payments'; import { ProjectCharge } from '@/types/payments';
import { Project } from '@/types/projects'; import { Project } from '@/types/projects';
import { Size } from '@/utils/bytesSize'; import { Size } from '@/utils/bytesSize';
import { SegmentEvent } from '@/utils/constants/analyticsEventNames';
import { toUnixTimestamp } from '@/utils/time';
@Component @Component
export default class UsageChargeItem extends Vue { export default class UsageChargeItem extends Vue {
@ -124,6 +127,30 @@ export default class UsageChargeItem extends Vue {
this.isDetailedInfoShown = !this.isDetailedInfoShown; this.isDetailedInfoShown = !this.isDetailedInfoShown;
} }
/**
* Opens new tab with advanced report table.
*/
public onReportClick(): void {
const projectID = this.$store.getters.selectedProject.id;
const startDate = this.$store.state.paymentsModule.startDate;
const endDate = this.$store.state.paymentsModule.endDate;
const url = new URL(location.origin);
url.pathname = 'usage-report';
url.searchParams.append('projectID', projectID);
url.searchParams.append('since', toUnixTimestamp(startDate).toString());
url.searchParams.append('before', toUnixTimestamp(endDate).toString());
this.$segment.track(SegmentEvent.REPORT_DOWNLOADED, {
start_date: startDate,
end_date: endDate,
project_id: projectID,
});
window.open(url.href, '_blank');
}
/** /**
* Returns formatted egress depending on amount of bytes. * Returns formatted egress depending on amount of bytes.
*/ */
@ -160,6 +187,15 @@ export default class UsageChargeItem extends Vue {
margin-right: 12px; margin-right: 12px;
} }
} }
&__report-link {
padding: 3px 5px;
font-size: 13px;
line-height: 19px;
color: #2683ff;
background-color: rgba(38, 131, 255, 0.16);
border-radius: 6px;
}
} }
&__detailed-info-container { &__detailed-info-container {

View File

@ -482,8 +482,8 @@ export default class PaymentMethods extends Vue {
&__title { &__title {
font-family: 'font_bold', sans-serif; font-family: 'font_bold', sans-serif;
font-size: 32px; font-size: 28px;
line-height: 48px; line-height: 42px;
color: #384b65; color: #384b65;
} }

View File

@ -105,14 +105,14 @@ export default class UsageReport extends Vue {
* Returns formatted starting date for report. * Returns formatted starting date for report.
*/ */
public get startDate(): string { public get startDate(): string {
return DateFormat.getUSDate(this.$store.state.usageModule.startDate, '/'); return DateFormat.getUTCDate(this.$store.state.usageModule.startDate, '/');
} }
/** /**
* Returns formatted end date for report. * Returns formatted end date for report.
*/ */
public get endDate(): string { public get endDate(): string {
return DateFormat.getUSDate(this.$store.state.usageModule.endDate, '/'); return DateFormat.getUTCDate(this.$store.state.usageModule.endDate, '/');
} }
/** /**
@ -368,7 +368,7 @@ export default class UsageReport extends Vue {
.usage-report-container__options-area__option__image { .usage-report-container__options-area__option__image {
.usage-report-svg-path { .date-picker-svg-path {
fill: #fff !important; fill: #fff !important;
} }
} }

View File

@ -340,7 +340,7 @@ export default class HeaderArea extends Vue {
} }
/deep/ .info__message-box { /deep/ .info__message-box {
background-image: url('../../../static/images/account/billing/MessageBox.png'); background-image: url('../../../static/images/team/MessageBox.png');
background-repeat: no-repeat; background-repeat: no-repeat;
min-height: 80px; min-height: 80px;
min-width: 200px; min-width: 200px;

View File

@ -188,15 +188,15 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
}, },
[GET_PROJECT_CHARGES]: async function({commit}: any, dateRange: DateRange): Promise<void> { [GET_PROJECT_CHARGES]: async function({commit}: any, dateRange: DateRange): Promise<void> {
const now = new Date(); const now = new Date();
let beforeUTC = new Date(Date.UTC(dateRange.endDate.getFullYear(), dateRange.endDate.getMonth(), dateRange.endDate.getDate(), 23, 59)); let beforeUTC = new Date(Date.UTC(dateRange.endDate.getUTCFullYear(), dateRange.endDate.getUTCMonth(), dateRange.endDate.getUTCDate(), 23, 59));
if (now.getUTCFullYear() === dateRange.endDate.getUTCFullYear() && if (now.getUTCFullYear() === dateRange.endDate.getUTCFullYear() &&
now.getUTCMonth() === dateRange.endDate.getUTCMonth() && now.getUTCMonth() === dateRange.endDate.getUTCMonth() &&
now.getUTCDate() <= dateRange.endDate.getUTCDate()) { now.getUTCDate() <= dateRange.endDate.getUTCDate()) {
beforeUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getMinutes())); beforeUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes()));
} }
const sinceUTC = new Date(Date.UTC(dateRange.startDate.getFullYear(), dateRange.startDate.getMonth(), dateRange.startDate.getDate())); const sinceUTC = new Date(Date.UTC(dateRange.startDate.getUTCFullYear(), dateRange.startDate.getUTCMonth(), dateRange.startDate.getUTCDate(), 0, 0));
const charges: ProjectCharge[] = await api.projectsCharges(sinceUTC, beforeUTC); const charges: ProjectCharge[] = await api.projectsCharges(sinceUTC, beforeUTC);
commit(SET_DATE, dateRange); commit(SET_DATE, dateRange);
@ -204,8 +204,8 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
}, },
[GET_PROJECT_CHARGES_CURRENT_ROLLUP]: async function({commit}: any): Promise<void> { [GET_PROJECT_CHARGES_CURRENT_ROLLUP]: async function({commit}: any): Promise<void> {
const now = new Date(); const now = new Date();
const endUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getMinutes())); const endUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes()));
const startUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); const startUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1, 0, 0));
const charges: ProjectCharge[] = await api.projectsCharges(startUTC, endUTC); const charges: ProjectCharge[] = await api.projectsCharges(startUTC, endUTC);
@ -214,7 +214,7 @@ export function makePaymentsModule(api: PaymentsApi): StoreModule<PaymentsState>
}, },
[GET_PROJECT_CHARGES_PREVIOUS_ROLLUP]: async function({commit}: any): Promise<void> { [GET_PROJECT_CHARGES_PREVIOUS_ROLLUP]: async function({commit}: any): Promise<void> {
const now = new Date(); const now = new Date();
const startUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1)); const startUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1, 0, 0));
const endUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0, 23, 59, 59)); const endUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0, 23, 59, 59));
const charges: ProjectCharge[] = await api.projectsCharges(startUTC, endUTC); const charges: ProjectCharge[] = await api.projectsCharges(startUTC, endUTC);

View File

@ -209,7 +209,7 @@ export class DateFormat {
* @param separator - symbol for joining date string * @param separator - symbol for joining date string
* @returns formatted date string * @returns formatted date string
*/ */
public static getUSDate(date: Date, separator: string): string { public static getUTCDate(date: Date, separator: string): string {
const month = date.getUTCMonth() + 1; const month = date.getUTCMonth() + 1;
const day = date.getUTCDate(); const day = date.getUTCDate();
const year = date.getUTCFullYear(); const year = date.getUTCFullYear();

View File

@ -227,7 +227,13 @@ export default class DashboardArea extends Vue {
public get storageRemaining(): string { public get storageRemaining(): string {
const storageUsed = this.$store.state.projectsModule.currentLimits.storageUsed; const storageUsed = this.$store.state.projectsModule.currentLimits.storageUsed;
const storageLimit = this.$store.state.projectsModule.currentLimits.storageLimit; const storageLimit = this.$store.state.projectsModule.currentLimits.storageLimit;
const remaining = new Size(storageLimit - storageUsed, 2);
const difference = storageLimit - storageUsed;
if (difference < 0) {
return '0 Bytes';
}
const remaining = new Size(difference, 2);
return `${remaining.formattedBytes}${remaining.label}`; return `${remaining.formattedBytes}${remaining.label}`;
} }
@ -238,7 +244,13 @@ export default class DashboardArea extends Vue {
public get bandwidthRemaining(): string { public get bandwidthRemaining(): string {
const bandwidthUsed = this.$store.state.projectsModule.currentLimits.bandwidthUsed; const bandwidthUsed = this.$store.state.projectsModule.currentLimits.bandwidthUsed;
const bandwidthLimit = this.$store.state.projectsModule.currentLimits.bandwidthLimit; const bandwidthLimit = this.$store.state.projectsModule.currentLimits.bandwidthLimit;
const remaining = new Size(bandwidthLimit - bandwidthUsed, 2);
const difference = bandwidthLimit - bandwidthUsed;
if (difference < 0) {
return '0 Bytes';
}
const remaining = new Size(difference, 2);
return `${remaining.formattedBytes}${remaining.label}`; return `${remaining.formattedBytes}${remaining.label}`;
} }

View File

@ -1,3 +1,3 @@
<svg class="banner__link__svg" width="10" height="17" viewBox="0 0 10 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.363528 13.6272C-0.121176 13.1302 -0.121176 12.3243 0.363528 11.8273L5.07113 7L0.363528 2.17268C-0.121177 1.67565 -0.121177 0.869804 0.363528 0.372774C0.848232 -0.124258 1.63409 -0.124258 2.1188 0.372774L8.58166 7L2.1188 13.6272C1.63409 14.1243 0.848232 14.1243 0.363528 13.6272Z" fill="#2683FF"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 479 B

After

Width:  |  Height:  |  Size: 454 B

View File

@ -1,3 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="usage-report-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"/> <path class="date-picker-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"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,63 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import Vuex from 'vuex';
import EstimatedCostsAndCredits from '@/components/account/billing/estimatedCostsAndCredits/EstimatedCostsAndCredits.vue';
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 { ProjectCharge } from '@/types/payments';
import { Project } from '@/types/projects';
import { User } from '@/types/users';
import { createLocalVue, mount } from '@vue/test-utils';
import { PaymentsMock } from '../../../mock/api/payments';
import { ProjectsApiMock } from '../../../mock/api/projects';
import { UsersApiMock } from '../../../mock/api/users';
const localVue = createLocalVue();
localVue.filter('centsToDollars', (cents: number): string => {
return `USD $${(cents / 100).toFixed(2)}`;
});
localVue.use(Vuex);
const usersApi = new UsersApiMock();
const usersModule = makeUsersModule(usersApi);
const projectsApi = new ProjectsApiMock();
const projectsModule = makeProjectsModule(projectsApi);
const paymentsApi = new PaymentsMock();
const paymentsModule = makePaymentsModule(paymentsApi);
const store = new Vuex.Store({ modules: { usersModule, projectsModule, paymentsModule }});
const project = new Project('id', 'projectName', 'projectDescription', 'test', 'testOwnerId', true);
const user = new User('testOwnerId');
const date = new Date(1970, 1, 1);
const projectCharge = new ProjectCharge(date, date, 100, 100, 100, 'id', 100, 100, 100);
describe('EstimatedCostsAndCredits', () => {
it('renders correctly with project and no project charges', () => {
store.commit(USER_MUTATIONS.SET_USER, user);
store.commit(PROJECTS_MUTATIONS.ADD, project);
const wrapper = mount(EstimatedCostsAndCredits, {
store,
localVue,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with project and project charges', () => {
store.commit(PAYMENTS_MUTATIONS.SET_BALANCE, 5500);
store.commit(PAYMENTS_MUTATIONS.SET_PROJECT_CHARGES, [projectCharge]);
const wrapper = mount(EstimatedCostsAndCredits, {
store,
localVue,
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -3,7 +3,7 @@
import Vuex from 'vuex'; import Vuex from 'vuex';
import UsageChargeItem from '@/components/account/billing/monthlySummary/UsageChargeItem.vue'; import UsageChargeItem from '@/components/account/billing/estimatedCostsAndCredits/UsageChargeItem.vue';
import { makePaymentsModule } from '@/store/modules/payments'; import { makePaymentsModule } from '@/store/modules/payments';
import { makeProjectsModule } from '@/store/modules/projects'; import { makeProjectsModule } from '@/store/modules/projects';

View File

@ -0,0 +1,52 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EstimatedCostsAndCredits renders correctly with project and no project charges 1`] = `
<div class="current-month-area">
<div class="current-month-area__title-area">
<h1 class="current-month-area__title-area__title">Estimated Costs for This Billing Period</h1> <span class="current-month-area__title-area__costs">USD $0.00</span>
</div>
<div class="current-month-area__content">
<h2 class="current-month-area__content__title">DETAILS</h2>
<div class="current-month-area__content__usage-charges">
<div class="current-month-area__content__usage-charges__head">
<div class="current-month-area__content__usage-charges__head__name-area">
<!----> <span class="current-month-area__content__usage-charges__head__name-area__title">Usage Charges</span></div> <span>Estimated total <span class="summary">USD $0.00</span></span>
</div>
<!---->
</div>
<div class="current-month-area__content__credits-area">
<div class="current-month-area__content__credits-area__title-area"><span class="current-month-area__content__credits-area__title-area__title">Earned Credits</span></div> <span class="current-month-area__content__credits-area__balance" style="color: rgb(0, 0, 0);">
USD $0.00
</span>
</div>
</div>
</div>
`;
exports[`EstimatedCostsAndCredits renders correctly with project and project charges 1`] = `
<div class="current-month-area">
<div class="current-month-area__title-area">
<h1 class="current-month-area__title-area__title">Estimated Costs for This Billing Period</h1> <span class="current-month-area__title-area__costs">USD $3.00</span>
</div>
<div class="current-month-area__content">
<h2 class="current-month-area__content__title">DETAILS</h2>
<div class="current-month-area__content__usage-charges">
<div class="current-month-area__content__usage-charges__head">
<div class="current-month-area__content__usage-charges__head__name-area">
<div class="current-month-area__content__usage-charges__head__name-area__image-container"><svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.363528 13.6272C-0.121176 13.1302 -0.121176 12.3243 0.363528 11.8273L5.07113 7L0.363528 2.17268C-0.121177 1.67565 -0.121177 0.869804 0.363528 0.372774C0.848232 -0.124258 1.63409 -0.124258 2.1188 0.372774L8.58166 7L2.1188 13.6272C1.63409 14.1243 0.848232 14.1243 0.363528 13.6272Z" fill="#2683FF"></path>
</svg>
<!---->
</div> <span class="current-month-area__content__usage-charges__head__name-area__title">Usage Charges</span>
</div> <span>Estimated total <span class="summary">USD $3.00</span></span>
</div>
<!---->
</div>
<div class="current-month-area__content__credits-area">
<div class="current-month-area__content__credits-area__title-area"><span class="current-month-area__content__credits-area__title-area__title">Earned Credits</span></div> <span class="current-month-area__content__credits-area__balance" style="color: rgb(0, 0, 0);">
USD $55.00
</span>
</div>
</div>
</div>
`;

View File

@ -7,6 +7,7 @@ exports[`UsageChargeItem renders correctly 1`] = `
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"></path>
</svg> </svg>
<!----> <span></span></div> <!----> <span></span></div>
<div class="usage-charge-item-container__summary__report-link">Advanced Report -&gt;</div>
</div> </div>
<!----> <!---->
</div> </div>
@ -19,6 +20,7 @@ exports[`UsageChargeItem toggling dropdown works correctly 1`] = `
<!----> <svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg" class="usage-charge-item-container__summary__name-container__expand-image"> <!----> <svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg" class="usage-charge-item-container__summary__name-container__expand-image">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.372773 0.338888C0.869804 -0.112963 1.67565 -0.112963 2.17268 0.338888L7 4.72741L11.8273 0.338888C12.3243 -0.112963 13.1302 -0.112963 13.6272 0.338888C14.1243 0.790739 14.1243 1.52333 13.6272 1.97519L7 8L0.372773 1.97519C-0.124258 1.52333 -0.124258 0.790739 0.372773 0.338888Z" fill="#2683FF"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.372773 0.338888C0.869804 -0.112963 1.67565 -0.112963 2.17268 0.338888L7 4.72741L11.8273 0.338888C12.3243 -0.112963 13.1302 -0.112963 13.6272 0.338888C14.1243 0.790739 14.1243 1.52333 13.6272 1.97519L7 8L0.372773 1.97519C-0.124258 1.52333 -0.124258 0.790739 0.372773 0.338888Z" fill="#2683FF"></path>
</svg> <span></span></div> </svg> <span></span></div>
<div class="usage-charge-item-container__summary__report-link">Advanced Report -&gt;</div>
</div> </div>
<div class="usage-charge-item-container__detailed-info-container"> <div class="usage-charge-item-container__detailed-info-container">
<div class="usage-charge-item-container__detailed-info-container__info-header"><span class="resource-header">RESOURCE</span> <span class="period-header">PERIOD</span> <span class="usage-header">USAGE</span> <span class="cost-header">COST</span></div> <div class="usage-charge-item-container__detailed-info-container__info-header"><span class="resource-header">RESOURCE</span> <span class="period-header">PERIOD</span> <span class="usage-header">USAGE</span> <span class="cost-header">COST</span></div>
@ -55,6 +57,7 @@ exports[`UsageChargeItem toggling dropdown works correctly 2`] = `
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"></path> <path fill-rule="evenodd" clip-rule="evenodd" d="M0.328889 13.6272C-0.10963 13.1302 -0.10963 12.3243 0.328889 11.8273L4.58792 7L0.328889 2.17268C-0.10963 1.67565 -0.10963 0.869804 0.328889 0.372774C0.767408 -0.124258 1.47839 -0.124258 1.91691 0.372774L7.76396 7L1.91691 13.6272C1.47839 14.1243 0.767409 14.1243 0.328889 13.6272Z" fill="#2683FF"></path>
</svg> </svg>
<!----> <span></span></div> <!----> <span></span></div>
<div class="usage-charge-item-container__summary__report-link">Advanced Report -&gt;</div>
</div> </div>
<!----> <!---->
</div> </div>

View File

@ -86,8 +86,8 @@ describe('datepicker', () => {
const expectedResult1 = '11/7/2019'; const expectedResult1 = '11/7/2019';
const expectedResult2 = '2-1-2019'; const expectedResult2 = '2-1-2019';
const actualResult1 = DateFormat.getUSDate(testDate1, '/'); const actualResult1 = DateFormat.getUTCDate(testDate1, '/');
const actualResult2 = DateFormat.getUSDate(testDate2, '-'); const actualResult2 = DateFormat.getUTCDate(testDate2, '-');
expect(actualResult1).toBe(expectedResult1); expect(actualResult1).toBe(expectedResult1);
expect(actualResult2).toBe(expectedResult2); expect(actualResult2).toBe(expectedResult2);