change(ui): dashboard redesign
This commit is contained in:
parent
3bfdc0e574
commit
3b2c988e42
3 changed files with 371 additions and 301 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import React, {useState} from 'react';
|
||||
import FilterList from 'Shared/Filters/FilterList';
|
||||
import {Icon} from 'UI';
|
||||
import SeriesName from './SeriesName';
|
||||
import cn from 'classnames';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
|
|
@ -9,6 +8,63 @@ import AddStepButton from "Components/Dashboard/components/FilterSeries/AddStepB
|
|||
import {Button, Space} from "antd";
|
||||
import {ChevronDown, ChevronUp, Trash} from "lucide-react";
|
||||
|
||||
|
||||
const FilterCountLabels = observer((props: { filters: any, toggleExpand: any }) => {
|
||||
const events = props.filters.filter((i: any) => i && i.isEvent).length;
|
||||
const filters = props.filters.filter((i: any) => i && !i.isEvent).length;
|
||||
return <div className="flex items-center">
|
||||
<Space>
|
||||
{events > 0 && (
|
||||
<Button type="primary" ghost size="small" onClick={props.toggleExpand}>
|
||||
{`${events} Event${events > 1 ? 's' : ''}`}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{filters > 0 && (
|
||||
<Button type="primary" ghost size="small" onClick={props.toggleExpand}>
|
||||
{`${filters} Filter${filters > 1 ? 's' : ''}`}
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</div>;
|
||||
});
|
||||
|
||||
const FilterSeriesHeader = observer((props: {
|
||||
expanded: boolean,
|
||||
hidden: boolean,
|
||||
seriesIndex: number,
|
||||
series: any,
|
||||
onRemove: (seriesIndex: any) => void,
|
||||
canDelete: boolean | undefined,
|
||||
toggleExpand: () => void
|
||||
}) => {
|
||||
|
||||
const onUpdate = (name: any) => {
|
||||
props.series.update('name', name)
|
||||
}
|
||||
return <div className={cn("border-b px-5 h-12 flex items-center relative", {hidden: props.hidden})}>
|
||||
<Space className="mr-auto" size={30}>
|
||||
<SeriesName
|
||||
seriesIndex={props.seriesIndex}
|
||||
name={props.series.name}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
{!props.expanded &&
|
||||
<FilterCountLabels filters={props.series.filter.filters} toggleExpand={props.toggleExpand}/>}
|
||||
</Space>
|
||||
|
||||
<Space>
|
||||
<Button onClick={props.onRemove}
|
||||
size="small"
|
||||
disabled={!props.canDelete}
|
||||
icon={<Trash size={14}/>}/>
|
||||
<Button onClick={props.toggleExpand}
|
||||
size="small"
|
||||
icon={props.expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
</Space>
|
||||
</div>;
|
||||
})
|
||||
|
||||
interface Props {
|
||||
seriesIndex: number;
|
||||
series: any;
|
||||
|
|
@ -20,62 +76,9 @@ interface Props {
|
|||
observeChanges?: () => void;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
canExclude?: boolean;
|
||||
expandable?: boolean;
|
||||
}
|
||||
|
||||
const FilterSeriesHeader = observer((props: {
|
||||
expanded: boolean,
|
||||
hidden: boolean,
|
||||
seriesIndex: number,
|
||||
series: any,
|
||||
onRemove: (seriesIndex: any) => void,
|
||||
canDelete: boolean | undefined,
|
||||
toggleExpand: () => void
|
||||
}) => {
|
||||
const events = props.series.filter.filters.filter((i: any) => i && i.isEvent).length;
|
||||
const filters = props.series.filter.filters.filter((i: any) => i && !i.isEvent).length;
|
||||
const onUpdate = (name: any) => {
|
||||
props.series.update('name', name)
|
||||
}
|
||||
return <div className={cn("border-b px-5 h-12 flex items-center relative", {hidden: props.hidden})}>
|
||||
<Space className="mr-auto" size={30}>
|
||||
<SeriesName
|
||||
seriesIndex={props.seriesIndex}
|
||||
name={props.series.name}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
{!props.expanded && (
|
||||
<Space>
|
||||
{events > 0 && (
|
||||
<Button type="primary" ghost size="small" onClick={props.toggleExpand}>
|
||||
{`${events} Event${events > 1 ? 's' : ''}`}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{filters > 0 && (
|
||||
<Button type="primary" ghost size="small" onClick={props.toggleExpand}>
|
||||
{`${filters} Filter${filters > 1 ? 's' : ''}`}
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
|
||||
{/*{events === 0 && filters === 0 && !props.expanded && (*/}
|
||||
{/* <AddStepButton series={props.series} excludeFilterKeys={[]}/>*/}
|
||||
{/*)}*/}
|
||||
</Space>
|
||||
|
||||
<Space>
|
||||
<Button onClick={props.onRemove}
|
||||
size="small"
|
||||
disabled={!props.canDelete}
|
||||
icon={<Trash size={16}/>}/>
|
||||
<Button onClick={props.toggleExpand}
|
||||
size="small"
|
||||
icon={props.expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
</Space>
|
||||
</div>;
|
||||
})
|
||||
|
||||
function FilterSeries(props: Props) {
|
||||
const {
|
||||
observeChanges = () => {
|
||||
|
|
@ -86,8 +89,9 @@ function FilterSeries(props: Props) {
|
|||
supportsEmpty = true,
|
||||
excludeFilterKeys = [],
|
||||
canExclude = false,
|
||||
expandable = false
|
||||
} = props;
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const {series, seriesIndex} = props;
|
||||
|
||||
const onUpdateFilter = (filterIndex: any, filter: any) => {
|
||||
|
|
@ -124,6 +128,15 @@ function FilterSeries(props: Props) {
|
|||
toggleExpand={() => setExpanded(!expanded)}/>
|
||||
)}
|
||||
|
||||
{expandable && !expanded && (
|
||||
<Space className="justify-between w-full px-6 py-2">
|
||||
<FilterCountLabels filters={series.filter.filters} toggleExpand={() => setExpanded(!expanded)}/>
|
||||
<Button onClick={() => setExpanded(!expanded)}
|
||||
size="small"
|
||||
icon={expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
</Space>
|
||||
)}
|
||||
|
||||
{expanded && (
|
||||
<>
|
||||
<div className="p-5">
|
||||
|
|
@ -136,6 +149,13 @@ function FilterSeries(props: Props) {
|
|||
supportsEmpty={supportsEmpty}
|
||||
onFilterMove={onFilterMove}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
actions={[
|
||||
expandable && (
|
||||
<Button onClick={() => setExpanded(!expanded)}
|
||||
size="small"
|
||||
icon={expanded ? <ChevronUp size={16}/> : <ChevronDown size={16}/>}/>
|
||||
)
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<div className="color-gray-medium">{emptyMessage}</div>
|
||||
|
|
|
|||
|
|
@ -1,29 +1,24 @@
|
|||
import React from 'react';
|
||||
import {Card, Space, Typography, Button} from "antd";
|
||||
import {useStore} from "App/mstore";
|
||||
import FilterSelection from "Shared/Filters/FilterSelection/FilterSelection";
|
||||
import {eventKeys} from "Types/filter/newFilter";
|
||||
import {CLICKMAP, FUNNEL, INSIGHTS, RETENTION, TABLE, USER_PATH} from "App/constants/card";
|
||||
import FilterSeries from "Components/Dashboard/components/FilterSeries/FilterSeries";
|
||||
import {metricOf} from "App/constants/filterOptions";
|
||||
import {AudioWaveform, PlusIcon} from "lucide-react";
|
||||
import {AudioWaveform, ChevronDown, ChevronUp, PlusIcon} from "lucide-react";
|
||||
import {observer} from "mobx-react-lite";
|
||||
import AddStepButton from "Components/Dashboard/components/FilterSeries/AddStepButton";
|
||||
|
||||
|
||||
interface Props {
|
||||
}
|
||||
|
||||
function WidgetFormNew(props: Props) {
|
||||
const [expanded, setExpanded] = React.useState(true);
|
||||
function WidgetFormNew() {
|
||||
// const [expanded, setExpanded] = React.useState(true);
|
||||
const {metricStore, dashboardStore, aiFiltersStore} = useStore();
|
||||
const metric: any = metricStore.instance;
|
||||
|
||||
const eventsLength = metric.series[0].filter.filters.filter((i: any) => i && i.isEvent).length;
|
||||
const filtersLength = metric.series[0].filter.filters.filter((i: any) => i && !i.isEvent).length;
|
||||
const isClickmap = metric.metricType === CLICKMAP;
|
||||
const isClickMap = metric.metricType === CLICKMAP;
|
||||
const isPathAnalysis = metric.metricType === USER_PATH;
|
||||
const excludeFilterKeys = isClickmap || isPathAnalysis ? eventKeys : [];
|
||||
const excludeFilterKeys = isClickMap || isPathAnalysis ? eventKeys : [];
|
||||
const hasFilters = filtersLength > 0 || eventsLength > 0;
|
||||
|
||||
return (
|
||||
|
|
@ -39,11 +34,7 @@ function WidgetFormNew(props: Props) {
|
|||
)}
|
||||
</Card>
|
||||
|
||||
{/*{eventsLength > 0 && !expanded && (*/}
|
||||
|
||||
{/*)}*/}
|
||||
|
||||
{hasFilters && expanded && (
|
||||
{hasFilters && (
|
||||
<FilterSection metric={metric} excludeFilterKeys={excludeFilterKeys}/>
|
||||
)}
|
||||
</>
|
||||
|
|
@ -105,6 +96,7 @@ const FilterSection = observer(({metric, excludeFilterKeys}: any) => {
|
|||
? 'Filter data using any event or attribute. Use Add Step button below to do so.'
|
||||
: 'Add user event or filter to define the series by clicking Add Step.'
|
||||
}
|
||||
expandable={isSingleSeries}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
|
|
@ -114,7 +106,10 @@ const FilterSection = observer(({metric, excludeFilterKeys}: any) => {
|
|||
<Card styles={{body: {padding: '4px'}}}>
|
||||
<Button
|
||||
type='link'
|
||||
onClick={() => metric.addSeries()}
|
||||
onClick={() => {
|
||||
metric.addSeries();
|
||||
|
||||
}}
|
||||
disabled={!canAddSeries}
|
||||
size="small"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,257 +1,312 @@
|
|||
import { Segmented } from 'antd';
|
||||
import { List } from 'immutable';
|
||||
import { GripHorizontal } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useEffect } from 'react';
|
||||
import {Segmented} from 'antd';
|
||||
import {List} from 'immutable';
|
||||
import {GripHorizontal} from 'lucide-react';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import React, {useEffect} from 'react';
|
||||
|
||||
import { Tooltip } from 'UI';
|
||||
import {Tooltip} from 'UI';
|
||||
|
||||
import FilterItem from '../FilterItem';
|
||||
|
||||
interface Props {
|
||||
filter?: any; // event/filter
|
||||
onUpdateFilter: (filterIndex: any, filter: any) => void;
|
||||
onFilterMove?: (filters: any) => void;
|
||||
onRemoveFilter: (filterIndex: any) => void;
|
||||
onChangeEventsOrder: (e: any, { name, value }: any) => void;
|
||||
hideEventsOrder?: boolean;
|
||||
observeChanges?: () => void;
|
||||
saveRequestPayloads?: boolean;
|
||||
supportsEmpty?: boolean;
|
||||
readonly?: boolean;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
isConditional?: boolean;
|
||||
filter?: any; // event/filter
|
||||
onUpdateFilter: (filterIndex: any, filter: any) => void;
|
||||
onFilterMove?: (filters: any) => void;
|
||||
onRemoveFilter: (filterIndex: any) => void;
|
||||
onChangeEventsOrder: (e: any, {name, value}: any) => void;
|
||||
hideEventsOrder?: boolean;
|
||||
observeChanges?: () => void;
|
||||
saveRequestPayloads?: boolean;
|
||||
supportsEmpty?: boolean;
|
||||
readonly?: boolean;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
isConditional?: boolean;
|
||||
actions?: React.ReactNode[];
|
||||
}
|
||||
|
||||
function FilterList(props: Props) {
|
||||
const {
|
||||
observeChanges = () => {},
|
||||
filter,
|
||||
hideEventsOrder = false,
|
||||
saveRequestPayloads,
|
||||
supportsEmpty = true,
|
||||
excludeFilterKeys = [],
|
||||
isConditional,
|
||||
} = props;
|
||||
const {
|
||||
observeChanges = () => {
|
||||
},
|
||||
filter,
|
||||
hideEventsOrder = false,
|
||||
saveRequestPayloads,
|
||||
supportsEmpty = true,
|
||||
excludeFilterKeys = [],
|
||||
isConditional,
|
||||
actions = []
|
||||
} = props;
|
||||
|
||||
const filters = List(filter.filters);
|
||||
const eventsOrderSupport = filter.eventsOrderSupport;
|
||||
const hasEvents = filters.filter((i: any) => i.isEvent).size > 0;
|
||||
const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0;
|
||||
const filters = List(filter.filters);
|
||||
const eventsOrderSupport = filter.eventsOrderSupport;
|
||||
const hasEvents = filters.filter((i: any) => i.isEvent).size > 0;
|
||||
const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0;
|
||||
|
||||
let rowIndex = 0;
|
||||
const cannotDeleteFilter = hasEvents && !supportsEmpty;
|
||||
let rowIndex = 0;
|
||||
const cannotDeleteFilter = hasEvents && !supportsEmpty;
|
||||
|
||||
useEffect(observeChanges, [filters]);
|
||||
useEffect(observeChanges, [filters]);
|
||||
|
||||
const onRemoveFilter = (filterIndex: any) => {
|
||||
props.onRemoveFilter(filterIndex);
|
||||
};
|
||||
const onRemoveFilter = (filterIndex: any) => {
|
||||
props.onRemoveFilter(filterIndex);
|
||||
};
|
||||
|
||||
const [hoveredItem, setHoveredItem] = React.useState<Record<string, any>>({
|
||||
i: null,
|
||||
position: null,
|
||||
});
|
||||
const [draggedInd, setDraggedItem] = React.useState<number | null>(null);
|
||||
const [hoveredItem, setHoveredItem] = React.useState<Record<string, any>>({
|
||||
i: null,
|
||||
position: null,
|
||||
});
|
||||
const [draggedInd, setDraggedItem] = React.useState<number | null>(null);
|
||||
|
||||
const handleDragOverEv = (event: Record<string, any>, i: number) => {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget.getBoundingClientRect();
|
||||
const hoverMiddleY = (target.bottom - target.top) / 2;
|
||||
const hoverClientY = event.clientY - target.top;
|
||||
const handleDragOverEv = (event: Record<string, any>, i: number) => {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget.getBoundingClientRect();
|
||||
const hoverMiddleY = (target.bottom - target.top) / 2;
|
||||
const hoverClientY = event.clientY - target.top;
|
||||
|
||||
const position = hoverClientY < hoverMiddleY ? 'top' : 'bottom';
|
||||
setHoveredItem({ position, i });
|
||||
};
|
||||
const position = hoverClientY < hoverMiddleY ? 'top' : 'bottom';
|
||||
setHoveredItem({position, i});
|
||||
};
|
||||
|
||||
const calculateNewPosition = React.useCallback(
|
||||
(draggedInd: number, hoveredIndex: number, hoveredPosition: string) => {
|
||||
if (hoveredPosition === 'bottom') {
|
||||
hoveredIndex++;
|
||||
}
|
||||
return draggedInd < hoveredIndex ? hoveredIndex - 1 : hoveredIndex;
|
||||
},
|
||||
[]
|
||||
);
|
||||
const calculateNewPosition = React.useCallback(
|
||||
(draggedInd: number, hoveredIndex: number, hoveredPosition: string) => {
|
||||
if (hoveredPosition === 'bottom') {
|
||||
hoveredIndex++;
|
||||
}
|
||||
return draggedInd < hoveredIndex ? hoveredIndex - 1 : hoveredIndex;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleDragStart = React.useCallback((
|
||||
ev: Record<string, any>,
|
||||
index: number,
|
||||
elId: string
|
||||
) => {
|
||||
ev.dataTransfer.setData("text/plain", index.toString());
|
||||
setDraggedItem(index);
|
||||
const el = document.getElementById(elId);
|
||||
if (el) {
|
||||
ev.dataTransfer.setDragImage(el, 0, 0);
|
||||
}
|
||||
}, [])
|
||||
const handleDragStart = React.useCallback((
|
||||
ev: Record<string, any>,
|
||||
index: number,
|
||||
elId: string
|
||||
) => {
|
||||
ev.dataTransfer.setData("text/plain", index.toString());
|
||||
setDraggedItem(index);
|
||||
const el = document.getElementById(elId);
|
||||
if (el) {
|
||||
ev.dataTransfer.setDragImage(el, 0, 0);
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleDrop = React.useCallback(
|
||||
(event: Record<string, any>) => {
|
||||
event.preventDefault();
|
||||
if (draggedInd === null) return;
|
||||
const newItems = filters.toArray();
|
||||
const newPosition = calculateNewPosition(
|
||||
draggedInd,
|
||||
hoveredItem.i,
|
||||
hoveredItem.position
|
||||
);
|
||||
const handleDrop = React.useCallback(
|
||||
(event: Record<string, any>) => {
|
||||
event.preventDefault();
|
||||
if (draggedInd === null) return;
|
||||
const newItems = filters.toArray();
|
||||
const newPosition = calculateNewPosition(
|
||||
draggedInd,
|
||||
hoveredItem.i,
|
||||
hoveredItem.position
|
||||
);
|
||||
|
||||
const reorderedItem = newItems.splice(draggedInd, 1)[0];
|
||||
newItems.splice(newPosition, 0, reorderedItem);
|
||||
const reorderedItem = newItems.splice(draggedInd, 1)[0];
|
||||
newItems.splice(newPosition, 0, reorderedItem);
|
||||
|
||||
props.onFilterMove?.(List(newItems));
|
||||
setHoveredItem({ i: null, position: null });
|
||||
setDraggedItem(null);
|
||||
},
|
||||
[draggedInd, hoveredItem, filters, props.onFilterMove]
|
||||
);
|
||||
props.onFilterMove?.(List(newItems));
|
||||
setHoveredItem({i: null, position: null});
|
||||
setDraggedItem(null);
|
||||
},
|
||||
[draggedInd, hoveredItem, filters, props.onFilterMove]
|
||||
);
|
||||
|
||||
const eventOrderItems = [
|
||||
{
|
||||
label: 'THEN',
|
||||
value: 'then',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('then'),
|
||||
},
|
||||
{
|
||||
label: 'AND',
|
||||
value: 'and',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('and'),
|
||||
},
|
||||
{
|
||||
label: 'OR',
|
||||
value: 'or',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('or'),
|
||||
},
|
||||
];
|
||||
const eventOrderItems = [
|
||||
{
|
||||
label: 'THEN',
|
||||
value: 'then',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('then'),
|
||||
|
||||
const eventsNum = filters.filter((i: any) => i.isEvent).size
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{hasEvents && (
|
||||
<>
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="text-sm color-gray-medium mr-auto">
|
||||
{filter.eventsHeader}
|
||||
</div>
|
||||
{!hideEventsOrder && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="color-gray-medium text-sm"
|
||||
style={{ textDecoration: 'underline dotted' }}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Select the operator to be applied between events in your search.`}
|
||||
>
|
||||
<div>Events Order</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
},
|
||||
{
|
||||
label: 'AND',
|
||||
value: 'and',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('and'),
|
||||
},
|
||||
{
|
||||
label: 'OR',
|
||||
value: 'or',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('or'),
|
||||
},
|
||||
];
|
||||
|
||||
<Segmented
|
||||
size={'small'}
|
||||
onChange={(v) =>
|
||||
props.onChangeEventsOrder(
|
||||
null,
|
||||
eventOrderItems.find((i) => i.value === v)
|
||||
)
|
||||
}
|
||||
value={filter.eventsOrder}
|
||||
options={eventOrderItems}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={'flex flex-col'}>
|
||||
{filters.map((filter: any, filterIndex: number) =>
|
||||
filter.isEvent ? (
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: 'unset',
|
||||
paddingTop:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'top'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
paddingBottom:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'bottom'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}
|
||||
className={
|
||||
'hover:bg-active-blue px-5 gap-2 items-center flex'
|
||||
}
|
||||
id={`${filter.key}-${filterIndex}`}
|
||||
onDragOver={(e) => handleDragOverEv(e, filterIndex)}
|
||||
onDrop={(e) => handleDrop(e)}
|
||||
key={`${filter.key}-${filterIndex}`}
|
||||
>
|
||||
{!!props.onFilterMove && eventsNum > 1 ? (
|
||||
<div
|
||||
className={'p-2 cursor-grab'}
|
||||
draggable={!!props.onFilterMove}
|
||||
onDragStart={(e) =>
|
||||
handleDragStart(
|
||||
e,
|
||||
filterIndex,
|
||||
`${filter.key}-${filterIndex}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<GripHorizontal size={16} />
|
||||
const eventsNum = filters.filter((i: any) => i.isEvent).size
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{hasEvents && (
|
||||
<>
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="text-sm color-gray-medium mr-auto">
|
||||
{filter.eventsHeader}
|
||||
</div>
|
||||
{!hideEventsOrder && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="color-gray-medium text-sm"
|
||||
style={{textDecoration: 'underline dotted'}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Select the operator to be applied between events in your search.`}
|
||||
>
|
||||
<div>Events Order</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<Segmented
|
||||
size={'small'}
|
||||
onChange={(v) =>
|
||||
props.onChangeEventsOrder(
|
||||
null,
|
||||
eventOrderItems.find((i) => i.value === v)
|
||||
)
|
||||
}
|
||||
value={filter.eventsOrder}
|
||||
options={eventOrderItems}
|
||||
/>
|
||||
{actions && actions.map((action, index) => (
|
||||
<div key={index}>{action}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<FilterItem
|
||||
filterIndex={rowIndex++}
|
||||
filter={filter}
|
||||
onUpdate={(filter) =>
|
||||
props.onUpdateFilter(filterIndex, filter)
|
||||
}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
disableDelete={cannotDeleteFilter}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
readonly={props.readonly}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
<div className={'flex flex-col'}>
|
||||
{filters.map((filter: any, filterIndex: number) =>
|
||||
filter.isEvent ? (
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: 'unset',
|
||||
paddingTop:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'top'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
paddingBottom:
|
||||
hoveredItem.i === filterIndex &&
|
||||
hoveredItem.position === 'bottom'
|
||||
? '1.5rem'
|
||||
: '0.5rem',
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}
|
||||
className={
|
||||
'hover:bg-active-blue px-5 gap-2 items-center flex'
|
||||
}
|
||||
id={`${filter.key}-${filterIndex}`}
|
||||
onDragOver={(e) => handleDragOverEv(e, filterIndex)}
|
||||
onDrop={(e) => handleDrop(e)}
|
||||
key={`${filter.key}-${filterIndex}`}
|
||||
>
|
||||
{!!props.onFilterMove && eventsNum > 1 ? (
|
||||
<div
|
||||
className={'p-2 cursor-grab'}
|
||||
draggable={!!props.onFilterMove}
|
||||
onDragStart={(e) =>
|
||||
handleDragStart(
|
||||
e,
|
||||
filterIndex,
|
||||
`${filter.key}-${filterIndex}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<GripHorizontal size={16}/>
|
||||
</div>
|
||||
) : null}
|
||||
<FilterItem
|
||||
filterIndex={rowIndex++}
|
||||
filter={filter}
|
||||
onUpdate={(filter) =>
|
||||
props.onUpdateFilter(filterIndex, filter)
|
||||
}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
saveRequestPayloads={saveRequestPayloads}
|
||||
disableDelete={cannotDeleteFilter}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
readonly={props.readonly}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-2"/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-2" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasFilters && (
|
||||
<>
|
||||
{hasEvents && <div className="border-t -mx-5 mb-4" />}
|
||||
<div className="mb-2 text-sm color-gray-medium mr-auto">FILTERS</div>
|
||||
{filters.map((filter: any, filterIndex: any) =>
|
||||
!filter.isEvent ? (
|
||||
<div className={'py-2 hover:bg-active-blue px-5'} style={{
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}>
|
||||
<FilterItem
|
||||
key={filterIndex}
|
||||
readonly={props.readonly}
|
||||
isFilter={true}
|
||||
filterIndex={filterIndex}
|
||||
filter={filter}
|
||||
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
{hasFilters && (
|
||||
<>
|
||||
{hasEvents && <div className="border-t -mx-5 mb-4"/>}
|
||||
<div className="mb-2 text-sm color-gray-medium mr-auto">FILTERS</div>
|
||||
{filters.map((filter: any, filterIndex: any) =>
|
||||
!filter.isEvent ? (
|
||||
<div className={'py-2 hover:bg-active-blue px-5'} style={{
|
||||
marginLeft: '-1.25rem',
|
||||
width: 'calc(100% + 2.5rem)',
|
||||
}}>
|
||||
<FilterItem
|
||||
key={filterIndex}
|
||||
readonly={props.readonly}
|
||||
isFilter={true}
|
||||
filterIndex={filterIndex}
|
||||
filter={filter}
|
||||
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
|
||||
onRemoveFilter={() => onRemoveFilter(filterIndex)}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
isConditional={isConditional}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(FilterList);
|
||||
|
||||
|
||||
function EventsOrder(props: {
|
||||
onChange: (e: any, v: any) => void,
|
||||
filter: any,
|
||||
eventsOrderSupport: any
|
||||
}) {
|
||||
const {filter, eventsOrderSupport, onChange} = props;
|
||||
const options = [
|
||||
{
|
||||
label: 'THEN',
|
||||
value: 'then',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('then'),
|
||||
},
|
||||
{
|
||||
label: 'AND',
|
||||
value: 'and',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('and'),
|
||||
},
|
||||
{
|
||||
label: 'OR',
|
||||
value: 'or',
|
||||
disabled: eventsOrderSupport && !eventsOrderSupport.includes('or'),
|
||||
},
|
||||
];
|
||||
|
||||
return <div className="flex items-center gap-2">
|
||||
<div
|
||||
className="color-gray-medium text-sm"
|
||||
style={{textDecoration: "underline dotted"}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Select the operator to be applied between events in your search.`}
|
||||
>
|
||||
<div>Events Order</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<Segmented
|
||||
size={"small"}
|
||||
// onChange={props.onChange}
|
||||
onChange={(v) => onChange(null, options.find((i) => i.value === v))}
|
||||
value={filter.eventsOrder}
|
||||
options={options}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue