feat ui update search filter icons (#2010)
This commit is contained in:
parent
2ac3d38078
commit
9c3493cf87
4 changed files with 210 additions and 99 deletions
|
|
@ -14,9 +14,6 @@
|
|||
&:hover {
|
||||
background-color: $active-blue;
|
||||
color: $teal !important;
|
||||
& svg {
|
||||
fill: $teal !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,97 @@
|
|||
import React from 'react';
|
||||
import { Icon, Loader } from 'UI';
|
||||
import { connect } from 'react-redux';
|
||||
import { filtersMap } from 'Types/filter/newFilter';
|
||||
import cn from 'classnames';
|
||||
import stl from './FilterModal.module.css';
|
||||
import { filtersMap, conditionalFiltersMap } from 'Types/filter/newFilter';
|
||||
import {
|
||||
AppWindow,
|
||||
ArrowUpDown,
|
||||
Chrome,
|
||||
CircleAlert,
|
||||
Clock2,
|
||||
Code,
|
||||
ContactRound,
|
||||
Cpu,
|
||||
Earth,
|
||||
FileStack,
|
||||
MapPin,
|
||||
MemoryStick,
|
||||
MonitorSmartphone,
|
||||
Navigation,
|
||||
Network,
|
||||
OctagonAlert,
|
||||
Pin,
|
||||
Pointer,
|
||||
RectangleEllipsis,
|
||||
SquareMousePointer,
|
||||
SquareUser,
|
||||
Timer,
|
||||
VenetianMask,
|
||||
Workflow,
|
||||
} from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Icon, Loader } from 'UI';
|
||||
|
||||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
|
||||
import { FilterKey } from '../../../../types/filter/filterType';
|
||||
import stl from './FilterModal.module.css';
|
||||
|
||||
const IconMap = {
|
||||
[FilterKey.CLICK]: <Pointer size={18} />,
|
||||
[FilterKey.LOCATION]: <Navigation size={18} />,
|
||||
[FilterKey.INPUT]: <RectangleEllipsis size={18} />,
|
||||
[FilterKey.CUSTOM]: <Code size={18} />,
|
||||
[FilterKey.FETCH]: <ArrowUpDown size={18} />,
|
||||
[FilterKey.GRAPHQL]: <Network size={18} />,
|
||||
[FilterKey.STATEACTION]: <RectangleEllipsis size={18} />,
|
||||
[FilterKey.ERROR]: <OctagonAlert size={18} />,
|
||||
[FilterKey.ISSUE]: <CircleAlert size={18} />,
|
||||
[FilterKey.FETCH_FAILED]: <Code size={18} />,
|
||||
[FilterKey.DOM_COMPLETE]: <ArrowUpDown size={18} />,
|
||||
[FilterKey.LARGEST_CONTENTFUL_PAINT_TIME]: <Network size={18} />,
|
||||
[FilterKey.TTFB]: <Timer size={18} />,
|
||||
[FilterKey.AVG_CPU_LOAD]: <Cpu size={18} />,
|
||||
[FilterKey.AVG_MEMORY_USAGE]: <MemoryStick size={18} />,
|
||||
[FilterKey.USERID]: <SquareUser size={18} />,
|
||||
[FilterKey.USERANONYMOUSID]: <VenetianMask size={18} />,
|
||||
[FilterKey.USER_CITY]: <Pin size={18} />,
|
||||
[FilterKey.USER_STATE]: <MapPin size={18} />,
|
||||
[FilterKey.USER_COUNTRY]: <Earth size={18} />,
|
||||
[FilterKey.USER_DEVICE]: <Code size={18} />,
|
||||
[FilterKey.USER_OS]: <AppWindow size={18} />,
|
||||
[FilterKey.USER_BROWSER]: <Chrome size={18} />,
|
||||
[FilterKey.PLATFORM]: <MonitorSmartphone size={18} />,
|
||||
[FilterKey.REVID]: <FileStack size={18} />,
|
||||
[FilterKey.REFERRER]: <Workflow size={18} />,
|
||||
[FilterKey.DURATION]: <Clock2 size={18} />,
|
||||
[FilterKey.TAGGED_ELEMENT]: <SquareMousePointer size={18} />,
|
||||
[FilterKey.METADATA]: <ContactRound size={18} />,
|
||||
};
|
||||
|
||||
function filterJson(
|
||||
jsonObj: Record<string, any>,
|
||||
excludeKeys: string[] = [],
|
||||
allowedFilterKeys: string[] = []
|
||||
): Record<string, any> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(jsonObj).map(([key, value]) => {
|
||||
const arr = value.filter((i: { key: string }) => {
|
||||
if (excludeKeys.includes(i.key)) return false;
|
||||
return !(allowedFilterKeys.length > 0 && !allowedFilterKeys.includes(i.key));
|
||||
});
|
||||
return [key, arr];
|
||||
}).filter(([_, arr]) => arr.length > 0)
|
||||
Object.entries(jsonObj)
|
||||
.map(([key, value]) => {
|
||||
const arr = value.filter((i: { key: string }) => {
|
||||
if (excludeKeys.includes(i.key)) return false;
|
||||
return !(
|
||||
allowedFilterKeys.length > 0 && !allowedFilterKeys.includes(i.key)
|
||||
);
|
||||
});
|
||||
return [key, arr];
|
||||
})
|
||||
.filter(([_, arr]) => arr.length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export const getMatchingEntries = (searchQuery: string, filters: Record<string, any>) => {
|
||||
export const getMatchingEntries = (
|
||||
searchQuery: string,
|
||||
filters: Record<string, any>
|
||||
) => {
|
||||
const matchingCategories: string[] = [];
|
||||
const matchingFilters: Record<string, any> = {};
|
||||
const lowerCaseQuery = searchQuery.toLowerCase();
|
||||
|
|
@ -31,7 +99,7 @@ export const getMatchingEntries = (searchQuery: string, filters: Record<string,
|
|||
if (lowerCaseQuery.length === 0)
|
||||
return {
|
||||
matchingCategories: Object.keys(filters),
|
||||
matchingFilters: filters
|
||||
matchingFilters: filters,
|
||||
};
|
||||
|
||||
Object.keys(filters).forEach((name) => {
|
||||
|
|
@ -88,7 +156,11 @@ function FilterModal(props: Props) {
|
|||
|
||||
const { matchingCategories, matchingFilters } = getMatchingEntries(
|
||||
searchQuery,
|
||||
filterJson(isConditional ? conditionalFilters : filters, excludeFilterKeys, allowedFilterKeys)
|
||||
filterJson(
|
||||
isConditional ? conditionalFilters : filters,
|
||||
excludeFilterKeys,
|
||||
allowedFilterKeys
|
||||
)
|
||||
);
|
||||
|
||||
const isResultEmpty =
|
||||
|
|
@ -96,31 +168,48 @@ function FilterModal(props: Props) {
|
|||
matchingCategories.length === 0 &&
|
||||
Object.keys(matchingFilters).length === 0;
|
||||
|
||||
const getNewIcon = (filter: Record<string, any>) => {
|
||||
if (filter.icon.includes('metadata')) {
|
||||
return IconMap[FilterKey.METADATA]
|
||||
}
|
||||
// @ts-ignore
|
||||
if (IconMap[filter.key]) {
|
||||
// @ts-ignore
|
||||
return IconMap[filter.key]
|
||||
}
|
||||
else return <Icon name={filter.icon} size={16} />
|
||||
}
|
||||
return (
|
||||
<div className={stl.wrapper} style={{ width: '480px', maxHeight: '380px', overflowY: 'auto' }}>
|
||||
<div
|
||||
className={stl.wrapper}
|
||||
style={{ width: '480px', maxHeight: '380px', overflowY: 'auto' }}
|
||||
>
|
||||
<div
|
||||
className={searchQuery && !isResultEmpty ? 'mb-6' : ''}
|
||||
style={{ columns: matchingCategories.length > 1 ? 'auto 200px' : 1 }}
|
||||
>
|
||||
{matchingCategories.map((key) => {
|
||||
return (
|
||||
<div className='mb-6 flex flex-col gap-2 break-inside-avoid' key={key}>
|
||||
<div className='uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm'>
|
||||
<div
|
||||
className="mb-6 flex flex-col gap-2 break-inside-avoid"
|
||||
key={key}
|
||||
>
|
||||
<div className="uppercase font-medium mb-1 color-gray-medium tracking-widest text-sm">
|
||||
{key}
|
||||
</div>
|
||||
<div>
|
||||
{matchingFilters[key] &&
|
||||
matchingFilters[key].map((filter: any) => (
|
||||
matchingFilters[key].map((filter: Record<string, any>) => (
|
||||
<div
|
||||
key={filter.label}
|
||||
className={cn(
|
||||
stl.optionItem,
|
||||
'flex items-center py-2 cursor-pointer -mx-2 px-2'
|
||||
'flex items-center py-2 cursor-pointer -mx-2 px-2 gap-2'
|
||||
)}
|
||||
onClick={() => onFilterClick({ ...filter, value: [''] })}
|
||||
>
|
||||
<Icon name={filter.icon} size='16' />
|
||||
<span className='ml-2'>{filter.label}</span>
|
||||
{getNewIcon(filter)}
|
||||
<span>{filter.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -130,11 +219,14 @@ function FilterModal(props: Props) {
|
|||
</div>
|
||||
{showSearchList && (
|
||||
<Loader loading={fetchingFilterSearchList}>
|
||||
<div className='-mx-6 px-6'>
|
||||
<div className="-mx-6 px-6">
|
||||
{isResultEmpty && !fetchingFilterSearchList ? (
|
||||
<div className='flex items-center flex-col'>
|
||||
<div className="flex items-center flex-col">
|
||||
<AnimatedSVG name={ICONS.NO_SEARCH_RESULTS} size={180} />
|
||||
<div className='color-gray-medium font-medium px-3'> No Suggestions Found</div>
|
||||
<div className="color-gray-medium font-medium px-3">
|
||||
{' '}
|
||||
No Suggestions Found
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
Object.keys(filterSearchList).map((key, index) => {
|
||||
|
|
@ -142,7 +234,7 @@ function FilterModal(props: Props) {
|
|||
const option = filtersMap[key];
|
||||
return option ? (
|
||||
<div key={index} className={cn('mb-3')}>
|
||||
<div className='font-medium uppercase color-gray-medium mb-2'>
|
||||
<div className="font-medium uppercase color-gray-medium mb-2">
|
||||
{option.label}
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -151,12 +243,14 @@ function FilterModal(props: Props) {
|
|||
key={i}
|
||||
className={cn(
|
||||
stl.filterSearchItem,
|
||||
'cursor-pointer px-3 py-1 flex items-center'
|
||||
'cursor-pointer px-3 py-1 flex items-center gap-2'
|
||||
)}
|
||||
onClick={() => onFilterSearchClick({ type: key, value: f.value })}
|
||||
onClick={() =>
|
||||
onFilterSearchClick({ type: key, value: f.value })
|
||||
}
|
||||
>
|
||||
<Icon className='mr-2' name={option.icon} size='16' />
|
||||
<div className='whitespace-nowrap text-ellipsis overflow-hidden'>
|
||||
{getNewIcon(option)}
|
||||
<div className="whitespace-nowrap text-ellipsis overflow-hidden">
|
||||
{f.value}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -186,6 +280,6 @@ export default connect((state: any, props: any) => {
|
|||
: state.getIn(['search', 'filterSearchList']),
|
||||
fetchingFilterSearchList: props.isLive
|
||||
? state.getIn(['liveSearch', 'fetchFilterSearch', 'loading'])
|
||||
: state.getIn(['search', 'fetchFilterSearch', 'loading'])
|
||||
: state.getIn(['search', 'fetchFilterSearch', 'loading']),
|
||||
};
|
||||
})(FilterModal);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export enum FilterCategory {
|
|||
INTERACTIONS = 'Interactions',
|
||||
GEAR = 'Gear',
|
||||
RECORDING_ATTRIBUTES = 'Recording Attributes',
|
||||
JAVASCRIPT = 'Javascript',
|
||||
TECHNICAL = 'Technical',
|
||||
USER = 'User Identification',
|
||||
METADATA = 'Session & User Metadata',
|
||||
PERFORMANCE = 'Performance',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ import { capitalize } from 'App/utils';
|
|||
const countryOptions = Object.keys(countries).map(i => ({ label: countries[i], value: i }));
|
||||
const containsFilters = [{ key: 'contains', label: 'contains', text: 'contains', value: 'contains' }];
|
||||
|
||||
const filterOrder = {
|
||||
[FilterCategory.INTERACTIONS]: 0,
|
||||
[FilterCategory.TECHNICAL]: 1,
|
||||
[FilterCategory.PERFORMANCE]: 2,
|
||||
[FilterCategory.USER]: 3,
|
||||
[FilterCategory.GEAR]: 4,
|
||||
}
|
||||
|
||||
export const filters = [
|
||||
{
|
||||
key: FilterKey.CLICK,
|
||||
|
|
@ -44,7 +52,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.CUSTOM,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'Custom Events',
|
||||
placeholder: 'Enter event key',
|
||||
operator: 'is',
|
||||
|
|
@ -56,7 +64,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.FETCH,
|
||||
type: FilterType.SUB_FILTERS,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
operator: 'is',
|
||||
label: 'Network Request',
|
||||
filters: [
|
||||
|
|
@ -126,7 +134,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.GRAPHQL,
|
||||
type: FilterType.SUB_FILTERS,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'GraphQL',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
|
|
@ -175,7 +183,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.STATEACTION,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'State Action',
|
||||
placeholder: 'E.g. 12',
|
||||
operator: 'is',
|
||||
|
|
@ -186,7 +194,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.ERROR,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'Error Message',
|
||||
placeholder: 'E.g. Uncaught SyntaxError',
|
||||
operator: 'is',
|
||||
|
|
@ -197,53 +205,6 @@ export const filters = [
|
|||
// { key: FilterKey.METADATA, type: FilterType.MULTIPLE, category: FilterCategory.METADATA, label: 'Metadata', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/metadata', isEvent: true },
|
||||
|
||||
// FILTERS
|
||||
{
|
||||
key: FilterKey.USER_OS,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User OS',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/os'
|
||||
},
|
||||
{
|
||||
key: FilterKey.USER_BROWSER,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User Browser',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/browser'
|
||||
},
|
||||
{
|
||||
key: FilterKey.USER_DEVICE,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User Device',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/device'
|
||||
},
|
||||
{
|
||||
key: FilterKey.PLATFORM,
|
||||
type: FilterType.MULTIPLE_DROPDOWN,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'Platform',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.baseOperators,
|
||||
icon: 'filters/platform',
|
||||
options: platformOptions
|
||||
},
|
||||
{
|
||||
key: FilterKey.REVID,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'Version ID',
|
||||
placeholder: 'E.g. v1.0.8',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'collection'
|
||||
},
|
||||
{
|
||||
key: FilterKey.REFERRER,
|
||||
type: FilterType.MULTIPLE,
|
||||
|
|
@ -297,7 +258,7 @@ export const filters = [
|
|||
key: FilterKey.USER_STATE,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.USER,
|
||||
label: 'User State',
|
||||
label: 'State / Province',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']),
|
||||
icon: 'filters/country',
|
||||
|
|
@ -429,15 +390,66 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.ISSUE,
|
||||
type: FilterType.ISSUE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'Issue',
|
||||
placeholder: 'Select an issue',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']),
|
||||
icon: 'filters/click',
|
||||
options: filterOptions.issueOptions
|
||||
}
|
||||
];
|
||||
},
|
||||
{
|
||||
key: FilterKey.USER_OS,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User OS',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/os'
|
||||
},
|
||||
{
|
||||
key: FilterKey.USER_BROWSER,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User Browser',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/browser'
|
||||
},
|
||||
{
|
||||
key: FilterKey.USER_DEVICE,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'User Device',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'filters/device'
|
||||
},
|
||||
{
|
||||
key: FilterKey.PLATFORM,
|
||||
type: FilterType.MULTIPLE_DROPDOWN,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'Platform',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.baseOperators,
|
||||
icon: 'filters/platform',
|
||||
options: platformOptions
|
||||
},
|
||||
{
|
||||
key: FilterKey.REVID,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.GEAR,
|
||||
label: 'Version ID',
|
||||
placeholder: 'E.g. v1.0.8',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.stringOperators,
|
||||
icon: 'collection'
|
||||
},
|
||||
].sort((a, b) => {
|
||||
const aOrder = filterOrder[a.category] ?? 9
|
||||
const bOrder = filterOrder[b.category] ?? 9
|
||||
return aOrder - bOrder
|
||||
})
|
||||
|
||||
export const flagConditionFilters = [
|
||||
{
|
||||
|
|
@ -500,7 +512,7 @@ export const flagConditionFilters = [
|
|||
key: FilterKey.USER_STATE,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.USER,
|
||||
label: 'User State',
|
||||
label: 'State / Province',
|
||||
operator: 'is',
|
||||
operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']),
|
||||
icon: 'filters/country',
|
||||
|
|
@ -519,7 +531,11 @@ export const flagConditionFilters = [
|
|||
}],
|
||||
icon: 'filters/userid'
|
||||
}
|
||||
];
|
||||
].sort((a, b) => {
|
||||
const aOrder = filterOrder[a.category] ?? 9
|
||||
const bOrder = filterOrder[b.category] ?? 9
|
||||
return aOrder - bOrder
|
||||
})
|
||||
|
||||
export const conditionalFilters = [
|
||||
{
|
||||
|
|
@ -546,7 +562,7 @@ export const conditionalFilters = [
|
|||
{
|
||||
key: FilterKey.CUSTOM,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'Custom Events',
|
||||
placeholder: 'Enter event key',
|
||||
operator: 'is',
|
||||
|
|
@ -557,7 +573,7 @@ export const conditionalFilters = [
|
|||
{
|
||||
key: FilterKey.FETCH,
|
||||
type: FilterType.SUB_FILTERS,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
operator: 'is',
|
||||
label: 'Network Request',
|
||||
filters: [
|
||||
|
|
@ -609,7 +625,7 @@ export const conditionalFilters = [
|
|||
{
|
||||
key: FilterKey.ERROR,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.JAVASCRIPT,
|
||||
category: FilterCategory.TECHNICAL,
|
||||
label: 'Error Message',
|
||||
placeholder: 'E.g. Uncaught SyntaxError',
|
||||
operator: 'is',
|
||||
|
|
@ -636,7 +652,11 @@ export const conditionalFilters = [
|
|||
operatorOptions: filterOptions.stringConditional,
|
||||
isEvent: false
|
||||
},
|
||||
];
|
||||
].sort((a, b) => {
|
||||
const aOrder = filterOrder[a.category] ?? 9
|
||||
const bOrder = filterOrder[b.category] ?? 9
|
||||
return aOrder - bOrder
|
||||
})
|
||||
|
||||
export const eventKeys = filters.filter((i) => i.isEvent).map(i => i.key);
|
||||
export const nonFlagFilters = filters.filter(i => {
|
||||
|
|
@ -671,7 +691,7 @@ const mapLiveFilters = (list) => {
|
|||
list.forEach(filter => {
|
||||
if (
|
||||
filter.category !== FilterCategory.INTERACTIONS &&
|
||||
filter.category !== FilterCategory.JAVASCRIPT &&
|
||||
filter.category !== FilterCategory.TECHNICAL &&
|
||||
filter.category !== FilterCategory.PERFORMANCE &&
|
||||
filter.key !== FilterKey.DURATION &&
|
||||
filter.key !== FilterKey.REFERRER &&
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue