ui: further changes for search/cards
This commit is contained in:
parent
908b0ac88b
commit
5fcc974382
15 changed files with 251 additions and 125 deletions
|
|
@ -60,6 +60,13 @@ function AlertForm(props) {
|
|||
|
||||
const triggerOptions = metricStore.instance.series.length > 0 ? allTriggerSeries.filter(s => {
|
||||
return metricStore.instance.series.findIndex(ms => ms.seriesId === s.value) !== -1
|
||||
}).map(v => {
|
||||
const labelArr = v.label.split('.')
|
||||
labelArr.shift()
|
||||
return {
|
||||
...v,
|
||||
label: labelArr.join('.')
|
||||
}
|
||||
}) : allTriggerSeries
|
||||
const instance = alertsStore.instance
|
||||
const deleting = loading
|
||||
|
|
|
|||
|
|
@ -18,19 +18,21 @@ function BigNumChart(props: Props) {
|
|||
onSeriesFocus,
|
||||
} = props;
|
||||
return (
|
||||
<div className={'flex flex-row flex-wrap gap-2'} style={{ height: 240 }}>
|
||||
{values.map((val, i) => (
|
||||
<BigNum
|
||||
key={i}
|
||||
color={colors[i]}
|
||||
series={val.series}
|
||||
value={val.value}
|
||||
label={label}
|
||||
compData={val.compData}
|
||||
valueLabel={val.valueLabel}
|
||||
onSeriesFocus={onSeriesFocus}
|
||||
/>
|
||||
))}
|
||||
<div className={'pb-3'}>
|
||||
<div className={'flex flex-row flex-wrap gap-2'} style={{ height: 240 }}>
|
||||
{values.map((val, i) => (
|
||||
<BigNum
|
||||
key={i}
|
||||
color={colors[i]}
|
||||
series={val.series}
|
||||
value={val.value}
|
||||
label={label}
|
||||
compData={val.compData}
|
||||
valueLabel={val.valueLabel}
|
||||
onSeriesFocus={onSeriesFocus}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,24 +9,13 @@ import {
|
|||
HEATMAP,
|
||||
ERRORS,
|
||||
FUNNEL,
|
||||
INSIGHTS,
|
||||
TABLE,
|
||||
TIMESERIES,
|
||||
USER_PATH,
|
||||
PERFORMANCE, RETENTION
|
||||
} from "App/constants/card";
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
import { BarChart, TrendingUp, SearchSlash } from 'lucide-react';
|
||||
import ByIssues from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByIssues';
|
||||
import InsightsExample from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/InsightsExample';
|
||||
import ByUser from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByUser';
|
||||
import BarChartCard from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/BarChart';
|
||||
import SpeedIndexByLocationExample
|
||||
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SpeedIndexByLocationExample';
|
||||
import CallsWithErrorsExample
|
||||
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/CallsWithErrorsExample';
|
||||
import SlowestDomains
|
||||
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/SlowestDomains';
|
||||
import HeatmapsExample from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/HeatmapsExample';
|
||||
import ByReferrer from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByRferrer';
|
||||
import ByFetch from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/ByFecth';
|
||||
|
|
@ -60,7 +49,7 @@ export interface CardType {
|
|||
|
||||
export const CARD_LIST: CardType[] = [
|
||||
{
|
||||
title: 'Funnel',
|
||||
title: 'Untitled Funnel',
|
||||
key: FUNNEL,
|
||||
cardType: FUNNEL,
|
||||
category: CARD_CATEGORIES[0].key,
|
||||
|
|
@ -92,7 +81,7 @@ export const CARD_LIST: CardType[] = [
|
|||
}
|
||||
},
|
||||
{
|
||||
title: 'Heatmaps',
|
||||
title: 'Untitled Heatmaps',
|
||||
key: HEATMAP,
|
||||
cardType: HEATMAP,
|
||||
metricOf: 'heatMapUrl',
|
||||
|
|
@ -100,14 +89,14 @@ export const CARD_LIST: CardType[] = [
|
|||
example: HeatmapsExample
|
||||
},
|
||||
{
|
||||
title: 'Journey',
|
||||
title: 'Untitled Journey',
|
||||
key: USER_PATH,
|
||||
cardType: USER_PATH,
|
||||
category: CARD_CATEGORIES[0].key,
|
||||
example: ExamplePath
|
||||
},
|
||||
{
|
||||
title: 'Trend',
|
||||
title: 'Untitled Trend',
|
||||
key: TIMESERIES,
|
||||
cardType: TIMESERIES,
|
||||
metricOf: 'sessionCount',
|
||||
|
|
@ -122,7 +111,7 @@ export const CARD_LIST: CardType[] = [
|
|||
example: ExampleTrend
|
||||
},
|
||||
{
|
||||
title: 'Users Trend',
|
||||
title: 'Untitled Users Trend',
|
||||
key: TIMESERIES + '_userCount',
|
||||
cardType: TIMESERIES,
|
||||
metricOf: 'userCount',
|
||||
|
|
@ -140,7 +129,7 @@ export const CARD_LIST: CardType[] = [
|
|||
|
||||
// Web analytics
|
||||
{
|
||||
title: 'Top Users',
|
||||
title: 'Untitled Top Users',
|
||||
key: FilterKey.USERID,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.USERID,
|
||||
|
|
@ -149,7 +138,7 @@ export const CARD_LIST: CardType[] = [
|
|||
},
|
||||
|
||||
{
|
||||
title: 'Top Browsers',
|
||||
title: 'Untitled Top Browsers',
|
||||
key: FilterKey.USER_BROWSER,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.USER_BROWSER,
|
||||
|
|
@ -165,7 +154,7 @@ export const CARD_LIST: CardType[] = [
|
|||
// example: BySystem,
|
||||
// },
|
||||
{
|
||||
title: 'Top Countries',
|
||||
title: 'Untitled Top Countries',
|
||||
key: FilterKey.USER_COUNTRY,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.USER_COUNTRY,
|
||||
|
|
@ -174,7 +163,7 @@ export const CARD_LIST: CardType[] = [
|
|||
},
|
||||
|
||||
{
|
||||
title: 'Top Devices',
|
||||
title: 'Untitled Top Devices',
|
||||
key: FilterKey.USER_DEVICE,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.USER_DEVICE,
|
||||
|
|
@ -182,7 +171,7 @@ export const CARD_LIST: CardType[] = [
|
|||
example: BySystem
|
||||
},
|
||||
{
|
||||
title: 'Top Pages',
|
||||
title: 'Untitled Top Pages',
|
||||
key: FilterKey.LOCATION,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.LOCATION,
|
||||
|
|
@ -191,7 +180,7 @@ export const CARD_LIST: CardType[] = [
|
|||
},
|
||||
|
||||
{
|
||||
title: 'Top Referrer',
|
||||
title: 'Untitled Top Referrer',
|
||||
key: FilterKey.REFERRER,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.REFERRER,
|
||||
|
|
@ -201,7 +190,7 @@ export const CARD_LIST: CardType[] = [
|
|||
|
||||
// Monitors
|
||||
{
|
||||
title: 'Table of Errors',
|
||||
title: 'Untitled Table of Errors',
|
||||
key: FilterKey.ERRORS,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.ERRORS,
|
||||
|
|
@ -216,7 +205,7 @@ export const CARD_LIST: CardType[] = [
|
|||
example: TableOfErrors
|
||||
},
|
||||
{
|
||||
title: 'Top Network Requests',
|
||||
title: 'Untitled Top Network Requests',
|
||||
key: FilterKey.FETCH,
|
||||
cardType: TABLE,
|
||||
metricOf: FilterKey.FETCH,
|
||||
|
|
@ -224,7 +213,7 @@ export const CARD_LIST: CardType[] = [
|
|||
example: ByFetch
|
||||
},
|
||||
{
|
||||
title: 'Sessions with 4xx/5xx Requests',
|
||||
title: 'Untitled Sessions with 4xx/5xx Requests',
|
||||
key: TIMESERIES + '_4xx_requests',
|
||||
cardType: TIMESERIES,
|
||||
metricOf: 'sessionCount',
|
||||
|
|
@ -258,7 +247,7 @@ export const CARD_LIST: CardType[] = [
|
|||
example: ExampleTrend
|
||||
},
|
||||
{
|
||||
title: 'Sessions with Slow Network Requests',
|
||||
title: 'Untitled Sessions with Slow Network Requests',
|
||||
key: TIMESERIES + '_slow_network_requests',
|
||||
cardType: TIMESERIES,
|
||||
metricOf: 'sessionCount',
|
||||
|
|
|
|||
|
|
@ -118,11 +118,13 @@ interface Props {
|
|||
emptyMessage?: any;
|
||||
observeChanges?: () => void;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
excludeCategory?: string[]
|
||||
canExclude?: boolean;
|
||||
expandable?: boolean;
|
||||
isHeatmap?: boolean;
|
||||
removeEvents?: boolean;
|
||||
defaultClosed?: boolean;
|
||||
collapseState: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
}
|
||||
|
||||
function FilterSeries(props: Props) {
|
||||
|
|
@ -137,23 +139,13 @@ function FilterSeries(props: Props) {
|
|||
expandable = false,
|
||||
isHeatmap,
|
||||
removeEvents,
|
||||
defaultClosed,
|
||||
collapseState,
|
||||
onToggleCollapse,
|
||||
excludeCategory
|
||||
} = props;
|
||||
const [expanded, setExpanded] = useState(!defaultClosed || hideHeader);
|
||||
const expanded = !collapseState
|
||||
const setExpanded = onToggleCollapse
|
||||
const { series, seriesIndex } = props;
|
||||
const [prevLength, setPrevLength] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
series.filter.filters.length === 1 &&
|
||||
prevLength === 0 &&
|
||||
seriesIndex === 0 &&
|
||||
!defaultClosed
|
||||
) {
|
||||
setExpanded(true);
|
||||
}
|
||||
setPrevLength(series.filter.filters.length);
|
||||
}, [series.filter.filters.length, defaultClosed]);
|
||||
|
||||
const onUpdateFilter = (filterIndex: any, filter: any) => {
|
||||
series.filter.updateFilter(filterIndex, filter);
|
||||
|
|
@ -234,6 +226,7 @@ function FilterSeries(props: Props) {
|
|||
mergeUp={!hideHeader}
|
||||
mergeDown
|
||||
cannotAdd={isHeatmap}
|
||||
excludeCategory={excludeCategory}
|
||||
/>
|
||||
}
|
||||
<FilterList
|
||||
|
|
@ -246,6 +239,7 @@ function FilterSeries(props: Props) {
|
|||
excludeFilterKeys={excludeFilterKeys}
|
||||
onAddFilter={onAddFilter}
|
||||
mergeUp={!removeEvents}
|
||||
excludeCategory={excludeCategory}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Card, Space, Button, Alert, Form, Select } from 'antd';
|
||||
import { Card, Space, Button, Alert, Form, Select, Tooltip } from 'antd';
|
||||
import { useStore } from 'App/mstore';
|
||||
import { eventKeys } from 'Types/filter/newFilter';
|
||||
import {
|
||||
|
|
@ -13,18 +13,37 @@ import {
|
|||
} from 'App/constants/card';
|
||||
import FilterSeries from 'Components/Dashboard/components/FilterSeries/FilterSeries';
|
||||
import { issueCategories } from 'App/constants/filterOptions';
|
||||
import { PlusIcon } from 'lucide-react';
|
||||
import { PlusIcon, ChevronUp } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import FilterItem from 'Shared/Filters/FilterItem';
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
import { FilterKey, FilterCategory } from 'Types/filter/filterType';
|
||||
|
||||
function WidgetFormNew() {
|
||||
const getExcludedKeys = (metricType: string) => {
|
||||
switch (metricType) {
|
||||
case USER_PATH:
|
||||
case HEATMAP:
|
||||
return eventKeys;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const getExcludedCategories = (metricType: string) => {
|
||||
switch (metricType) {
|
||||
case USER_PATH:
|
||||
case FUNNEL:
|
||||
return [FilterCategory.DEVTOOLS]
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function WidgetFormNew({ layout }: { layout: string }) {
|
||||
const { metricStore } = useStore();
|
||||
const metric: any = metricStore.instance;
|
||||
const excludeFilterKeys = getExcludedKeys(metric.metricType);
|
||||
const excludeCategory = getExcludedCategories(metric.metricType);
|
||||
|
||||
const isHeatMap = metric.metricType === HEATMAP;
|
||||
const isPathAnalysis = metric.metricType === USER_PATH;
|
||||
const excludeFilterKeys = isHeatMap || isPathAnalysis ? eventKeys : [];
|
||||
const isPredefined = metric.metricType === ERRORS;
|
||||
|
||||
return isPredefined ? (
|
||||
|
|
@ -32,16 +51,36 @@ function WidgetFormNew() {
|
|||
) : (
|
||||
<Space direction="vertical" className="w-full">
|
||||
<AdditionalFilters />
|
||||
<FilterSection metric={metric} excludeFilterKeys={excludeFilterKeys} />
|
||||
<FilterSection
|
||||
layout={layout}
|
||||
metric={metric}
|
||||
excludeCategory={excludeCategory}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(WidgetFormNew);
|
||||
|
||||
const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
||||
const defaultClosed = React.useRef(metric.exists());
|
||||
const defaultSeries = React.useRef(metric.series.map((s) => s.name));
|
||||
const FilterSection = observer(({ layout, metric, excludeFilterKeys, excludeCategory }: any) => {
|
||||
const allOpen = layout.startsWith('flex-row');
|
||||
const defaultClosed = React.useRef(!allOpen && metric.exists());
|
||||
const [seriesCollapseState, setSeriesCollapseState] = React.useState<Record<number, boolean>>({});
|
||||
|
||||
React.useEffect(() => {
|
||||
const defaultSeriesCollapseState: Record<number, boolean> = {};
|
||||
metric.series.forEach((s: any) => {
|
||||
defaultSeriesCollapseState[s.seriesId] = defaultSeriesCollapseState[
|
||||
s.seriesId
|
||||
]
|
||||
? defaultSeriesCollapseState[s.seriesId]
|
||||
: allOpen
|
||||
? false
|
||||
: defaultClosed.current;
|
||||
});
|
||||
setSeriesCollapseState(defaultSeriesCollapseState);
|
||||
}, [metric.series]);
|
||||
const isTable = metric.metricType === TABLE;
|
||||
const isHeatMap = metric.metricType === HEATMAP;
|
||||
const isFunnel = metric.metricType === FUNNEL;
|
||||
|
|
@ -57,6 +96,27 @@ const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
|||
isInsights ||
|
||||
isRetention ||
|
||||
isPathAnalysis;
|
||||
|
||||
const collapseAll = () => {
|
||||
setSeriesCollapseState((seriesCollapseState) => {
|
||||
const newState = { ...seriesCollapseState };
|
||||
Object.keys(newState).forEach((key) => {
|
||||
newState[key] = true;
|
||||
});
|
||||
return newState;
|
||||
});
|
||||
}
|
||||
const expandAll = () => {
|
||||
setSeriesCollapseState((seriesCollapseState) => {
|
||||
const newState = { ...seriesCollapseState };
|
||||
Object.keys(newState).forEach((key) => {
|
||||
newState[key] = false;
|
||||
});
|
||||
return newState;
|
||||
});
|
||||
}
|
||||
|
||||
const allCollapsed = Object.values(seriesCollapseState).every((v) => v);
|
||||
return (
|
||||
<>
|
||||
{metric.series.length > 0 &&
|
||||
|
|
@ -70,6 +130,7 @@ const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
|||
removeEvents={isPathAnalysis}
|
||||
supportsEmpty={!isHeatMap && !isPathAnalysis}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
excludeCategory={excludeCategory}
|
||||
observeChanges={() => metric.updateKey('hasChanged', true)}
|
||||
hideHeader={
|
||||
isTable ||
|
||||
|
|
@ -82,11 +143,13 @@ const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
|||
series={series}
|
||||
onRemoveSeries={() => metric.removeSeries(index)}
|
||||
canDelete={metric.series.length > 1}
|
||||
defaultClosed={
|
||||
metric.hasChanged
|
||||
? defaultSeries.current.includes(series.name)
|
||||
: defaultClosed.current
|
||||
}
|
||||
collapseState={seriesCollapseState[series.seriesId]}
|
||||
onToggleCollapse={() => {
|
||||
setSeriesCollapseState((seriesCollapseState) => ({
|
||||
...seriesCollapseState,
|
||||
[series.seriesId]: !seriesCollapseState[series.seriesId],
|
||||
}));
|
||||
}}
|
||||
emptyMessage={
|
||||
isTable
|
||||
? 'Filter data using any event or attribute. Use Add Step button below to do so.'
|
||||
|
|
@ -96,21 +159,30 @@ const FilterSection = observer(({ metric, excludeFilterKeys }: any) => {
|
|||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{!isSingleSeries && canAddSeries && (
|
||||
<div className={'mx-auto flex items-center gap-2 w-fit'}>
|
||||
<Tooltip title={canAddSeries ? '' : 'Maximum of 3 series reached.'}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!canAddSeries) return;
|
||||
metric.addSeries();
|
||||
}}
|
||||
disabled={!canAddSeries || isSingleSeries}
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<PlusIcon size={16} />}
|
||||
>
|
||||
Add Series
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!canAddSeries) return;
|
||||
metric.addSeries();
|
||||
}}
|
||||
size="small"
|
||||
type="text"
|
||||
className="w-full cursor-pointer flex items-center py-2 justify-center gap-2 font-medium hover:text-teal btn-add-series"
|
||||
size={'small'}
|
||||
type={'text'}
|
||||
icon={<ChevronUp size={16} className={allCollapsed ? 'rotate-180' : ''} />}
|
||||
onClick={allCollapsed ? expandAll : collapseAll}
|
||||
>
|
||||
<PlusIcon size={16} />
|
||||
Add Series
|
||||
{allCollapsed ? 'Expand' : 'Collapse'} All
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ function WidgetView(props: Props) {
|
|||
/>
|
||||
<div className={cn('flex gap-4', layout)}>
|
||||
<div className={layout.startsWith('flex-row') ? 'w-1/3 ' : 'w-full'}>
|
||||
<WidgetFormNew />
|
||||
<WidgetFormNew layout={layout} />
|
||||
</div>
|
||||
<div className={layout.startsWith('flex-row') ? 'w-2/3' : 'w-full'}>
|
||||
<WidgetPreview name={widget.name} isEditing={expanded} />
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ interface Props {
|
|||
saveRequestPayloads?: boolean;
|
||||
disableDelete?: boolean;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
excludeCategory?: Array<string>;
|
||||
allowedFilterKeys?: Array<string>;
|
||||
readonly?: boolean;
|
||||
hideIndex?: boolean;
|
||||
|
|
@ -35,6 +36,7 @@ function FilterItem(props: Props) {
|
|||
hideDelete = false,
|
||||
allowedFilterKeys = [],
|
||||
excludeFilterKeys = [],
|
||||
excludeCategory = [],
|
||||
isConditional,
|
||||
hideIndex = false,
|
||||
} = props;
|
||||
|
|
@ -84,6 +86,7 @@ function FilterItem(props: Props) {
|
|||
onFilterClick={replaceFilter}
|
||||
allowedFilterKeys={allowedFilterKeys}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
excludeCategory={excludeCategory}
|
||||
disabled={disableDelete || props.readonly}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import EventsOrder from 'Shared/Filters/FilterList/EventsOrder';
|
|||
import FilterSelection from '../FilterSelection/FilterSelection';
|
||||
|
||||
interface Props {
|
||||
filter?: any; // event/filter
|
||||
filter?: any;
|
||||
onUpdateFilter: (filterIndex: any, filter: any) => void;
|
||||
onFilterMove?: (filters: any) => void;
|
||||
onRemoveFilter: (filterIndex: any) => void;
|
||||
|
|
@ -19,6 +19,7 @@ interface Props {
|
|||
supportsEmpty?: boolean;
|
||||
readonly?: boolean;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
excludeCategory?: string[];
|
||||
isConditional?: boolean;
|
||||
actions?: React.ReactNode[];
|
||||
onAddFilter: (filter: any) => void;
|
||||
|
|
@ -37,6 +38,7 @@ export const FilterList = observer((props: Props) => {
|
|||
onAddFilter,
|
||||
readonly,
|
||||
borderless,
|
||||
excludeCategory,
|
||||
} = props;
|
||||
|
||||
const filters = filter.filters;
|
||||
|
|
@ -65,6 +67,8 @@ export const FilterList = observer((props: Props) => {
|
|||
filter={undefined}
|
||||
onFilterClick={onAddFilter}
|
||||
disabled={readonly}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
excludeCategory={excludeCategory}
|
||||
>
|
||||
<Button
|
||||
icon={<Filter size={16} strokeWidth={1} />}
|
||||
|
|
@ -116,6 +120,7 @@ export const EventsList = observer((props: Props) => {
|
|||
actions = [],
|
||||
onAddFilter,
|
||||
cannotAdd,
|
||||
excludeCategory,
|
||||
} = props;
|
||||
|
||||
const filters = filter.filters;
|
||||
|
|
@ -210,6 +215,7 @@ export const EventsList = observer((props: Props) => {
|
|||
mode={'events'}
|
||||
filter={undefined}
|
||||
onFilterClick={onAddFilter}
|
||||
excludeCategory={excludeCategory}
|
||||
>
|
||||
<Button
|
||||
icon={<Plus size={16} strokeWidth={1} />}
|
||||
|
|
@ -298,6 +304,7 @@ export const EventsList = observer((props: Props) => {
|
|||
excludeFilterKeys={excludeFilterKeys}
|
||||
readonly={props.readonly}
|
||||
isConditional={isConditional}
|
||||
excludeCategory={excludeCategory}
|
||||
/>
|
||||
</div>
|
||||
) : null
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import { Icon, Loader } from 'UI';
|
|||
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
|
||||
import { Input } from 'antd';
|
||||
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
import { FilterCategory, FilterKey } from "Types/filter/filterType";
|
||||
import stl from './FilterModal.module.css';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useStore } from 'App/mstore';
|
||||
|
|
@ -80,13 +80,15 @@ export const IconMap = {
|
|||
function filterJson(
|
||||
jsonObj: Record<string, any>,
|
||||
excludeKeys: string[] = [],
|
||||
excludeCategory: string[] = [],
|
||||
allowedFilterKeys: string[] = [],
|
||||
mode: 'filters' | 'events'
|
||||
): Record<string, any> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(jsonObj)
|
||||
.map(([key, value]) => {
|
||||
const arr = value.filter((i: { key: string, isEvent: boolean }) => {
|
||||
const arr = value.filter((i: { key: string, isEvent: boolean, category: string }) => {
|
||||
if (excludeCategory.includes(i.category)) return false;
|
||||
if (excludeKeys.includes(i.key)) return false;
|
||||
if (mode === 'events' && !i.isEvent) return false;
|
||||
if (mode === 'filters' && i.isEvent) return false;
|
||||
|
|
@ -139,6 +141,7 @@ interface Props {
|
|||
isMainSearch?: boolean;
|
||||
searchQuery?: string;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
excludeCategory?: Array<string>;
|
||||
allowedFilterKeys?: Array<string>;
|
||||
isConditional?: boolean;
|
||||
isMobile?: boolean;
|
||||
|
|
@ -162,6 +165,7 @@ function FilterModal(props: Props) {
|
|||
onFilterClick = () => null,
|
||||
isMainSearch = false,
|
||||
excludeFilterKeys = [],
|
||||
excludeCategory = [],
|
||||
allowedFilterKeys = [],
|
||||
isConditional,
|
||||
mode,
|
||||
|
|
@ -185,10 +189,18 @@ function FilterModal(props: Props) {
|
|||
? searchStoreLive.loadingFilterSearch
|
||||
: searchStore.loadingFilterSearch;
|
||||
|
||||
const parseAndAdd = (filter) => {
|
||||
if (filter.category === FilterCategory.EVENTS && filter.key.startsWith('_')) {
|
||||
filter.value = [filter.key.substring(1)];
|
||||
filter.key = FilterKey.CUSTOM;
|
||||
filter.label = 'Custom Events'
|
||||
}
|
||||
onFilterClick(filter)
|
||||
}
|
||||
const onFilterSearchClick = (filter: any) => {
|
||||
const _filter = { ...filtersMap[filter.type] };
|
||||
_filter.value = [filter.value];
|
||||
onFilterClick(_filter);
|
||||
parseAndAdd(_filter);
|
||||
};
|
||||
|
||||
const filterJsonObj = isConditional
|
||||
|
|
@ -198,7 +210,7 @@ function FilterModal(props: Props) {
|
|||
: filters;
|
||||
const { matchingCategories, matchingFilters } = getMatchingEntries(
|
||||
searchQuery,
|
||||
filterJson(filterJsonObj, excludeFilterKeys, allowedFilterKeys, mode)
|
||||
filterJson(filterJsonObj, excludeFilterKeys, excludeCategory, allowedFilterKeys, mode)
|
||||
);
|
||||
|
||||
const isResultEmpty =
|
||||
|
|
@ -247,7 +259,7 @@ function FilterModal(props: Props) {
|
|||
className={cn(
|
||||
'flex items-center p-2 cursor-pointer gap-1 rounded-lg hover:bg-active-blue'
|
||||
)}
|
||||
onClick={() => onFilterClick({ ...filter })}
|
||||
onClick={() => parseAndAdd({ ...filter })}
|
||||
>
|
||||
{filter.category ? <div style={{ width: 100 }} className={'text-neutral-500/90 w-full flex justify-between items-center'}>
|
||||
<span>{filter.category}</span>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ interface Props {
|
|||
onFilterClick: (filter: any) => void;
|
||||
children?: any;
|
||||
excludeFilterKeys?: Array<string>;
|
||||
excludeCategory?: Array<string>;
|
||||
allowedFilterKeys?: Array<string>;
|
||||
disabled?: boolean;
|
||||
isConditional?: boolean;
|
||||
|
|
@ -26,6 +27,7 @@ function FilterSelection(props: Props) {
|
|||
onFilterClick,
|
||||
children,
|
||||
excludeFilterKeys = [],
|
||||
excludeCategory = [],
|
||||
allowedFilterKeys = [],
|
||||
disabled = false,
|
||||
isConditional,
|
||||
|
|
@ -86,6 +88,7 @@ function FilterSelection(props: Props) {
|
|||
onFilterClick={onAddFilter}
|
||||
excludeFilterKeys={excludeFilterKeys}
|
||||
allowedFilterKeys={allowedFilterKeys}
|
||||
excludeCategory={excludeCategory}
|
||||
isConditional={isConditional}
|
||||
isMobile={isMobile}
|
||||
mode={mode}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ interface Props {
|
|||
}
|
||||
function FilterValue(props: Props) {
|
||||
const { filter } = props;
|
||||
const isEvent = filter.isEvent;
|
||||
const [durationValues, setDurationValues] = useState({
|
||||
minDuration: filter.value[0],
|
||||
minDuration: filter.value?.[0],
|
||||
maxDuration: filter.value.length > 1 ? filter.value[1] : filter.value[0],
|
||||
});
|
||||
const showCloseButton = filter.value.length > 1;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
import { customFieldService } from 'App/services';
|
||||
|
||||
import { customFieldService, filterService } from 'App/services';
|
||||
import {
|
||||
addElementToConditionalFiltersMap,
|
||||
addElementToMobileConditionalFiltersMap,
|
||||
addElementToFiltersMap,
|
||||
addElementToFlagConditionsMap,
|
||||
addElementToLiveFiltersMap,
|
||||
clearMetaFilters
|
||||
clearMetaFilters,
|
||||
} from 'Types/filter/newFilter';
|
||||
import { FilterCategory } from 'Types/filter/filterType';
|
||||
import { FilterCategory, FilterType } from "Types/filter/filterType";
|
||||
import CustomField from 'App/mstore/types/customField';
|
||||
import customFields from 'Components/Client/CustomFields';
|
||||
import filterOptions from 'App/constants';
|
||||
|
||||
class CustomFieldStore {
|
||||
isLoading: boolean = false;
|
||||
|
|
@ -48,14 +47,40 @@ class CustomFieldStore {
|
|||
const response = await customFieldService.fetchList(siteId);
|
||||
clearMetaFilters();
|
||||
response.forEach((item: any) => {
|
||||
addElementToFiltersMap(FilterCategory.METADATA, '_' + item.key);
|
||||
addElementToLiveFiltersMap(FilterCategory.METADATA, '_' + item.key);
|
||||
addElementToFlagConditionsMap(FilterCategory.METADATA, '_' + item.key);
|
||||
addElementToConditionalFiltersMap(FilterCategory.METADATA, '_' + item.key);
|
||||
addElementToMobileConditionalFiltersMap(FilterCategory.METADATA, '_' + item.key);
|
||||
const calls = [
|
||||
addElementToFiltersMap,
|
||||
addElementToLiveFiltersMap,
|
||||
addElementToFlagConditionsMap,
|
||||
addElementToConditionalFiltersMap,
|
||||
addElementToMobileConditionalFiltersMap,
|
||||
];
|
||||
calls.forEach((call) => {
|
||||
call(FilterCategory.METADATA, '_' + item.key);
|
||||
});
|
||||
});
|
||||
this.list = response.map((item_1: any) => new CustomField(item_1));
|
||||
this.fetchedMetadata = true;
|
||||
filterService.fetchTopValues('custom', undefined).then((response: []) => {
|
||||
response.forEach((item: any) => {
|
||||
const calls = [
|
||||
addElementToFiltersMap,
|
||||
addElementToFlagConditionsMap,
|
||||
addElementToConditionalFiltersMap,
|
||||
addElementToMobileConditionalFiltersMap,
|
||||
];
|
||||
calls.forEach((call) => {
|
||||
call(
|
||||
FilterCategory.EVENTS,
|
||||
'_' + item.value,
|
||||
FilterType.MULTIPLE,
|
||||
'is',
|
||||
filterOptions.stringOperators,
|
||||
'filters/custom',
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
|
@ -65,11 +90,14 @@ class CustomFieldStore {
|
|||
this.isLoading = true;
|
||||
try {
|
||||
const response = await customFieldService.get('/integration/sources');
|
||||
this.sources = response.map(({ value, ...item }: any) => new CustomField({
|
||||
label: value,
|
||||
key: value,
|
||||
...item
|
||||
}));
|
||||
this.sources = response.map(
|
||||
({ value, ...item }: any) =>
|
||||
new CustomField({
|
||||
label: value,
|
||||
key: value,
|
||||
...item,
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
|
@ -79,16 +107,18 @@ class CustomFieldStore {
|
|||
this.isSaving = true;
|
||||
try {
|
||||
const wasCreating = !instance.exists();
|
||||
const response = wasCreating ? await customFieldService.create(siteId, instance.toData()) :
|
||||
await customFieldService.update(siteId, instance.toData());
|
||||
const response = wasCreating
|
||||
? await customFieldService.create(siteId, instance.toData())
|
||||
: await customFieldService.update(siteId, instance.toData());
|
||||
const updatedInstance = new CustomField(response);
|
||||
|
||||
if (wasCreating) {
|
||||
this.list.push(updatedInstance);
|
||||
} else {
|
||||
const index = this.list.findIndex(item => item.index === instance.index);
|
||||
if (index >= 0)
|
||||
this.list[index] = updatedInstance;
|
||||
const index = this.list.findIndex(
|
||||
(item) => item.index === instance.index
|
||||
);
|
||||
if (index >= 0) this.list[index] = updatedInstance;
|
||||
}
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
|
|
@ -99,7 +129,7 @@ class CustomFieldStore {
|
|||
this.isSaving = true;
|
||||
try {
|
||||
await customFieldService.delete(siteId, index);
|
||||
this.list = this.list.filter(item => item.index !== index);
|
||||
this.list = this.list.filter((item) => item.index !== index);
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
}
|
||||
|
|
@ -115,5 +145,4 @@ class CustomFieldStore {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export default CustomFieldStore;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { makeAutoObservable } from 'mobx';
|
||||
import { filters } from 'Types/filter/newFilter';
|
||||
import { filterService } from 'App/services';
|
||||
|
||||
interface TopValue {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
export enum FilterCategory {
|
||||
AUTOCAPTURE = 'Autocapture',
|
||||
DEVTOOLS = 'Devtools',
|
||||
DEVTOOLS = 'DevTools',
|
||||
USER = 'User',
|
||||
METADATA = 'Metadata',
|
||||
SESSION = 'Session',
|
||||
ISSUE = 'Issue',
|
||||
EVENTS = 'Events',
|
||||
}
|
||||
|
||||
export const setQueryParamKeyFromFilterkey = (filterKey: string) => {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export const filters = [
|
|||
{
|
||||
key: FilterKey.CUSTOM,
|
||||
type: FilterType.MULTIPLE,
|
||||
category: FilterCategory.DEVTOOLS,
|
||||
category: FilterCategory.EVENTS,
|
||||
label: 'Custom Events',
|
||||
placeholder: 'Enter event key',
|
||||
operator: 'is',
|
||||
|
|
@ -951,6 +951,7 @@ export const clearMetaFilters = () => {
|
|||
* @param {*} operator
|
||||
* @param {*} operatorOptions
|
||||
* @param {*} icon
|
||||
* @param {*} isEvent
|
||||
*/
|
||||
export const addElementToFiltersMap = (
|
||||
category = FilterCategory.METADATA,
|
||||
|
|
@ -958,7 +959,8 @@ export const addElementToFiltersMap = (
|
|||
type = FilterType.MULTIPLE,
|
||||
operator = 'is',
|
||||
operatorOptions = filterOptions.stringOperators,
|
||||
icon = 'filters/metadata'
|
||||
icon = 'filters/metadata',
|
||||
isEvent = false
|
||||
) => {
|
||||
filtersMap[key] = {
|
||||
key,
|
||||
|
|
@ -969,7 +971,8 @@ export const addElementToFiltersMap = (
|
|||
operator: operator,
|
||||
operatorOptions,
|
||||
icon,
|
||||
isLive: true
|
||||
isLive: true,
|
||||
isEvent,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -992,7 +995,8 @@ export const addElementToFlagConditionsMap = (
|
|||
type = FilterType.MULTIPLE,
|
||||
operator = 'is',
|
||||
operatorOptions = filterOptions.stringOperators,
|
||||
icon = 'filters/metadata'
|
||||
icon = 'filters/metadata',
|
||||
isEvent = false
|
||||
) => {
|
||||
fflagsConditionsMap[key] = {
|
||||
key,
|
||||
|
|
@ -1002,7 +1006,8 @@ export const addElementToFlagConditionsMap = (
|
|||
operator: operator,
|
||||
operatorOptions,
|
||||
icon,
|
||||
isLive: true
|
||||
isLive: true,
|
||||
isEvent,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -1012,7 +1017,8 @@ export const addElementToConditionalFiltersMap = (
|
|||
type = FilterType.MULTIPLE,
|
||||
operator = 'is',
|
||||
operatorOptions = filterOptions.stringOperators,
|
||||
icon = 'filters/metadata'
|
||||
icon = 'filters/metadata',
|
||||
isEvent = false
|
||||
) => {
|
||||
conditionalFiltersMap[key] = {
|
||||
key,
|
||||
|
|
@ -1022,7 +1028,8 @@ export const addElementToConditionalFiltersMap = (
|
|||
operator: operator,
|
||||
operatorOptions,
|
||||
icon,
|
||||
isLive: true
|
||||
isLive: true,
|
||||
isEvent,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -1032,7 +1039,8 @@ export const addElementToMobileConditionalFiltersMap = (
|
|||
type = FilterType.MULTIPLE,
|
||||
operator = 'is',
|
||||
operatorOptions = filterOptions.stringOperators,
|
||||
icon = 'filters/metadata'
|
||||
icon = 'filters/metadata',
|
||||
isEvent = false
|
||||
) => {
|
||||
mobileConditionalFiltersMap[key] = {
|
||||
key,
|
||||
|
|
@ -1042,7 +1050,8 @@ export const addElementToMobileConditionalFiltersMap = (
|
|||
operator: operator,
|
||||
operatorOptions,
|
||||
icon,
|
||||
isLive: true
|
||||
isLive: true,
|
||||
isEvent,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue