mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-11 15:21:35 +08:00

* feat: qb-suggestions base setup * chore: make the dropdown a little similar to the designs * chore: move out example queries from og and add to renderer * chore: added the handlers for example queries * chore: hide the example queries as soon as the user starts typing * feat: handle changes for cancel query * chore: remove stupid concept of option group * chore: show only first 3 items and add option to show all filters * chore: minor css changes and remove transitions * feat: integrate suggestions api and control re-renders * feat: added keyboard shortcuts for the dropdown * fix: design cleanups and touchups * fix: build issues and tests * chore: extra safety check for base64 and fix tests * fix: qs doesn't handle padding in base64 strings, added client logic * chore: some code comments * chore: some code comments * chore: increase the height of the bar when key is set * chore: address minor designs * chore: update the keyboard shortcut to cmd+/ * feat: correct the option render for logs for tooltip * chore: search bar to not loose focus on btn click * fix: update the spacing and icon for search bar * chore: address review comments
190 lines
4.5 KiB
TypeScript
190 lines
4.5 KiB
TypeScript
import {
|
|
checkCommaInValue,
|
|
getTagToken,
|
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
|
import { Option } from 'container/QueryBuilder/type';
|
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
|
import { isEmpty } from 'lodash-es';
|
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
|
|
import { WhereClauseConfig } from './useAutoComplete';
|
|
import { useOperators } from './useOperators';
|
|
|
|
export const WHERE_CLAUSE_CUSTOM_SUFFIX = '-custom';
|
|
|
|
export const useOptions = (
|
|
key: string,
|
|
keys: BaseAutocompleteData[],
|
|
operator: string,
|
|
searchValue: string,
|
|
isMulti: boolean,
|
|
isValidOperator: boolean,
|
|
isExist: boolean,
|
|
results: string[],
|
|
result: string[],
|
|
isFetching: boolean,
|
|
whereClauseConfig?: WhereClauseConfig,
|
|
): Option[] => {
|
|
const [options, setOptions] = useState<Option[]>([]);
|
|
const operators = useOperators(key, keys);
|
|
|
|
const getLabel = useCallback(
|
|
(data: BaseAutocompleteData): Option['label'] =>
|
|
transformStringWithPrefix({
|
|
str: data?.key,
|
|
prefix: data?.type || '',
|
|
condition: !data?.isColumn,
|
|
}),
|
|
[],
|
|
);
|
|
|
|
const getOptionsFromKeys = useCallback(
|
|
(items: BaseAutocompleteData[]): Option[] =>
|
|
items?.map((item) => ({
|
|
label: `${getLabel(item)}`,
|
|
value: item.key,
|
|
dataType: item.dataType,
|
|
isIndexed: item?.isIndexed,
|
|
})),
|
|
[getLabel],
|
|
);
|
|
|
|
const getKeyOpValue = useCallback(
|
|
(items: string[]): Option[] =>
|
|
items?.map((item) => ({
|
|
label: `${key} ${operator} ${item}`,
|
|
value: `${key} ${operator} ${item}`,
|
|
})),
|
|
[key, operator],
|
|
);
|
|
|
|
const getOptionsWithValidOperator = useCallback(
|
|
(key: string, results: string[], searchValue: string) => {
|
|
const hasAllResults = results.every((value) => result.includes(value));
|
|
const values = getKeyOpValue(results);
|
|
|
|
return hasAllResults
|
|
? [
|
|
{
|
|
label: searchValue,
|
|
value: searchValue,
|
|
},
|
|
]
|
|
: [
|
|
{
|
|
label: searchValue,
|
|
value: searchValue,
|
|
},
|
|
...values,
|
|
];
|
|
},
|
|
[getKeyOpValue, result],
|
|
);
|
|
|
|
const getKeyOperatorOptions = useCallback(
|
|
(key: string) => {
|
|
const keyOperator = key.split(' ');
|
|
const partialOperator = keyOperator?.[1];
|
|
const partialKey = keyOperator?.[0];
|
|
const filteredOperators = !isEmpty(partialOperator)
|
|
? operators?.filter((operator) =>
|
|
operator.startsWith(partialOperator?.toUpperCase()),
|
|
)
|
|
: operators;
|
|
const operatorsOptions = filteredOperators?.map((operator) => ({
|
|
value: `${partialKey} ${operator} `,
|
|
label: `${partialKey} ${operator} `,
|
|
}));
|
|
if (whereClauseConfig) {
|
|
return [
|
|
{
|
|
label: `${searchValue} `,
|
|
value: `${searchValue}${WHERE_CLAUSE_CUSTOM_SUFFIX}`,
|
|
},
|
|
...operatorsOptions,
|
|
];
|
|
}
|
|
return operatorsOptions;
|
|
},
|
|
[operators, searchValue, whereClauseConfig],
|
|
);
|
|
|
|
useEffect(() => {
|
|
let newOptions: Option[] = [];
|
|
|
|
if (!key) {
|
|
newOptions = searchValue
|
|
? [
|
|
{
|
|
label: `${searchValue} `,
|
|
value: `${searchValue} `,
|
|
},
|
|
...getOptionsFromKeys(keys),
|
|
]
|
|
: getOptionsFromKeys(keys);
|
|
} else if (key && !operator) {
|
|
newOptions = getKeyOperatorOptions(key);
|
|
} else if (key && operator) {
|
|
if (isMulti) {
|
|
newOptions = results.map((item) => ({
|
|
label: checkCommaInValue(String(item)),
|
|
value: String(item),
|
|
}));
|
|
} else if (isExist) {
|
|
newOptions = [];
|
|
} else if (isValidOperator) {
|
|
newOptions = getOptionsWithValidOperator(key, results, searchValue);
|
|
}
|
|
}
|
|
if (newOptions.length > 0) {
|
|
setOptions(newOptions);
|
|
}
|
|
if (isFetching) {
|
|
setOptions([]);
|
|
}
|
|
}, [
|
|
whereClauseConfig,
|
|
getKeyOpValue,
|
|
getOptionsFromKeys,
|
|
isExist,
|
|
isMulti,
|
|
isValidOperator,
|
|
key,
|
|
keys,
|
|
operator,
|
|
operators,
|
|
result,
|
|
results,
|
|
searchValue,
|
|
getKeyOperatorOptions,
|
|
getOptionsWithValidOperator,
|
|
isFetching,
|
|
]);
|
|
|
|
return useMemo(
|
|
() =>
|
|
(
|
|
options.filter(
|
|
(option, index, self) =>
|
|
index ===
|
|
self.findIndex(
|
|
(o) => o.label === option.label && o.value === option.value, // to remove duplicate & empty options from list
|
|
) && option.value !== '',
|
|
) || []
|
|
).map((option) => {
|
|
const { tagValue } = getTagToken(searchValue);
|
|
if (isMulti) {
|
|
return {
|
|
...option,
|
|
selected: tagValue
|
|
.filter((i) => i.trim().replace(/^\s+/, '') === option.value)
|
|
.includes(option.value),
|
|
};
|
|
}
|
|
return option;
|
|
}),
|
|
[isMulti, options, searchValue],
|
|
);
|
|
};
|