mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 00:39:06 +08:00
refactor: dashboard views (#3175)
* refactor: dashboard views * fix: switch component by another render approach * fix: build * feat: add table panel styles * fix: render query data in table * feat: add separated columns for group by * fix: add return for creation label for formula * fix: table columns Remove label repeating, remove aggregate column if values don’t exist * fix: render columns with empty value * fix: pr comments for panel swither * fix: change key to dataIndex * chore: removed gridgraph component --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
59deac01bd
commit
e6fa1383f3
@ -343,7 +343,7 @@ type CustomChartOptions = ChartOptions & {
|
||||
};
|
||||
};
|
||||
|
||||
interface GraphProps {
|
||||
export interface GraphProps {
|
||||
animate?: boolean;
|
||||
type: ChartType;
|
||||
data: Chart['data'];
|
||||
|
14
frontend/src/constants/panelTypes.ts
Normal file
14
frontend/src/constants/panelTypes.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import Graph from 'components/Graph';
|
||||
import GridTableComponent from 'container/GridTableComponent';
|
||||
import GridValueComponent from 'container/GridValueComponent';
|
||||
|
||||
import { PANEL_TYPES } from './queryBuilder';
|
||||
|
||||
export const PANEL_TYPES_COMPONENT_MAP = {
|
||||
[PANEL_TYPES.TIME_SERIES]: Graph,
|
||||
[PANEL_TYPES.VALUE]: GridValueComponent,
|
||||
[PANEL_TYPES.TABLE]: GridTableComponent,
|
||||
[PANEL_TYPES.TRACE]: null,
|
||||
[PANEL_TYPES.LIST]: null,
|
||||
[PANEL_TYPES.EMPTY_WIDGET]: null,
|
||||
} as const;
|
@ -1,5 +1,4 @@
|
||||
// ** Helpers
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||
import {
|
||||
@ -24,7 +23,6 @@ import {
|
||||
LogsAggregatorOperator,
|
||||
MetricAggregateOperator,
|
||||
NumberOperators,
|
||||
PanelTypeKeys,
|
||||
QueryAdditionalFilter,
|
||||
QueryBuilderData,
|
||||
ReduceOperators,
|
||||
@ -124,7 +122,7 @@ export const initialFilters: TagFilter = {
|
||||
op: 'AND',
|
||||
};
|
||||
|
||||
const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
dataSource: DataSource.METRICS,
|
||||
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||
aggregateOperator: MetricAggregateOperator.NOOP,
|
||||
@ -238,14 +236,15 @@ export const operatorsByTypes: Record<LocalDataType, string[]> = {
|
||||
bool: Object.values(BoolOperators),
|
||||
};
|
||||
|
||||
export const PANEL_TYPES: Record<PanelTypeKeys, GRAPH_TYPES> = {
|
||||
TIME_SERIES: 'graph',
|
||||
VALUE: 'value',
|
||||
TABLE: 'table',
|
||||
LIST: 'list',
|
||||
TRACE: 'trace',
|
||||
EMPTY_WIDGET: 'EMPTY_WIDGET',
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export enum PANEL_TYPES {
|
||||
TIME_SERIES = 'graph',
|
||||
VALUE = 'value',
|
||||
TABLE = 'table',
|
||||
LIST = 'list',
|
||||
TRACE = 'trace',
|
||||
EMPTY_WIDGET = 'EMPTY_WIDGET',
|
||||
}
|
||||
|
||||
export type IQueryBuilderState = 'search';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { StaticLineProps } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
@ -113,13 +113,15 @@ function ChartPreview({
|
||||
<Spinner size="large" tip="Loading..." height="70vh" />
|
||||
)}
|
||||
{chartDataSet && !queryResponse.isError && (
|
||||
<GridGraphComponent
|
||||
<GridPanelSwitch
|
||||
panelType={graphType}
|
||||
title={name}
|
||||
data={chartDataSet}
|
||||
isStacked
|
||||
GRAPH_TYPES={graphType || PANEL_TYPES.TIME_SERIES}
|
||||
name={name || 'Chart Preview'}
|
||||
staticLine={staticLine}
|
||||
panelData={queryResponse.data?.payload.data.newResult.data.result || []}
|
||||
query={query || initialQueriesMap.metrics}
|
||||
/>
|
||||
)}
|
||||
</ChartContainer>
|
||||
|
@ -3,6 +3,7 @@ import { Col, FormInstance, Modal, Tooltip, Typography } from 'antd';
|
||||
import saveAlertApi from 'api/alerts/save';
|
||||
import testAlertApi from 'api/alerts/testAlert';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
|
||||
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
|
||||
@ -58,6 +59,7 @@ function FormAlertRules({
|
||||
|
||||
const {
|
||||
currentQuery,
|
||||
panelType,
|
||||
stagedQuery,
|
||||
handleRunQuery,
|
||||
redirectWithQueryBuilderData,
|
||||
@ -351,7 +353,12 @@ function FormAlertRules({
|
||||
|
||||
const renderQBChartPreview = (): JSX.Element => (
|
||||
<ChartPreview
|
||||
headline={<PlotTag queryType={currentQuery.queryType} />}
|
||||
headline={
|
||||
<PlotTag
|
||||
queryType={currentQuery.queryType}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
/>
|
||||
}
|
||||
name=""
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
@ -361,7 +368,12 @@ function FormAlertRules({
|
||||
|
||||
const renderPromChartPreview = (): JSX.Element => (
|
||||
<ChartPreview
|
||||
headline={<PlotTag queryType={currentQuery.queryType} />}
|
||||
headline={
|
||||
<PlotTag
|
||||
queryType={currentQuery.queryType}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
/>
|
||||
}
|
||||
name="Chart Preview"
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
@ -370,7 +382,12 @@ function FormAlertRules({
|
||||
|
||||
const renderChQueryChartPreview = (): JSX.Element => (
|
||||
<ChartPreview
|
||||
headline={<PlotTag queryType={currentQuery.queryType} />}
|
||||
headline={
|
||||
<PlotTag
|
||||
queryType={currentQuery.queryType}
|
||||
panelType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||
/>
|
||||
}
|
||||
name="Chart Preview"
|
||||
threshold={alertDef.condition?.target}
|
||||
query={stagedQuery}
|
||||
|
@ -1,103 +0,0 @@
|
||||
import { Typography } from 'antd';
|
||||
import { ChartData } from 'chart.js';
|
||||
import Graph, { GraphOnClickHandler, StaticLineProps } from 'components/Graph';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
import ValueGraph from 'components/ValueGraph';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import history from 'lib/history';
|
||||
|
||||
import { TitleContainer, ValueContainer } from './styles';
|
||||
|
||||
function GridGraphComponent({
|
||||
GRAPH_TYPES,
|
||||
data,
|
||||
title,
|
||||
opacity,
|
||||
isStacked,
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit,
|
||||
staticLine,
|
||||
onDragSelect,
|
||||
}: GridGraphComponentProps): JSX.Element | null {
|
||||
const location = history.location.pathname;
|
||||
|
||||
const isDashboardPage = location.split('/').length === 3;
|
||||
|
||||
if (GRAPH_TYPES === PANEL_TYPES.TIME_SERIES) {
|
||||
return (
|
||||
<Graph
|
||||
{...{
|
||||
data,
|
||||
title,
|
||||
type: 'line',
|
||||
isStacked,
|
||||
opacity,
|
||||
xAxisType: 'time',
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit,
|
||||
staticLine,
|
||||
onDragSelect,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (GRAPH_TYPES === PANEL_TYPES.VALUE) {
|
||||
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
||||
|
||||
if (data.datasets.length === 0) {
|
||||
return (
|
||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||
<Typography>No Data</Typography>
|
||||
</ValueContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleContainer isDashboardPage={isDashboardPage}>
|
||||
<Typography>{title}</Typography>
|
||||
</TitleContainer>
|
||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||
<ValueGraph
|
||||
value={
|
||||
yAxisUnit
|
||||
? getYAxisFormattedValue(String(value), yAxisUnit)
|
||||
: value.toString()
|
||||
}
|
||||
/>
|
||||
</ValueContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export interface GridGraphComponentProps {
|
||||
GRAPH_TYPES: GRAPH_TYPES;
|
||||
data: ChartData;
|
||||
title?: string;
|
||||
opacity?: string;
|
||||
isStacked?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
staticLine?: StaticLineProps;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
}
|
||||
|
||||
GridGraphComponent.defaultProps = {
|
||||
title: undefined,
|
||||
opacity: undefined,
|
||||
isStacked: undefined,
|
||||
onClickHandler: undefined,
|
||||
yAxisUnit: undefined,
|
||||
staticLine: undefined,
|
||||
onDragSelect: undefined,
|
||||
};
|
||||
|
||||
export default GridGraphComponent;
|
@ -2,7 +2,7 @@ import { Button } from 'antd';
|
||||
import { GraphOnClickHandler } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import {
|
||||
timeItems,
|
||||
timePreferance,
|
||||
@ -85,7 +85,7 @@ function FullView({
|
||||
return (
|
||||
<>
|
||||
{fullViewOptions && (
|
||||
<TimeContainer>
|
||||
<TimeContainer $panelType={widget.panelTypes}>
|
||||
<TimePreference
|
||||
selectedTime={selectedTime}
|
||||
setSelectedTime={setSelectedTime}
|
||||
@ -101,8 +101,8 @@ function FullView({
|
||||
</TimeContainer>
|
||||
)}
|
||||
|
||||
<GridGraphComponent
|
||||
GRAPH_TYPES={widget.panelTypes}
|
||||
<GridPanelSwitch
|
||||
panelType={widget.panelTypes}
|
||||
data={chartDataSet}
|
||||
isStacked={widget.isStacked}
|
||||
opacity={widget.opacity}
|
||||
@ -111,6 +111,8 @@ function FullView({
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
panelData={response.data?.payload.data.newResult.data.result || []}
|
||||
query={widget.query}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -1,4 +1,10 @@
|
||||
import styled from 'styled-components';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import styled, { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||
|
||||
interface Props {
|
||||
$panelType: ITEMS;
|
||||
}
|
||||
|
||||
export const NotFoundContainer = styled.div`
|
||||
display: flex;
|
||||
@ -7,7 +13,13 @@ export const NotFoundContainer = styled.div`
|
||||
min-height: 55vh;
|
||||
`;
|
||||
|
||||
export const TimeContainer = styled.div`
|
||||
export const TimeContainer = styled.div<Props>`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
${({ $panelType }): FlattenSimpleInterpolation =>
|
||||
$panelType === PANEL_TYPES.TABLE
|
||||
? css`
|
||||
margin-bottom: 1rem;
|
||||
`
|
||||
: css``}
|
||||
`;
|
||||
|
@ -2,8 +2,8 @@ import { Typography } from 'antd';
|
||||
import { ChartData } from 'chart.js';
|
||||
import { GraphOnClickHandler } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import { UpdateDashboard } from 'container/GridGraphLayout/utils';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
@ -272,8 +272,8 @@ function GridCardGraph({
|
||||
allowEdit={allowEdit}
|
||||
/>
|
||||
</div>
|
||||
<GridGraphComponent
|
||||
GRAPH_TYPES={widget?.panelTypes}
|
||||
<GridPanelSwitch
|
||||
panelType={widget?.panelTypes}
|
||||
data={prevChartDataSetRef}
|
||||
isStacked={widget?.isStacked}
|
||||
opacity={widget?.opacity}
|
||||
@ -281,6 +281,8 @@ function GridCardGraph({
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onClickHandler={onClickHandler}
|
||||
panelData={[]}
|
||||
query={widget.query}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@ -308,8 +310,8 @@ function GridCardGraph({
|
||||
allowEdit={allowEdit}
|
||||
/>
|
||||
</div>
|
||||
<GridGraphComponent
|
||||
GRAPH_TYPES={widget.panelTypes}
|
||||
<GridPanelSwitch
|
||||
panelType={widget.panelTypes}
|
||||
data={prevChartDataSetRef}
|
||||
isStacked={widget.isStacked}
|
||||
opacity={widget.opacity}
|
||||
@ -317,6 +319,8 @@ function GridCardGraph({
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onClickHandler={onClickHandler}
|
||||
panelData={[]}
|
||||
query={widget.query}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@ -363,8 +367,8 @@ function GridCardGraph({
|
||||
{!isEmptyLayout && getModals()}
|
||||
|
||||
{!isEmpty(widget) && !!queryResponse.data?.payload && (
|
||||
<GridGraphComponent
|
||||
GRAPH_TYPES={widget.panelTypes}
|
||||
<GridPanelSwitch
|
||||
panelType={widget.panelTypes}
|
||||
data={chartData}
|
||||
isStacked={widget.isStacked}
|
||||
opacity={widget.opacity}
|
||||
@ -373,6 +377,8 @@ function GridCardGraph({
|
||||
yAxisUnit={yAxisUnit}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={onClickHandler}
|
||||
panelData={queryResponse.data?.payload.data.newResult.data.result || []}
|
||||
query={widget.query}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { PlusOutlined, SaveFilled } from '@ant-design/icons';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
@ -83,7 +84,7 @@ function GraphLayout({
|
||||
key={currentWidget?.id || 'empty'} // don't change this key
|
||||
data-grid={rest}
|
||||
>
|
||||
<Card>
|
||||
<Card $panelType={currentWidget?.panelTypes || PANEL_TYPES.TIME_SERIES}>
|
||||
<Component setLayout={setLayout} />
|
||||
</Card>
|
||||
</CardContainer>
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { Button as ButtonComponent, Card as CardComponent, Space } from 'antd';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { StyledCSS } from 'container/GantChart/Trace/styles';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import RGL, { WidthProvider } from 'react-grid-layout';
|
||||
import styled, { css } from 'styled-components';
|
||||
import styled, { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||
|
||||
const ReactGridLayoutComponent = WidthProvider(RGL);
|
||||
|
||||
export const Card = styled(CardComponent)`
|
||||
interface CardProps {
|
||||
$panelType: ITEMS;
|
||||
}
|
||||
|
||||
export const Card = styled(CardComponent)<CardProps>`
|
||||
&&& {
|
||||
height: 100%;
|
||||
}
|
||||
@ -13,6 +19,12 @@ export const Card = styled(CardComponent)`
|
||||
.ant-card-body {
|
||||
height: 95%;
|
||||
padding: 0;
|
||||
${({ $panelType }): FlattenSimpleInterpolation =>
|
||||
$panelType === PANEL_TYPES.TABLE
|
||||
? css`
|
||||
padding-top: 1.8rem;
|
||||
`
|
||||
: css``}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -21,6 +33,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export const CardContainer = styled.div<Props>`
|
||||
overflow: auto;
|
||||
|
||||
:hover {
|
||||
.react-resizable-handle {
|
||||
position: absolute;
|
||||
@ -44,6 +58,7 @@ export const CardContainer = styled.div<Props>`
|
||||
background-image: ${(): string => `url("${uri}")`};
|
||||
`;
|
||||
}}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
73
frontend/src/container/GridPanelSwitch/index.tsx
Normal file
73
frontend/src/container/GridPanelSwitch/index.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { PANEL_TYPES_COMPONENT_MAP } from 'constants/panelTypes';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
||||
import { FC, memo, useMemo } from 'react';
|
||||
|
||||
import { GridPanelSwitchProps, PropsTypePropsMap } from './types';
|
||||
|
||||
function GridPanelSwitch({
|
||||
panelType,
|
||||
data,
|
||||
title,
|
||||
isStacked,
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit,
|
||||
staticLine,
|
||||
onDragSelect,
|
||||
panelData,
|
||||
query,
|
||||
}: GridPanelSwitchProps): JSX.Element | null {
|
||||
const currentProps: PropsTypePropsMap = useMemo(() => {
|
||||
const result: PropsTypePropsMap = {
|
||||
[PANEL_TYPES.TIME_SERIES]: {
|
||||
type: 'line',
|
||||
data,
|
||||
title,
|
||||
isStacked,
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit,
|
||||
staticLine,
|
||||
onDragSelect,
|
||||
},
|
||||
[PANEL_TYPES.VALUE]: {
|
||||
title,
|
||||
data,
|
||||
yAxisUnit,
|
||||
},
|
||||
[PANEL_TYPES.TABLE]: { ...GRID_TABLE_CONFIG, data: panelData, query },
|
||||
[PANEL_TYPES.LIST]: null,
|
||||
[PANEL_TYPES.TRACE]: null,
|
||||
[PANEL_TYPES.EMPTY_WIDGET]: null,
|
||||
};
|
||||
|
||||
return result;
|
||||
}, [
|
||||
data,
|
||||
isStacked,
|
||||
name,
|
||||
onClickHandler,
|
||||
onDragSelect,
|
||||
staticLine,
|
||||
title,
|
||||
yAxisUnit,
|
||||
panelData,
|
||||
query,
|
||||
]);
|
||||
|
||||
const Component = PANEL_TYPES_COMPONENT_MAP[panelType] as FC<
|
||||
PropsTypePropsMap[typeof panelType]
|
||||
>;
|
||||
const componentProps = useMemo(() => currentProps[panelType], [
|
||||
panelType,
|
||||
currentProps,
|
||||
]);
|
||||
|
||||
if (!Component || !componentProps) return null;
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return <Component {...componentProps} />;
|
||||
}
|
||||
|
||||
export default memo(GridPanelSwitch);
|
37
frontend/src/container/GridPanelSwitch/types.ts
Normal file
37
frontend/src/container/GridPanelSwitch/types.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { ChartData } from 'chart.js';
|
||||
import {
|
||||
GraphOnClickHandler,
|
||||
GraphProps,
|
||||
StaticLineProps,
|
||||
} from 'components/Graph';
|
||||
import { GridTableComponentProps } from 'container/GridTableComponent/types';
|
||||
import { GridValueComponentProps } from 'container/GridValueComponent/types';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
import { PANEL_TYPES } from '../../constants/queryBuilder';
|
||||
|
||||
export type GridPanelSwitchProps = {
|
||||
panelType: ITEMS;
|
||||
data: ChartData;
|
||||
title?: string;
|
||||
opacity?: string;
|
||||
isStacked?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
staticLine?: StaticLineProps;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
panelData: QueryDataV3[];
|
||||
query: Query;
|
||||
};
|
||||
|
||||
export type PropsTypePropsMap = {
|
||||
[PANEL_TYPES.TIME_SERIES]: GraphProps;
|
||||
[PANEL_TYPES.VALUE]: GridValueComponentProps;
|
||||
[PANEL_TYPES.TABLE]: GridTableComponentProps;
|
||||
[PANEL_TYPES.TRACE]: null;
|
||||
[PANEL_TYPES.LIST]: null;
|
||||
[PANEL_TYPES.EMPTY_WIDGET]: null;
|
||||
};
|
9
frontend/src/container/GridTableComponent/config.ts
Normal file
9
frontend/src/container/GridTableComponent/config.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { TableProps } from 'antd';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
|
||||
export const GRID_TABLE_CONFIG: Omit<
|
||||
TableProps<RowData>,
|
||||
'columns' | 'dataSource'
|
||||
> = {
|
||||
size: 'small',
|
||||
};
|
25
frontend/src/container/GridTableComponent/index.tsx
Normal file
25
frontend/src/container/GridTableComponent/index.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { QueryTable } from 'container/QueryTable';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { WrapperStyled } from './styles';
|
||||
import { GridTableComponentProps } from './types';
|
||||
|
||||
function GridTableComponent({
|
||||
data,
|
||||
query,
|
||||
...props
|
||||
}: GridTableComponentProps): JSX.Element {
|
||||
return (
|
||||
<WrapperStyled>
|
||||
<QueryTable
|
||||
query={query}
|
||||
queryTableData={data}
|
||||
loading={false}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
/>
|
||||
</WrapperStyled>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(GridTableComponent);
|
23
frontend/src/container/GridTableComponent/styles.ts
Normal file
23
frontend/src/container/GridTableComponent/styles.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const WrapperStyled = styled.div`
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
& .ant-table-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
& .ant-spin-nested-loading {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& .ant-spin-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
& .ant-table {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
`;
|
10
frontend/src/container/GridTableComponent/types.ts
Normal file
10
frontend/src/container/GridTableComponent/types.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { TableProps } from 'antd';
|
||||
import { LogsExplorerTableProps } from 'container/LogsExplorerTable/LogsExplorerTable.interfaces';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type GridTableComponentProps = { query: Query } & Pick<
|
||||
LogsExplorerTableProps,
|
||||
'data'
|
||||
> &
|
||||
Omit<TableProps<RowData>, 'columns' | 'dataSource'>;
|
3
frontend/src/container/GridValueComponent/config.ts
Normal file
3
frontend/src/container/GridValueComponent/config.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { GridValueComponentProps } from './types';
|
||||
|
||||
export const GridValueConfig: Pick<GridValueComponentProps, 'title'> = {};
|
47
frontend/src/container/GridValueComponent/index.tsx
Normal file
47
frontend/src/container/GridValueComponent/index.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import { Typography } from 'antd';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
import ValueGraph from 'components/ValueGraph';
|
||||
import { memo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { TitleContainer, ValueContainer } from './styles';
|
||||
import { GridValueComponentProps } from './types';
|
||||
|
||||
function GridValueComponent({
|
||||
data,
|
||||
title,
|
||||
yAxisUnit,
|
||||
}: GridValueComponentProps): JSX.Element {
|
||||
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
const isDashboardPage = location.pathname.split('/').length === 3;
|
||||
|
||||
if (data.datasets.length === 0) {
|
||||
return (
|
||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||
<Typography>No Data</Typography>
|
||||
</ValueContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleContainer isDashboardPage={isDashboardPage}>
|
||||
<Typography>{title}</Typography>
|
||||
</TitleContainer>
|
||||
<ValueContainer isDashboardPage={isDashboardPage}>
|
||||
<ValueGraph
|
||||
value={
|
||||
yAxisUnit
|
||||
? getYAxisFormattedValue(String(value), yAxisUnit)
|
||||
: value.toString()
|
||||
}
|
||||
/>
|
||||
</ValueContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(GridValueComponent);
|
7
frontend/src/container/GridValueComponent/types.ts
Normal file
7
frontend/src/container/GridValueComponent/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { ChartData } from 'chart.js';
|
||||
|
||||
export type GridValueComponentProps = {
|
||||
data: ChartData;
|
||||
title?: string;
|
||||
yAxisUnit?: string;
|
||||
};
|
@ -14,6 +14,7 @@ const Items: ItemsProps[] = [
|
||||
Icon: ValueIcon,
|
||||
display: 'Value',
|
||||
},
|
||||
{ name: PANEL_TYPES.TABLE, Icon: TimeSeries, display: 'Table' },
|
||||
];
|
||||
|
||||
export type ITEMS =
|
||||
|
@ -1,20 +1,23 @@
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
|
||||
import QueryTypeTag from '../QueryTypeTag';
|
||||
import { PlotTagWrapperStyled } from './styles';
|
||||
|
||||
interface IPlotTagProps {
|
||||
queryType: EQueryType;
|
||||
panelType: ITEMS;
|
||||
}
|
||||
|
||||
function PlotTag({ queryType }: IPlotTagProps): JSX.Element | null {
|
||||
function PlotTag({ queryType, panelType }: IPlotTagProps): JSX.Element | null {
|
||||
if (queryType === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ marginLeft: '2rem', position: 'absolute', top: '1rem' }}>
|
||||
<PlotTagWrapperStyled $panelType={panelType}>
|
||||
Plotted using <QueryTypeTag queryType={queryType} />
|
||||
</div>
|
||||
</PlotTagWrapperStyled>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Card, Typography } from 'antd';
|
||||
import Spinner from 'components/Spinner';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import getChartData from 'lib/getChartData';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
@ -16,6 +17,7 @@ function WidgetGraph({
|
||||
yAxisUnit,
|
||||
selectedTime,
|
||||
}: WidgetGraphProps): JSX.Element {
|
||||
const { stagedQuery } = useQueryBuilder();
|
||||
const { dashboards } = useSelector<AppState, DashboardReducer>(
|
||||
(state) => state.dashboards,
|
||||
);
|
||||
@ -39,7 +41,7 @@ function WidgetGraph({
|
||||
return <Card>Invalid widget</Card>;
|
||||
}
|
||||
|
||||
const { title, opacity, isStacked } = selectedWidget;
|
||||
const { title, opacity, isStacked, query } = selectedWidget;
|
||||
|
||||
if (getWidgetQueryRange.error) {
|
||||
return (
|
||||
@ -66,14 +68,18 @@ function WidgetGraph({
|
||||
});
|
||||
|
||||
return (
|
||||
<GridGraphComponent
|
||||
<GridPanelSwitch
|
||||
title={title}
|
||||
isStacked={isStacked}
|
||||
opacity={opacity}
|
||||
data={chartDataSet}
|
||||
GRAPH_TYPES={selectedGraph}
|
||||
panelType={selectedGraph}
|
||||
name={widgetId || 'legend_widget'}
|
||||
yAxisUnit={yAxisUnit}
|
||||
panelData={
|
||||
getWidgetQueryRange.data?.payload.data.newResult.data.result || []
|
||||
}
|
||||
query={stagedQuery || query}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -41,12 +41,12 @@ function WidgetGraph({
|
||||
});
|
||||
|
||||
if (selectedWidget === undefined) {
|
||||
return <Card>Invalid widget</Card>;
|
||||
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PlotTag queryType={currentQuery.queryType} />
|
||||
<Container $panelType={selectedGraph}>
|
||||
<PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} />
|
||||
{getWidgetQueryRange.error && (
|
||||
<AlertIconContainer color="red" title={getWidgetQueryRange.error.message}>
|
||||
<InfoCircleOutlined />
|
||||
|
@ -1,15 +1,24 @@
|
||||
import { Card, Tooltip } from 'antd';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Card)`
|
||||
interface Props {
|
||||
$panelType: ITEMS;
|
||||
}
|
||||
|
||||
export const Container = styled(Card)<Props>`
|
||||
&&& {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 1.5rem 0;
|
||||
padding: ${({ $panelType }): string =>
|
||||
$panelType === PANEL_TYPES.TABLE ? '0 0' : '1.5rem 0'};
|
||||
height: 57vh;
|
||||
/* padding-bottom: 2rem; */
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -23,5 +32,14 @@ export const NotFoundContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 55vh;
|
||||
min-height: 47vh;
|
||||
`;
|
||||
|
||||
export const PlotTagWrapperStyled = styled.div<Props>`
|
||||
margin-left: 2rem;
|
||||
margin-top: ${({ $panelType }): string =>
|
||||
$panelType === PANEL_TYPES.TABLE ? '1rem' : '0'};
|
||||
|
||||
margin-bottom: ${({ $panelType }): string =>
|
||||
$panelType === PANEL_TYPES.TABLE ? '1rem' : '0'};
|
||||
`;
|
||||
|
@ -23,11 +23,7 @@ export function QueryTable({
|
||||
[query, queryTableData, renderColumnCell, renderActionCell],
|
||||
);
|
||||
|
||||
const filteredColumns = columns.filter((item) => item.key !== 'timestamp');
|
||||
|
||||
const tableColumns = modifyColumns
|
||||
? modifyColumns(filteredColumns)
|
||||
: filteredColumns;
|
||||
const tableColumns = modifyColumns ? modifyColumns(columns) : columns;
|
||||
|
||||
return (
|
||||
<ResizeTable
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import { ColumnType } from 'antd/lib/table';
|
||||
import {
|
||||
initialFormulaBuilderFormValues,
|
||||
initialQueryBuilderFormValues,
|
||||
} from 'constants/queryBuilder';
|
||||
import { FORMULA_REGEXP } from 'constants/regExp';
|
||||
import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces';
|
||||
import { toCapitalize } from 'lib/toCapitalize';
|
||||
@ -25,9 +29,10 @@ export type RowData = {
|
||||
};
|
||||
|
||||
type DynamicColumn = {
|
||||
key: keyof RowData;
|
||||
query: IBuilderQuery | IBuilderFormula;
|
||||
field: string;
|
||||
dataIndex: string;
|
||||
title: string;
|
||||
sourceLabel: string;
|
||||
data: (string | number)[];
|
||||
type: 'field' | 'operator' | 'formula';
|
||||
// sortable: boolean;
|
||||
@ -55,7 +60,6 @@ type GetDynamicColumns = (
|
||||
|
||||
type ListItemData = ListItem['data'];
|
||||
type ListItemKey = keyof ListItemData;
|
||||
type SeriesItemLabels = SeriesItem['labels'];
|
||||
|
||||
const isFormula = (queryName: string): boolean =>
|
||||
FORMULA_REGEXP.test(queryName);
|
||||
@ -74,32 +78,31 @@ const getQueryByName = <T extends keyof QueryBuilderData>(
|
||||
builder: QueryBuilderData,
|
||||
currentQueryName: string,
|
||||
type: T,
|
||||
): (T extends 'queryData' ? IBuilderQuery : IBuilderFormula) | null => {
|
||||
): T extends 'queryData' ? IBuilderQuery : IBuilderFormula => {
|
||||
const queryArray = builder[type];
|
||||
const defaultValue =
|
||||
type === 'queryData'
|
||||
? initialQueryBuilderFormValues
|
||||
: initialFormulaBuilderFormValues;
|
||||
|
||||
const currentQuery =
|
||||
queryArray.find((q) => q.queryName === currentQueryName) || null;
|
||||
|
||||
if (!currentQuery) return null;
|
||||
queryArray.find((q) => q.queryName === currentQueryName) || defaultValue;
|
||||
|
||||
return currentQuery as T extends 'queryData' ? IBuilderQuery : IBuilderFormula;
|
||||
};
|
||||
|
||||
const createLabels = <T extends ListItemData | SeriesItemLabels>(
|
||||
// labels: T,
|
||||
label: keyof T,
|
||||
const addListLabels = (
|
||||
query: IBuilderQuery | IBuilderFormula,
|
||||
label: ListItemKey,
|
||||
dynamicColumns: DynamicColumns,
|
||||
): void => {
|
||||
if (isValueExist('key', label as string, dynamicColumns)) return;
|
||||
|
||||
// const labelValue = labels[label];
|
||||
|
||||
// const isNumber = !Number.isNaN(parseFloat(String(labelValue)));
|
||||
if (isValueExist('dataIndex', label, dynamicColumns)) return;
|
||||
|
||||
const fieldObj: DynamicColumn = {
|
||||
key: label as string,
|
||||
query,
|
||||
field: 'label',
|
||||
dataIndex: label as string,
|
||||
title: label as string,
|
||||
sourceLabel: label as string,
|
||||
data: [],
|
||||
type: 'field',
|
||||
// sortable: isNumber,
|
||||
@ -108,42 +111,59 @@ const createLabels = <T extends ListItemData | SeriesItemLabels>(
|
||||
dynamicColumns.push(fieldObj);
|
||||
};
|
||||
|
||||
const appendOperatorFormulaColumns = (
|
||||
builder: QueryBuilderData,
|
||||
currentQueryName: string,
|
||||
const addSeriaLabels = (
|
||||
label: string,
|
||||
dynamicColumns: DynamicColumns,
|
||||
query: IBuilderQuery | IBuilderFormula,
|
||||
): void => {
|
||||
const currentFormula = getQueryByName(
|
||||
builder,
|
||||
currentQueryName,
|
||||
'queryFormulas',
|
||||
);
|
||||
if (currentFormula) {
|
||||
let formulaLabel = `${currentFormula.queryName}(${currentFormula.expression})`;
|
||||
if (isValueExist('dataIndex', label, dynamicColumns)) return;
|
||||
|
||||
if (currentFormula.legend) {
|
||||
formulaLabel += ` - ${currentFormula.legend}`;
|
||||
// const labelValue = labels[label];
|
||||
|
||||
// const isNumber = !Number.isNaN(parseFloat(String(labelValue)));
|
||||
|
||||
const fieldObj: DynamicColumn = {
|
||||
query,
|
||||
field: label as string,
|
||||
dataIndex: label,
|
||||
title: label,
|
||||
data: [],
|
||||
type: 'field',
|
||||
// sortable: isNumber,
|
||||
};
|
||||
|
||||
dynamicColumns.push(fieldObj);
|
||||
};
|
||||
|
||||
const addOperatorFormulaColumns = (
|
||||
query: IBuilderFormula | IBuilderQuery,
|
||||
dynamicColumns: DynamicColumns,
|
||||
customLabel?: string,
|
||||
): void => {
|
||||
if (isFormula(query.queryName)) {
|
||||
const formulaQuery = query as IBuilderFormula;
|
||||
let formulaLabel = `${formulaQuery.queryName}(${formulaQuery.expression})`;
|
||||
|
||||
if (formulaQuery.legend) {
|
||||
formulaLabel += ` - ${formulaQuery.legend}`;
|
||||
}
|
||||
|
||||
const formulaColumn: DynamicColumn = {
|
||||
key: currentQueryName,
|
||||
title: formulaLabel,
|
||||
sourceLabel: formulaLabel,
|
||||
query,
|
||||
field: formulaQuery.queryName,
|
||||
dataIndex: formulaQuery.queryName,
|
||||
title: customLabel || formulaLabel,
|
||||
data: [],
|
||||
type: 'formula',
|
||||
// sortable: isNumber,
|
||||
};
|
||||
|
||||
dynamicColumns.push(formulaColumn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const currentQueryData = getQueryByName(
|
||||
builder,
|
||||
currentQueryName,
|
||||
'queryData',
|
||||
);
|
||||
|
||||
if (!currentQueryData) return;
|
||||
const currentQueryData = query as IBuilderQuery;
|
||||
|
||||
let operatorLabel = `${currentQueryData.aggregateOperator}`;
|
||||
if (currentQueryData.aggregateAttribute.key) {
|
||||
@ -159,9 +179,10 @@ const appendOperatorFormulaColumns = (
|
||||
const resultValue = `${toCapitalize(operatorLabel)}`;
|
||||
|
||||
const operatorColumn: DynamicColumn = {
|
||||
key: currentQueryName,
|
||||
title: resultValue,
|
||||
sourceLabel: resultValue,
|
||||
query,
|
||||
field: currentQueryData.queryName,
|
||||
dataIndex: currentQueryData.queryName,
|
||||
title: customLabel || resultValue,
|
||||
data: [],
|
||||
type: 'operator',
|
||||
// sortable: isNumber,
|
||||
@ -170,59 +191,73 @@ const appendOperatorFormulaColumns = (
|
||||
dynamicColumns.push(operatorColumn);
|
||||
};
|
||||
|
||||
const transformColumnTitles = (
|
||||
dynamicColumns: DynamicColumns,
|
||||
): DynamicColumns =>
|
||||
dynamicColumns.map((item) => {
|
||||
if (isFormula(item.field as string)) {
|
||||
return item;
|
||||
}
|
||||
|
||||
const sameValues = dynamicColumns.filter(
|
||||
(column) => column.title === item.title,
|
||||
);
|
||||
|
||||
if (sameValues.length > 1) {
|
||||
return {
|
||||
...item,
|
||||
dataIndex: `${item.title} - ${item.query.queryName}`,
|
||||
title: `${item.title} - ${item.query.queryName}`,
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => {
|
||||
const dynamicColumns: DynamicColumns = [];
|
||||
|
||||
queryTableData.forEach((currentQuery) => {
|
||||
const currentStagedQuery = getQueryByName(
|
||||
query.builder,
|
||||
currentQuery.queryName,
|
||||
isFormula(currentQuery.queryName) ? 'queryFormulas' : 'queryData',
|
||||
);
|
||||
if (currentQuery.list) {
|
||||
currentQuery.list.forEach((listItem) => {
|
||||
Object.keys(listItem.data).forEach((label) => {
|
||||
createLabels<ListItemData>(label as ListItemKey, dynamicColumns);
|
||||
addListLabels(currentStagedQuery, label as ListItemKey, dynamicColumns);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (currentQuery.series) {
|
||||
if (!isValueExist('key', 'timestamp', dynamicColumns)) {
|
||||
dynamicColumns.push({
|
||||
key: 'timestamp',
|
||||
title: 'Timestamp',
|
||||
sourceLabel: 'Timestamp',
|
||||
data: [],
|
||||
type: 'field',
|
||||
// sortable: true,
|
||||
});
|
||||
}
|
||||
|
||||
appendOperatorFormulaColumns(
|
||||
query.builder,
|
||||
currentQuery.queryName,
|
||||
dynamicColumns,
|
||||
const isValuesColumnExist = currentQuery.series.some(
|
||||
(item) => item.values.length > 0,
|
||||
);
|
||||
const isEveryValuesExist = currentQuery.series.every(
|
||||
(item) => item.values.length > 0,
|
||||
);
|
||||
|
||||
if (isValuesColumnExist) {
|
||||
addOperatorFormulaColumns(
|
||||
currentStagedQuery,
|
||||
dynamicColumns,
|
||||
isEveryValuesExist ? undefined : currentStagedQuery.queryName,
|
||||
);
|
||||
}
|
||||
|
||||
currentQuery.series.forEach((seria) => {
|
||||
Object.keys(seria.labels).forEach((label) => {
|
||||
createLabels<SeriesItemLabels>(label, dynamicColumns);
|
||||
if (label === currentQuery?.queryName) return;
|
||||
|
||||
addSeriaLabels(label as string, dynamicColumns, currentStagedQuery);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return dynamicColumns.map((item) => {
|
||||
if (isFormula(item.key as string)) {
|
||||
return item;
|
||||
}
|
||||
|
||||
const sameValues = dynamicColumns.filter(
|
||||
(column) => column.sourceLabel === item.sourceLabel,
|
||||
);
|
||||
|
||||
if (sameValues.length > 1) {
|
||||
return { ...item, title: `${item.title} - ${item.key}` };
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
return transformColumnTitles(dynamicColumns);
|
||||
};
|
||||
|
||||
const fillEmptyRowCells = (
|
||||
@ -231,8 +266,8 @@ const fillEmptyRowCells = (
|
||||
currentColumn: DynamicColumn,
|
||||
): void => {
|
||||
unusedColumnsKeys.forEach((key) => {
|
||||
if (key === currentColumn.key) {
|
||||
const unusedCol = sourceColumns.find((item) => item.key === key);
|
||||
if (key === currentColumn.field) {
|
||||
const unusedCol = sourceColumns.find((item) => item.field === key);
|
||||
|
||||
if (unusedCol) {
|
||||
unusedCol.data.push('N/A');
|
||||
@ -242,40 +277,49 @@ const fillEmptyRowCells = (
|
||||
});
|
||||
};
|
||||
|
||||
const fillData = (
|
||||
seria: SeriesItem,
|
||||
columns: DynamicColumns,
|
||||
queryName: string,
|
||||
value?: SeriesItem['values'][number],
|
||||
): void => {
|
||||
const labelEntries = Object.entries(seria.labels);
|
||||
|
||||
const unusedColumnsKeys = new Set<keyof RowData>(
|
||||
columns.map((item) => item.field),
|
||||
);
|
||||
|
||||
columns.forEach((column) => {
|
||||
if (queryName === column.field && value) {
|
||||
column.data.push(parseFloat(value.value).toFixed(2));
|
||||
unusedColumnsKeys.delete(column.field);
|
||||
return;
|
||||
}
|
||||
|
||||
labelEntries.forEach(([key, currentValue]) => {
|
||||
if (column.field === key) {
|
||||
column.data.push(currentValue);
|
||||
unusedColumnsKeys.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
fillEmptyRowCells(unusedColumnsKeys, columns, column);
|
||||
});
|
||||
};
|
||||
|
||||
const fillDataFromSeria = (
|
||||
seria: SeriesItem,
|
||||
columns: DynamicColumns,
|
||||
queryName: string,
|
||||
): void => {
|
||||
const labelEntries = Object.entries(seria.labels);
|
||||
if (seria.values.length === 0) {
|
||||
fillData(seria, columns, queryName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
seria.values.forEach((value) => {
|
||||
const unusedColumnsKeys = new Set<keyof RowData>(
|
||||
columns.map((item) => item.key),
|
||||
);
|
||||
|
||||
columns.forEach((column) => {
|
||||
if (column.key === 'timestamp') {
|
||||
column.data.push(value.timestamp);
|
||||
unusedColumnsKeys.delete('timestamp');
|
||||
return;
|
||||
}
|
||||
|
||||
if (queryName === column.key) {
|
||||
column.data.push(parseFloat(value.value).toFixed(2));
|
||||
unusedColumnsKeys.delete(column.key);
|
||||
return;
|
||||
}
|
||||
|
||||
labelEntries.forEach(([key, currentValue]) => {
|
||||
if (column.key === key) {
|
||||
column.data.push(currentValue);
|
||||
unusedColumnsKeys.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
fillEmptyRowCells(unusedColumnsKeys, columns, column);
|
||||
});
|
||||
fillData(seria, columns, queryName, value);
|
||||
});
|
||||
};
|
||||
|
||||
@ -284,10 +328,10 @@ const fillDataFromList = (
|
||||
columns: DynamicColumns,
|
||||
): void => {
|
||||
columns.forEach((column) => {
|
||||
if (isFormula(column.key as string)) return;
|
||||
if (isFormula(column.field)) return;
|
||||
|
||||
Object.keys(listItem.data).forEach((label) => {
|
||||
if (column.key === label) {
|
||||
if (column.dataIndex === label) {
|
||||
if (listItem.data[label as ListItemKey] !== '') {
|
||||
column.data.push(listItem.data[label as ListItemKey].toString());
|
||||
} else {
|
||||
@ -331,9 +375,9 @@ const generateData = (
|
||||
|
||||
for (let i = 0; i < rowsLength; i += 1) {
|
||||
const rowData: RowData = dynamicColumns.reduce((acc, item) => {
|
||||
const { key } = item;
|
||||
const { dataIndex } = item;
|
||||
|
||||
acc[key] = item.data[i];
|
||||
acc[dataIndex] = item.data[i];
|
||||
acc.key = uuid();
|
||||
|
||||
return acc;
|
||||
@ -353,10 +397,9 @@ const generateTableColumns = (
|
||||
ColumnsType<RowData>
|
||||
>((acc, item) => {
|
||||
const column: ColumnType<RowData> = {
|
||||
dataIndex: item.key,
|
||||
key: item.key,
|
||||
dataIndex: item.dataIndex,
|
||||
title: item.title,
|
||||
render: renderColumnCell && renderColumnCell[item.key],
|
||||
render: renderColumnCell && renderColumnCell[item.dataIndex],
|
||||
// sorter: item.sortable
|
||||
// ? (a: RowData, b: RowData): number =>
|
||||
// (a[item.key] as number) - (b[item.key] as number)
|
||||
|
Loading…
x
Reference in New Issue
Block a user