From 3f2a4d6eac10f30db5cf5164c38946dad2dfad21 Mon Sep 17 00:00:00 2001 From: palash-signoz Date: Tue, 5 Apr 2022 13:23:08 +0530 Subject: [PATCH] bug: Trace filter page fixes (#846) * order is added in the url * local min max duration is kept in memory to show min and max even after filtering by duration * checkbox ordering does not change when the user selects or un-selects a checkbox --- .../Panel/PanelBody/Common/Checkbox.tsx | 16 ++-- .../Panel/PanelBody/CommonCheckBox/index.tsx | 30 ++++--- .../Panel/PanelBody/Duration/index.tsx | 79 +++++++++------- .../Filters/Panel/PanelHeading/index.tsx | 90 +++++++++++-------- .../Filters/Panel/PanelHeading/styles.ts | 2 +- .../container/Trace/Search/AllTags/index.tsx | 12 +-- frontend/src/container/Trace/Search/index.tsx | 18 ++-- .../src/container/Trace/TraceTable/index.tsx | 72 ++++++++------- frontend/src/pages/Trace/index.tsx | 5 +- .../store/actions/trace/getInitialFilter.ts | 7 ++ .../actions/trace/getInitialSpansAggregate.ts | 31 +++++-- .../store/actions/trace/parseFilter/index.ts | 3 +- ...current.ts => spanAggregateCurrentPage.ts} | 2 +- .../trace/parseFilter/spanAggregateOrder.ts | 39 ++++++++ .../store/actions/trace/selectTraceFilter.ts | 1 + .../actions/trace/updateTagPanelVisiblity.ts | 6 +- frontend/src/store/actions/trace/util.ts | 10 ++- frontend/src/store/reducers/trace.ts | 22 ++++- frontend/src/types/actions/trace.ts | 25 ++++-- .../src/types/api/trace/getSpanAggregate.ts | 2 +- frontend/src/types/reducer/trace.ts | 3 + 21 files changed, 308 insertions(+), 167 deletions(-) rename frontend/src/store/actions/trace/parseFilter/{current.ts => spanAggregateCurrentPage.ts} (93%) create mode 100644 frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx index e177f1b4b3..3ac230f669 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Common/Checkbox.tsx @@ -93,11 +93,15 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { if (response.statusCode === 200) { const updatedFilter = getFilter(response.payload); - updatedFilter.forEach((value, key) => { - if (key !== 'duration' && name !== key) { - preUserSelectedMap.set(key, Object.keys(value)); - } - }); + // updatedFilter.forEach((value, key) => { + // if (key !== 'duration' && name !== key) { + // preUserSelectedMap.set(key, Object.keys(value)); + // } + + // if (key === 'duration') { + // newSelectedMap.set('duration', [value.maxDuration, value.minDuration]); + // } + // }); updatedFilter.set(name, { [`${keyValue}`]: '-1', @@ -115,6 +119,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { selectedFilter: newSelectedMap, userSelected: preUserSelectedMap, isFilterExclude: preIsFilterExclude, + order: spansAggregate.order, }, }); @@ -128,6 +133,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element { updatedFilter, preIsFilterExclude, preUserSelectedMap, + spansAggregate.order, ); } else { setIsLoading(false); diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx index 161bb6f8ac..0681e7e5d5 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/CommonCheckBox/index.tsx @@ -18,16 +18,26 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element { return ( <> - {statusObj.map((e) => ( - - ))} + {statusObj + .sort((a, b) => { + const countA = +status[a]; + const countB = +status[b]; + + if (countA === countB) { + return a.length - b.length; + } + return countA - countB; + }) + .map((e) => ( + + ))} ); } diff --git a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx index ef61e3a6a5..8453e5ca90 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelBody/Duration/index.tsx @@ -5,7 +5,7 @@ import getFilters from 'api/trace/getFilters'; import dayjs from 'dayjs'; import durationPlugin from 'dayjs/plugin/duration'; import useDebouncedFn from 'hooks/useDebouncedFunction'; -import React, { useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Dispatch } from 'redux'; import { getFilter, updateURL } from 'store/actions/trace/util'; @@ -20,11 +20,13 @@ import { Container, InputContainer, Text } from './styles'; dayjs.extend(durationPlugin); const getMs = (value: string): string => { - return dayjs - .duration({ - milliseconds: parseInt(value, 10) / 1000000, - }) - .format('SSS'); + return parseFloat( + dayjs + .duration({ + milliseconds: parseInt(value, 10) / 1000000, + }) + .format('SSS'), + ).toFixed(2); }; function Duration(): JSX.Element { @@ -43,9 +45,10 @@ function Duration(): JSX.Element { (state) => state.globalTime, ); - const getDuration = (): - | { maxDuration: string; minDuration: string } - | Record => { + const preLocalMaxDuration = useRef(); + const preLocalMinDuration = useRef(); + + const getDuration = useMemo(() => { const selectedDuration = selectedFilter.get('duration'); if (selectedDuration) { @@ -56,17 +59,29 @@ function Duration(): JSX.Element { } return filter.get('duration') || {}; - }; + }, [selectedFilter, filter]); - const duration = getDuration(); + const [preMax, setPreMax] = useState(''); + const [preMin, setPreMin] = useState(''); - const maxDuration = duration.maxDuration || '0'; - const minDuration = duration.minDuration || '0'; + useEffect(() => { + const duration = getDuration || {}; - const [localMax, setLocalMax] = useState(maxDuration); - const [localMin, setLocalMin] = useState(minDuration); + const maxDuration = duration.maxDuration || '0'; + const minDuration = duration.minDuration || '0'; - const defaultValue = [parseFloat(minDuration), parseFloat(maxDuration)]; + if (preLocalMaxDuration.current === undefined) { + preLocalMaxDuration.current = parseFloat(maxDuration); + } + if (preLocalMinDuration.current === undefined) { + preLocalMinDuration.current = parseFloat(minDuration); + } + + setPreMax(maxDuration); + setPreMin(minDuration); + }, [getDuration]); + + const defaultValue = [parseFloat(preMin), parseFloat(preMax)]; const updatedUrl = async (min: number, max: number): Promise => { const preSelectedFilter = new Map(selectedFilter); @@ -74,7 +89,6 @@ function Duration(): JSX.Element { preSelectedFilter.set('duration', [String(max), String(min)]); - console.log('on the update Url'); const response = await getFilters({ end: String(globalTime.maxTime), getFilters: filterToFetchData, @@ -87,8 +101,9 @@ function Duration(): JSX.Element { const preFilter = getFilter(response.payload); preFilter.forEach((value, key) => { - if (key !== 'duration') { - preUserSelected.set(key, Object.keys(value)); + const values = Object.keys(value); + if (key !== 'duration' && values.length) { + preUserSelected.set(key, values); } }); @@ -102,6 +117,7 @@ function Duration(): JSX.Element { selectedTags, userSelected: preUserSelected, isFilterExclude, + order: spansAggregate.order, }, }); @@ -113,6 +129,7 @@ function Duration(): JSX.Element { preFilter, isFilterExclude, userSelectedFilter, + spansAggregate.order, ); } }; @@ -120,13 +137,12 @@ function Duration(): JSX.Element { const onRangeSliderHandler = (number: [number, number]): void => { const [min, max] = number; - setLocalMin(min.toString()); - setLocalMax(max.toString()); + setPreMin(min.toString()); + setPreMax(max.toString()); }; const debouncedFunction = useDebouncedFn( (min, max) => { - console.log('debounce function'); updatedUrl(min as number, max as number); }, 500, @@ -137,11 +153,9 @@ function Duration(): JSX.Element { event, ) => { const { value } = event.target; - const min = parseFloat(localMin); + const min = parseFloat(preMin); const max = parseFloat(value) * 1000000; - console.log('on change in max'); - onRangeSliderHandler([min, max]); debouncedFunction(min, max); }; @@ -151,9 +165,8 @@ function Duration(): JSX.Element { ) => { const { value } = event.target; const min = parseFloat(value) * 1000000; - const max = parseFloat(localMax); + const max = parseFloat(preMax); onRangeSliderHandler([min, max]); - console.log('on change in min'); debouncedFunction(min, max); }; @@ -170,7 +183,7 @@ function Duration(): JSX.Element { @@ -179,27 +192,27 @@ function Duration(): JSX.Element { { if (value === undefined) { return
; } - return
{`${getMs(value.toString())}ms`}
; + return
{`${getMs(value?.toString())}ms`}
; }} onChange={([min, max]): void => { onRangeSliderHandler([min, max]); }} onAfterChange={onRangeHandler} - value={[parseFloat(localMin), parseFloat(localMax)]} + value={[parseFloat(preMin), parseFloat(preMax)]} />
diff --git a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx index 50686d1b78..8ab6e29d88 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx +++ b/frontend/src/container/Trace/Filters/Panel/PanelHeading/index.tsx @@ -21,7 +21,7 @@ import { ButtonContainer, Container, IconContainer, - TextCotainer, + TextContainer, } from './styles'; const { Text } = Typography; @@ -64,16 +64,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { const getprepdatedSelectedFilter = new Map(selectedFilter); const getPreUserSelected = new Map(userSelectedFilter); - if (!isDefaultOpen) { - updatedFilterData = [PanelName]; - } else { - // removing the selected filter - updatedFilterData = [ - ...filterToFetchData.filter((name) => name !== PanelName), - ]; - getprepdatedSelectedFilter.delete(PanelName); - getPreUserSelected.delete(PanelName); - } + updatedFilterData = [PanelName]; const response = await getFilters({ end: String(global.maxTime), @@ -86,33 +77,14 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { if (response.statusCode === 200) { const updatedFilter = getFilter(response.payload); - // is closed - if (!isDefaultOpen) { - // getprepdatedSelectedFilter.set( - // props.name, - // Object.keys(updatedFilter.get(props.name) || {}), - // ); - + if (!getPreUserSelected.has(PanelName)) { getPreUserSelected.set( PanelName, - Object.keys(updatedFilter.get(PanelName) || {}), + Object.keys(updatedFilter.get(PanelName) || []), ); - - updatedFilterData = [...filterToFetchData, PanelName]; } - // now append the non prop.name trace filter enum over the list - // selectedFilter.forEach((value, key) => { - // if (key !== props.name) { - // getprepdatedSelectedFilter.set(key, value); - // } - // }); - - getPreUserSelected.forEach((value, key) => { - if (key !== PanelName) { - getPreUserSelected.set(key, value); - } - }); + updatedFilterData = [...filterToFetchData, PanelName]; filter.forEach((value, key) => { if (key !== PanelName) { updatedFilter.set(key, value); @@ -129,6 +101,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { selectedTags, userSelected: getPreUserSelected, isFilterExclude, + order: spansAggregate.order, }, }); @@ -140,6 +113,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { updatedFilter, isFilterExclude, getPreUserSelected, + spansAggregate.order, ); } else { notification.error({ @@ -155,6 +129,44 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { } }; + /** + * @description this function removes the selected filter + */ + const onCloseHandler = (): void => { + const preSelectedFilter = new Map(selectedFilter); + // removing the filter from filter to fetch the data + const preFilterToFetchTheData = [ + ...filterToFetchData.filter((name) => name !== PanelName), + ]; + + preSelectedFilter.delete(PanelName); + + dispatch({ + type: UPDATE_ALL_FILTERS, + payload: { + current: spansAggregate.currentPage, + filter, + filterToFetchData: preFilterToFetchTheData, + selectedFilter: preSelectedFilter, + selectedTags, + userSelected: userSelectedFilter, + isFilterExclude, + order: spansAggregate.order, + }, + }); + + updateURL( + preSelectedFilter, + preFilterToFetchTheData, + spansAggregate.currentPage, + selectedTags, + filter, + isFilterExclude, + userSelectedFilter, + spansAggregate.order, + ); + }; + const onClearAllHandler = async (): Promise => { try { setIsLoading(true); @@ -177,18 +189,19 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { }); if (response.statusCode === 200 && response.payload) { - const getUpatedFilter = getFilter(response.payload); + const getUpdatedFilter = getFilter(response.payload); dispatch({ type: UPDATE_ALL_FILTERS, payload: { current: spansAggregate.currentPage, - filter: getUpatedFilter, + filter: getUpdatedFilter, filterToFetchData, selectedFilter: updatedFilter, selectedTags, userSelected: preUserSelected, isFilterExclude: postIsFilterExclude, + order: spansAggregate.order, }, }); @@ -197,9 +210,10 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { filterToFetchData, spansAggregate.currentPage, selectedTags, - getUpatedFilter, + getUpdatedFilter, postIsFilterExclude, preUserSelected, + spansAggregate.order, ); } else { notification.error({ @@ -280,7 +294,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { aria-disabled={filterLoading || isLoading} aria-expanded={IsPanelOpen} > - + {!IsPanelOpen ? : } @@ -288,7 +302,7 @@ function PanelHeading(props: PanelHeadingProps): JSX.Element { {AllPanelHeading.find((e) => e.key === PanelName)?.displayValue || ''} - + {PanelName !== 'duration' && ( diff --git a/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts b/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts index d2c66cfb23..812be65ac7 100644 --- a/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts +++ b/frontend/src/container/Trace/Filters/Panel/PanelHeading/styles.ts @@ -30,7 +30,7 @@ export const IconContainer = styled.div` } `; -export const TextCotainer = styled.div` +export const TextContainer = styled.div` &&& { display: flex; cursor: pointer; diff --git a/frontend/src/container/Trace/Search/AllTags/index.tsx b/frontend/src/container/Trace/Search/AllTags/index.tsx index 91e2d08cb0..693f3c8451 100644 --- a/frontend/src/container/Trace/Search/AllTags/index.tsx +++ b/frontend/src/container/Trace/Search/AllTags/index.tsx @@ -5,7 +5,7 @@ import { connect, useSelector } from 'react-redux'; import { bindActionCreators } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError'; -import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity'; +import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; import { TraceReducer } from 'types/reducer/trace'; @@ -27,7 +27,7 @@ const { Paragraph } = Typography; function AllTags({ updateTagIsError, onChangeHandler, - updateTagVisiblity, + updateTagVisibility, updateFilters, }: AllTagsProps): JSX.Element { const traces = useSelector((state) => state.traces); @@ -63,7 +63,7 @@ function AllTags({ onChangeHandler(parsedQuery.payload); updateFilters(localSelectedTags); updateTagIsError(false); - updateTagVisiblity(false); + updateTagVisibility(false); } }; @@ -75,7 +75,7 @@ function AllTags({ return ( - Unrecognised query format. Please reset your query by clicking `X` in the + Unrecognized query format. Please reset your query by clicking `X` in the search bar above. @@ -131,14 +131,14 @@ function AllTags({ interface DispatchProps { updateTagIsError: (value: boolean) => void; - updateTagVisiblity: (value: boolean) => void; + updateTagVisibility: (value: boolean) => void; } const mapDispatchToProps = ( dispatch: ThunkDispatch, ): DispatchProps => ({ updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch), - updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch), + updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch), }); interface AllTagsProps extends DispatchProps { diff --git a/frontend/src/container/Trace/Search/index.tsx b/frontend/src/container/Trace/Search/index.tsx index cb8fdd453d..1590fd74b2 100644 --- a/frontend/src/container/Trace/Search/index.tsx +++ b/frontend/src/container/Trace/Search/index.tsx @@ -6,7 +6,7 @@ import { connect, useDispatch, useSelector } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { UpdateTagIsError } from 'store/actions/trace/updateIsTagsError'; -import { UpdateTagVisiblity } from 'store/actions/trace/updateTagPanelVisiblity'; +import { UpdateTagVisibility } from 'store/actions/trace/updateTagPanelVisiblity'; import { updateURL } from 'store/actions/trace/util'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; @@ -18,7 +18,7 @@ import { Container, SearchComponent } from './styles'; import { parseQueryToTags, parseTagsToQuery } from './util'; function Search({ - updateTagVisiblity, + updateTagVisibility, updateTagIsError, }: SearchProps): JSX.Element { const traces = useSelector((state) => state.traces); @@ -66,7 +66,7 @@ function Search({ !(e.ariaSelected === 'true') && traces.isTagModalOpen ) { - updateTagVisiblity(false); + updateTagVisibility(false); } }); @@ -75,7 +75,7 @@ function Search({ }; const setIsTagsModalHandler = (value: boolean): void => { - updateTagVisiblity(value); + updateTagVisibility(value); }; const onFocusHandler: React.FocusEventHandler = (e) => { @@ -96,6 +96,7 @@ function Search({ selectedFilter: traces.selectedFilter, userSelected: traces.userSelectedFilter, isFilterExclude: traces.isFilterExclude, + order: traces.spansAggregate.order, }, }); @@ -107,6 +108,7 @@ function Search({ traces.filter, traces.isFilterExclude, traces.userSelectedFilter, + traces.spansAggregate.order, ); }; @@ -124,7 +126,7 @@ function Search({ enterButton={} onSearch={(string): void => { if (string.length === 0) { - updateTagVisiblity(false); + updateTagVisibility(false); updateFilters([]); return; } @@ -135,7 +137,7 @@ function Search({ updateTagIsError(true); } else { updateTagIsError(false); - updateTagVisiblity(false); + updateTagVisibility(false); updateFilters(payload); } }} @@ -150,14 +152,14 @@ function Search({ } interface DispatchProps { - updateTagVisiblity: (value: boolean) => void; + updateTagVisibility: (value: boolean) => void; updateTagIsError: (value: boolean) => void; } const mapDispatchToProps = ( dispatch: ThunkDispatch, ): DispatchProps => ({ - updateTagVisiblity: bindActionCreators(UpdateTagVisiblity, dispatch), + updateTagVisibility: bindActionCreators(UpdateTagVisibility, dispatch), updateTagIsError: bindActionCreators(UpdateTagIsError, dispatch), }); diff --git a/frontend/src/container/Trace/TraceTable/index.tsx b/frontend/src/container/Trace/TraceTable/index.tsx index 944afddb45..b78d709b4f 100644 --- a/frontend/src/container/Trace/TraceTable/index.tsx +++ b/frontend/src/container/Trace/TraceTable/index.tsx @@ -4,34 +4,32 @@ import ROUTES from 'constants/routes'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import React from 'react'; -import { connect, useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; -import { bindActionCreators } from 'redux'; -import { ThunkDispatch } from 'redux-thunk'; -import { - GetSpansAggregate, - GetSpansAggregateProps, -} from 'store/actions/trace/getInitialSpansAggregate'; +import { Dispatch } from 'redux'; +import { updateURL } from 'store/actions/trace/util'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; -import { GlobalReducer } from 'types/reducer/globalTime'; +import { UPDATE_SPAN_ORDER } from 'types/actions/trace'; import { TraceReducer } from 'types/reducer/trace'; dayjs.extend(duration); -function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { +function TraceTable(): JSX.Element { const { spansAggregate, selectedFilter, selectedTags, filterLoading, + userSelectedFilter, + filter, + isFilterExclude, + filterToFetchData, } = useSelector((state) => state.traces); - const globalTime = useSelector( - (state) => state.globalTime, - ); + const dispatch = useDispatch>(); - const { loading, total } = spansAggregate; + const { loading, total, order: spansAggregateOrder } = spansAggregate; type TableType = FlatArray; @@ -101,7 +99,8 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { {`${dayjs .duration({ milliseconds: value / 1000000 }) - .asMilliseconds()} ms`} + .asMilliseconds() + .toFixed(2)} ms`} ), @@ -126,17 +125,27 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { sort, ) => { if (!Array.isArray(sort)) { - const { order = 'ascend' } = sort; + const { order = spansAggregateOrder } = sort; if (props.current && props.pageSize) { - getSpansAggregate({ - maxTime: globalTime.maxTime, - minTime: globalTime.minTime, - selectedFilter, - current: props.current, - pageSize: props.pageSize, - selectedTags, - order: order === 'ascend' ? 'ascending' : 'descending', + const spanOrder = order || spansAggregateOrder; + + dispatch({ + type: UPDATE_SPAN_ORDER, + payload: { + order: spanOrder, + }, }); + + updateURL( + selectedFilter, + filterToFetchData, + props.current, + selectedTags, + filter, + isFilterExclude, + userSelectedFilter, + spanOrder, + ); } } }; @@ -147,7 +156,7 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { dataSource={spansAggregate.data} loading={loading || filterLoading} columns={columns} - rowKey="timestamp" + rowKey={(record): string => `${record.traceID}-${record.spanID}`} style={{ cursor: 'pointer', }} @@ -158,20 +167,9 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element { position: ['bottomLeft'], total, }} + sortDirections={['ascend', 'descend']} /> ); } -interface DispatchProps { - getSpansAggregate: (props: GetSpansAggregateProps) => void; -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, -): DispatchProps => ({ - getSpansAggregate: bindActionCreators(GetSpansAggregate, dispatch), -}); - -type TraceProps = DispatchProps; - -export default connect(null, mapDispatchToProps)(TraceTable); +export default TraceTable; diff --git a/frontend/src/pages/Trace/index.tsx b/frontend/src/pages/Trace/index.tsx index acdc32aeb1..427dd0d14e 100644 --- a/frontend/src/pages/Trace/index.tsx +++ b/frontend/src/pages/Trace/index.tsx @@ -63,7 +63,7 @@ function Trace({ current: spansAggregate.currentPage, pageSize: spansAggregate.pageSize, selectedTags, - order: 'ascending', + order: spansAggregate.order === 'ascend' ? 'ascending' : 'descending', }); }, [ selectedTags, @@ -73,6 +73,7 @@ function Trace({ getSpansAggregate, spansAggregate.currentPage, spansAggregate.pageSize, + spansAggregate.order, ]); useEffect(() => { @@ -93,8 +94,8 @@ function Trace({ selectedTags, maxTime, minTime, - isFilterExclude, getSpans, + isFilterExclude, ]); useEffect(() => { diff --git a/frontend/src/store/actions/trace/getInitialFilter.ts b/frontend/src/store/actions/trace/getInitialFilter.ts index ab0a49f79e..9b189cf60c 100644 --- a/frontend/src/store/actions/trace/getInitialFilter.ts +++ b/frontend/src/store/actions/trace/getInitialFilter.ts @@ -18,6 +18,7 @@ import { parseIsSkippedSelection, parseQueryIntoCurrent, parseQueryIntoFilter, + parseQueryIntoOrder, parseQueryIntoSelectedTags, parseSelectedFilter, } from './util'; @@ -66,6 +67,11 @@ export const GetInitialTraceFilter = ( traces.spansAggregate.currentPage, ); + const parsedQueryOrder = parseQueryIntoOrder( + query, + traces.spansAggregate.order, + ); + const isSelectionSkipped = parseIsSkippedSelection(query); const parsedSelectedTags = parseQueryIntoSelectedTags( @@ -148,6 +154,7 @@ export const GetInitialTraceFilter = ( selectedTags: parsedSelectedTags.currentValue, userSelected: getUserSelected.currentValue, isFilterExclude: getIsFilterExcluded.currentValue, + order: parsedQueryOrder.currentValue, }, }); } else { diff --git a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts index 11d061e079..a0725c9535 100644 --- a/frontend/src/store/actions/trace/getInitialSpansAggregate.ts +++ b/frontend/src/store/actions/trace/getInitialSpansAggregate.ts @@ -3,11 +3,13 @@ import getSpansAggregate from 'api/trace/getSpansAggregate'; import { Dispatch, Store } from 'redux'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; -import { UPDATE_SPANS_AGGREEGATE } from 'types/actions/trace'; +import { UPDATE_SPANS_AGGREGATE } from 'types/actions/trace'; import { Props as GetSpanAggregateProps } from 'types/api/trace/getSpanAggregate'; import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceReducer } from 'types/reducer/trace'; +import { updateURL } from './util'; + export const GetSpansAggregate = ( props: GetSpansAggregateProps, ): (( @@ -29,10 +31,12 @@ export const GetSpansAggregate = ( return; } + const order = props.order === 'ascending' ? 'ascend' : 'descend'; + try { // triggering loading dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -41,6 +45,7 @@ export const GetSpansAggregate = ( error: false, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); @@ -53,12 +58,12 @@ export const GetSpansAggregate = ( offset: props.current * props.pageSize - props.pageSize, selectedTags: props.selectedTags, isFilterExclude: traces.isFilterExclude, - order: props.order, + order, }); if (response.statusCode === 200) { dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -67,16 +72,28 @@ export const GetSpansAggregate = ( error: false, total: response.payload.totalSpans, pageSize: props.pageSize, + order, }, }, }); + + updateURL( + traces.selectedFilter, + traces.filterToFetchData, + props.current, + traces.selectedTags, + traces.filter, + traces.isFilterExclude, + traces.userSelectedFilter, + order, + ); } else { notification.error({ message: response.error || 'Something went wrong', }); dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -85,13 +102,14 @@ export const GetSpansAggregate = ( error: true, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); } } catch (error) { dispatch({ - type: UPDATE_SPANS_AGGREEGATE, + type: UPDATE_SPANS_AGGREGATE, payload: { spansAggregate: { currentPage: props.current, @@ -100,6 +118,7 @@ export const GetSpansAggregate = ( error: true, total: spansAggregate.total, pageSize: props.pageSize, + order, }, }, }); diff --git a/frontend/src/store/actions/trace/parseFilter/index.ts b/frontend/src/store/actions/trace/parseFilter/index.ts index fc7a14bc9f..f2489c2076 100644 --- a/frontend/src/store/actions/trace/parseFilter/index.ts +++ b/frontend/src/store/actions/trace/parseFilter/index.ts @@ -1,4 +1,3 @@ -export * from './current'; export * from './filter'; export * from './filterToFetchData'; export * from './isFilterExclude'; @@ -6,3 +5,5 @@ export * from './minMaxTime'; export * from './selectedFilter'; export * from './selectedTags'; export * from './skippedSelected'; +export * from './spanAggregateCurrentPage'; +export * from './spanAggregateOrder'; diff --git a/frontend/src/store/actions/trace/parseFilter/current.ts b/frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts similarity index 93% rename from frontend/src/store/actions/trace/parseFilter/current.ts rename to frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts index 4bdd0aa7f7..3135992558 100644 --- a/frontend/src/store/actions/trace/parseFilter/current.ts +++ b/frontend/src/store/actions/trace/parseFilter/spanAggregateCurrentPage.ts @@ -10,7 +10,7 @@ export const parseQueryIntoCurrent = ( let current = 1; - const selected = url.get('current'); + const selected = url.get('spanAggregateCurrentPage'); if (selected) { try { diff --git a/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts b/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts new file mode 100644 index 0000000000..1641ebe8f2 --- /dev/null +++ b/frontend/src/store/actions/trace/parseFilter/spanAggregateOrder.ts @@ -0,0 +1,39 @@ +import { TraceReducer } from 'types/reducer/trace'; + +import { ParsedUrl } from '../util'; + +export const parseQueryIntoOrder = ( + query: string, + stateCurrent: TraceReducer['spansAggregate']['order'], +): ParsedUrl => { + const url = new URLSearchParams(query); + + let current = 'ascend'; + + const selected = url.get('spanAggregateOrder'); + + if (selected) { + try { + const parsedValue = selected; + + if (parsedValue && typeof parsedValue === 'string') { + current = parsedValue; + } + } catch (error) { + console.log(error); + console.log('error while parsing json'); + } + } + + if (selected) { + return { + currentValue: current, + urlValue: current, + }; + } + + return { + currentValue: stateCurrent, + urlValue: current, + }; +}; diff --git a/frontend/src/store/actions/trace/selectTraceFilter.ts b/frontend/src/store/actions/trace/selectTraceFilter.ts index f23a361f0a..14e6b39fb4 100644 --- a/frontend/src/store/actions/trace/selectTraceFilter.ts +++ b/frontend/src/store/actions/trace/selectTraceFilter.ts @@ -47,6 +47,7 @@ export const SelectedTraceFilter = (props: { traces.filter, traces.isFilterExclude, traces.userSelectedFilter, + traces.spansAggregate.order, ); }; }; diff --git a/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts b/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts index 9fce20d0de..59fb070007 100644 --- a/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts +++ b/frontend/src/store/actions/trace/updateTagPanelVisiblity.ts @@ -1,14 +1,14 @@ import { Dispatch } from 'redux'; import AppActions from 'types/actions'; -import { UPDATE_TAG_MODAL_VISIBLITY } from 'types/actions/trace'; +import { UPDATE_TAG_MODAL_VISIBILITY } from 'types/actions/trace'; import { TraceReducer } from 'types/reducer/trace'; -export const UpdateTagVisiblity = ( +export const UpdateTagVisibility = ( isTagModalOpen: TraceReducer['isTagModalOpen'], ): ((dispatch: Dispatch) => void) => { return (dispatch): void => { dispatch({ - type: UPDATE_TAG_MODAL_VISIBLITY, + type: UPDATE_TAG_MODAL_VISIBILITY, payload: { isTagModalOpen, }, diff --git a/frontend/src/store/actions/trace/util.ts b/frontend/src/store/actions/trace/util.ts index 63d91216f7..5355fc0660 100644 --- a/frontend/src/store/actions/trace/util.ts +++ b/frontend/src/store/actions/trace/util.ts @@ -18,11 +18,12 @@ export function isTraceFilterEnum( export const updateURL = ( selectedFilter: TraceReducer['selectedFilter'], filterToFetchData: TraceReducer['filterToFetchData'], - current: TraceReducer['spansAggregate']['total'], + spanAggregateCurrentPage: TraceReducer['spansAggregate']['currentPage'], selectedTags: TraceReducer['selectedTags'], filter: TraceReducer['filter'], isFilterExclude: TraceReducer['isFilterExclude'], userSelectedFilter: TraceReducer['userSelectedFilter'], + spanAggregateOrder: TraceReducer['spansAggregate']['order'], ): void => { const search = new URLSearchParams(window.location.search); const preResult: { key: string; value: string }[] = []; @@ -30,11 +31,12 @@ export const updateURL = ( const keyToSkip = [ 'selected', 'filterToFetchData', - 'current', 'selectedTags', 'filter', 'isFilterExclude', 'userSelectedFilter', + 'spanAggregateCurrentPage', + 'spanAggregateOrder', ]; search.forEach((value, key) => { @@ -51,7 +53,7 @@ export const updateURL = ( Object.fromEntries(selectedFilter), )}&filterToFetchData=${JSON.stringify( filterToFetchData, - )}¤t=${current}&selectedTags=${JSON.stringify( + )}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&selectedTags=${JSON.stringify( selectedTags, )}&filter=${JSON.stringify(Object.fromEntries(filter))}&${preResult .map((e) => `${e.key}=${e.value}`) @@ -59,7 +61,7 @@ export const updateURL = ( Object.fromEntries(isFilterExclude), )}&userSelectedFilter=${JSON.stringify( Object.fromEntries(userSelectedFilter), - )}`, + )}&spanAggregateCurrentPage=${spanAggregateCurrentPage}&spanAggregateOrder=${spanAggregateOrder}`, ); }; diff --git a/frontend/src/store/reducers/trace.ts b/frontend/src/store/reducers/trace.ts index 81b27b7e9e..46328c4858 100644 --- a/frontend/src/store/reducers/trace.ts +++ b/frontend/src/store/reducers/trace.ts @@ -9,8 +9,9 @@ import { UPDATE_SELECTED_FUNCTION, UPDATE_SELECTED_GROUP_BY, UPDATE_SELECTED_TAGS, - UPDATE_SPANS_AGGREEGATE, - UPDATE_TAG_MODAL_VISIBLITY, + UPDATE_SPAN_ORDER, + UPDATE_SPANS_AGGREGATE, + UPDATE_TAG_MODAL_VISIBILITY, UPDATE_TRACE_FILTER, UPDATE_TRACE_FILTER_LOADING, UPDATE_TRACE_GRAPH_ERROR, @@ -37,6 +38,7 @@ const initialValue: TraceReducer = { error: false, total: 0, pageSize: 10, + order: 'ascend', }, selectedGroupBy: '', selectedFunction: 'count', @@ -71,6 +73,7 @@ const traceReducer = ( selectedTags, userSelected, isFilterExclude, + order, } = payload; return { @@ -84,6 +87,7 @@ const traceReducer = ( spansAggregate: { ...state.spansAggregate, currentPage: current, + order, }, }; } @@ -115,14 +119,14 @@ const traceReducer = ( }; } - case UPDATE_SPANS_AGGREEGATE: { + case UPDATE_SPANS_AGGREGATE: { return { ...state, spansAggregate: action.payload.spansAggregate, }; } - case UPDATE_TAG_MODAL_VISIBLITY: { + case UPDATE_TAG_MODAL_VISIBILITY: { return { ...state, isTagModalOpen: action.payload.isTagModalOpen, @@ -199,6 +203,16 @@ const traceReducer = ( }; } + case UPDATE_SPAN_ORDER: { + return { + ...state, + spansAggregate: { + ...state.spansAggregate, + order: action.payload.order, + }, + }; + } + default: return state; } diff --git a/frontend/src/types/actions/trace.ts b/frontend/src/types/actions/trace.ts index 9c22a55220..c6f1f12bba 100644 --- a/frontend/src/types/actions/trace.ts +++ b/frontend/src/types/actions/trace.ts @@ -7,9 +7,9 @@ export const UPDATE_TRACE_FILTER_LOADING = 'UPDATE_TRACE_FILTER_LOADING'; export const SELECT_TRACE_FILTER = 'SELECT_TRACE_FILTER'; export const UPDATE_ALL_FILTERS = 'UPDATE_ALL_FILTERS'; export const UPDATE_SELECTED_TAGS = 'UPDATE_SELECTED_TAGS'; -export const UPDATE_TAG_MODAL_VISIBLITY = 'UPDATE_TAG_MODAL_VISIBLITY'; +export const UPDATE_TAG_MODAL_VISIBILITY = 'UPDATE_TAG_MODAL_VISIBILITY'; -export const UPDATE_SPANS_AGGREEGATE = 'UPDATE_SPANS_AGGREEGATE'; +export const UPDATE_SPANS_AGGREGATE = 'UPDATE_SPANS_AGGREGATE'; export const UPDATE_IS_TAG_ERROR = 'UPDATE_IS_TAG_ERROR'; @@ -25,6 +25,8 @@ export const UPDATE_FILTER_RESPONSE_SELECTED = 'UPDATE_FILTER_RESPONSE_SELECTED'; export const UPDATE_FILTER_EXCLUDE = 'UPDATE_FILTER_EXCLUDE'; +export const UPDATE_SPAN_ORDER = 'UPDATE_SPAN_ORDER'; + export interface UpdateFilter { type: typeof UPDATE_TRACE_FILTER; payload: { @@ -33,14 +35,14 @@ export interface UpdateFilter { } export interface UpdateSpansAggregate { - type: typeof UPDATE_SPANS_AGGREEGATE; + type: typeof UPDATE_SPANS_AGGREGATE; payload: { spansAggregate: TraceReducer['spansAggregate']; }; } -export interface UpdateTagVisiblity { - type: typeof UPDATE_TAG_MODAL_VISIBLITY; +export interface UpdateTagVisibility { + type: typeof UPDATE_TAG_MODAL_VISIBILITY; payload: { isTagModalOpen: TraceReducer['isTagModalOpen']; }; @@ -70,6 +72,7 @@ export interface UpdateAllFilters { selectedTags: TraceReducer['selectedTags']; userSelected: TraceReducer['userSelectedFilter']; isFilterExclude: TraceReducer['isFilterExclude']; + order: TraceReducer['spansAggregate']['order']; }; } @@ -149,6 +152,13 @@ export interface UpdateSpans { }; } +export interface UpdateSpanOrder { + type: typeof UPDATE_SPAN_ORDER; + payload: { + order: TraceReducer['spansAggregate']['order']; + }; +} + export type TraceActions = | UpdateFilter | GetTraceFilter @@ -156,7 +166,7 @@ export type TraceActions = | SelectTraceFilter | UpdateAllFilters | UpdateSelectedTags - | UpdateTagVisiblity + | UpdateTagVisibility | UpdateSpansAggregate | UpdateIsTagError | UpdateSelectedGroupBy @@ -166,4 +176,5 @@ export type TraceActions = | UpdateSpans | ResetTraceFilter | UpdateSelected - | UpdateFilterExclude; + | UpdateFilterExclude + | UpdateSpanOrder; diff --git a/frontend/src/types/api/trace/getSpanAggregate.ts b/frontend/src/types/api/trace/getSpanAggregate.ts index 3263621009..5f6d413a81 100644 --- a/frontend/src/types/api/trace/getSpanAggregate.ts +++ b/frontend/src/types/api/trace/getSpanAggregate.ts @@ -7,7 +7,7 @@ export interface Props { limit: number; offset: number; selectedTags: TraceReducer['selectedTags']; - order?: 'descending' | 'ascending'; + order?: TraceReducer['spansAggregate']['order']; isFilterExclude: TraceReducer['isFilterExclude']; } diff --git a/frontend/src/types/reducer/trace.ts b/frontend/src/types/reducer/trace.ts index a033bf2879..339fd63483 100644 --- a/frontend/src/types/reducer/trace.ts +++ b/frontend/src/types/reducer/trace.ts @@ -4,8 +4,10 @@ export interface TraceReducer { filter: Map>; filterToFetchData: TraceFilterEnum[]; filterLoading: boolean; + selectedFilter: Map; userSelectedFilter: Map; + isFilterExclude: Map; selectedTags: Tags[]; isTagModalOpen: boolean; @@ -18,6 +20,7 @@ export interface TraceReducer { error: boolean; total: number; pageSize: number; + order: string; }; selectedGroupBy: string; selectedFunction: string;