From 3a1b71a757428f84e5e626a7962ab0a60f4cfd6b Mon Sep 17 00:00:00 2001 From: NickolaiYurchenko Date: Sun, 15 Mar 2020 22:03:28 +0200 Subject: [PATCH] web/storagenode: payout info month range selection logic Change-Id: Ibb8ab24475212bed5fe8790a317f2d9f4e4105dd --- .../payments/PayoutPeriodCalendar.vue | 188 ++++++++++++++++-- 1 file changed, 176 insertions(+), 12 deletions(-) diff --git a/web/storagenode/src/app/components/payments/PayoutPeriodCalendar.vue b/web/storagenode/src/app/components/payments/PayoutPeriodCalendar.vue index ecc87aca4..46b391c60 100644 --- a/web/storagenode/src/app/components/payments/PayoutPeriodCalendar.vue +++ b/web/storagenode/src/app/components/payments/PayoutPeriodCalendar.vue @@ -5,28 +5,29 @@
-
+
-

2020

-
+

{{ displayedYear }}

+
-

All time

+

All time

{{ item.name }}

@@ -37,6 +38,10 @@ import { Component, Vue } from 'vue-property-decorator'; import GrayArrowLeftIcon from '@/../static/images/payments/GrayArrowLeft.svg'; +interface StoredMonthsByYear { + [key: number]: MonthButton[]; +} + /** * Holds all months names. */ @@ -51,6 +56,7 @@ const monthNames = [ */ class MonthButton { public constructor( + public year: number = 0, public index: number = 0, public active: boolean = false, public selected: boolean = false, @@ -59,7 +65,7 @@ class MonthButton { /** * Returns month label depends on index. */ - public get name() { + public get name(): string { return monthNames[this.index].slice(0, 3); } } @@ -73,14 +79,171 @@ export default class PayoutPeriodCalendar extends Vue { /** * Contains current months list depends on active and selected month state. */ - public displayedMonths: MonthButton[] = []; + public currentDisplayedMonths: MonthButton[] = []; + public displayedYear: number; + public period: string; - public constructor() { - super(); + private displayedMonths: StoredMonthsByYear = {}; + private now: Date; + private firstSelectedMonth: MonthButton | null; + private secondSelectedMonth: MonthButton | null; - for (let i = 0; i < monthNames.length; i++) { - this.displayedMonths.push(new MonthButton(i, true, false)); + /** + * Lifecycle hook after initial render. + * Sets up current calendar state. + */ + public mounted(): void { + this.now = new Date(); + this.displayedYear = this.now.getUTCFullYear(); + this.populateMonths(this.displayedYear); + this.currentDisplayedMonths = this.displayedMonths[this.displayedYear]; + } + + /** + * Updates selected period label. + */ + public updatePeriod(): void { + if (!this.firstSelectedMonth) { + this.period = ''; + + return; } + + this.period = this.secondSelectedMonth ? + `${this.firstSelectedMonth.name}, ${this.firstSelectedMonth.year} - ${this.secondSelectedMonth.name}, ${this.secondSelectedMonth.year}` + : `${monthNames[this.firstSelectedMonth.index]}, ${this.firstSelectedMonth.year}`; + } + + /** + * Selects period between node start and now. + */ + public selectAllTime(): void { + const nodeStartedAt = this.$store.state.node.info.startedAt; + + this.firstSelectedMonth = new MonthButton(nodeStartedAt.getUTCFullYear(), nodeStartedAt.getUTCMonth()); + this.secondSelectedMonth = new MonthButton(this.now.getUTCFullYear(), this.now.getUTCMonth()); + this.updateMonthsSelection(true); + this.updatePeriod(); + } + + /** + * Updates first and second selected month on click. + */ + public checkMonth(month: MonthButton): void { + if (this.firstSelectedMonth && this.secondSelectedMonth) { + this.updateMonthsSelection(false); + this.firstSelectedMonth = this.secondSelectedMonth = null; + + if (month.active) { + this.firstSelectedMonth = month; + month.selected = true; + } + + this.updatePeriod(); + + return; + } + + if (!month.active) return; + + if (!this.firstSelectedMonth) { + this.firstSelectedMonth = month; + month.selected = true; + this.updatePeriod(); + + return; + } + + if (this.firstSelectedMonth === month) { + this.firstSelectedMonth = null; + month.selected = false; + this.updatePeriod(); + + return; + } + + this.secondSelectedMonth = month; + if ((this.secondSelectedMonth && this.firstSelectedMonth) && new Date(this.secondSelectedMonth.year, this.secondSelectedMonth.index) < new Date(this.firstSelectedMonth.year, this.firstSelectedMonth.index)) { + [this.secondSelectedMonth, this.firstSelectedMonth] = [this.firstSelectedMonth, this.secondSelectedMonth]; + } + + this.updatePeriod(); + this.updateMonthsSelection(true); + } + + /** + * Increments year and updates current months set. + */ + public incrementYear(): void { + if (this.displayedYear === this.now.getUTCFullYear()) return; + + this.displayedYear += 1; + this.populateMonths(this.displayedYear); + this.currentDisplayedMonths = this.displayedMonths[this.displayedYear]; + } + + /** + * Decrement year and updates current months set. + */ + public decrementYear(): void { + if (this.displayedYear === this.$store.state.node.info.startedAt.getUTCFullYear()) return; + + this.displayedYear -= 1; + this.populateMonths(this.displayedYear); + this.currentDisplayedMonths = this.displayedMonths[this.displayedYear]; + } + + /** + * Marks all months between first and second selected as selected/unselected. + */ + private updateMonthsSelection(value: boolean): void { + if (!this.secondSelectedMonth || !this.firstSelectedMonth) return; + + for (let i = this.firstSelectedMonth.year; i <= this.secondSelectedMonth.year; i++) { + if (!this.displayedMonths[i]) { + this.populateMonths(i); + } + + this.displayedMonths[i].forEach(month => { + const date = new Date(month.year, month.index); + + if ( + (this.secondSelectedMonth && this.firstSelectedMonth) + && new Date(this.firstSelectedMonth.year, this.firstSelectedMonth.index) <= date + && date <= new Date(this.secondSelectedMonth.year, this.secondSelectedMonth.index) + ) { + month.selected = value; + } + }); + } + } + + /** + * Sets months set in displayedMonths with year as key. + */ + private populateMonths(year: number): void { + if (this.displayedMonths[year]) { + this.currentDisplayedMonths = this.displayedMonths[year]; + + return; + } + + const months: MonthButton[] = []; + const isCurrentYear = year === this.now.getUTCFullYear(); + const nowMonth = this.now.getUTCMonth(); + const nodeStartedAt = this.$store.state.node.info.startedAt; + + for (let i = 0; i < 12; i++) { + const notBeforeNodeStart = + (nodeStartedAt.getUTCFullYear() === year && nodeStartedAt.getUTCMonth() <= i) + || nodeStartedAt.getUTCFullYear() < year; + const inFuture = isCurrentYear && i > nowMonth; + + const isMonthActive = notBeforeNodeStart && !inFuture; + months.push(new MonthButton(year, i, isMonthActive, false)); + } + + this.displayedMonths[year] = months; } } @@ -98,6 +261,7 @@ export default class PayoutPeriodCalendar extends Vue { border-radius: 5px; padding: 24px; font-family: 'font_regular', sans-serif; + cursor: default; &__header { display: flex;