From 0f998a48450cf6ab3f56ed3a5e207de636743554 Mon Sep 17 00:00:00 2001 From: Yevhen Shevchenko <90138953+yeshev@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:49:56 +0300 Subject: [PATCH] feat: add custom orderBy (#2975) * feat: add custom orderBy * chore: magic string is removed --------- Co-authored-by: Vishal Sharma Co-authored-by: Palash Gupta --- .../filters/OrderByFilter/OrderByFilter.tsx | 77 +++++++++++++++---- .../filters/OrderByFilter/config.ts | 4 + .../filters/OrderByFilter/utils.ts | 16 +++- 3 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 frontend/src/container/QueryBuilder/filters/OrderByFilter/config.ts diff --git a/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx b/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx index 13a8baad33..96187ed6f6 100644 --- a/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx +++ b/frontend/src/container/QueryBuilder/filters/OrderByFilter/OrderByFilter.tsx @@ -1,6 +1,8 @@ import { Select, Spin } from 'antd'; import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys'; import { QueryBuilderKeys } from 'constants/queryBuilder'; +import { IOption } from 'hooks/useResourceAttribute/types'; +import { uniqWith } from 'lodash-es'; import * as Papa from 'papaparse'; import { useCallback, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; @@ -9,6 +11,7 @@ import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder'; import { selectStyle } from '../QueryBuilderSearch/config'; import { getRemoveOrderFromValue } from '../QueryBuilderSearch/utils'; +import { FILTERS } from './config'; import { OrderByFilterProps } from './OrderByFilter.interfaces'; import { checkIfKeyPresent, @@ -22,7 +25,7 @@ export function OrderByFilter({ onChange, }: OrderByFilterProps): JSX.Element { const [searchText, setSearchText] = useState(''); - const [selectedValue, setSelectedValue] = useState([]); + const [selectedValue, setSelectedValue] = useState([]); const { data, isFetching } = useQuery( [QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText], @@ -55,23 +58,41 @@ export function OrderByFilter({ .flat() .concat([ { - label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) asc`, - value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}asc`, + label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.ASC}`, + value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}${FILTERS.ASC}`, }, { - label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) desc`, - value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}desc`, + label: `${query.aggregateOperator}(${query.aggregateAttribute.key}) ${FILTERS.DESC}`, + value: `${query.aggregateOperator}(${query.aggregateAttribute.key})${orderByValueDelimiter}${FILTERS.DESC}`, }, ]), [query.aggregateAttribute.key, query.aggregateOperator, query.groupBy], ); + const customValue: IOption[] = useMemo(() => { + if (!searchText) return []; + + return [ + { + label: `${searchText} ${FILTERS.ASC}`, + value: `${searchText}${orderByValueDelimiter}${FILTERS.ASC}`, + }, + { + label: `${searchText} ${FILTERS.DESC}`, + value: `${searchText}${orderByValueDelimiter}${FILTERS.DESC}`, + }, + ]; + }, [searchText]); + const optionsData = useMemo(() => { const options = query.aggregateOperator === MetricAggregateOperator.NOOP ? noAggregationOptions : aggregationOptions; - return options.filter( + + const resultOption = [...customValue, ...options]; + + return resultOption.filter( (option) => !getLabelFromValue(selectedValue).includes( getRemoveOrderFromValue(option.value), @@ -79,30 +100,58 @@ export function OrderByFilter({ ); }, [ aggregationOptions, + customValue, noAggregationOptions, query.aggregateOperator, selectedValue, ]); - const handleChange = (values: string[]): void => { - setSelectedValue(values); - const orderByValues: OrderByPayload[] = values.map((item) => { - const match = Papa.parse(item, { delimiter: '|' }); + const getUniqValues = useCallback((values: IOption[]): IOption[] => { + const modifiedValues = values.map((item) => { + const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter }); + if (!match) return { label: item.label, value: item.value }; + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-unused-vars + const [_, order] = match.data.flat() as string[]; + if (order) return { label: item.label, value: item.value }; + + return { + label: `${item.value} ${FILTERS.ASC}`, + value: `${item.value}${orderByValueDelimiter}${FILTERS.ASC}`, + }; + }); + + return uniqWith( + modifiedValues, + (current, next) => + getRemoveOrderFromValue(current.value) === + getRemoveOrderFromValue(next.value), + ); + }, []); + + const handleChange = (values: IOption[]): void => { + const result = getUniqValues(values); + + setSelectedValue(result); + const orderByValues: OrderByPayload[] = result.map((item) => { + const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter }); + if (match) { const [columnName, order] = match.data.flat() as string[]; return { columnName: checkIfKeyPresent(columnName, query.aggregateAttribute.key) ? '#SIGNOZ_VALUE' : columnName, - order, + order: order ?? 'asc', }; } return { - columnName: item, - order: '', + columnName: item.value, + order: 'asc', }; }); + + setSearchText(''); onChange(orderByValues); }; @@ -126,6 +175,8 @@ export function OrderByFilter({ showSearch disabled={isMetricsDataSource && isDisabledSelect} showArrow={false} + value={selectedValue} + labelInValue filterOption={false} options={optionsData} notFoundContent={isFetching ? : null} diff --git a/frontend/src/container/QueryBuilder/filters/OrderByFilter/config.ts b/frontend/src/container/QueryBuilder/filters/OrderByFilter/config.ts new file mode 100644 index 0000000000..9e4b0c9a1b --- /dev/null +++ b/frontend/src/container/QueryBuilder/filters/OrderByFilter/config.ts @@ -0,0 +1,4 @@ +export const FILTERS = { + ASC: 'asc', + DESC: 'desc', +}; diff --git a/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts b/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts index 9907e5b88f..540674dec2 100644 --- a/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts +++ b/frontend/src/container/QueryBuilder/filters/OrderByFilter/utils.ts @@ -2,9 +2,18 @@ import { IOption } from 'hooks/useResourceAttribute/types'; import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix'; import * as Papa from 'papaparse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData'; export const orderByValueDelimiter = '|'; +export const transformToOrderByStringValues = ( + orderBy: OrderByPayload[], +): IOption[] => + orderBy.map((item) => ({ + label: `${item.columnName} ${item.order}`, + value: `${item.columnName}${orderByValueDelimiter}${item.order}`, + })); + export function mapLabelValuePairs( arr: BaseAutocompleteData[], ): Array[] { @@ -28,14 +37,15 @@ export function mapLabelValuePairs( }); } -export function getLabelFromValue(arr: string[]): string[] { +export function getLabelFromValue(arr: IOption[]): string[] { return arr.flat().map((item) => { - const match = Papa.parse(item, { delimiter: orderByValueDelimiter }); + const match = Papa.parse(item.value, { delimiter: orderByValueDelimiter }); if (match) { const [key] = match.data as string[]; return key[0]; } - return item; + + return item.value; }); }