Skip to content

Commit

Permalink
feat(dcellar-web-ui): add the download quota usage chart
Browse files Browse the repository at this point in the history
  • Loading branch information
devinxl committed May 31, 2024
1 parent 14b9b36 commit ae86b84
Show file tree
Hide file tree
Showing 10 changed files with 448 additions and 124 deletions.
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,173 @@
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, mergeArr } 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 {
selectBucketDailyQuotaUsage,
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 bucketDailyQuotaUsage = useAppSelector(selectBucketDailyQuotaUsage());
const filteredBuckets = useAppSelector(selectFilterQuotaUsageBuckets());
const isLoading = bucketDailyQuotaUsage === undefined;
const dayjs = getUtcDayjs();
const noData = !isLoading && isEmpty(bucketDailyQuotaUsage);
const bucketNames = Object.keys(bucketDailyQuotaUsage);

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

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

const lineOptions = useMemo(() => {
const lineData = Object.entries(groupDataByTime).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,
},
],
};
}, [groupDataByTime, 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 ae86b84

Please sign in to comment.