web/storagenode: ingress chart implemented (#3618)

This commit is contained in:
Vitalii Shpital 2019-11-26 11:19:57 +02:00 committed by GitHub
parent 79a4fff6c7
commit 59385aff66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 397 additions and 42 deletions

View File

@ -17,6 +17,7 @@ export default class App extends Vue {}
<style lang="scss">
body {
margin: 0 !important;
position: relative;
font-family: 'font_regular', sans-serif;
}

View File

@ -139,22 +139,22 @@ export default class BandwidthChart extends Vue {
</div>`;
}
// `this` will be the overall tooltip
const bandwidthChart = document.getElementById('bandwidth-chart');
if (bandwidthChart) {
const position = bandwidthChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.left = `${position.left + tooltipModel.caretX - 125}px`;
tooltipEl.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY - 83}px`;
tooltipArrow.style.opacity = this.TOOLTIP_OPACITY;
tooltipArrow.style.position = this.TOOLTIP_POSITION;
tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`;
tooltipArrow.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY - 103}px`;
if (!bandwidthChart) {
return;
}
return;
// `this` will be the overall tooltip.
const position = bandwidthChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.left = `${position.left + tooltipModel.caretX - 125}px`;
tooltipEl.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 150}px`;
tooltipArrow.style.opacity = this.TOOLTIP_OPACITY;
tooltipArrow.style.position = this.TOOLTIP_POSITION;
tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`;
tooltipArrow.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 125}px`;
}
}
</script>

View File

@ -104,16 +104,16 @@ export default class DiskSpaceChart extends Vue {
}
const diskSpaceChart = document.getElementById('disk-space-chart');
if (diskSpaceChart) {
const position = diskSpaceChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.right = `${position.left + window.pageXOffset - tooltipModel.caretX - this.TOOLTIP_MARGIN}px`;
tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY}px`;
if (!diskSpaceChart) {
return;
}
return;
// `this` will be the overall tooltip.
const position = diskSpaceChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.right = `${position.left + window.pageXOffset - tooltipModel.caretX - this.TOOLTIP_MARGIN}px`;
tooltipEl.style.top = `${position.top + window.pageYOffset + tooltipModel.caretY}px`;
}
}
</script>

View File

@ -25,7 +25,7 @@ import { formatBytes } from '@/app/utils/converter';
import { EgressUsed } from '@/storagenode/satellite';
/**
* stores egress bandwidth data for egress bandwidth chart's tooltip
* stores egress data for egress bandwidth chart's tooltip
*/
class EgressTooltip {
public normalEgress: string;
@ -129,22 +129,23 @@ export default class EgressChart extends Vue {
</div>`;
}
// `this` will be the overall tooltip
const bandwidthChart = document.getElementById('egress-chart');
if (bandwidthChart) {
const position = bandwidthChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.left = `${position.left + tooltipModel.caretX - 94}px`;
tooltipEl.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY - 83}px`;
tooltipArrow.style.opacity = this.TOOLTIP_OPACITY;
tooltipArrow.style.position = this.TOOLTIP_POSITION;
tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`;
tooltipArrow.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY - 103}px`;
const egressChart = document.getElementById('egress-chart');
if (!egressChart) {
return;
}
return;
// `this` will be the overall tooltip.
const position = egressChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.left = `${position.left + tooltipModel.caretX - 94}px`;
tooltipEl.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 150}px`;
tooltipArrow.style.opacity = this.TOOLTIP_OPACITY;
tooltipArrow.style.position = this.TOOLTIP_POSITION;
tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`;
tooltipArrow.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 125}px`;
}
}
</script>

View File

@ -0,0 +1,214 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="chart">
<p class="ingress-chart__data-dimension">{{chartDataDimension}}</p>
<VChart
id="ingress-chart"
:chart-data="chartData"
:width="400"
:height="240"
:tooltip-constructor="ingressTooltip"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import VChart from '@/app/components/VChart.vue';
import { ChartData } from '@/app/types/chartData';
import { ChartUtils } from '@/app/utils/chart';
import { formatBytes } from '@/app/utils/converter';
import { IngressUsed } from '@/storagenode/satellite';
/**
* stores ingress data for ingress bandwidth chart's tooltip
*/
class IngressTooltip {
public normalIngress: string;
public repairIngress: string;
public date: string;
public constructor(bandwidth: IngressUsed) {
this.normalIngress = formatBytes(bandwidth.ingress.usage);
this.repairIngress = formatBytes(bandwidth.ingress.repair);
this.date = bandwidth.intervalStart.toUTCString().slice(0, 16);
}
}
@Component ({
components: {
VChart,
},
})
export default class IngressChart extends Vue {
private readonly TOOLTIP_OPACITY: string = '1';
private readonly TOOLTIP_POSITION: string = 'absolute';
private get allBandwidth(): IngressUsed[] {
return ChartUtils.populateEmptyBandwidth(this.$store.state.node.ingressChartData);
}
public get chartDataDimension(): string {
if (!this.$store.state.node.ingressChartData.length) {
return 'Bytes';
}
return ChartUtils.getChartDataDimension(this.allBandwidth.map((elem) => {
return elem.summary();
}));
}
public get chartData(): ChartData {
let data: number[] = [0];
const daysCount = ChartUtils.daysDisplayedOnChart();
const chartBackgroundColor = '#fff4df';
const chartBorderColor = '#e1a128';
const chartBorderWidth = 2;
if (this.allBandwidth.length) {
data = ChartUtils.normalizeChartData(this.allBandwidth.map(elem => elem.summary()));
}
return new ChartData(daysCount, chartBackgroundColor, chartBorderColor, chartBorderWidth, data);
}
public ingressTooltip(tooltipModel): void {
// Tooltip Element
let tooltipEl = document.getElementById('ingress-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'ingress-tooltip';
document.body.appendChild(tooltipEl);
}
// Tooltip Arrow
let tooltipArrow = document.getElementById('ingress-tooltip-arrow');
// Create element on first render
if (!tooltipArrow) {
tooltipArrow = document.createElement('div');
tooltipArrow.id = 'ingress-tooltip-arrow';
document.body.appendChild(tooltipArrow);
}
// Hide if no tooltip
if (!tooltipModel.opacity) {
document.body.removeChild(tooltipEl);
document.body.removeChild(tooltipArrow);
return;
}
// Set Text
if (tooltipModel.body) {
const dataIndex = tooltipModel.dataPoints[0].index;
const dataPoint = new IngressTooltip(this.allBandwidth[dataIndex]);
tooltipEl.innerHTML = `<div class='ingress-tooltip-body'>
<div class='ingress-tooltip-body__info'>
<p>USAGE</p>
<b class="ingress-tooltip-bold-text">${dataPoint.normalIngress}</b>
</div>
<div class='ingress-tooltip-body__info'>
<p>REPAIR</p>
<b class="ingress-tooltip-bold-text">${dataPoint.repairIngress}</b>
</div>
</div>
<div class='ingress-tooltip-footer'>
<p>${dataPoint.date}</p>
</div>`;
}
const ingressChart = document.getElementById('ingress-chart');
if (!ingressChart) {
return;
}
// `this` will be the overall tooltip.
const position = ingressChart.getBoundingClientRect();
tooltipEl.style.opacity = this.TOOLTIP_OPACITY;
tooltipEl.style.position = this.TOOLTIP_POSITION;
tooltipEl.style.left = `${position.left + tooltipModel.caretX - 94}px`;
tooltipEl.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 150}px`;
tooltipArrow.style.opacity = this.TOOLTIP_OPACITY;
tooltipArrow.style.position = this.TOOLTIP_POSITION;
tooltipArrow.style.left = `${position.left + tooltipModel.caretX - 24}px`;
tooltipArrow.style.bottom = `${position.bottom + window.pageYOffset - tooltipModel.caretY + 125}px`;
}
}
</script>
<style lang="scss">
p {
margin: 0;
}
.ingress-chart {
&__data-dimension {
font-size: 13px;
color: #586c86;
margin: 0 0 5px 31px;
font-family: 'font_medium', sans-serif;
}
}
#ingress-tooltip {
background-image: url('../../../static/images/tooltipBack.png');
background-repeat: no-repeat;
background-size: cover;
min-width: 190px;
min-height: 170px;
font-size: 12px;
border-radius: 14px;
box-shadow: 0 2px 10px #d2d6de;
color: #535f77;
pointer-events: none;
}
#ingress-tooltip-arrow {
background-image: url('../../../static/images/tooltipArrow.png');
background-repeat: no-repeat;
background-size: 50px 30px;
min-width: 50px;
min-height: 30px;
pointer-events: none;
}
.ingress-tooltip-body {
margin: 8px;
&__info {
display: flex;
background-color: rgba(254, 238, 215, 0.3);
border-radius: 12px;
padding: 14px;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
position: relative;
color: #6e4f15;
}
}
.ingress-tooltip-bold-text {
font-family: 'font_bold', sans-serif;
font-size: 14px;
}
.ingress-tooltip-footer {
position: relative;
font-size: 12px;
width: auto;
display: flex;
align-items: center;
justify-content: center;
padding: 10px 0 16px 0;
color: rgba(83, 95, 119, 0.44);
}
</style>

View File

@ -24,14 +24,15 @@
<div class="chart-container__title-area">
<p class="chart-container__title-area__title">Bandwidth Used This Month</p>
<div class="chart-container__title-area__chart-choice-item" :class="{'egress-chart-shown' : isEgressChartShown}" @click.stop="toggleEgressChartShowing">Egress</div>
<div class="chart-container__title-area__chart-choice-item">Ingress</div>
<div class="chart-container__title-area__chart-choice-item" :class="{'ingress-chart-shown' : isIngressChartShown}" @click.stop="toggleIngressChartShowing">Ingress</div>
</div>
<p class="chart-container__amount" v-if="!isEgressChartShown"><b>{{bandwidthSummary}}</b></p>
<p class="chart-container__amount" v-if="isBandwidthChartShown"><b>{{bandwidthSummary}}</b></p>
<p class="chart-container__amount" v-if="isEgressChartShown"><b>{{egressSummary}}</b></p>
<p class="chart-container__amount" v-if="false"><b>{{ingressSummary}}</b></p>
<p class="chart-container__amount" v-if="isIngressChartShown"><b>{{ingressSummary}}</b></p>
<div class="chart-container__chart">
<BandwidthChart v-if="!isEgressChartShown"/>
<BandwidthChart v-if="isBandwidthChartShown"/>
<EgressChart v-if="isEgressChartShown"/>
<IngressChart v-if="isIngressChartShown"/>
</div>
</div>
<div class="chart-container">
@ -94,6 +95,7 @@ import BarInfo from '@/app/components/BarInfo.vue';
import ChecksArea from '@/app/components/ChecksArea.vue';
import DiskSpaceChart from '@/app/components/DiskSpaceChart.vue';
import EgressChart from '@/app/components/EgressChart.vue';
import IngressChart from '@/app/components/IngressChart.vue';
import PayoutArea from '@/app/components/PayoutArea.vue';
import SatelliteSelection from '@/app/components/SatelliteSelection.vue';
@ -119,6 +121,7 @@ class Checks {
@Component ({
components: {
EgressChart,
IngressChart,
SatelliteSelection,
BandwidthChart,
DiskSpaceChart,
@ -129,58 +132,148 @@ class Checks {
},
})
export default class SNOContentFilling extends Vue {
/**
* isBandwidthChartShown showing status of bandwidth chart from store.
* @return boolean - bandwidth chart displaying status
*/
public get isBandwidthChartShown(): boolean {
return this.$store.state.appStateModule.isBandwidthChartShown;
}
/**
* isIngressChartShown showing status of ingress chart from store.
* @return boolean - ingress chart displaying status
*/
public get isIngressChartShown(): boolean {
return this.$store.state.appStateModule.isIngressChartShown;
}
/**
* isEgressChartShown showing status of egress chart from store.
* @return boolean - egress chart displaying status
*/
public get isEgressChartShown(): boolean {
return this.$store.state.appStateModule.isEgressChartShown;
}
/**
* toggleEgressChartShowing toggles displaying of egress chart.
*/
public toggleEgressChartShowing(): void {
this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_EGRESS_CHART);
if (this.isBandwidthChartShown || this.isIngressChartShown) {
this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_EGRESS_CHART);
return;
}
this.$store.dispatch(APPSTATE_ACTIONS.CLOSE_ADDITIONAL_CHARTS);
}
/**
* toggleIngressChartShowing toggles displaying of ingress chart.
*/
public toggleIngressChartShowing(): void {
if (this.isBandwidthChartShown || this.isEgressChartShown) {
this.$store.dispatch(APPSTATE_ACTIONS.TOGGLE_INGRESS_CHART);
return;
}
this.$store.dispatch(APPSTATE_ACTIONS.CLOSE_ADDITIONAL_CHARTS);
}
/**
* wallet - wallet address as string from store.
* @return string - wallet address
*/
public get wallet(): string {
return this.$store.state.node.info.wallet;
}
/**
* bandwidthSummary - amount of monthly bandwidth used from store.
* @return string - formatted amount of monthly bandwidth used
*/
public get bandwidthSummary(): string {
return formatBytes(this.$store.state.node.bandwidthSummary);
}
/**
* egressSummary - amount of monthly egress used from store.
* @return string - formatted amount of monthly egress used
*/
public get egressSummary(): string {
return formatBytes(this.$store.state.node.egressSummary);
}
/**
* ingressSummary - amount of monthly ingress used from store.
* @return string - formatted amount of monthly ingress used
*/
public get ingressSummary(): string {
return formatBytes(this.$store.state.node.ingressSummary);
}
/**
* storageSummary - amount of monthly disk space used from store.
* @return string - formatted amount of monthly disk space used
*/
public get storageSummary(): string {
return formatBytes(this.$store.state.node.storageSummary);
}
/**
* bandwidth - remaining amount of bandwidth from store.
* @return BandwidthInfo - remaining amount of bandwidth
*/
public get bandwidth(): BandwidthInfo {
return this.$store.state.node.utilization.bandwidth;
}
/**
* diskSpace - remaining amount of diskSpace from store.
* @return DiskSpaceInfo - remaining amount of diskSpace
*/
public get diskSpace(): DiskSpaceInfo {
return this.$store.state.node.utilization.diskSpace;
}
/**
* checks - uptime and audit checks statuses from store.
* @return Checks - uptime and audit checks statuses
*/
public get checks(): Checks {
return this.$store.state.node.checks;
}
/**
* selectedSatellite - current selected satellite from store.
* @return SatelliteInfo - current selected satellite
*/
public get selectedSatellite(): SatelliteInfo {
return this.$store.state.node.selectedSatellite;
}
/**
* disqualifiedSatellites - array of disqualified satellites from store.
* @return SatelliteInfo[] - array of disqualified satellites
*/
public get disqualifiedSatellites(): SatelliteInfo[] {
return this.$store.state.node.disqualifiedSatellites;
}
/**
* isDisqualifiedInfoShown checks if disqualification status is shown.
* @return boolean - disqualification status
*/
public get isDisqualifiedInfoShown(): boolean {
return !!(this.selectedSatellite.id && this.selectedSatellite.disqualified);
}
/**
* getDisqualificationDate gets a date of disqualification.
* @return String - date of disqualification
*/
public get getDisqualificationDate(): string {
if (this.selectedSatellite.disqualified) {
return this.selectedSatellite.disqualified.toUTCString();
@ -189,6 +282,10 @@ export default class SNOContentFilling extends Vue {
return '';
}
/**
* doDisqualifiedSatellitesExist checks if disqualified satellites exist.
* @return boolean - disqualified satellites existing status
*/
public get doDisqualifiedSatellitesExist(): boolean {
return this.disqualifiedSatellites.length > 0;
}
@ -292,4 +389,9 @@ export default class SNOContentFilling extends Vue {
background-color: #d3f2cc;
color: #2e5f46;
}
.ingress-chart-shown {
background-color: #ffeac2;
color: #c48c4b;
}
</style>

View File

@ -5,6 +5,8 @@ export const APPSTATE_MUTATIONS = {
TOGGLE_SATELLITE_SELECTION: 'TOGGLE_SATELLITE_SELECTION',
TOGGLE_BANDWIDTH_CHART: 'TOGGLE_BANDWIDTH_CHART',
TOGGLE_EGRESS_CHART: 'TOGGLE_EGRESS_CHART',
TOGGLE_INGRESS_CHART: 'TOGGLE_INGRESS_CHART,',
CLOSE_ADDITIONAL_CHARTS: 'CLOSE_ADDITIONAL_CHARTS',
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
};
@ -12,6 +14,8 @@ export const APPSTATE_ACTIONS = {
TOGGLE_SATELLITE_SELECTION: 'TOGGLE_SATELLITE_SELECTION',
TOGGLE_BANDWIDTH_CHART: 'TOGGLE_BANDWIDTH_CHART',
TOGGLE_EGRESS_CHART: 'TOGGLE_EGRESS_CHART',
TOGGLE_INGRESS_CHART: 'TOGGLE_INGRESS_CHART',
CLOSE_ADDITIONAL_CHARTS: 'CLOSE_ADDITIONAL_CHARTS',
CLOSE_ALL_POPUPS: 'CLOSE_ALL_POPUPS',
};
@ -19,6 +23,8 @@ const {
TOGGLE_SATELLITE_SELECTION,
TOGGLE_BANDWIDTH_CHART,
TOGGLE_EGRESS_CHART,
TOGGLE_INGRESS_CHART,
CLOSE_ADDITIONAL_CHARTS,
CLOSE_ALL_POPUPS,
} = APPSTATE_MUTATIONS;
@ -27,6 +33,7 @@ export const appStateModule = {
isSatelliteSelectionShown: false,
isBandwidthChartShown: true,
isEgressChartShown: false,
isIngressChartShown: false,
},
mutations: {
[TOGGLE_SATELLITE_SELECTION](state: any): void {
@ -38,6 +45,14 @@ export const appStateModule = {
[TOGGLE_EGRESS_CHART](state: any): void {
state.isEgressChartShown = !state.isEgressChartShown;
},
[TOGGLE_INGRESS_CHART](state: any): void {
state.isIngressChartShown = !state.isIngressChartShown;
},
[CLOSE_ADDITIONAL_CHARTS](state: any): void {
state.isBandwidthChartShown = true;
state.isIngressChartShown = false;
state.isEgressChartShown = false;
},
[CLOSE_ALL_POPUPS](state: any): void {
state.isSatelliteSelectionShown = false;
},
@ -52,10 +67,32 @@ export const appStateModule = {
commit(APPSTATE_MUTATIONS.CLOSE_ALL_POPUPS);
},
[APPSTATE_ACTIONS.TOGGLE_EGRESS_CHART]: function ({commit}: any): void {
[APPSTATE_ACTIONS.TOGGLE_EGRESS_CHART]: function ({commit, state}: any): void {
if (!state.isBandwidthChartShown) {
commit(APPSTATE_MUTATIONS.TOGGLE_EGRESS_CHART);
commit(APPSTATE_MUTATIONS.TOGGLE_INGRESS_CHART);
return;
}
commit(APPSTATE_MUTATIONS.TOGGLE_BANDWIDTH_CHART);
commit(APPSTATE_MUTATIONS.TOGGLE_EGRESS_CHART);
},
[APPSTATE_ACTIONS.TOGGLE_INGRESS_CHART]: function ({commit, state}: any): void {
if (!state.isBandwidthChartShown) {
commit(APPSTATE_MUTATIONS.TOGGLE_INGRESS_CHART);
commit(APPSTATE_MUTATIONS.TOGGLE_EGRESS_CHART);
return;
}
commit(APPSTATE_MUTATIONS.TOGGLE_BANDWIDTH_CHART);
commit(APPSTATE_MUTATIONS.TOGGLE_INGRESS_CHART);
},
[APPSTATE_ACTIONS.CLOSE_ADDITIONAL_CHARTS]: function ({commit}: any): void {
commit(APPSTATE_MUTATIONS.CLOSE_ADDITIONAL_CHARTS);
},
[APPSTATE_ACTIONS.CLOSE_ALL_POPUPS]: function ({commit}: any): void {
commit(APPSTATE_MUTATIONS.CLOSE_ALL_POPUPS);
}