mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 06:19:03 +08:00
feat: consume the new search bar (#5728)
* feat: consume the new search bar * fix: minor css issue * chore: address review comments * fix: very fast typing * chore: added inline code comments * chore: add the changes behind FF
This commit is contained in:
parent
5dc5b2e366
commit
6b096576ee
@ -13,6 +13,7 @@ const Onboarding = "ONBOARDING"
|
|||||||
const ChatSupport = "CHAT_SUPPORT"
|
const ChatSupport = "CHAT_SUPPORT"
|
||||||
const Gateway = "GATEWAY"
|
const Gateway = "GATEWAY"
|
||||||
const PremiumSupport = "PREMIUM_SUPPORT"
|
const PremiumSupport = "PREMIUM_SUPPORT"
|
||||||
|
const QueryBuilderSearchV2 = "QUERY_BUILDER_SEARCH_V2"
|
||||||
|
|
||||||
var BasicPlan = basemodel.FeatureSet{
|
var BasicPlan = basemodel.FeatureSet{
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
@ -127,6 +128,13 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: QueryBuilderSearchV2,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProPlan = basemodel.FeatureSet{
|
var ProPlan = basemodel.FeatureSet{
|
||||||
@ -235,6 +243,13 @@ var ProPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: QueryBuilderSearchV2,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var EnterprisePlan = basemodel.FeatureSet{
|
var EnterprisePlan = basemodel.FeatureSet{
|
||||||
@ -357,4 +372,11 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: QueryBuilderSearchV2,
|
||||||
|
Active: false,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,5 @@ export enum FeatureKeys {
|
|||||||
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||||
GATEWAY = 'GATEWAY',
|
GATEWAY = 'GATEWAY',
|
||||||
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
||||||
|
QUERY_BUILDER_SEARCH_V2 = 'QUERY_BUILDER_SEARCH_V2',
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import './LogsExplorerQuerySection.styles.scss';
|
import './LogsExplorerQuerySection.styles.scss';
|
||||||
|
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
import {
|
import {
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
OPERATORS,
|
OPERATORS,
|
||||||
@ -9,11 +10,13 @@ import ExplorerOrderBy from 'container/ExplorerOrderBy';
|
|||||||
import { QueryBuilder } from 'container/QueryBuilder';
|
import { QueryBuilder } from 'container/QueryBuilder';
|
||||||
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
import { OrderByFilterProps } from 'container/QueryBuilder/filters/OrderByFilter/OrderByFilter.interfaces';
|
||||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||||
|
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
|
import useFeatureFlags from 'hooks/useFeatureFlag';
|
||||||
import {
|
import {
|
||||||
prepareQueryWithDefaultTimestamp,
|
prepareQueryWithDefaultTimestamp,
|
||||||
SELECTED_VIEWS,
|
SELECTED_VIEWS,
|
||||||
@ -86,15 +89,26 @@ function LogExplorerQuerySection({
|
|||||||
[handleChangeQueryData],
|
[handleChangeQueryData],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isSearchV2Enabled =
|
||||||
|
useFeatureFlags(FeatureKeys.QUERY_BUILDER_SEARCH_V2)?.active || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{selectedView === SELECTED_VIEWS.SEARCH && (
|
{selectedView === SELECTED_VIEWS.SEARCH && (
|
||||||
<div className="qb-search-view-container">
|
<div className="qb-search-view-container">
|
||||||
<QueryBuilderSearch
|
{isSearchV2Enabled ? (
|
||||||
query={query}
|
<QueryBuilderSearchV2
|
||||||
onChange={handleChangeTagFilters}
|
query={query}
|
||||||
whereClauseConfig={filterConfigs?.filters}
|
onChange={handleChangeTagFilters}
|
||||||
/>
|
whereClauseConfig={filterConfigs?.filters}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<QueryBuilderSearch
|
||||||
|
query={query}
|
||||||
|
onChange={handleChangeTagFilters}
|
||||||
|
whereClauseConfig={filterConfigs?.filters}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -56,7 +56,11 @@ import { v4 as uuid } from 'uuid';
|
|||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
import { PLACEHOLDER } from '../QueryBuilderSearch/constant';
|
import { PLACEHOLDER } from '../QueryBuilderSearch/constant';
|
||||||
import { TypographyText } from '../QueryBuilderSearch/style';
|
import { TypographyText } from '../QueryBuilderSearch/style';
|
||||||
import { getTagToken, isInNInOperator } from '../QueryBuilderSearch/utils';
|
import {
|
||||||
|
checkCommaInValue,
|
||||||
|
getTagToken,
|
||||||
|
isInNInOperator,
|
||||||
|
} from '../QueryBuilderSearch/utils';
|
||||||
import QueryBuilderSearchDropdown from './QueryBuilderSearchDropdown';
|
import QueryBuilderSearchDropdown from './QueryBuilderSearchDropdown';
|
||||||
import Suggestions from './Suggestions';
|
import Suggestions from './Suggestions';
|
||||||
|
|
||||||
@ -213,18 +217,11 @@ function QueryBuilderSearchV2(
|
|||||||
const isQueryEnabled = useMemo(() => {
|
const isQueryEnabled = useMemo(() => {
|
||||||
if (currentState === DropdownState.ATTRIBUTE_KEY) {
|
if (currentState === DropdownState.ATTRIBUTE_KEY) {
|
||||||
return query.dataSource === DataSource.METRICS
|
return query.dataSource === DataSource.METRICS
|
||||||
? !!query.aggregateOperator &&
|
? !!query.dataSource && !!query.aggregateAttribute.dataType
|
||||||
!!query.dataSource &&
|
|
||||||
!!query.aggregateAttribute.dataType
|
|
||||||
: true;
|
: true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, [
|
}, [currentState, query.aggregateAttribute.dataType, query.dataSource]);
|
||||||
currentState,
|
|
||||||
query.aggregateAttribute.dataType,
|
|
||||||
query.aggregateOperator,
|
|
||||||
query.dataSource,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const { data, isFetching } = useGetAggregateKeys(
|
const { data, isFetching } = useGetAggregateKeys(
|
||||||
{
|
{
|
||||||
@ -324,6 +321,23 @@ function QueryBuilderSearchV2(
|
|||||||
|
|
||||||
if (isMulti) {
|
if (isMulti) {
|
||||||
const { tagKey, tagOperator, tagValue } = getTagToken(searchValue);
|
const { tagKey, tagOperator, tagValue } = getTagToken(searchValue);
|
||||||
|
// this condition takes care of adding the IN/NIN multi values when pressed enter on an already existing value.
|
||||||
|
// not the best interaction but in sync with what we have today!
|
||||||
|
if (tagValue.includes(String(value))) {
|
||||||
|
setSearchValue('');
|
||||||
|
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
||||||
|
setCurrentFilterItem(undefined);
|
||||||
|
setTags((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
key: currentFilterItem?.key,
|
||||||
|
op: currentFilterItem?.op,
|
||||||
|
value: tagValue.join(','),
|
||||||
|
} as ITag,
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// this is for adding subsequent comma seperated values
|
||||||
const newSearch = [...tagValue];
|
const newSearch = [...tagValue];
|
||||||
newSearch[newSearch.length === 0 ? 0 : newSearch.length - 1] = value;
|
newSearch[newSearch.length === 0 ? 0 : newSearch.length - 1] = value;
|
||||||
const newSearchValue = newSearch.join(',');
|
const newSearchValue = newSearch.join(',');
|
||||||
@ -356,6 +370,7 @@ function QueryBuilderSearchV2(
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
setTags((prev) => prev.slice(0, -1));
|
setTags((prev) => prev.slice(0, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.ctrlKey || event.metaKey) && event.key === '/') {
|
if ((event.ctrlKey || event.metaKey) && event.key === '/') {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@ -375,6 +390,7 @@ function QueryBuilderSearchV2(
|
|||||||
if (searchValue) {
|
if (searchValue) {
|
||||||
const operatorType =
|
const operatorType =
|
||||||
operatorTypeMapper[currentFilterItem?.op || ''] || 'NOT_VALID';
|
operatorTypeMapper[currentFilterItem?.op || ''] || 'NOT_VALID';
|
||||||
|
// if key is added and operator is not present then convert to body CONTAINS key
|
||||||
if (
|
if (
|
||||||
currentFilterItem?.key &&
|
currentFilterItem?.key &&
|
||||||
isEmpty(currentFilterItem?.op) &&
|
isEmpty(currentFilterItem?.op) &&
|
||||||
@ -403,6 +419,7 @@ function QueryBuilderSearchV2(
|
|||||||
currentFilterItem?.op === OPERATORS.EXISTS ||
|
currentFilterItem?.op === OPERATORS.EXISTS ||
|
||||||
currentFilterItem?.op === OPERATORS.NOT_EXISTS
|
currentFilterItem?.op === OPERATORS.NOT_EXISTS
|
||||||
) {
|
) {
|
||||||
|
// is exists and not exists operator is present then convert directly to tag! no need of value here
|
||||||
setTags((prev) => [
|
setTags((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
@ -415,6 +432,7 @@ function QueryBuilderSearchV2(
|
|||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
||||||
} else if (
|
} else if (
|
||||||
|
// if the current state is in sync with the kind of operator used then convert into a tag
|
||||||
validationMapper[operatorType]?.(
|
validationMapper[operatorType]?.(
|
||||||
isArray(currentFilterItem?.value)
|
isArray(currentFilterItem?.value)
|
||||||
? currentFilterItem?.value.length || 0
|
? currentFilterItem?.value.length || 0
|
||||||
@ -445,15 +463,21 @@ function QueryBuilderSearchV2(
|
|||||||
|
|
||||||
// this useEffect takes care of tokenisation based on the search state
|
// this useEffect takes care of tokenisation based on the search state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// if we are still fetching the suggestions then return as we won't know the type / data-type etc for the attribute key
|
||||||
if (isFetchingSuggestions) {
|
if (isFetchingSuggestions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there is no search value reset to the default state
|
||||||
if (!searchValue) {
|
if (!searchValue) {
|
||||||
setCurrentFilterItem(undefined);
|
setCurrentFilterItem(undefined);
|
||||||
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// split the current search value based on delimiters
|
||||||
const { tagKey, tagOperator, tagValue } = getTagToken(searchValue);
|
const { tagKey, tagOperator, tagValue } = getTagToken(searchValue);
|
||||||
|
|
||||||
|
// Case 1 -> when typing an attribute key (not selecting from dropdown)
|
||||||
if (tagKey && isUndefined(currentFilterItem?.key)) {
|
if (tagKey && isUndefined(currentFilterItem?.key)) {
|
||||||
let currentRunningAttributeKey;
|
let currentRunningAttributeKey;
|
||||||
const isSuggestedKeyInAutocomplete = suggestionsData?.payload?.attributes?.some(
|
const isSuggestedKeyInAutocomplete = suggestionsData?.payload?.attributes?.some(
|
||||||
@ -470,8 +494,17 @@ function QueryBuilderSearchV2(
|
|||||||
[currentRunningAttributeKey] = allAttributesMatchingTheKey;
|
[currentRunningAttributeKey] = allAttributesMatchingTheKey;
|
||||||
}
|
}
|
||||||
if (allAttributesMatchingTheKey?.length > 1) {
|
if (allAttributesMatchingTheKey?.length > 1) {
|
||||||
// the priority logic goes here
|
// when there are multiple options let the user choose it until they do not select an operator
|
||||||
[currentRunningAttributeKey] = allAttributesMatchingTheKey;
|
if (tagOperator) {
|
||||||
|
// if they select the operator then pick the first one from the ranked list
|
||||||
|
setCurrentFilterItem({
|
||||||
|
key: allAttributesMatchingTheKey?.[0],
|
||||||
|
op: tagOperator,
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
setCurrentState(DropdownState.ATTRIBUTE_VALUE);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentRunningAttributeKey) {
|
if (currentRunningAttributeKey) {
|
||||||
@ -488,7 +521,6 @@ function QueryBuilderSearchV2(
|
|||||||
setCurrentFilterItem({
|
setCurrentFilterItem({
|
||||||
key: {
|
key: {
|
||||||
key: tagKey.split(' ')[0],
|
key: tagKey.split(' ')[0],
|
||||||
// update this for has and nhas operator , check the useEffect of source keys in older component for details
|
|
||||||
dataType: DataTypes.EMPTY,
|
dataType: DataTypes.EMPTY,
|
||||||
type: '',
|
type: '',
|
||||||
isColumn: false,
|
isColumn: false,
|
||||||
@ -500,12 +532,15 @@ function QueryBuilderSearchV2(
|
|||||||
setCurrentState(DropdownState.OPERATOR);
|
setCurrentState(DropdownState.OPERATOR);
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
// Case 2 - if key is defined but the search text doesn't match with the set key,
|
||||||
|
// can happen when user selects from dropdown and then deletes a few characters
|
||||||
currentFilterItem?.key &&
|
currentFilterItem?.key &&
|
||||||
currentFilterItem?.key?.key !== tagKey.split(' ')[0]
|
currentFilterItem?.key?.key !== tagKey.split(' ')[0]
|
||||||
) {
|
) {
|
||||||
setCurrentFilterItem(undefined);
|
setCurrentFilterItem(undefined);
|
||||||
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
setCurrentState(DropdownState.ATTRIBUTE_KEY);
|
||||||
} else if (tagOperator && isEmpty(currentFilterItem?.op)) {
|
} else if (tagOperator && isEmpty(currentFilterItem?.op)) {
|
||||||
|
// Case 3 -> key is set and now typing for the operator
|
||||||
if (
|
if (
|
||||||
tagOperator === OPERATORS.EXISTS ||
|
tagOperator === OPERATORS.EXISTS ||
|
||||||
tagOperator === OPERATORS.NOT_EXISTS
|
tagOperator === OPERATORS.NOT_EXISTS
|
||||||
@ -531,6 +566,7 @@ function QueryBuilderSearchV2(
|
|||||||
setCurrentState(DropdownState.ATTRIBUTE_VALUE);
|
setCurrentState(DropdownState.ATTRIBUTE_VALUE);
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
// Case 4 -> selected operator from dropdown and then erased a part of it
|
||||||
!isEmpty(currentFilterItem?.op) &&
|
!isEmpty(currentFilterItem?.op) &&
|
||||||
tagOperator !== currentFilterItem?.op
|
tagOperator !== currentFilterItem?.op
|
||||||
) {
|
) {
|
||||||
@ -540,10 +576,12 @@ function QueryBuilderSearchV2(
|
|||||||
value: '',
|
value: '',
|
||||||
}));
|
}));
|
||||||
setCurrentState(DropdownState.OPERATOR);
|
setCurrentState(DropdownState.OPERATOR);
|
||||||
} else if (!isEmpty(tagValue)) {
|
} else if (currentState === DropdownState.ATTRIBUTE_VALUE) {
|
||||||
|
// Case 5 -> the final value state where we set the current filter values and the tokenisation happens on either
|
||||||
|
// dropdown click or blur event
|
||||||
const currentValue = {
|
const currentValue = {
|
||||||
key: currentFilterItem?.key as BaseAutocompleteData,
|
key: currentFilterItem?.key as BaseAutocompleteData,
|
||||||
operator: currentFilterItem?.op as string,
|
op: currentFilterItem?.op as string,
|
||||||
value: tagValue,
|
value: tagValue,
|
||||||
};
|
};
|
||||||
if (!isEqual(currentValue, currentFilterItem)) {
|
if (!isEqual(currentValue, currentFilterItem)) {
|
||||||
@ -561,6 +599,7 @@ function QueryBuilderSearchV2(
|
|||||||
suggestionsData?.payload?.attributes,
|
suggestionsData?.payload?.attributes,
|
||||||
searchValue,
|
searchValue,
|
||||||
isFetchingSuggestions,
|
isFetchingSuggestions,
|
||||||
|
currentState,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// the useEffect takes care of setting the dropdown values correctly on change of the current state
|
// the useEffect takes care of setting the dropdown values correctly on change of the current state
|
||||||
@ -627,28 +666,27 @@ function QueryBuilderSearchV2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentState === DropdownState.ATTRIBUTE_VALUE) {
|
if (currentState === DropdownState.ATTRIBUTE_VALUE) {
|
||||||
const values: string[] =
|
const values: string[] = [];
|
||||||
Object.values(attributeValues?.payload || {}).find((el) => !!el) || [];
|
|
||||||
|
|
||||||
const { tagValue } = getTagToken(searchValue);
|
const { tagValue } = getTagToken(searchValue);
|
||||||
|
if (isArray(tagValue)) {
|
||||||
|
if (!isEmpty(tagValue[tagValue.length - 1]))
|
||||||
|
values.push(tagValue[tagValue.length - 1]);
|
||||||
|
} else if (!isEmpty(tagValue)) values.push(tagValue);
|
||||||
|
|
||||||
if (values.length === 0) {
|
values.push(
|
||||||
if (isArray(tagValue)) {
|
...(Object.values(attributeValues?.payload || {}).find((el) => !!el) || []),
|
||||||
if (!isEmpty(tagValue[tagValue.length - 1]))
|
);
|
||||||
values.push(tagValue[tagValue.length - 1]);
|
|
||||||
} else if (!isEmpty(tagValue)) values.push(tagValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
setDropdownOptions(
|
setDropdownOptions(
|
||||||
values.map((val) => ({
|
values.map((val) => ({
|
||||||
label: val,
|
label: checkCommaInValue(String(val)),
|
||||||
value: val,
|
value: val,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
attributeValues?.payload,
|
attributeValues?.payload,
|
||||||
currentFilterItem?.key.dataType,
|
currentFilterItem?.key?.dataType,
|
||||||
currentState,
|
currentState,
|
||||||
data?.payload?.attributeKeys,
|
data?.payload?.attributeKeys,
|
||||||
isLogsExplorerPage,
|
isLogsExplorerPage,
|
||||||
@ -674,7 +712,15 @@ function QueryBuilderSearchV2(
|
|||||||
onChange(filterTags);
|
onChange(filterTags);
|
||||||
setTags(filterTags.items as ITag[]);
|
setTags(filterTags.items as ITag[]);
|
||||||
}
|
}
|
||||||
}, [onChange, query.filters, tags]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [tags]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEqual(query.filters.items, tags)) {
|
||||||
|
setTags(getInitTags(query));
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
const isLastQuery = useMemo(
|
const isLastQuery = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -843,6 +889,7 @@ function QueryBuilderSearchV2(
|
|||||||
label={option.label}
|
label={option.label}
|
||||||
value={option.value}
|
value={option.value}
|
||||||
option={currentState}
|
option={currentState}
|
||||||
|
searchValue={searchValue}
|
||||||
/>
|
/>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
);
|
);
|
||||||
|
@ -105,29 +105,52 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
.OPERATOR {
|
.left {
|
||||||
color: var(--bg-vanilla-400);
|
display: flex;
|
||||||
font-family: 'Space Mono';
|
align-items: center;
|
||||||
font-size: 14px;
|
gap: 8px;
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
.OPERATOR {
|
||||||
line-height: 20px; /* 142.857% */
|
color: var(--bg-vanilla-400);
|
||||||
letter-spacing: -0.07px;
|
font-family: 'Space Mono';
|
||||||
text-transform: uppercase;
|
font-size: 14px;
|
||||||
width: 100%;
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VALUE {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-family: 'Space Mono';
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
letter-spacing: -0.07px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.VALUE {
|
.right {
|
||||||
color: var(--bg-vanilla-400);
|
display: flex;
|
||||||
font-family: 'Space Mono';
|
align-items: center;
|
||||||
font-size: 14px;
|
gap: 4px;
|
||||||
font-style: normal;
|
.data-type {
|
||||||
font-weight: 500;
|
display: flex;
|
||||||
line-height: 20px; /* 142.857% */
|
height: 20px;
|
||||||
letter-spacing: -0.07px;
|
padding: 4px 8px;
|
||||||
text-transform: uppercase;
|
justify-content: center;
|
||||||
width: 100%;
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,22 @@ import { Color } from '@signozhq/design-tokens';
|
|||||||
import { Tooltip, Typography } from 'antd';
|
import { Tooltip, Typography } from 'antd';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import { isEmpty, isObject } from 'lodash-es';
|
import { isEmpty, isObject } from 'lodash-es';
|
||||||
import { Zap } from 'lucide-react';
|
import { Check, Zap } from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
|
import { getTagToken } from '../QueryBuilderSearch/utils';
|
||||||
import { DropdownState } from './QueryBuilderSearchV2';
|
import { DropdownState } from './QueryBuilderSearchV2';
|
||||||
|
|
||||||
interface ISuggestionsProps {
|
interface ISuggestionsProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: BaseAutocompleteData | string;
|
value: BaseAutocompleteData | string;
|
||||||
option: DropdownState;
|
option: DropdownState;
|
||||||
|
searchValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Suggestions(props: ISuggestionsProps): React.ReactElement {
|
function Suggestions(props: ISuggestionsProps): React.ReactElement {
|
||||||
const { label, value, option } = props;
|
const { label, value, option, searchValue } = props;
|
||||||
|
|
||||||
const optionType = useMemo(() => {
|
const optionType = useMemo(() => {
|
||||||
if (isObject(value)) {
|
if (isObject(value)) {
|
||||||
@ -26,6 +28,15 @@ function Suggestions(props: ISuggestionsProps): React.ReactElement {
|
|||||||
return '';
|
return '';
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
const dataType = useMemo(() => {
|
||||||
|
if (isObject(value)) {
|
||||||
|
return value.dataType;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const { tagValue } = getTagToken(searchValue);
|
||||||
|
|
||||||
const [truncated, setTruncated] = useState<boolean>(false);
|
const [truncated, setTruncated] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -58,13 +69,21 @@ function Suggestions(props: ISuggestionsProps): React.ReactElement {
|
|||||||
) : (
|
) : (
|
||||||
<Tooltip title={truncated ? label : ''} placement="topLeft">
|
<Tooltip title={truncated ? label : ''} placement="topLeft">
|
||||||
<div className="container-without-tag">
|
<div className="container-without-tag">
|
||||||
<div className="dot" />
|
<section className="left">
|
||||||
<Typography.Text
|
<div className="dot" />
|
||||||
className={cx('text value', option)}
|
<Typography.Text
|
||||||
ellipsis={{ onEllipsis: (ellipsis): void => setTruncated(ellipsis) }}
|
className={cx('text value', option)}
|
||||||
>
|
ellipsis={{ onEllipsis: (ellipsis): void => setTruncated(ellipsis) }}
|
||||||
{`${label}`}
|
>
|
||||||
</Typography.Text>
|
{`${label}`}
|
||||||
|
</Typography.Text>
|
||||||
|
</section>
|
||||||
|
<section className="right">
|
||||||
|
{dataType && (
|
||||||
|
<Typography.Text className="data-type">{dataType}</Typography.Text>
|
||||||
|
)}
|
||||||
|
{tagValue.includes(label) && <Check size={14} />}
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user