change(ui) - clickmaps disable first event selection and delete btn, also restrict the filters

This commit is contained in:
Shekar Siri 2023-01-06 18:04:26 +01:00
parent a30da5ef77
commit 9e7486c6b7
7 changed files with 78 additions and 29 deletions

View file

@ -15,6 +15,7 @@ interface Props {
hideHeader?: boolean;
emptyMessage?: any;
observeChanges?: () => void;
excludeFilterKeys?: Array<string>
}
function FilterSeries(props: Props) {
@ -25,6 +26,7 @@ function FilterSeries(props: Props) {
hideHeader = false,
emptyMessage = 'Add user event or filter to define the series by clicking Add Step.',
supportsEmpty = true,
excludeFilterKeys = []
} = props;
const [expanded, setExpanded] = useState(true)
const { series, seriesIndex } = props;
@ -76,6 +78,7 @@ function FilterSeries(props: Props) {
onRemoveFilter={onRemoveFilter}
onChangeEventsOrder={onChangeEventsOrder}
supportsEmpty={supportsEmpty}
excludeFilterKeys={excludeFilterKeys}
/>
) : (
<div className="color-gray-medium">{emptyMessage}</div>
@ -86,6 +89,7 @@ function FilterSeries(props: Props) {
<FilterSelection
filter={undefined}
onFilterClick={onAddFilter}
excludeFilterKeys={excludeFilterKeys}
>
<Button variant="text-primary" icon="plus">ADD STEP</Button>
</FilterSelection>

View file

@ -19,9 +19,8 @@ import {
PERFORMANCE,
WEB_VITALS,
} from 'App/constants/card';
import { clickmapFilter } from 'App/types/filter/newFilter';
import { clickmapFilter, eventKeys } from 'App/types/filter/newFilter';
import { renderClickmapThumbnail } from './renderMap';
interface Props {
history: any;
match: any;
@ -51,6 +50,8 @@ function WidgetForm(props: Props) {
metric.metricType
);
const excludeFilterKeys = isClickmap ? eventKeys : []
const writeOption = ({ value, name }: { value: any; name: any }) => {
value = Array.isArray(value) ? value : value.value;
const obj: any = { [name]: value };
@ -202,6 +203,7 @@ function WidgetForm(props: Props) {
<div className="mb-2" key={series.name}>
<FilterSeries
supportsEmpty={!isClickmap}
excludeFilterKeys={excludeFilterKeys}
observeChanges={() => metric.updateKey('hasChanged', true)}
hideHeader={isTable || isClickmap}
seriesIndex={index}

View file

@ -14,10 +14,11 @@ interface Props {
onRemoveFilter: () => void;
isFilter?: boolean;
saveRequestPayloads?: boolean;
disableDelete?: boolean
disableDelete?: boolean;
excludeFilterKeys?: Array<string>;
}
function FilterItem(props: Props) {
const { isFilter = false, filterIndex, filter, saveRequestPayloads, disableDelete = false } = props;
const { isFilter = false, filterIndex, filter, saveRequestPayloads, disableDelete = false, excludeFilterKeys = [] } = props;
const canShowValues = !(filter.operator === 'isAny' || filter.operator === 'onAny' || filter.operator === 'isUndefined');
const isSubFilter = filter.type === FilterType.SUB_FILTERS;
@ -57,7 +58,7 @@ function FilterItem(props: Props) {
<span>{filterIndex + 1}</span>
</div>
)}
<FilterSelection filter={filter} onFilterClick={replaceFilter} />
<FilterSelection filter={filter} onFilterClick={replaceFilter} excludeFilterKeys={excludeFilterKeys} disabled={disableDelete} />
{/* Filter with Source */}
{filter.hasSource && (

View file

@ -13,14 +13,15 @@ interface Props {
observeChanges?: () => void;
saveRequestPayloads?: boolean;
supportsEmpty?: boolean
excludeFilterKeys?: Array<string>
}
function FilterList(props: Props) {
const { observeChanges = () => {}, filter, hideEventsOrder = false, saveRequestPayloads, supportsEmpty = true } = props;
const { observeChanges = () => {}, filter, hideEventsOrder = false, saveRequestPayloads, supportsEmpty = true, excludeFilterKeys = [] } = props;
const filters = List(filter.filters);
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 = filters.size === 1 && !supportsEmpty;
const cannotDeleteFilter = hasEvents && !supportsEmpty;
useEffect(observeChanges, [filters]);
@ -72,6 +73,7 @@ function FilterList(props: Props) {
onRemoveFilter={() => onRemoveFilter(filterIndex)}
saveRequestPayloads={saveRequestPayloads}
disableDelete={cannotDeleteFilter}
excludeFilterKeys={excludeFilterKeys}
/>
) : null
)}
@ -92,6 +94,7 @@ function FilterList(props: Props) {
filter={filter}
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
onRemoveFilter={() => onRemoveFilter(filterIndex)}
excludeFilterKeys={excludeFilterKeys}
/>
) : null
)}

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Icon, Loader } from 'UI';
import { connect } from 'react-redux';
import cn from 'classnames';
@ -6,10 +6,27 @@ import stl from './FilterModal.module.css';
import { filtersMap } from 'Types/filter/newFilter';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
function filterJson(
jsonObj: Record<string, any>,
excludeKeys: string[] = []
): Record<string, any> {
let filtered: Record<string, any> = {};
for (const key in jsonObj) {
const arr = jsonObj[key].filter((i: any) => !excludeKeys.includes(i.key));
if (arr.length) {
filtered[key] = arr;
}
}
return filtered;
}
export const getMatchingEntries = (searchQuery: string, filters: Record<string, any>) => {
const matchingCategories: string[] = [];
const matchingFilters: Record<string, any> = {};
const lowerCaseQuery = searchQuery.toLowerCase();
if (lowerCaseQuery.length === 0) return {
matchingCategories: Object.keys(filters),
matchingFilters: filters,
@ -33,12 +50,13 @@ export const getMatchingEntries = (searchQuery: string, filters: Record<string,
interface Props {
filters: any,
onFilterClick?: (filter) => void,
onFilterClick?: (filter: any) => void,
filterSearchList: any,
// metaOptions: any,
isMainSearch?: boolean,
fetchingFilterSearchList: boolean,
searchQuery?: string,
excludeFilterKeys?: Array<string>
}
function FilterModal(props: Props) {
const {
@ -48,6 +66,7 @@ function FilterModal(props: Props) {
isMainSearch = false,
fetchingFilterSearchList,
searchQuery = '',
excludeFilterKeys = []
} = props;
const showSearchList = isMainSearch && searchQuery.length > 0;
@ -57,7 +76,7 @@ function FilterModal(props: Props) {
onFilterClick(_filter);
}
const { matchingCategories, matchingFilters } = getMatchingEntries(searchQuery, filters);
const { matchingCategories, matchingFilters } = getMatchingEntries(searchQuery, filterJson(filters, excludeFilterKeys));
const isResultEmpty = (!filterSearchList || Object.keys(filterSearchList).length === 0)
&& matchingCategories.length === 0 && Object.keys(matchingFilters).length === 0

View file

@ -3,7 +3,8 @@ import FilterModal from '../FilterModal';
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
import { Icon } from 'UI';
import { connect } from 'react-redux';
import { assist as assistRoute, isRoute } from "App/routes";
import { assist as assistRoute, isRoute } from 'App/routes';
import cn from 'classnames';
const ASSIST_ROUTE = assistRoute();
@ -14,40 +15,54 @@ interface Props {
onFilterClick: (filter: any) => void;
children?: any;
isLive?: boolean;
excludeFilterKeys?: Array<string>
disabled?: boolean
}
function FilterSelection(props: Props) {
const { filter, onFilterClick, children } = props;
const { filter, onFilterClick, children, excludeFilterKeys = [], disabled = false } = props;
const [showModal, setShowModal] = useState(false);
return (
<div className="relative flex-shrink-0">
<OutsideClickDetectingDiv
className="relative"
onClickOutside={ () => setTimeout(function() {
setShowModal(false)
}, 200)}
onClickOutside={() =>
setTimeout(function () {
setShowModal(false);
}, 200)
}
>
{ children ? React.cloneElement(children, { onClick: (e) => {
e.stopPropagation();
e.preventDefault();
setShowModal(true);
}}) : (
{children ? (
React.cloneElement(children, {
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
setShowModal(true);
},
disabled: disabled
})
) : (
<div
className="rounded py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade"
className={cn("rounded py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade", { 'opacity-50 pointer-events-none' : disabled })}
style={{ width: '150px', height: '26px', border: 'solid thin #e9e9e9' }}
onClick={() => setShowModal(true)}
>
<div className="overflow-hidden whitespace-nowrap text-ellipsis mr-auto truncate" style={{ textOverflow: 'ellipsis'}}>{filter.label}</div>
<div
className="overflow-hidden whitespace-nowrap text-ellipsis mr-auto truncate"
style={{ textOverflow: 'ellipsis' }}
>
{filter.label}
</div>
<Icon name="chevron-down" size="14" />
</div>
) }
)}
</OutsideClickDetectingDiv>
{showModal && (
<div className="absolute left-0 border shadow rounded bg-white z-50">
<FilterModal
isLive={isRoute(ASSIST_ROUTE, window.location.pathname)}
onFilterClick={onFilterClick}
// filters={isRoute(ASSIST_ROUTE, window.location.pathname) ? props.filterListLive : props.filterList }
excludeFilterKeys={excludeFilterKeys}
/>
</div>
)}
@ -55,8 +70,11 @@ function FilterSelection(props: Props) {
);
}
export default connect((state: any) => ({
filterList: state.getIn([ 'search', 'filterList' ]),
filterListLive: state.getIn([ 'search', 'filterListLive' ]),
isLive: state.getIn([ 'sessions', 'activeTab' ]).type === 'live',
}), { })(FilterSelection);
export default connect(
(state: any) => ({
filterList: state.getIn(['search', 'filterList']),
filterListLive: state.getIn(['search', 'filterListLive']),
isLive: state.getIn(['sessions', 'activeTab']).type === 'live',
}),
{}
)(FilterSelection);

View file

@ -53,6 +53,8 @@ export const filters = [
{ key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', placeholder: 'Select an issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions },
];
export const eventKeys = filters.filter((i) => i.isEvent).map(i => i.key);
export const clickmapFilter = {
key: FilterKey.LOCATION,
type: FilterType.MULTIPLE,