web/satellite/vuetify: add charts to project dashboard

Imported chart components from main project to vuetify app project dashboard with slight updates.

Issue:
https://github.com/storj/storj/issues/6071

Change-Id: I51254ea60aab59c7a841784b5dac7d41446e1df4
This commit is contained in:
Vitalii 2023-07-27 14:41:22 +03:00 committed by Storj Robot
parent fcbb37fb66
commit 62f52c829a
3 changed files with 125 additions and 6 deletions

View File

@ -29,6 +29,7 @@ const props = withDefaults(defineProps<{
before: Date,
width: number,
height: number,
isVuetify?: boolean,
}>(), {
settledData: () => [],
allocatedData: () => [],
@ -36,6 +37,7 @@ const props = withDefaults(defineProps<{
before: () => new Date(),
width: 0,
height: 0,
isVuetify: false,
});
const chartKey = ref<number>(0);
@ -99,10 +101,10 @@ function tooltip(tooltipModel: TooltipModel<ChartType>): void {
let tooltipParams: TooltipParams;
if (p.datasetIndex === 0) {
tooltipParams = new TooltipParams(tooltipModel, 'bandwidth-chart', 'settled-bandwidth-tooltip',
settledTooltipMarkUp(tooltipModel), -20, 78);
settledTooltipMarkUp(tooltipModel), -20, props.isVuetify ? 68 : 78);
} else {
tooltipParams = new TooltipParams(tooltipModel, 'bandwidth-chart', 'allocated-bandwidth-tooltip',
allocatedTooltipMarkUp(tooltipModel), 95, 78);
allocatedTooltipMarkUp(tooltipModel), 95, props.isVuetify ? 68 : 78);
}
Tooltip.custom(tooltipParams);

View File

@ -10,6 +10,7 @@
*/
@use './settings';
@import 'static/styles/variables';
// Light Theme
.v-theme--light {

View File

@ -6,6 +6,39 @@
<PageTitleComponent title="Project Overview" />
<PageSubtitleComponent :subtitle="`Your ${limits.objectCount.toLocaleString()} files are stored in ${limits.segmentCount.toLocaleString()} segments around the world.`" />
<v-row class="d-flex align-center justify-center mt-2">
<v-col cols="12" md="6">
<v-card ref="chartContainer" title="Storage" variant="flat" :border="true" rounded="xlg">
<template v-if="!isDataFetching">
<h4 class="pl-4">{{ getDimension(storageUsage) }}</h4>
<StorageChart
:width="chartWidth"
:height="170"
:data="storageUsage"
:since="chartsSinceDate"
:before="chartsBeforeDate"
/>
</template>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card title="Bandwidth" variant="flat" :border="true" rounded="xlg">
<template v-if="!isDataFetching">
<h4 class="pl-4">{{ getDimension([...settledBandwidthUsage, ...allocatedBandwidthUsage]) }}</h4>
<BandwidthChart
:width="chartWidth"
:height="170"
:settled-data="settledBandwidthUsage"
:allocated-data="allocatedBandwidthUsage"
:since="chartsSinceDate"
:before="chartsBeforeDate"
is-vuetify
/>
</template>
</v-card>
</v-col>
</v-row>
<v-row class="d-flex align-center justify-center mt-2">
<v-col cols="12" sm="6" md="4" lg="2">
<CardStatsComponent title="Files" subtitle="Project files" :data="limits.objectCount.toLocaleString()" />
@ -45,20 +78,25 @@
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { VContainer, VRow, VCol } from 'vuetify/components';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { VContainer, VRow, VCol, VCard } from 'vuetify/components';
import { ComponentPublicInstance } from '@vue/runtime-core';
import { useProjectsStore } from '@/store/modules/projectsStore';
import { useProjectMembersStore } from '@/store/modules/projectMembersStore';
import { useAccessGrantsStore } from '@/store/modules/accessGrantsStore';
import { useBillingStore } from '@/store/modules/billingStore';
import { useBucketsStore } from '@/store/modules/bucketsStore';
import { Project, ProjectLimits } from '@/types/projects';
import { DataStamp, Project, ProjectLimits } from '@/types/projects';
import { Dimensions, Size } from '@/utils/bytesSize';
import { ChartUtils } from '@/utils/chart';
import PageTitleComponent from '@poc/components/PageTitleComponent.vue';
import PageSubtitleComponent from '@poc/components/PageSubtitleComponent.vue';
import CardStatsComponent from '@poc/components/CardStatsComponent.vue';
import UsageProgressComponent from '@poc/components/UsageProgressComponent.vue';
import BandwidthChart from '@/components/project/dashboard/BandwidthChart.vue';
import StorageChart from '@/components/project/dashboard/StorageChart.vue';
const projectsStore = useProjectsStore();
const pmStore = useProjectMembersStore();
@ -66,6 +104,10 @@ const agStore = useAccessGrantsStore();
const billingStore = useBillingStore();
const bucketsStore = useBucketsStore();
const isDataFetching = ref<boolean>(true);
const chartWidth = ref<number>(0);
const chartContainer = ref<ComponentPublicInstance>();
/**
* Returns current limits from store.
*/
@ -101,6 +143,62 @@ const bucketsCount = computed((): number => {
return bucketsStore.state.page.totalCount;
});
/**
* Returns charts since date from store.
*/
const chartsSinceDate = computed((): Date => {
return projectsStore.state.chartDataSince;
});
/**
* Returns charts before date from store.
*/
const chartsBeforeDate = computed((): Date => {
return projectsStore.state.chartDataBefore;
});
/**
* Returns storage chart data from store.
*/
const storageUsage = computed((): DataStamp[] => {
return ChartUtils.populateEmptyUsage(
projectsStore.state.storageChartData, chartsSinceDate.value, chartsBeforeDate.value,
);
});
/**
* Returns settled bandwidth chart data from store.
*/
const settledBandwidthUsage = computed((): DataStamp[] => {
return ChartUtils.populateEmptyUsage(
projectsStore.state.settledBandwidthChartData, chartsSinceDate.value, chartsBeforeDate.value,
);
});
/**
* Returns allocated bandwidth chart data from store.
*/
const allocatedBandwidthUsage = computed((): DataStamp[] => {
return ChartUtils.populateEmptyUsage(
projectsStore.state.allocatedBandwidthChartData, chartsSinceDate.value, chartsBeforeDate.value,
);
});
/**
* Used container size recalculation for charts resizing.
*/
function recalculateChartWidth(): void {
chartWidth.value = chartContainer.value?.$el.getBoundingClientRect().width || 0;
}
/**
* Returns dimension for given data values.
*/
function getDimension(dataStamps: DataStamp[]): Dimensions {
const maxValue = Math.max(...dataStamps.map(s => s.value));
return new Size(maxValue).label;
}
/**
* Lifecycle hook after initial render.
* Fetches project limits.
@ -109,14 +207,32 @@ onMounted(async (): Promise<void> => {
const projectID = selectedProject.value.id;
const FIRST_PAGE = 1;
window.addEventListener('resize', recalculateChartWidth);
recalculateChartWidth();
const now = new Date();
const past = new Date();
past.setDate(past.getDate() - 30);
try {
await Promise.all([
projectsStore.getDailyProjectData({ since: past, before: now }),
projectsStore.getProjectLimits(projectID),
billingStore.getProjectUsageAndChargesCurrentRollup(),
pmStore.getProjectMembers(FIRST_PAGE, projectID),
agStore.getAccessGrants(FIRST_PAGE, projectID),
bucketsStore.getBuckets(FIRST_PAGE, projectID),
]);
} catch (error) { /* empty */ }
} catch (error) { /* empty */ } finally {
isDataFetching.value = false;
}
});
/**
* Lifecycle hook before component destruction.
* Removes event listener on window resizing.
*/
onBeforeUnmount((): void => {
window.removeEventListener('resize', recalculateChartWidth);
});
</script>