Skip to content

Commit

Permalink
feat(dcellar-web-ui): add the download quota usage chart (#390)
Browse files Browse the repository at this point in the history
* feat(dcellar-web-ui): add the download quota usage chart

* fix(dcellar-web-ui): total quota usage cal error

* fix(dcellar-web-ui): add loading for the dashboard charts

* docs(dcellar-web-ui): update changelog
  • Loading branch information
devinxl authored Jun 6, 2024
1 parent 944f3a4 commit 6865344
Show file tree
Hide file tree
Showing 13 changed files with 455 additions and 124 deletions.
12 changes: 12 additions & 0 deletions apps/dcellar-web-ui/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
{
"name": "dcellar-web-ui",
"entries": [
{
"version": "1.7.0",
"tag": "dcellar-web-ui_v1.7.0",
"date": "Thu, 06 Jun 2024 03:14:47 GMT",
"comments": {
"minor": [
{
"comment": "Add the download quota usage chart"
}
]
}
},
{
"version": "1.6.1",
"tag": "dcellar-web-ui_v1.6.1",
Expand Down
9 changes: 8 additions & 1 deletion apps/dcellar-web-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Change Log - dcellar-web-ui

This log was last generated on Thu, 30 May 2024 10:00:54 GMT and should not be manually modified.
This log was last generated on Thu, 06 Jun 2024 03:14:47 GMT and should not be manually modified.

## 1.7.0
Thu, 06 Jun 2024 03:14:47 GMT

### Minor changes

- Add the download quota usage chart

## 1.6.1
Thu, 30 May 2024 10:00:54 GMT
Expand Down
2 changes: 1 addition & 1 deletion apps/dcellar-web-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dcellar-web-ui",
"version": "1.6.1",
"version": "1.7.0",
"private": false,
"scripts": {
"dev": "node ./scripts/dev.js -p 3200",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,27 @@ export function useLineChartOptions(options: any, noData: boolean) {
`;
},
},
grid: {
left: 20,
right: 20,
top: 20,
bottom: 0,
containLabel: true,
},
legend: {
icon: 'circle',
itemHeight: 8,
itemWidth: 8,
itemGap: 16,
right: 0,
textStyle: {
fontWeight: 400,
},
},
xAxis: {
type: 'category',
boundaryGap: false,
contianerLabel: true,
axisLabel: {
marginTop: 4,
fontWeight: 500,
Expand Down Expand Up @@ -81,11 +99,6 @@ export function useLineChartOptions(options: any, noData: boolean) {
show: true,
type: 'value',
scale: true,
spiltLine: {
lineStyle: {
color: 'red',
},
},
axisTick: {
show: true,
alignWithLabel: true,
Expand All @@ -111,13 +124,6 @@ export function useLineChartOptions(options: any, noData: boolean) {
fontFamily: 'Inter',
},
},
grid: {
left: 4,
right: 20,
top: 6,
bottom: 0,
containLabel: true,
},
series: [
{
type: 'line',
Expand Down
48 changes: 48 additions & 0 deletions apps/dcellar-web-ui/src/facade/dashboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { get } from '@/base/http';
import { ErrorResponse, commonFault } from './error';

export type GetBucketDailyUsageByOwnerParams = {
page: number;
per_page: number;
owner: string;
};

export type BucketDailyStorageUsage = {
BucketNumID: string;
BucketID: string;
BucketName: string;
Owner: string;
TotalTxCount: number;
DailyTxCount: number;
DailyTxCountList: number[];
DailyTotalChargedStorageSize: string[];
};

export type BucketDailyQuotaUsage = {
BucketID: string;
BucketName: string;
MonthlyQuotaSize: string;
MonthlyQuotaConsumedSize: number;
Date: number;
};

// key is bucketId
export type BucketDailyQuotaUsageResponse = Record<string, BucketDailyQuotaUsage[]>;

export const getBucketDailyStorageUsage = (
params: GetBucketDailyUsageByOwnerParams,
): Promise<[BucketDailyStorageUsage[], null] | ErrorResponse> => {
const url = `/api/chart/daily_bucket/list`;
return get({ url, data: params }).then((res) => {
return [res.result, null];
}, commonFault);
};

export const getBucketDailyQuotaUsage = (
params: GetBucketDailyUsageByOwnerParams,
): Promise<[BucketDailyQuotaUsageResponse, null] | ErrorResponse> => {
const url = `/api/chart/daily_bucket_quota/list`;
return get({ url, data: params }).then((res) => {
return [res.result, null];
}, commonFault);
};
28 changes: 0 additions & 28 deletions apps/dcellar-web-ui/src/facade/explorer.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { LineChart } from '@/components/charts/LineChart';
import { Loading } from '@/components/common/Loading';
import { FilterContainer } from '@/modules/accounts/components/Common';
import { useAppDispatch, useAppSelector } from '@/store';
import { setBucketDailyQuotaFilter } from '@/store/slices/dashboard';
import { formatChartTime } from '@/utils/dashboard';
import { formatBytes } from '@/utils/formatter';
import { getMillisecond, getUtcDayjs } from '@/utils/time';
import { Box, Flex } from '@node-real/uikit';
import { isEmpty } from 'lodash-es';
import { useMemo } from 'react';
import { LABEL_STYLES, VALUE_STYLES } from '../constants';
import { selectFilterQuotaUsageBuckets } from '@/store/slices/dashboard';
import { BN } from '@/utils/math';
import { BucketsFilter } from './BucketsFilter';

export const BucketQuotaUsage = () => {
const dispatch = useAppDispatch();
const loginAccount = useAppSelector((root) => root.persist.loginAccount);
const bucketDailyQuotaUsageRecords = useAppSelector(
(root) => root.dashboard.bucketDailyQuotaUsageRecords,
);
const bucketDailyQuotaUsage = bucketDailyQuotaUsageRecords[loginAccount];
const filteredBuckets = useAppSelector(selectFilterQuotaUsageBuckets());
const isLoading = bucketDailyQuotaUsage === undefined;
const dayjs = getUtcDayjs();
const noData = !isLoading && isEmpty(bucketDailyQuotaUsage);
const bucketNames = (!isLoading && Object.keys(bucketDailyQuotaUsage)) || [''];

const onBucketFiltered = (bucketNames: string[]) => {
dispatch(setBucketDailyQuotaFilter({ loginAccount, bucketNames }));
};

const quotaUsageByTime = useMemo(() => {
const data = bucketDailyQuotaUsage || {};
const filterData = isEmpty(filteredBuckets)
? data
: filteredBuckets.map((item) => data[item]).filter((item) => item !== undefined);
const quotaUsageByTime: Record<
string,
{ MonthlyQuotaSize: string; MonthlyQuotaConsumedSize: string }
> = {};
Object.values(filterData).forEach((quotaUsages) => {
quotaUsages.forEach((item) => {
if (!quotaUsageByTime[item.Date]) {
return (quotaUsageByTime[item.Date] = {
MonthlyQuotaSize: String(item.MonthlyQuotaSize),
MonthlyQuotaConsumedSize: String(item.MonthlyQuotaConsumedSize),
});
}
quotaUsageByTime[item.Date] = {
MonthlyQuotaSize: BN(quotaUsageByTime[item.Date].MonthlyQuotaSize)
.plus(item.MonthlyQuotaSize)
.toString(),
MonthlyQuotaConsumedSize: BN(quotaUsageByTime[item.Date].MonthlyQuotaConsumedSize)
.plus(BN(item.MonthlyQuotaConsumedSize))
.toString(),
};
});
});
return quotaUsageByTime;
}, [bucketDailyQuotaUsage, filteredBuckets]);

const lineOptions = useMemo(() => {
const lineData = Object.entries(quotaUsageByTime).map(([time, quotaData]) => ({
time: getMillisecond(+time),
totalQuota: quotaData.MonthlyQuotaSize,
quotaUsage: quotaData.MonthlyQuotaConsumedSize,
formatTime: dayjs(getMillisecond(+time)).format('DD, MMM'),
formatSize: formatBytes(quotaData.MonthlyQuotaSize),
}));
const xData = lineData.map((item) => item.formatTime);
const yQuotaUsage = lineData.map((item) => item.quotaUsage);
const yTotalQuota = lineData.map((item) => item.totalQuota);

return {
tooltip: {
trigger: 'axis',
content: (params: any) => {
const { data: quotaUsage } =
params.find((item: any) => item.seriesName === 'Quota Usage') || {};
const { data: totalQuota } =
params.find((item: any) => item.seriesName === 'Total Quota') || {};
const curData = lineData[params[0].dataIndex] || {};

const TotalQuotaFragment =
totalQuota !== undefined
? ` <p ${VALUE_STYLES}>Total Quota: ${formatBytes(totalQuota)}</p>`
: '';
const QuotaUsageFragment =
quotaUsage !== undefined
? `<p ${VALUE_STYLES}>Quota Usage: ${formatBytes(quotaUsage)}</p>`
: '';
return `
<p ${LABEL_STYLES}>${formatChartTime(curData.time)}</p>
${TotalQuotaFragment}
${QuotaUsageFragment}
`;
},
},
legend: {
icon: 'circle',
itemHeight: 8,
itemWidth: 8,
itemGap: 16,
right: 12,
textStyle: { fontWeight: 400 },
data: ['Total Quota', 'Quota Usage'],
},
xAxis: {
data: xData,
},
yAxis: {
axisLabel: {
formatter: (value: string, index: number) => {
return formatBytes(value);
},
},
},
series: [
{
symbol: 'circle',
symbolSize: 5,
lineStyle: { color: '#00BA34' },
itemStyle: {
color: '#00BA34',
opacity: 1,
},
smooth: false,
name: 'Quota Usage',
type: 'line',
stack: 'Quota Usage',
data: yQuotaUsage,
},
{
symbol: 'circle',
symbolSize: 5,
lineStyle: { color: '#EE7C11' },
itemStyle: {
color: '#EE7C11',
},
emphasis: { itemStyle: { opacity: 1 } },
animationDuration: 600,
smooth: false,
name: 'Total Quota',
type: 'line',
stack: 'Total Quota',
data: yTotalQuota,
},
],
};
}, [quotaUsageByTime, dayjs]);

return (
<Flex gap={12} flexDirection={'column'}>
<FilterContainer>
<BucketsFilter
bucketNames={bucketNames}
filteredBuckets={filteredBuckets}
onBucketFiltered={onBucketFiltered}
/>
</FilterContainer>
<Box h={290} minW={0}>
<Box w={'100%'} h={'100%'}>
{isLoading && <Loading />}
{!isLoading && <LineChart options={lineOptions} noData={noData} />}
</Box>
</Box>
</Flex>
);
};
Loading

0 comments on commit 6865344

Please sign in to comment.