From 9ee8cbd24a708f438cdfbeb0ce6b33ff7aa95b0e Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 4 Apr 2025 18:28:00 +0200 Subject: [PATCH] feat(ui): dynamic fitlers - apply filters to cards --- .../components/FilterSeries/FilterSeries.tsx | 129 ++++-- .../shared/Filters/FilterItem/FilterItem.tsx | 87 ++-- .../shared/Filters/FilterList/FilterList.tsx | 5 +- .../Filters/FilterList/UnifiedFilterList.tsx | 26 +- .../Filters/FilterOperator/FilterOperator.tsx | 36 +- .../FilterSelection/FilterSelection.tsx | 43 +- .../Filters/FilterValue/FilterValue.tsx | 373 ++++++++------- .../Filters/FilterValue/ValueAutoComplete.tsx | 73 ++- .../shared/SessionFilters/SessionFilters.tsx | 180 ++++---- .../SessionsTabOverview.tsx | 2 +- frontend/app/mstore/filterStore.ts | 8 +- frontend/app/mstore/searchStore.ts | 68 ++- frontend/app/mstore/types/filter.ts | 424 ++++++++++++------ frontend/app/mstore/types/filterConstants.ts | 113 +++-- frontend/app/mstore/types/filterItem.ts | 153 ++----- frontend/app/mstore/types/filterSeries.ts | 27 +- frontend/app/mstore/types/search.ts | 6 +- frontend/app/styles/general.css | 2 +- frontend/app/types/filter/filterType.ts | 8 +- 19 files changed, 1007 insertions(+), 756 deletions(-) diff --git a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx index b80a456cb..a59072905 100644 --- a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx @@ -1,11 +1,16 @@ -import React, { useEffect, useState } from 'react'; -import { EventsList, FilterList } from 'Shared/Filters/FilterList'; +import React from 'react'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; -import { Button, Space } from 'antd'; +import { Button, Divider, Space } from 'antd'; import { ChevronDown, ChevronUp, Trash } from 'lucide-react'; import ExcludeFilters from './ExcludeFilters'; import SeriesName from './SeriesName'; +import FilterListHeader from 'Shared/Filters/FilterList/FilterListHeader'; +import FilterSelection from 'Shared/Filters/FilterSelection'; +import { Filter } from '@/mstore/types/filterConstants'; +import { Plus } from '.store/lucide-react-virtual-9282d60eb0/package'; +import UnifiedFilterList from 'Shared/Filters/FilterList/UnifiedFilterList'; +import { useStore } from '@/mstore'; const FilterCountLabels = observer( (props: { filters: any; toggleExpand: any }) => { @@ -38,7 +43,7 @@ const FilterCountLabels = observer( ); - }, + } ); const FilterSeriesHeader = observer( @@ -62,8 +67,8 @@ const FilterSeriesHeader = observer( 'px-4 ps-2 h-12 flex items-center relative bg-white border-gray-lighter border-t border-l border-r rounded-t-xl', { hidden: props.hidden, - 'rounded-b-xl': !props.expanded, - }, + 'rounded-b-xl': !props.expanded + } )} > @@ -106,7 +111,7 @@ const FilterSeriesHeader = observer( ); - }, + } ); interface Props { @@ -130,7 +135,8 @@ interface Props { function FilterSeries(props: Props) { const { - observeChanges = () => {}, + observeChanges = () => { + }, canDelete, hideHeader = false, emptyMessage = 'Add an event or filter step to define the series.', @@ -142,12 +148,17 @@ function FilterSeries(props: Props) { removeEvents, collapseState, onToggleCollapse, - excludeCategory, + excludeCategory } = props; + const { filterStore } = useStore(); const expanded = isHeatmap || !collapseState; const setExpanded = onToggleCollapse; const { series, seriesIndex } = props; + const allFilterOptions: Filter[] = filterStore.getCurrentProjectFilters(); + const eventOptions: Filter[] = allFilterOptions.filter((i) => i.isEvent); + const propertyOptions: Filter[] = allFilterOptions.filter((i) => !i.isEvent); + const onUpdateFilter = (filterIndex: any, filter: any) => { series.filter.updateFilter(filterIndex, filter); observeChanges(); @@ -174,6 +185,8 @@ function FilterSeries(props: Props) { observeChanges(); }; + console.log('series.filter.filters', series.filter.filters); + return (
{canExclude && } @@ -216,33 +229,79 @@ function FilterSeries(props: Props) { {expanded ? ( <> {removeEvents ? null : ( - +
+ f.isEvent).length > 0} + orderProps={{ + eventsOrder: series.filter.eventsOrder, + eventsOrderSupport: ['then', 'and', 'or'] + }} + onChangeOrder={onChangeEventsOrder} + filterSelection={ + { + onAddFilter(newFilter); + }} + > + + + } + /> + + f.isEvent)} + isDraggable={true} + showIndices={true} + className="mt-2" + handleRemove={onRemoveFilter} + handleUpdate={onUpdateFilter} + handleAdd={onAddFilter} + handleMove={onFilterMove} + /> + + + + !f.isEvent).length > 0} + filterSelection={ + { + onAddFilter(newFilter); + }} + > + + + } + /> + + !f.isEvent)} + isDraggable={false} + showIndices={false} + className="mt-2" + handleRemove={onRemoveFilter} + handleUpdate={onUpdateFilter} + handleAdd={onAddFilter} + handleMove={onFilterMove} + /> +
)} - ) : null}
diff --git a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx index 4181a6198..68b9723fc 100644 --- a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx +++ b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx @@ -26,7 +26,7 @@ interface Props { isSubItem?: boolean; subFilterIndex?: number; propertyOrder?: string; - onToggleOperator?: (newOp: string) => void; + onPropertyOrderChange?: (newOp: string) => void; parentEventFilterOptions?: Filter[]; isDragging?: boolean; isFirst?: boolean; @@ -47,7 +47,7 @@ function FilterItem(props: Props) { isSubItem = false, subFilterIndex = 0, // Default to 0 propertyOrder, - onToggleOperator, + onPropertyOrderChange, parentEventFilterOptions, isDragging, isFirst = false // Default to false @@ -70,27 +70,59 @@ function FilterItem(props: Props) { const operatorOptions = getOperatorsByType(filter.type); useEffect(() => { + let isMounted = true; // Mounted flag + async function loadFilters() { - if (!isSubItem && filter.isEvent && filter.name) { + const shouldFetch = !isSubItem && filter.isEvent && filter.name; + const fetchName = filter.name; // Capture value at effect start + + if (shouldFetch) { try { - setEventFiltersLoading(true); - const options = await filterStore.getEventFilters(filter.name); - setEventFilterOptions(options); + // Only set loading if not already loading for this specific fetch + if (isMounted) setEventFiltersLoading(true); + + const options = await filterStore.getEventFilters(fetchName); + + // Check mount status AND if the relevant dependencies are still the same + if (isMounted && filter.name === fetchName && !isSubItem && filter.isEvent) { + // Avoid setting state if options haven't actually changed (optional optimization) + // This requires comparing options, which might be complex/costly. + // Sticking to setting state is usually fine if dependencies are stable. + setEventFilterOptions(options); + } } catch (error) { console.error('Failed to load event filters:', error); - setEventFilterOptions([]); + if (isMounted && filter.name === fetchName && !isSubItem && filter.isEvent) { + setEventFilterOptions([]); + } } finally { - setEventFiltersLoading(false); + if (isMounted && filter.name === fetchName && !isSubItem && filter.isEvent) { + setEventFiltersLoading(false); + } } } else { - if (eventFilterOptions.length > 0) { - setEventFilterOptions([]); + // Reset state only if necessary and component is mounted + if (isMounted) { + // Avoid calling setState if already in the desired state + if (eventFilterOptions.length > 0) { + setEventFilterOptions([]); + } + // Might need to check loading state too if it could be stuck true + if (eventFiltersLoading) { + setEventFiltersLoading(false); + } } } } void loadFilters(); - }, [filter.name, filter.isEvent, isSubItem, filterStore]); + + return () => { + isMounted = false; // Cleanup on unmount + }; + // Dependencies should be the minimal primitive values or stable references + // that determine *if* and *what* to fetch. + }, [filter.name, filter.isEvent, isSubItem, filterStore]); // const canShowValues = useMemo( () => @@ -169,7 +201,7 @@ function FilterItem(props: Props) { const newSubFilter = { ...selectedFilter, value: selectedFilter.value || [''], - operator: selectedFilter.operator || getOperatorsByType(selectedFilter.type)[0]?.value // Default operator + operator: selectedFilter.operator || 'is' }; onUpdate({ ...filter, @@ -193,7 +225,6 @@ function FilterItem(props: Props) { return (
{/* Use items-start */} - {!isSubItem && !hideIndex && filterIndex !== undefined && filterIndex >= 0 && (
{/* Align index top */} @@ -203,20 +234,20 @@ function FilterItem(props: Props) { {isSubItem && (
+ className="flex-shrink-0 w-14 text-right text-neutral-500/90 pr-2"> {subFilterIndex === 0 && ( where )} - {subFilterIndex !== 0 && propertyOrder && onToggleOperator && ( + {subFilterIndex !== 0 && propertyOrder && onPropertyOrderChange && ( - !readonly && onToggleOperator(propertyOrder === 'and' ? 'or' : 'and') + !readonly && onPropertyOrderChange(propertyOrder === 'and' ? 'or' : 'and') } > {propertyOrder} @@ -227,7 +258,7 @@ function FilterItem(props: Props) { {/* Main content area */}
+ className="flex flex-grow flex-wrap gap-x-2 items-center"> + {categoryPart} )} @@ -263,7 +294,7 @@ function FilterItem(props: Props) { )} - + {hasName ? namePart : (hasCategory ? '' : defaultText)} {/* Show name or placeholder */} @@ -301,7 +332,7 @@ function FilterItem(props: Props) { {canShowValues && (readonly ? (
{filter.value @@ -359,20 +390,20 @@ function FilterItem(props: Props) { {filteredSubFilters.length > 0 && (
{/* Dashed line */}
{filteredSubFilters.map((subFilter: any, index: number) => (
))} diff --git a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx index e3213a74f..6d8269955 100644 --- a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx +++ b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx @@ -252,7 +252,10 @@ export const EventsList = observer((props: Props) => {
{!hideEventsOrder && ( - + )} {actions && actions.map((action, index) =>
{action}
)} diff --git a/frontend/app/components/shared/Filters/FilterList/UnifiedFilterList.tsx b/frontend/app/components/shared/Filters/FilterList/UnifiedFilterList.tsx index e8722438e..6c7f61b31 100644 --- a/frontend/app/components/shared/Filters/FilterList/UnifiedFilterList.tsx +++ b/frontend/app/components/shared/Filters/FilterList/UnifiedFilterList.tsx @@ -63,9 +63,6 @@ const UnifiedFilterList = (props: UnifiedFilterListProps) => { const calculateNewPosition = useCallback( (hoverIndex: number, hoverPosition: string) => { - // Calculate the target *visual* position - // If hovering top half, target index is hoverIndex. - // If bottom half, target index is hoverIndex + 1. return hoverPosition === 'bottom' ? hoverIndex + 1 : hoverIndex; }, [] @@ -124,13 +121,10 @@ const UnifiedFilterList = (props: UnifiedFilterListProps) => { let newPosition = calculateNewPosition(hoverIndex, hoverPosition); - // Important: Adjust newPosition if dragging downwards past the original position - // because the removal shifts subsequent indices up. if (dragInd < newPosition) { newPosition--; } - // Only call move if the position actually changed if (dragInd !== newPosition && !(dragInd === hoverIndex && hoverPosition === 'top') && !(dragInd === hoverIndex - 1 && hoverPosition === 'bottom')) { handleMove(dragInd, newPosition); } @@ -148,21 +142,18 @@ const UnifiedFilterList = (props: UnifiedFilterListProps) => { }, []); const handleDragLeave = useCallback(() => { - // Only clear if leaving the specific item, not just moving within it setHoveredItem({ i: null, position: null }); }, []); return filters.length ? ( -
+
{filters.map((filterItem: any, filterIndex: number) => (
{ {isDraggable && filters.length > 1 && (
@@ -186,22 +176,26 @@ const UnifiedFilterList = (props: UnifiedFilterListProps) => { )} {!isDraggable && showIndices && -
} {/* Placeholder for alignment if not draggable but indices shown */} +
} {!isDraggable && !showIndices && -
} {/* Placeholder for alignment if not draggable and no indices */} +
} updateFilter(filterItem.key, updatedFilter)} - onRemoveFilter={() => removeFilter(filterItem.key)} + onUpdate={(updatedFilter) => updateFilter(filterItem.id, updatedFilter)} + onRemoveFilter={() => removeFilter(filterItem.id)} saveRequestPayloads={saveRequestPayloads} disableDelete={cannotDelete} readonly={readonly} isConditional={isConditional} hideIndex={!showIndices} isDragging={draggedInd === filterIndex} + onPropertyOrderChange={filterItem.isEvent ? (order: string) => { + const newFilter = { ...filterItem, propertyOrder: order }; + updateFilter(filterItem.id, newFilter); + } : undefined} // Pass down if this is the first item for potential styling (e.g., no 'and'/'or' toggle) isFirst={filterIndex === 0} /> diff --git a/frontend/app/components/shared/Filters/FilterOperator/FilterOperator.tsx b/frontend/app/components/shared/Filters/FilterOperator/FilterOperator.tsx index dc066f183..113e6e8ea 100644 --- a/frontend/app/components/shared/Filters/FilterOperator/FilterOperator.tsx +++ b/frontend/app/components/shared/Filters/FilterOperator/FilterOperator.tsx @@ -1,28 +1,26 @@ import React from 'react'; -import { Dropdown, Menu, Button } from 'antd'; // Import Dropdown, Menu, Button -import { DownOutlined } from '@ant-design/icons'; // Optional: Icon for the button +import { Dropdown, Menu, Button, Typography } from 'antd'; interface OptionType { - label: React.ReactNode; // Label can be text or other React elements - value: string | number; // Value is typically string or number + label: React.ReactNode; + value: string | number; } interface Props { name: string; options: OptionType[]; - value?: string | number; // Should match the type of OptionType.value + value?: string | number; onChange: ( - event: unknown, // Keep original signature for compatibility upstream + event: unknown, payload: { name: string; value: string | number | undefined } ) => void; isDisabled?: boolean; className?: string; placeholder?: string; - allowClear?: boolean; // Prop name from original component - popupClassName?: string; // Use this for the dropdown overlay class + allowClear?: boolean; + popupClassName?: string; } -// Define a special key for the clear action const CLEAR_VALUE_KEY = '__antd_clear_value__'; function FilterOperator(props: Props) { @@ -33,37 +31,30 @@ function FilterOperator(props: Props) { onChange, isDisabled = false, className = '', - placeholder = 'Select', // Default placeholder + placeholder = 'select', // Default placeholder allowClear = false, // Default from original component popupClassName = 'shadow-lg border border-gray-200 rounded-md w-fit' // Default popup class } = props; - // Find the label of the currently selected option const selectedOption = options.find(option => option.value === value); - const displayLabel = selectedOption ? selectedOption.label : placeholder; + const displayLabel = selectedOption ? selectedOption.label : + {placeholder}; - // Handler for menu item clicks const handleMenuClick = (e: { key: string }) => { let selectedValue: string | number | undefined; if (e.key === CLEAR_VALUE_KEY) { - // Handle the clear action selectedValue = undefined; } else { - // Find the option corresponding to the key (which we set as the value) - // Antd Menu keys are strings, so convert value to string for comparison/lookup if needed const clickedOption = options.find(option => String(option.value) === e.key); selectedValue = clickedOption?.value; } - // Call the original onChange prop with the expected structure onChange(null, { name: name, value: selectedValue }); }; - // Construct the menu items const menu = ( - {/* Add Clear Option if allowClear is true and a value is selected */} {allowClear && value !== undefined && ( <> @@ -85,12 +76,11 @@ function FilterOperator(props: Props) { <> - {/* The Button acts as the trigger */} - diff --git a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx index 5a7cfdd22..6eb9015e3 100644 --- a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx +++ b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx @@ -1,5 +1,4 @@ import React, { useState, useCallback } from 'react'; -// Import Spin and potentially classnames import { Popover, Spin } from 'antd'; import cn from 'classnames'; import { observer } from 'mobx-react-lite'; @@ -11,8 +10,8 @@ interface FilterSelectionProps { onFilterClick: (filter: Filter) => void; children?: React.ReactNode; disabled?: boolean; - isLive?: boolean; // This prop seems unused, consider removing if not needed downstream - loading?: boolean; // <-- Add loading prop + isLive?: boolean; + loading?: boolean; } const FilterSelection: React.FC = observer(({ @@ -26,43 +25,32 @@ const FilterSelection: React.FC = observer(({ const [open, setOpen] = useState(false); const handleFilterClick = useCallback((selectedFilter: Filter) => { - // Don't do anything if loading - though modal shouldn't be clickable then anyway if (loading) return; onFilterClick(selectedFilter); setOpen(false); }, [onFilterClick, loading]); const handleOpenChange = useCallback((newOpen: boolean) => { - // Prevent opening if disabled or loading if (!disabled && !loading) { setOpen(newOpen); } else if (!newOpen) { - // Allow closing even if disabled/loading (e.g., clicking outside) setOpen(newOpen); } }, [disabled, loading]); - // Determine the content for the Popover const content = ( loading - // Show a spinner centered in the popover content area while loading ?
- // Otherwise, show the filter modal : ); - // Combine disabled and loading states for the trigger element const isDisabled = disabled || loading; - // Clone the trigger element (children) and pass the effective disabled state - // Also add loading class for potential styling (e.g., opacity) const triggerElement = React.isValidElement(children) ? React.cloneElement(children as React.ReactElement, { disabled: isDisabled, @@ -71,21 +59,20 @@ const FilterSelection: React.FC = observer(({ : children; return ( - // Add a class to the wrapper if needed, e.g., for opacity when loading //
- - {triggerElement} - + + {triggerElement} + //
); }); diff --git a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx index af65dd032..775ba5644 100644 --- a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx +++ b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx @@ -1,14 +1,14 @@ -import React, { useState } from 'react'; +import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { FilterKey, FilterCategory, FilterType } from 'Types/filter/filterType'; import { debounce } from 'App/utils'; import { assist as assistRoute, isRoute } from 'App/routes'; -import cn from 'classnames'; import { observer } from 'mobx-react-lite'; import FilterDuration from '../FilterDuration'; import FilterValueDropdown from '../FilterValueDropdown'; import FilterAutoCompleteLocal from '../FilterAutoCompleteLocal'; import FilterAutoComplete from '../FilterAutoComplete'; import ValueAutoComplete from 'Shared/Filters/FilterValue/ValueAutoComplete'; +import { Input, Select } from 'antd'; const ASSIST_ROUTE = assistRoute(); @@ -18,233 +18,260 @@ interface Props { isConditional?: boolean; } +function BaseFilterLocalAutoComplete(props: any) { + return ( + + ); +} + +function BaseDropDown(props: any) { + return ( + + ); +} + + function FilterValue(props: Props) { - const { filter } = props; - const isAutoOpen = filter.autoOpen; + const { filter, onUpdate, isConditional } = props; // Destructure props early + const isAutoOpen = filter.autoOpen; // Assume parent now controls this correctly - React.useEffect(() => { - if (isAutoOpen) { - setTimeout(() => { - filter.autoOpen = false; - }, 250); - } - }, [isAutoOpen]); - const [durationValues, setDurationValues] = useState({ + const [durationValues, setDurationValues] = useState(() => ({ minDuration: filter.value?.[0], - maxDuration: filter.value.length > 1 ? filter.value[1] : filter.value[0] - }); + maxDuration: filter.value?.length > 1 ? filter.value[1] : filter.value?.[0] + })); + + useEffect(() => { + if (filter.type === FilterType.DURATION) { + const incomingMin = filter.value?.[0]; + const incomingMax = filter.value?.length > 1 ? filter.value[1] : filter.value?.[0]; + if (durationValues.minDuration !== incomingMin || durationValues.maxDuration !== incomingMax) { + setDurationValues({ minDuration: incomingMin, maxDuration: incomingMax }); + } + } + }, [filter.value, filter.type]); + + const showCloseButton = filter.value.length > 1; + const showOrButton = filter.value.length > 1; - const onAddValue = () => { + const onAddValue = useCallback(() => { const newValue = filter.value.concat(''); - props.onUpdate({ ...filter, value: newValue }); - }; + onUpdate({ ...filter, value: newValue }); + }, [filter, onUpdate]); - const onApplyValues = (values: string[]) => { - props.onUpdate({ ...filter, value: values }); - }; + const onApplyValues = useCallback((values: string[]) => { + onUpdate({ ...filter, value: values }); + }, [filter, onUpdate]); - const onRemoveValue = (valueIndex: any) => { + const onRemoveValue = useCallback((valueIndex: any) => { const newValue = filter.value.filter( (_: any, index: any) => index !== valueIndex ); - props.onUpdate({ ...filter, value: newValue }); - }; + onUpdate({ ...filter, value: newValue }); + }, [filter, onUpdate]); - const onChange = (e: any, item: any, valueIndex: any) => { - const newValues = filter.value.map((_: any, _index: any) => { + const stableOnChange = useCallback((e: any, item: any, valueIndex: any) => { + const newValues = filter.value.map((val: any, _index: any) => { if (_index === valueIndex) { return item; } - return _; + return val; }); - props.onUpdate({ ...filter, value: newValues }); - }; + onUpdate({ ...filter, value: newValues }); + }, [filter, onUpdate]); - const debounceOnSelect = React.useCallback(debounce(onChange, 500), [ - onChange - ]); + const debounceOnSelect = useCallback(debounce(stableOnChange, 500), [stableOnChange]); - const onDurationChange = (newValues: any) => { - setDurationValues({ ...durationValues, ...newValues }); - }; + const onDurationChange = useCallback((newValues: any) => { + setDurationValues(current => ({ ...current, ...newValues })); + }, []); - const handleBlur = () => { + const handleBlur = useCallback(() => { if (filter.type === FilterType.DURATION) { - const { maxDuration, minDuration } = filter; - if (maxDuration || minDuration) return; - if ( - maxDuration !== durationValues.maxDuration || - minDuration !== durationValues.minDuration - ) { - props.onUpdate({ + const currentMinInProp = filter.value?.[0]; + const currentMaxInProp = filter.value?.length > 1 ? filter.value[1] : filter.value?.[0]; + + if (durationValues.minDuration !== currentMinInProp || durationValues.maxDuration !== currentMaxInProp) { + onUpdate({ ...filter, value: [durationValues.minDuration, durationValues.maxDuration] }); } } - }; + }, [filter, onUpdate, filter.value, durationValues.minDuration, durationValues.maxDuration]); // Add durationValues dependency - const getParams = (key: any) => { - let params: any = { + const params = useMemo(() => { + let baseParams: any = { type: filter.key, name: filter.name, isEvent: filter.isEvent, id: filter.id }; - switch (filter.category) { - case FilterCategory.METADATA: - params = { type: FilterKey.METADATA, key }; + if (filter.category === FilterCategory.METADATA) { + baseParams = { type: FilterKey.METADATA, key: filter.key }; } - if (isRoute(ASSIST_ROUTE, window.location.pathname)) { - params = { ...params, live: true }; + baseParams = { ...baseParams, live: true }; } + return baseParams; + }, [filter.key, filter.name, filter.isEvent, filter.id, filter.category]); - return params; - }; + const value = filter.value; - const renderValueFiled = (value: any[]) => { - const showOrButton = filter.value.length > 1; - - function BaseFilterLocalAutoComplete(props) { + switch (filter.type) { + case FilterType.DOUBLE: return ( - { + const newValue = e.target.value; + onUpdate({ ...filter, value: newValue }); + }} + placeholder={filter.placeholder} + onBlur={handleBlur} + /> + ); + case FilterType.BOOLEAN: + return ( +