web/storagenode: period selection separation

Change-Id: I9f50f4bb9282410f9f3cc4124e71a7a631767460
This commit is contained in:
NickolaiYurchenko 2020-05-04 21:45:38 +03:00
parent d13b693b0c
commit b6771d0c52
5 changed files with 285 additions and 64 deletions

View File

@ -4,8 +4,27 @@
<template>
<div class="estimation-container">
<div class="estimation-container__header">
<p class="estimation-container__header__title">Info & Estimation</p>
<EstimationPeriodDropdown />
<p class="estimation-container__header__title">Info & Estimation,
<span class="estimation-container__header__period">{{ currentPeriod }}</span>
</p>
<div class="estimation-container__header__selection-area">
<div
class="estimation-container__header__selection-area__item"
:class="{ active: isCurrentPeriod }"
@click.stop="selectCurrentPeriod"
>
<p class="estimation-container__header__selection-area__item__label long-text">
Current Period
</p>
<p class="estimation-container__header__selection-area__item__label short-text">
Current Per.
</p>
</div>
<EstimationPeriodDropdown
class="estimation-container__header__selection-area__item"
:class="{ active: !isCurrentPeriod }"
/>
</div>
</div>
<div class="estimation-container__divider"></div>
<div class="estimation-table-container" v-if="!isPayoutNoDataState">
@ -95,14 +114,24 @@ import { Component, Vue } from 'vue-property-decorator';
import EstimationPeriodDropdown from '@/app/components/payments/EstimationPeriodDropdown.vue';
import { APPSTATE_ACTIONS } from '@/app/store/modules/appState';
import {
BANDWIDTH_DOWNLOAD_PRICE_PER_TB,
BANDWIDTH_REPAIR_PRICE_PER_TB,
DISK_SPACE_PRICE_PER_TB,
DISK_SPACE_PRICE_PER_TB, PAYOUT_ACTIONS,
} from '@/app/store/modules/payout';
import { HeldInfo } from '@/app/types/payout';
import { HeldInfo, PayoutInfoRange, PayoutPeriod } from '@/app/types/payout';
import { formatBytes, TB } from '@/app/utils/converter';
/**
* Holds all months names.
*/
const monthNames = [
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December',
];
/**
* Describes table row data item.
*/
@ -125,6 +154,18 @@ class EstimationTableRow {
export default class EstimationArea extends Vue {
public now: Date = new Date();
/**
* Returns formatted selected payout period.
*/
public get currentPeriod(): string {
const start: PayoutPeriod = this.$store.state.payoutModule.periodRange.start;
const end: PayoutPeriod = this.$store.state.payoutModule.periodRange.end;
return start && start.period !== end.period ?
`${monthNames[start.month].slice(0, 3)} ${start.year} - ${monthNames[end.month].slice(0, 3)} ${end.year}`
: `${monthNames[end.month].slice(0, 3)} ${end.year}`;
}
/**
* Indicates if current month selected.
*/
@ -278,6 +319,21 @@ export default class EstimationArea extends Vue {
];
}
/**
* Selects current month as selected payout period.
*/
public async selectCurrentPeriod(): Promise<void> {
const now = new Date();
await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false);
await this.$store.dispatch(
PAYOUT_ACTIONS.SET_PERIODS_RANGE, new PayoutInfoRange(
null,
new PayoutPeriod(now.getUTCFullYear(), now.getUTCMonth()),
),
);
}
/**
* Returns current month held amount based on currend day of month.
*/
@ -303,12 +359,50 @@ export default class EstimationArea extends Vue {
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 40px;
&__title {
font-weight: 500;
font-size: 18px;
color: var(--regular-text-color);
}
&__period {
color: #909bad;
}
&__selection-area {
display: flex;
align-items: center;
justify-content: flex-end;
height: 100%;
&__item {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
height: 100%;
padding: 0 20px;
border-bottom: 3px solid transparent;
z-index: 102;
&__label {
text-align: center;
font-size: 16px;
color: var(--regular-text-color);
}
&.active {
border-bottom: 3px solid var(--navigation-link-color);
&__label {
font-size: 16px;
color: var(--regular-text-color);
}
}
}
}
}
&__total-held,
@ -341,7 +435,6 @@ export default class EstimationArea extends Vue {
&__divider {
width: 100%;
height: 1px;
margin-top: 18px;
background-color: #eaeaea;
}
}
@ -428,6 +521,10 @@ export default class EstimationArea extends Vue {
}
}
.short-text {
display: none;
}
.column {
display: flex;
flex-direction: row;
@ -493,6 +590,33 @@ export default class EstimationArea extends Vue {
}
}
@media screen and (max-width: 870px) {
.estimation-container {
&__header {
flex-direction: column;
align-items: flex-start;
height: auto;
&__selection-area {
width: 100%;
height: 41px;
margin: 20px 0;
&__item {
width: calc(50% - 40px);
border-bottom: 3px solid #eaeaea;
}
}
}
&__divider {
display: none;
}
}
}
@media screen and (max-width: 640px) {
.estimation-container {
@ -514,4 +638,24 @@ export default class EstimationArea extends Vue {
width: 30%;
}
}
@media screen and (max-width: 505px) {
.short-text {
display: inline-block;
font-size: 14px;
}
.long-text {
display: none;
}
}
@media screen and (max-width: 430px) {
.estimation-container__header__period {
display: block;
margin-top: 8px;
}
}
</style>

View File

@ -3,7 +3,8 @@
<template>
<div class="period-container" @click.stop="openPeriodDropdown">
<p class="period-container__label">{{ currentPeriod }}</p>
<p class="period-container__label long-text">Custom Date Range</p>
<p class="period-container__label short-text">Custom Range</p>
<BlackArrowHide v-if="isCalendarShown" />
<BlackArrowExpand v-else />
<PayoutPeriodCalendar
@ -23,16 +24,6 @@ import BlackArrowExpand from '@/../static/images/BlackArrowExpand.svg';
import BlackArrowHide from '@/../static/images/BlackArrowHide.svg';
import { APPSTATE_ACTIONS } from '@/app/store/modules/appState';
import { PayoutPeriod } from '@/app/types/payout';
/**
* Holds all months names.
*/
const monthNames = [
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December',
];
@Component({
components: {
@ -42,18 +33,6 @@ const monthNames = [
},
})
export default class EstimationPeriodDropdown extends Vue {
/**
* Returns formatted selected payout period.
*/
public get currentPeriod(): string {
const start: PayoutPeriod = this.$store.state.payoutModule.periodRange.start;
const end: PayoutPeriod = this.$store.state.payoutModule.periodRange.end;
return start && start.period !== end.period ?
`${monthNames[start.month]}, ${start.year} - ${monthNames[end.month]}, ${end.year}`
: `${monthNames[end.month]}, ${end.year}`;
}
/**
* Indicates if period selection calendar should appear.
*/
@ -92,7 +71,7 @@ export default class EstimationPeriodDropdown extends Vue {
font-family: 'font_regular', sans-serif;
font-weight: 500;
font-size: 16px;
color: var(--month-label-color);
color: var(--regular-text-color);
}
&__calendar {
@ -102,10 +81,37 @@ export default class EstimationPeriodDropdown extends Vue {
}
}
.active {
.period-container__label {
color: var(--navigation-link-color);
}
}
.arrow {
path {
fill: var(--period-selection-arrow-color);
}
}
.short-text {
display: none;
}
@media screen and (max-width: 505px) {
.period-container__label {
margin-right: 4px;
}
.short-text {
display: inline-block;
font-size: 14px;
}
.long-text {
display: none;
}
}
</style>

View File

@ -108,37 +108,6 @@ export default class PayoutPeriodCalendar extends Vue {
return;
}
// TODO: remove checks when buttons will be separated
if (!this.secondSelectedMonth) {
const now = new Date();
if (this.firstSelectedMonth.year === now.getUTCFullYear() && this.firstSelectedMonth.index === now.getUTCMonth()) {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false);
await this.$store.dispatch(
PAYOUT_ACTIONS.SET_PERIODS_RANGE, new PayoutInfoRange(
null,
new PayoutPeriod(this.firstSelectedMonth.year, this.firstSelectedMonth.index),
),
);
this.close();
return;
}
}
if (this.secondSelectedMonth && this.secondSelectedMonth.year === this.firstSelectedMonth.year && this.secondSelectedMonth.index === this.firstSelectedMonth.index) {
await this.$store.dispatch(APPSTATE_ACTIONS.SET_NO_PAYOUT_DATA, false);
await this.$store.dispatch(
PAYOUT_ACTIONS.SET_PERIODS_RANGE, new PayoutInfoRange(
null,
new PayoutPeriod(this.firstSelectedMonth.year, this.firstSelectedMonth.index),
),
);
this.close();
return;
}
this.secondSelectedMonth ? await this.$store.dispatch(
PAYOUT_ACTIONS.SET_PERIODS_RANGE, new PayoutInfoRange(
new PayoutPeriod(this.firstSelectedMonth.year, this.firstSelectedMonth.index),
@ -183,8 +152,23 @@ export default class PayoutPeriodCalendar extends Vue {
public selectAllTime(): void {
const nodeStartedAt = this.$store.state.node.selectedSatellite.joinDate;
if (nodeStartedAt.getUTCMonth() === this.now.getUTCMonth() && nodeStartedAt.getUTCFullYear() === this.now.getUTCFullYear()) {
return;
}
this.firstSelectedMonth = new MonthButton(nodeStartedAt.getUTCFullYear(), nodeStartedAt.getUTCMonth());
this.secondSelectedMonth = new MonthButton(this.now.getUTCFullYear(), this.now.getUTCMonth());
this.secondSelectedMonth = this.now.getUTCMonth() === 0 ?
new MonthButton(this.now.getUTCFullYear() - 1, 11)
: new MonthButton(this.now.getUTCFullYear(), this.now.getUTCMonth() - 1);
if (
this.firstSelectedMonth.year === this.secondSelectedMonth.year
&& this.firstSelectedMonth.index === this.secondSelectedMonth.index
) {
this.secondSelectedMonth = null;
this.checkMonth(this.firstSelectedMonth);
}
this.updateMonthsSelection(true);
this.updatePeriod();
}
@ -260,7 +244,21 @@ export default class PayoutPeriodCalendar extends Vue {
* Marks all months between first and second selected as selected/unselected.
*/
private updateMonthsSelection(value: boolean): void {
if (!this.secondSelectedMonth || !this.firstSelectedMonth) return;
if (!this.firstSelectedMonth) return;
if (!this.secondSelectedMonth) {
const selectedMonth = this.displayedMonths[this.firstSelectedMonth.year].find(month => {
if (this.firstSelectedMonth) {
return month.index === this.firstSelectedMonth.index;
}
});
if (selectedMonth) {
selectedMonth.selected = value;
}
return;
}
for (let i = this.firstSelectedMonth.year; i <= this.secondSelectedMonth.year; i++) {
if (!this.displayedMonths[i]) {
@ -300,9 +298,9 @@ export default class PayoutPeriodCalendar extends Vue {
const notBeforeNodeStart =
nodeStartedAt.getUTCFullYear() < year
|| (nodeStartedAt.getUTCFullYear() === year && nodeStartedAt.getUTCMonth() <= i);
const inFuture = isCurrentYear && i > nowMonth;
const inFutureOrCurrent = isCurrentYear && i >= nowMonth;
const isMonthActive = notBeforeNodeStart && !inFuture;
const isMonthActive = notBeforeNodeStart && !inFutureOrCurrent;
months.push(new MonthButton(year, i, isMonthActive, false));
}

View File

@ -0,0 +1,54 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
import Vue, { VNode } from 'vue';
import { DirectiveBinding } from 'vue/types/options';
import Vuex from 'vuex';
import EstimationPeriodDropdown from '@/app/components/payments/EstimationPeriodDropdown.vue';
import { APPSTATE_ACTIONS, appStateModule } from '@/app/store/modules/appState';
import { createLocalVue, shallowMount } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.directive('click-outside', {
bind: (): void => { return; },
unbind: (): void => { return; },
});
const store = new Vuex.Store({ modules: { appStateModule }});
describe('DiskStatChart', (): void => {
it('renders correctly', (): void => {
const wrapper = shallowMount(EstimationPeriodDropdown, {
store,
localVue,
});
expect(wrapper).toMatchSnapshot();
});
it('renders correctly with calendar', async (): Promise<void> => {
const wrapper = shallowMount(EstimationPeriodDropdown, {
store,
localVue,
});
await store.dispatch(APPSTATE_ACTIONS.TOGGLE_PAYOUT_CALENDAR, true);
expect(wrapper).toMatchSnapshot();
});
it('opens calendar on click', (): void => {
const wrapper = shallowMount(EstimationPeriodDropdown, {
store,
localVue,
});
wrapper.find('.period-container').trigger('click');
expect(wrapper.find('.period-container__calendar').exists()).toBe(true);
});
});

View File

@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DiskStatChart renders correctly 1`] = `
<div class="period-container">
<p class="period-container__label long-text">Custom Date Range</p>
<p class="period-container__label short-text">Custom Range</p>
<blackarrowexpand-stub></blackarrowexpand-stub>
<!---->
</div>
`;
exports[`DiskStatChart renders correctly with calendar 1`] = `
<div class="period-container">
<p class="period-container__label long-text">Custom Date Range</p>
<p class="period-container__label short-text">Custom Range</p>
<blackarrowhide-stub></blackarrowhide-stub>
<payoutperiodcalendar-stub class="period-container__calendar"></payoutperiodcalendar-stub>
</div>
`;