feat: duration filter is updated (#1272)

* feat: duration filter is updated

* feat: search filter is added in the trace filter

Co-authored-by: Palash gupta <palash@signoz.io>
This commit is contained in:
Palash 2022-06-23 19:12:43 +05:30 committed by GitHub
parent 1ebf3dbf65
commit bef83d30cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 51 deletions

View File

@ -1,12 +1,19 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { Button, Input } from 'antd';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import { INITIAL_FILTER_VALUE } from 'store/reducers/trace';
import AppActions from 'types/actions';
import { UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE } from 'types/actions/trace';
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
import CheckBoxComponent from '../Common/Checkbox';
const { Search } = Input;
function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const { filter } = useSelector<AppState, TraceReducer>(
const { filter, filterDisplayValue } = useSelector<AppState, TraceReducer>(
(state) => state.traces,
);
@ -15,9 +22,34 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const status = filter.get(name) || {};
const statusObj = Object.keys(status);
const numberOfFilters = filterDisplayValue.get(name) || 0;
const dispatch = useDispatch<Dispatch<AppActions>>();
const [searchFilter, setSearchFilter] = useState<string>('');
const onClickMoreHandler = (): void => {
const newFilterDisplayValue = new Map(filterDisplayValue);
const preValue =
(newFilterDisplayValue.get(name) || 0) + INITIAL_FILTER_VALUE;
newFilterDisplayValue.set(name, preValue);
dispatch({
type: UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE,
payload: newFilterDisplayValue,
});
};
return (
<>
<Search
value={searchFilter}
onChange={(e): void => setSearchFilter(e.target.value)}
style={{
padding: '0 3%',
}}
placeholder="Filter Values"
/>
{statusObj
.sort((a, b) => {
const countA = +status[a];
@ -28,6 +60,13 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
}
return countA - countB;
})
.filter((_, index) => index < numberOfFilters)
.filter((filter) => {
if (searchFilter.length === 0) {
return true;
}
return filter.includes(searchFilter);
})
.map((e) => (
<CheckBoxComponent
key={e}
@ -38,6 +77,12 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
}}
/>
))}
{numberOfFilters && statusObj.length > numberOfFilters && (
<Button onClick={onClickMoreHandler} type="link">
More
</Button>
)}
</>
);
}

View File

@ -1,11 +1,14 @@
/* eslint-disable react/no-unstable-nested-components */
import { Input, Slider } from 'antd';
import { Slider } from 'antd';
import { SliderRangeProps } from 'antd/lib/slider';
import getFilters from 'api/trace/getFilters';
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { getFilter, updateURL } from 'store/actions/trace/util';
@ -15,19 +18,8 @@ import { UPDATE_ALL_FILTERS } from 'types/actions/trace';
import { GlobalReducer } from 'types/reducer/globalTime';
import { TraceReducer } from 'types/reducer/trace';
import { Container, InputContainer, Text } from './styles';
dayjs.extend(durationPlugin);
const getMs = (value: string): string => {
return parseFloat(
dayjs
.duration({
milliseconds: parseInt(value, 10) / 1000000,
})
.format('SSS'),
).toFixed(2);
};
import { Container, InputComponent, InputContainer, Text } from './styles';
import { getMs } from './util';
function Duration(): JSX.Element {
const {
@ -77,17 +69,18 @@ function Duration(): JSX.Element {
preLocalMinDuration.current = parseFloat(minDuration);
}
setPreMax(maxDuration);
setPreMin(minDuration);
setPreMax(getMs(maxDuration));
setPreMin(getMs(minDuration));
}, [getDuration]);
const defaultValue = [parseFloat(preMin), parseFloat(preMax)];
const updatedUrl = async (min: number, max: number): Promise<void> => {
const preSelectedFilter = new Map(selectedFilter);
const preUserSelected = new Map(userSelectedFilter);
preSelectedFilter.set('duration', [String(max), String(min)]);
preSelectedFilter.set('duration', [
String(max * 1000000),
String(min * 1000000),
]);
const response = await getFilters({
end: String(globalTime.maxTime),
@ -137,18 +130,18 @@ function Duration(): JSX.Element {
}
};
const onRangeSliderHandler = (number: [number, number]): void => {
const onRangeSliderHandler = (number: [string, string]): void => {
const [min, max] = number;
setPreMin(min.toString());
setPreMax(max.toString());
setPreMin(min);
setPreMax(max);
};
const debouncedFunction = useDebouncedFn(
(min, max) => {
updatedUrl(min as number, max as number);
},
500,
1500,
undefined,
);
@ -156,8 +149,8 @@ function Duration(): JSX.Element {
event,
) => {
const { value } = event.target;
const min = parseFloat(preMin);
const max = parseFloat(value) * 1000000;
const min = preMin;
const max = value;
onRangeSliderHandler([min, max]);
debouncedFunction(min, max);
@ -167,8 +160,9 @@ function Duration(): JSX.Element {
event,
) => {
const { value } = event.target;
const min = parseFloat(value) * 1000000;
const max = parseFloat(preMax);
const min = value;
const max = preMax;
onRangeSliderHandler([min, max]);
debouncedFunction(min, max);
};
@ -177,45 +171,48 @@ function Duration(): JSX.Element {
updatedUrl(min, max);
};
const TipComponent = useCallback((value) => {
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}, []);
return (
<div>
<Container>
<InputContainer>
<Text>Min</Text>
</InputContainer>
<Input
<InputComponent
addonAfter="ms"
type="number"
onChange={onChangeMinHandler}
value={getMs(preMin)}
value={preMin}
/>
<InputContainer>
<Text>Max</Text>
</InputContainer>
<Input
<InputComponent
addonAfter="ms"
type="number"
onChange={onChangeMaxHandler}
value={getMs(preMax)}
value={preMax}
/>
</Container>
<Container>
<Slider
defaultValue={[defaultValue[0], defaultValue[1]]}
min={parseFloat((preLocalMinDuration.current || 0).toString())}
max={parseFloat((preLocalMaxDuration.current || 0).toString())}
min={Number(getMs(String(preLocalMinDuration.current || 0)))}
max={Number(getMs(String(preLocalMaxDuration.current || 0)))}
range
tipFormatter={(value): JSX.Element => {
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}}
tipFormatter={TipComponent}
onChange={([min, max]): void => {
onRangeSliderHandler([min, max]);
onRangeSliderHandler([String(min), String(max)]);
}}
onAfterChange={onRangeHandler}
value={[parseFloat(preMin), parseFloat(preMax)]}
value={[Number(preMin), Number(preMax)]}
/>
</Container>
</div>

View File

@ -1,4 +1,4 @@
import { Typography } from 'antd';
import { Input, Typography } from 'antd';
import styled from 'styled-components';
export const DurationText = styled.div`
@ -9,6 +9,19 @@ export const DurationText = styled.div`
flex-direction: column;
`;
export const InputComponent = styled(Input)`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type='number'] {
-moz-appearance: textfield;
}
`;
export const InputContainer = styled.div`
width: 100%;
margin-top: 0.5rem;

View File

@ -0,0 +1,13 @@
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
dayjs.extend(durationPlugin);
export const getMs = (value: string): string =>
parseFloat(
dayjs
.duration({
milliseconds: parseInt(value, 10) / 1000000,
})
.format('SSS'),
).toFixed(2);

View File

@ -11,6 +11,7 @@ import {
UPDATE_SELECTED_TAGS,
UPDATE_SPAN_ORDER,
UPDATE_SPAN_ORDER_PARAMS,
UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE,
UPDATE_SPANS_AGGREGATE,
UPDATE_SPANS_AGGREGATE_PAGE_NUMBER,
UPDATE_SPANS_AGGREGATE_PAGE_SIZE,
@ -23,6 +24,8 @@ import {
} from 'types/actions/trace';
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
export const INITIAL_FILTER_VALUE = 4;
const initialValue: TraceReducer = {
filter: new Map(),
filterToFetchData: ['duration', 'status', 'serviceName'],
@ -53,6 +56,17 @@ const initialValue: TraceReducer = {
loading: true,
payload: { items: {} },
},
filterDisplayValue: new Map<TraceFilterEnum, number>([
['component', INITIAL_FILTER_VALUE],
['duration', INITIAL_FILTER_VALUE],
['httpCode', INITIAL_FILTER_VALUE],
['httpHost', INITIAL_FILTER_VALUE],
['httpMethod', INITIAL_FILTER_VALUE],
['httpUrl', INITIAL_FILTER_VALUE],
['operation', INITIAL_FILTER_VALUE],
['serviceName', INITIAL_FILTER_VALUE],
['status', INITIAL_FILTER_VALUE],
]),
};
const traceReducer = (
@ -251,6 +265,13 @@ const traceReducer = (
};
}
case UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE: {
return {
...state,
filterDisplayValue: action.payload,
};
}
default:
return state;
}

View File

@ -31,6 +31,8 @@ export const UPDATE_SPANS_AGGREGATE_PAGE_NUMBER =
export const UPDATE_SPANS_AGGREGATE_PAGE_SIZE =
'UPDATE_SPANS_AGGREGATE_PAGE_SIZE';
export const UPDATE_SPAN_ORDER_PARAMS = 'UPDATE_SPAN_ORDER_PARAMS';
export const UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE =
'UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE';
export interface UpdateFilter {
type: typeof UPDATE_TRACE_FILTER;
@ -187,6 +189,11 @@ export interface UpdateSpanParams {
};
}
export interface UpdateTraceFilterDisplayValue {
type: typeof UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE;
payload: TraceReducer['filterDisplayValue'];
}
export type TraceActions =
| UpdateFilter
| GetTraceFilter
@ -208,4 +215,5 @@ export type TraceActions =
| UpdateSpanOrder
| UpdateSpansAggregatePageNumber
| UpdateSpanSize
| UpdateSpanParams;
| UpdateSpanParams
| UpdateTraceFilterDisplayValue;

View File

@ -32,6 +32,7 @@ export interface TraceReducer {
payload: PayloadProps;
};
yAxisUnit: string | undefined;
filterDisplayValue: Map<TraceFilterEnum, number>;
}
interface SpansAggregateData {