change(ui) - clickmaps disable first event selection and delete btn, also restrict the filters
This commit is contained in:
parent
a30da5ef77
commit
9e7486c6b7
7 changed files with 78 additions and 29 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 && (
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue