mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 04:05:56 +08:00
fix: resolve the list view issues (#3020)
* feat: add dynamic table based on query * feat: add the list view for the traces explorer * fix: fix the options menu * feat: update the list view columns config for the traces explorer * feat: fix columns for the list view for the traces explorer page * feat: update customization columns for the list view from the traces explorer * feat: add error msg for the list view, fix creating data for the table * fix: resolve the list view issues * fix: update the date column for the list view * fix: remove additional filter title for the list view * fix: add initial orderBy filter for the list view --------- Co-authored-by: Yevhen Shevchenko <y.shevchenko@seedium.io> Co-authored-by: Nazarenko19 <danil.nazarenko2000@gmail.com> Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
b1c1a95e29
commit
8363dadd8d
@ -38,20 +38,12 @@ function AddColumnField({ config }: AddColumnFieldProps): JSX.Element | null {
|
|||||||
</SearchIconWrapper>
|
</SearchIconWrapper>
|
||||||
</Input.Group>
|
</Input.Group>
|
||||||
|
|
||||||
{config.value.map((selectedValue: string) => {
|
{config.value?.map(({ key, id }) => (
|
||||||
const option = config?.options?.find(
|
<AddColumnItem direction="horizontal" key={id}>
|
||||||
({ value }) => value === selectedValue,
|
<Typography>{key}</Typography>
|
||||||
);
|
<DeleteOutlinedIcon onClick={(): void => config.onRemove(id as string)} />
|
||||||
|
</AddColumnItem>
|
||||||
return (
|
))}
|
||||||
<AddColumnItem direction="horizontal" key={option?.value}>
|
|
||||||
<Typography>{option?.label}</Typography>
|
|
||||||
<DeleteOutlinedIcon
|
|
||||||
onClick={(): void => config.onRemove(selectedValue)}
|
|
||||||
/>
|
|
||||||
</AddColumnItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</AddColumnWrapper>
|
</AddColumnWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@ export interface InitialOptions
|
|||||||
export type OptionsMenuConfig = {
|
export type OptionsMenuConfig = {
|
||||||
format?: Pick<RadioProps, 'value' | 'onChange'>;
|
format?: Pick<RadioProps, 'value' | 'onChange'>;
|
||||||
maxLines?: Pick<InputNumberProps, 'value' | 'onChange'>;
|
maxLines?: Pick<InputNumberProps, 'value' | 'onChange'>;
|
||||||
addColumn?: Pick<SelectProps, 'options' | 'value' | 'onChange'> & {
|
addColumn?: Pick<SelectProps, 'options' | 'onChange'> & {
|
||||||
|
value: BaseAutocompleteData[];
|
||||||
onRemove: (key: string) => void;
|
onRemove: (key: string) => void;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -63,15 +63,16 @@ const useOptionsMenu = ({
|
|||||||
[initialOptions, attributeKeys],
|
[initialOptions, attributeKeys],
|
||||||
);
|
);
|
||||||
|
|
||||||
const options = useMemo(() => getOptionsFromKeys(attributeKeys), [
|
|
||||||
attributeKeys,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const selectedColumnKeys = useMemo(
|
const selectedColumnKeys = useMemo(
|
||||||
() => optionsQueryData?.selectColumns?.map(({ id }) => id) || [],
|
() => optionsQueryData?.selectColumns?.map(({ id }) => id) || [],
|
||||||
[optionsQueryData],
|
[optionsQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const addColumnOptions = useMemo(
|
||||||
|
() => getOptionsFromKeys(attributeKeys, selectedColumnKeys),
|
||||||
|
[attributeKeys, selectedColumnKeys],
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelectedColumnsChange = useCallback(
|
const handleSelectedColumnsChange = useCallback(
|
||||||
(value: string[]) => {
|
(value: string[]) => {
|
||||||
const newSelectedColumnKeys = [
|
const newSelectedColumnKeys = [
|
||||||
@ -135,8 +136,8 @@ const useOptionsMenu = ({
|
|||||||
const optionsMenuConfig: Required<OptionsMenuConfig> = useMemo(
|
const optionsMenuConfig: Required<OptionsMenuConfig> = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
addColumn: {
|
addColumn: {
|
||||||
value: selectedColumnKeys || defaultOptionsQuery.selectColumns,
|
value: optionsQueryData?.selectColumns || defaultOptionsQuery.selectColumns,
|
||||||
options: options || [],
|
options: addColumnOptions || [],
|
||||||
onChange: handleSelectedColumnsChange,
|
onChange: handleSelectedColumnsChange,
|
||||||
onRemove: handleRemoveSelectedColumn,
|
onRemove: handleRemoveSelectedColumn,
|
||||||
},
|
},
|
||||||
@ -150,10 +151,10 @@ const useOptionsMenu = ({
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
options,
|
addColumnOptions,
|
||||||
selectedColumnKeys,
|
|
||||||
optionsQueryData?.maxLines,
|
optionsQueryData?.maxLines,
|
||||||
optionsQueryData?.format,
|
optionsQueryData?.format,
|
||||||
|
optionsQueryData?.selectColumns,
|
||||||
handleSelectedColumnsChange,
|
handleSelectedColumnsChange,
|
||||||
handleRemoveSelectedColumn,
|
handleRemoveSelectedColumn,
|
||||||
handleFormatChange,
|
handleFormatChange,
|
||||||
|
@ -3,12 +3,18 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe
|
|||||||
|
|
||||||
export const getOptionsFromKeys = (
|
export const getOptionsFromKeys = (
|
||||||
keys: BaseAutocompleteData[],
|
keys: BaseAutocompleteData[],
|
||||||
): SelectProps['options'] =>
|
selectedKeys: (string | undefined)[],
|
||||||
keys.map(({ id, key }) => ({
|
): SelectProps['options'] => {
|
||||||
|
const options = keys.map(({ id, key }) => ({
|
||||||
label: key,
|
label: key,
|
||||||
value: id,
|
value: id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return options.filter(
|
||||||
|
({ value }) => !selectedKeys.find((key) => key === value),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const getInitialColumns = (
|
export const getInitialColumns = (
|
||||||
initialColumnTitles: string[],
|
initialColumnTitles: string[],
|
||||||
attributeKeys: BaseAutocompleteData[],
|
attributeKeys: BaseAutocompleteData[],
|
||||||
|
@ -230,19 +230,21 @@ export const Query = memo(function Query({
|
|||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={11}>
|
{panelType !== PANEL_TYPES.LIST && (
|
||||||
<Row gutter={[11, 5]}>
|
<Col span={11}>
|
||||||
<Col flex="5.93rem">
|
<Row gutter={[11, 5]}>
|
||||||
<FilterLabel label="Aggregate Every" />
|
<Col flex="5.93rem">
|
||||||
</Col>
|
<FilterLabel label="Aggregate Every" />
|
||||||
<Col flex="1 1 6rem">
|
</Col>
|
||||||
<AggregateEveryFilter
|
<Col flex="1 1 6rem">
|
||||||
query={query}
|
<AggregateEveryFilter
|
||||||
onChange={handleChangeAggregateEvery}
|
query={query}
|
||||||
/>
|
onChange={handleChangeAggregateEvery}
|
||||||
</Col>
|
/>
|
||||||
</Row>
|
</Col>
|
||||||
</Col>
|
</Row>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -326,8 +328,11 @@ export const Query = memo(function Query({
|
|||||||
</Col>
|
</Col>
|
||||||
<Col flex="1 1 12.5rem">
|
<Col flex="1 1 12.5rem">
|
||||||
<AggregatorFilter
|
<AggregatorFilter
|
||||||
onChange={handleChangeAggregatorAttribute}
|
|
||||||
query={query}
|
query={query}
|
||||||
|
onChange={handleChangeAggregatorAttribute}
|
||||||
|
disabled={
|
||||||
|
panelType === PANEL_TYPES.LIST || panelType === PANEL_TYPES.TRACE
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -362,14 +367,16 @@ export const Query = memo(function Query({
|
|||||||
</AdditionalFiltersToggler>
|
</AdditionalFiltersToggler>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
<Row style={{ width: '100%' }}>
|
{panelType !== PANEL_TYPES.LIST && panelType !== PANEL_TYPES.TRACE && (
|
||||||
<Input
|
<Row style={{ width: '100%' }}>
|
||||||
onChange={handleChangeQueryLegend}
|
<Input
|
||||||
size="middle"
|
onChange={handleChangeQueryLegend}
|
||||||
value={query.legend}
|
size="middle"
|
||||||
addonBefore="Legend Format"
|
value={query.legend}
|
||||||
/>
|
addonBefore="Legend Format"
|
||||||
</Row>
|
/>
|
||||||
|
</Row>
|
||||||
|
)}
|
||||||
</ListItemWrapper>
|
</ListItemWrapper>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { AutoCompleteProps } from 'antd';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
export type AgregatorFilterProps = {
|
export type AgregatorFilterProps = Pick<AutoCompleteProps, 'disabled'> & {
|
||||||
onChange: (value: BaseAutocompleteData) => void;
|
|
||||||
query: IBuilderQuery;
|
query: IBuilderQuery;
|
||||||
|
onChange: (value: BaseAutocompleteData) => void;
|
||||||
};
|
};
|
||||||
|
@ -28,8 +28,9 @@ import { selectStyle } from '../QueryBuilderSearch/config';
|
|||||||
import { AgregatorFilterProps } from './AggregatorFilter.intefaces';
|
import { AgregatorFilterProps } from './AggregatorFilter.intefaces';
|
||||||
|
|
||||||
export const AggregatorFilter = memo(function AggregatorFilter({
|
export const AggregatorFilter = memo(function AggregatorFilter({
|
||||||
onChange,
|
|
||||||
query,
|
query,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
}: AgregatorFilterProps): JSX.Element {
|
}: AgregatorFilterProps): JSX.Element {
|
||||||
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
||||||
const debouncedValue = useDebounce(query.aggregateAttribute.key, 300);
|
const debouncedValue = useDebounce(query.aggregateAttribute.key, 300);
|
||||||
@ -119,6 +120,7 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
options={optionsData}
|
options={optionsData}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChangeAttribute}
|
onChange={handleChangeAttribute}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
getLabelFromValue,
|
getLabelFromValue,
|
||||||
mapLabelValuePairs,
|
mapLabelValuePairs,
|
||||||
orderByValueDelimiter,
|
orderByValueDelimiter,
|
||||||
|
transformToOrderByStringValues,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
export function OrderByFilter({
|
export function OrderByFilter({
|
||||||
@ -25,7 +26,9 @@ export function OrderByFilter({
|
|||||||
onChange,
|
onChange,
|
||||||
}: OrderByFilterProps): JSX.Element {
|
}: OrderByFilterProps): JSX.Element {
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
const [selectedValue, setSelectedValue] = useState<IOption[]>([]);
|
const [selectedValue, setSelectedValue] = useState<IOption[]>(
|
||||||
|
transformToOrderByStringValues(query.orderBy) || [],
|
||||||
|
);
|
||||||
|
|
||||||
const { data, isFetching } = useQuery(
|
const { data, isFetching } = useQuery(
|
||||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
||||||
|
@ -21,7 +21,7 @@ function TimeSeriesView({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
[data],
|
[data?.payload?.data?.result],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
|
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
|
||||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
@ -15,22 +12,17 @@ import TimeSeriesView from './TimeSeriesView';
|
|||||||
function TimeSeriesViewContainer({
|
function TimeSeriesViewContainer({
|
||||||
dataSource = DataSource.TRACES,
|
dataSource = DataSource.TRACES,
|
||||||
}: TimeSeriesViewProps): JSX.Element {
|
}: TimeSeriesViewProps): JSX.Element {
|
||||||
const { stagedQuery } = useQueryBuilder();
|
const { stagedQuery, panelType } = useQueryBuilder();
|
||||||
|
|
||||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||||
AppState,
|
AppState,
|
||||||
GlobalReducer
|
GlobalReducer
|
||||||
>((state) => state.globalTime);
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
const { queryData: panelTypeParam } = useUrlQueryData<GRAPH_TYPES>(
|
|
||||||
PANEL_TYPES_QUERY,
|
|
||||||
PANEL_TYPES.TIME_SERIES,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, isLoading, isError } = useGetQueryRange(
|
const { data, isLoading, isError } = useGetQueryRange(
|
||||||
{
|
{
|
||||||
query: stagedQuery || initialQueriesMap[dataSource],
|
query: stagedQuery || initialQueriesMap[dataSource],
|
||||||
graphType: panelTypeParam,
|
graphType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||||
selectedTime: 'GLOBAL_TIME',
|
selectedTime: 'GLOBAL_TIME',
|
||||||
globalSelectedInterval: globalSelectedTime,
|
globalSelectedInterval: globalSelectedTime,
|
||||||
params: {
|
params: {
|
||||||
@ -45,7 +37,7 @@ function TimeSeriesViewContainer({
|
|||||||
minTime,
|
minTime,
|
||||||
stagedQuery,
|
stagedQuery,
|
||||||
],
|
],
|
||||||
enabled: !!stagedQuery && panelTypeParam === PANEL_TYPES.TIME_SERIES,
|
enabled: !!stagedQuery && panelType === PANEL_TYPES.TIME_SERIES,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -65,7 +65,8 @@ function ListView(): JSX.Element {
|
|||||||
paginationQueryData,
|
paginationQueryData,
|
||||||
options?.selectColumns,
|
options?.selectColumns,
|
||||||
],
|
],
|
||||||
enabled: !!stagedQuery && panelType === PANEL_TYPES.LIST,
|
enabled:
|
||||||
|
!!stagedQuery && panelType === PANEL_TYPES.LIST && !!options?.selectColumns,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -15,3 +15,7 @@ export const Container = styled.div`
|
|||||||
export const ErrorText = styled(Typography)`
|
export const ErrorText = styled(Typography)`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DateText = styled(Typography)`
|
||||||
|
min-width: 145px;
|
||||||
|
`;
|
||||||
|
@ -9,6 +9,8 @@ import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
|||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||||
|
|
||||||
|
import { DateText } from './styles';
|
||||||
|
|
||||||
export const transformDataWithDate = (data: QueryDataV3[]): QueryDataV3[] =>
|
export const transformDataWithDate = (data: QueryDataV3[]): QueryDataV3[] =>
|
||||||
data.map((query) => ({
|
data.map((query) => ({
|
||||||
...query,
|
...query,
|
||||||
@ -78,9 +80,10 @@ export const modifyColumns = (
|
|||||||
if (key === 'date') {
|
if (key === 'date') {
|
||||||
return {
|
return {
|
||||||
...column,
|
...column,
|
||||||
|
width: 145,
|
||||||
render: (date: string): JSX.Element => {
|
render: (date: string): JSX.Element => {
|
||||||
const day = dayjs(date);
|
const day = dayjs(date);
|
||||||
return <Typography>{day.format('YYYY/MM/DD HH:mm:ss')}</Typography>;
|
return <DateText>{day.format('YYYY/MM/DD HH:mm:ss')}</DateText>;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,16 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const getNewListOfAdditionalFilters = useCallback(
|
const getNewListOfAdditionalFilters = useCallback(
|
||||||
(dataSource: DataSource): string[] =>
|
(dataSource: DataSource): string[] => {
|
||||||
mapOfFilters[dataSource].map((item) => item.text),
|
const listOfFilters = mapOfFilters[dataSource].map((item) => item.text);
|
||||||
[],
|
|
||||||
|
if (panelType === PANEL_TYPES.LIST) {
|
||||||
|
return listOfFilters.filter((filter) => filter !== 'Aggregation interval');
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOfFilters;
|
||||||
|
},
|
||||||
|
[panelType],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeAggregatorAttribute = useCallback(
|
const handleChangeAggregatorAttribute = useCallback(
|
||||||
|
@ -52,15 +52,26 @@ function TracesExplorer(): JSX.Element {
|
|||||||
return groupByCount > 0;
|
return groupByCount > 0;
|
||||||
}, [currentQuery]);
|
}, [currentQuery]);
|
||||||
|
|
||||||
const defaultQuery = useMemo(
|
const defaultQuery = useMemo(() => {
|
||||||
() =>
|
const query = updateAllQueriesOperators(
|
||||||
updateAllQueriesOperators(
|
initialQueriesMap.traces,
|
||||||
initialQueriesMap.traces,
|
PANEL_TYPES.LIST,
|
||||||
PANEL_TYPES.LIST,
|
DataSource.TRACES,
|
||||||
DataSource.TRACES,
|
);
|
||||||
),
|
|
||||||
[updateAllQueriesOperators],
|
return {
|
||||||
);
|
...query,
|
||||||
|
builder: {
|
||||||
|
...query.builder,
|
||||||
|
queryData: [
|
||||||
|
{
|
||||||
|
...query.builder.queryData[0],
|
||||||
|
orderBy: [{ columnName: 'timestamp', order: 'desc' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [updateAllQueriesOperators]);
|
||||||
|
|
||||||
const tabsItems = getTabsItems({
|
const tabsItems = getTabsItems({
|
||||||
isListViewDisabled: isMultipleQueries || isGroupByExist,
|
isListViewDisabled: isMultipleQueries || isGroupByExist,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user