mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-17 16:02:14 +00:00
add drill down dimensions and metric period compare and modify layout (#22)
* [feature](webapp) add drill down dimensions and metric period compare and modify layout * [feature](webapp) add drill down dimensions and metric period compare and modify layout --------- Co-authored-by: williamhliu <williamhliu@tencent.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { PREFIX_CLS } from '../../../common/constants';
|
||||
import { formatByThousandSeperator } from '../../../utils/utils';
|
||||
import { AggregateInfoType } from '../../../common/type';
|
||||
import PeriodCompareItem from '../MetricCard/PeriodCompareItem';
|
||||
|
||||
type Props = {
|
||||
aggregateInfo: AggregateInfoType;
|
||||
};
|
||||
|
||||
const MetricInfo: React.FC<Props> = ({ aggregateInfo }) => {
|
||||
const { metricInfos } = aggregateInfo || {};
|
||||
const metricInfo = metricInfos?.[0] || {};
|
||||
const { date, value, statistics } = metricInfo || {};
|
||||
|
||||
const prefixCls = `${PREFIX_CLS}-metric-info`;
|
||||
|
||||
return (
|
||||
<div className={prefixCls}>
|
||||
<div className={`${prefixCls}-indicator`}>
|
||||
<div className={`${prefixCls}-date`}>{date}</div>
|
||||
<div className={`${prefixCls}-indicator-value`}>{formatByThousandSeperator(value)}</div>
|
||||
{metricInfos?.length > 0 && (
|
||||
<div className={`${prefixCls}-period-compare`}>
|
||||
{Object.keys(statistics).map((key: any) => (
|
||||
<PeriodCompareItem title={key} value={metricInfos[0].statistics[key]} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MetricInfo;
|
||||
@@ -1,24 +1,27 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CLS_PREFIX, DATE_TYPES } from '../../../common/constants';
|
||||
import { ColumnType, FieldType, MsgDataType } from '../../../common/type';
|
||||
import { ColumnType, DrillDownDimensionType, FieldType, MsgDataType } from '../../../common/type';
|
||||
import { isMobile } from '../../../utils/utils';
|
||||
import { queryData } from '../../../service';
|
||||
import MetricTrendChart from './MetricTrendChart';
|
||||
import classNames from 'classnames';
|
||||
import { Spin } from 'antd';
|
||||
import Table from '../Table';
|
||||
import DrillDownDimensions from '../../DrillDownDimensions';
|
||||
import MetricInfo from './MetricInfo';
|
||||
import FilterSection from '../FilterSection';
|
||||
import moment from 'moment';
|
||||
|
||||
type Props = {
|
||||
data: MsgDataType;
|
||||
triggerResize?: boolean;
|
||||
onApplyAuth?: (domain: string) => void;
|
||||
onCheckMetricInfo?: (data: any) => void;
|
||||
};
|
||||
|
||||
const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onCheckMetricInfo }) => {
|
||||
const { queryColumns, queryResults, entityInfo, chatContext } = data;
|
||||
const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth }) => {
|
||||
const { queryColumns, queryResults, entityInfo, chatContext, queryMode, aggregateInfo } = data;
|
||||
|
||||
const dateOptions = DATE_TYPES[chatContext?.dateInfo?.period] || DATE_TYPES[0];
|
||||
const dateOptions = DATE_TYPES[chatContext?.dateInfo?.period] || DATE_TYPES.DAY;
|
||||
const initialDateOption = dateOptions.find(
|
||||
(option: any) => option.value === chatContext?.dateInfo?.unit
|
||||
)?.value;
|
||||
@@ -29,6 +32,7 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
const [activeMetricField, setActiveMetricField] = useState<FieldType>(chatContext.metrics?.[0]);
|
||||
const [dataSource, setDataSource] = useState<any[]>(queryResults);
|
||||
const [currentDateOption, setCurrentDateOption] = useState<number>(initialDateOption);
|
||||
const [drillDownDimension, setDrillDownDimension] = useState<DrillDownDimensionType>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const dateField: any = columns.find(
|
||||
@@ -57,9 +61,21 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
|
||||
const selectDateOption = (dateOption: number) => {
|
||||
setCurrentDateOption(dateOption);
|
||||
const endDate = moment().subtract(1, 'days').format('YYYY-MM-DD');
|
||||
const startDate = moment(endDate)
|
||||
.subtract(dateOption - 1, 'days')
|
||||
.format('YYYY-MM-DD');
|
||||
onLoadData({
|
||||
metrics: [activeMetricField],
|
||||
dateInfo: { ...chatContext?.dateInfo, unit: dateOption },
|
||||
dimensions: drillDownDimension
|
||||
? [...(chatContext.dimensions || []), drillDownDimension]
|
||||
: undefined,
|
||||
dateInfo: {
|
||||
...chatContext?.dateInfo,
|
||||
startDate,
|
||||
endDate,
|
||||
unit: dateOption,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -67,10 +83,23 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
setActiveMetricField(metricField);
|
||||
onLoadData({
|
||||
dateInfo: { ...chatContext.dateInfo, unit: currentDateOption || chatContext.dateInfo.unit },
|
||||
dimensions: drillDownDimension
|
||||
? [...(chatContext.dimensions || []), drillDownDimension]
|
||||
: undefined,
|
||||
metrics: [metricField],
|
||||
});
|
||||
};
|
||||
|
||||
const onSelectDimension = (dimension?: DrillDownDimensionType) => {
|
||||
setDrillDownDimension(dimension);
|
||||
onLoadData({
|
||||
dateInfo: { ...chatContext.dateInfo, unit: currentDateOption || chatContext.dateInfo.unit },
|
||||
metrics: [activeMetricField],
|
||||
dimensions:
|
||||
dimension === undefined ? undefined : [...(chatContext.dimensions || []), dimension],
|
||||
});
|
||||
};
|
||||
|
||||
if (!currentMetricField) {
|
||||
return null;
|
||||
}
|
||||
@@ -80,6 +109,33 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
return (
|
||||
<div className={prefixCls}>
|
||||
<div className={`${prefixCls}-charts`}>
|
||||
{chatContext.metrics.length > 0 && (
|
||||
<div className={`${prefixCls}-metric-fields`}>
|
||||
{chatContext.metrics.map((metricField: FieldType) => {
|
||||
const metricFieldClass = classNames(`${prefixCls}-metric-field`, {
|
||||
[`${prefixCls}-metric-field-active`]:
|
||||
activeMetricField?.bizName === metricField.bizName &&
|
||||
chatContext.metrics.length > 1,
|
||||
[`${prefixCls}-metric-field-single`]: chatContext.metrics.length === 1,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={metricFieldClass}
|
||||
key={metricField.bizName}
|
||||
onClick={() => {
|
||||
if (chatContext.metrics.length > 1) {
|
||||
onSwitchMetric(metricField);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{metricField.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{aggregateInfo?.metricInfos?.length > 0 && <MetricInfo aggregateInfo={aggregateInfo} />}
|
||||
<FilterSection chatContext={chatContext} />
|
||||
<div className={`${prefixCls}-date-options`}>
|
||||
{dateOptions.map((dateOption: { label: string; value: number }, index: number) => {
|
||||
const dateOptionClass = classNames(`${prefixCls}-date-option`, {
|
||||
@@ -107,41 +163,10 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{chatContext.metrics.length > 0 && (
|
||||
<div className={`${prefixCls}-metric-fields`}>
|
||||
{chatContext.metrics.map((metricField: FieldType) => {
|
||||
const metricFieldClass = classNames(`${prefixCls}-metric-field`, {
|
||||
[`${prefixCls}-metric-field-active`]:
|
||||
activeMetricField?.bizName === metricField.bizName &&
|
||||
chatContext.metrics.length > 1,
|
||||
[`${prefixCls}-metric-field-single`]: chatContext.metrics.length === 1,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={metricFieldClass}
|
||||
key={metricField.bizName}
|
||||
onClick={() => {
|
||||
if (chatContext.metrics.length > 1) {
|
||||
onSwitchMetric(metricField);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* <SemanticInfoPopover
|
||||
classId={chatContext.domainId}
|
||||
uniqueId={metricField.bizName}
|
||||
onDetailBtnClick={onCheckMetricInfo}
|
||||
> */}
|
||||
{metricField.name}
|
||||
{/* </SemanticInfoPopover> */}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{dataSource?.length === 1 ? (
|
||||
<Table data={data} onApplyAuth={onApplyAuth} />
|
||||
) : (
|
||||
<Spin spinning={loading}>
|
||||
<Spin spinning={loading}>
|
||||
{dataSource?.length === 1 ? (
|
||||
<Table data={{ ...data, queryResults: dataSource }} onApplyAuth={onApplyAuth} />
|
||||
) : (
|
||||
<MetricTrendChart
|
||||
domain={entityInfo?.domainInfo.name}
|
||||
dateColumnName={dateColumnName}
|
||||
@@ -151,7 +176,15 @@ const MetricTrend: React.FC<Props> = ({ data, triggerResize, onApplyAuth, onChec
|
||||
triggerResize={triggerResize}
|
||||
onApplyAuth={onApplyAuth}
|
||||
/>
|
||||
</Spin>
|
||||
)}
|
||||
</Spin>
|
||||
{(queryMode === 'METRIC_DOMAIN' || queryMode === 'METRIC_FILTER') && (
|
||||
<DrillDownDimensions
|
||||
domainId={chatContext.domainId}
|
||||
drillDownDimension={drillDownDimension}
|
||||
dimensionFilters={chatContext.dimensionFilters}
|
||||
onSelectDimension={onSelectDimension}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
@metric-trend-prefix-cls: ~'@{supersonic-chat-prefix}-metric-trend';
|
||||
|
||||
@metric-info-prefix-cls: ~'@{supersonic-chat-prefix}-metric-info';
|
||||
|
||||
.@{metric-trend-prefix-cls} {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
row-gap: 4px;
|
||||
|
||||
@@ -35,14 +37,15 @@
|
||||
}
|
||||
|
||||
&-flow-trend-chart {
|
||||
height: 270px;
|
||||
margin-top: 4px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
&-charts {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
row-gap: 16px;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
&-metric-fields {
|
||||
@@ -50,6 +53,8 @@
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
row-gap: 12px;
|
||||
color: var(--text-color);
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
&-metric-field {
|
||||
@@ -85,10 +90,11 @@
|
||||
padding-left: 0;
|
||||
font-weight: 500;
|
||||
cursor: default;
|
||||
color: var(--text-color-secondary);
|
||||
font-size: 15px;
|
||||
color: var(--text-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--text-color-secondary);
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,3 +139,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
.@{metric-info-prefix-cls} {
|
||||
&-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&-date {
|
||||
color: var(--text-color-fourth);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&-indicator-value {
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
font-size: 36px;
|
||||
line-height: 40px;
|
||||
margin-top: 2px;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
&-period-compare {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
margin-top: 2px;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user