feat(ui) - subfilters - wip

This commit is contained in:
Shekar Siri 2022-02-25 15:19:24 +01:00
parent 2e33398efa
commit 1a55f9a897
11 changed files with 123 additions and 79 deletions

View file

@ -47,15 +47,17 @@ function FilterAutoComplete(props: Props) {
const requestValues = (q) => {
setLoading(true);
return new APIClient()[method?.toLowerCase()](endpoint, { ...params, q })
.then(response => response.json())
.then(({ errors, data }) => {
if (errors) {
// this.setError();
} else {
setOptions(data);
}
}).finally(() => setLoading(false));
return new APIClient()[method?.toLocaleLowerCase()](endpoint, { ...params, q })
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error(response.statusText);
})
.then(({ data }) => {
setOptions(data);
})
.finally(() => setLoading(false));
}
const debouncedRequestValues = React.useCallback(debounce(requestValues, 300), []);

View file

@ -4,6 +4,8 @@ import FilterSelection from '../FilterSelection';
import FilterValue from '../FilterValue';
import { Icon } from 'UI';
import FilterSource from '../FilterSource';
import { FilterType } from 'App/types/filter/filterType';
import SubFilterItem from '../SubFilterItem';
interface Props {
filterIndex: number;
@ -15,9 +17,14 @@ interface Props {
function FilterItem(props: Props) {
const { isFilter = false, filterIndex, filter } = props;
const canShowValues = !(filter.operator === "isAny" || filter.operator === "onAny" || filter.operator === "isUndefined");
const isSubFilter = filter.type === FilterType.SUB_FILTERS;
const replaceFilter = (filter) => {
props.onUpdate({ ...filter, value: [""]});
props.onUpdate({
...filter,
value: [""],
subFilters: filter.subFilters ? filter.subFilters.map(i => ({ ...i, value: [""] })) : []
});
};
const onOperatorChange = (e, { name, value }) => {
@ -28,6 +35,19 @@ function FilterItem(props: Props) {
props.onUpdate({ ...filter, sourceOperator: value })
}
const onUpdateSubFilter = (subFilter, subFilterIndex) => {
props.onUpdate({
...filter,
subFilters: filter.subFilters.map((i, index) => {
if (index === subFilterIndex) {
return subFilter;
}
return i;
})
});
};
return (
<div className="flex items-center hover:bg-active-blue -mx-5 px-5 py-2">
<div className="flex items-start w-full">
@ -48,14 +68,31 @@ function FilterItem(props: Props) {
)}
{/* Filter values */}
<FilterOperator
options={filter.operatorOptions}
onChange={onOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.operator}
/>
{ canShowValues && (<FilterValue filter={filter} onUpdate={props.onUpdate} />) }
{ !isSubFilter && (
<>
<FilterOperator
options={filter.operatorOptions}
onChange={onOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.operator}
/>
{ canShowValues && (<FilterValue filter={filter} onUpdate={props.onUpdate} />) }
</>
)}
{/* SubFilters */}
{isSubFilter && (
<div className="grid grid-col ml-3 w-full">
{filter.subFilters.map((subFilter, subFilterIndex) => (
<SubFilterItem
filterIndex={subFilterIndex}
filter={subFilter}
onUpdate={(f) => onUpdateSubFilter(f, subFilterIndex)}
onRemoveFilter={props.onRemoveFilter}
/>
))}
</div>
)}
</div>
<div className="flex flex-shrink-0 self-start mt-1 ml-auto px-2">
<div

View file

@ -0,0 +1,34 @@
import { filter } from 'App/components/BugFinder/ManageFilters/savedFilterList.css'
import React from 'react'
import FilterOperator from '../FilterOperator';
import FilterValue from '../FilterValue';
interface Props {
filterIndex: number;
filter: any; // event/filter
onUpdate: (filter) => void;
onRemoveFilter: () => void;
isFilter?: boolean;
}
export default function SubFilterItem(props: Props) {
const { isFilter = false, filterIndex, filter } = props;
const canShowValues = !(filter.operator === "isAny" || filter.operator === "onAny" || filter.operator === "isUndefined");
const onOperatorChange = (e, { name, value }) => {
props.onUpdate({ ...filter, operator: value })
}
return (
<div className="flex items-center hover:bg-active-blue pb-4">
<div className="flex-shrink-0 py-1">{filter.label}</div>
<FilterOperator
options={filter.operatorOptions}
onChange={onOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.operator}
/>
{ canShowValues && (<FilterValue filter={filter} onUpdate={props.onUpdate} />) }
</div>
)
}

View file

@ -0,0 +1 @@
export { default } from './SubFilterItem';

View file

@ -18,12 +18,6 @@ function FunnelSearch(props: Props) {
const onAddFilter = (filter) => {
props.addFilter(filter);
// filter.value = [""]
// const newFilters = appliedFilter.filters.concat(filter);
// props.edit({
// ...appliedFilter.filter,
// filters: newFilters,
// });
}
const onUpdateFilter = (filterIndex, filter) => {

View file

@ -19,12 +19,6 @@ function SessionSearch(props: Props) {
const onAddFilter = (filter) => {
props.addFilter(filter);
// filter.value = [""]
// const newFilters = appliedFilter.filters.concat(filter);
// props.edit({
// ...appliedFilter.filter,
// filters: newFilters,
// });
}
const onUpdateFilter = (filterIndex, filter) => {

View file

@ -1,4 +1,4 @@
import { fromJS, List, Map, Set } from 'immutable';
import { List, Map, Set } from 'immutable';
import { errors as errorsRoute, isRoute } from "App/routes";
import Filter from 'Types/filter';
import SavedFilter from 'Types/filter/savedFilter';
@ -8,15 +8,6 @@ import withRequestState, { RequestTypes } from './requestStateCreator';
import { fetchList as fetchSessionList } from './sessions';
import { fetchList as fetchErrorsList } from './errors';
import { fetchListType, fetchType, saveType, editType, initType, removeType } from './funcTools/crud/types';
import logger from 'App/logger';
import { newFiltersList } from 'Types/filter'
import NewFilter, { filtersMap } from 'Types/filter/newFilter';
// for (var i = 0; i < newFiltersList.length; i++) {
// filterOptions[newFiltersList[i].category] = newFiltersList.filter(filter => filter.category === newFiltersList[i].category)
// }
const ERRORS_ROUTE = errorsRoute();
@ -44,11 +35,8 @@ const ADD_ATTRIBUTE = 'filters/ADD_ATTRIBUTE';
const EDIT_ATTRIBUTE = 'filters/EDIT_ATTRIBUTE';
const REMOVE_ATTRIBUTE = 'filters/REMOVE_ATTRIBUTE';
const SET_ACTIVE_FLOW = 'filters/SET_ACTIVE_FLOW';
const UPDATE_VALUE = 'filters/UPDATE_VALUE';
const REFRESH_FILTER_OPTIONS = 'filters/REFRESH_FILTER_OPTIONS';
const initialState = Map({
instance: Filter(),
activeFilter: null,

View file

@ -161,7 +161,7 @@ export const applySavedSearch = (filter) => (dispatch, getState) => {
export const fetchSessions = (filter) => (dispatch, getState) => {
const _filter = filter ? filter : getState().getIn([ 'search', 'instance']);
return dispatch(applyFilter(_filter));
// return dispatch(applyFilter(_filter)); // TODO uncomment this line
};
export const updateSeries = (index, series) => ({
@ -233,6 +233,10 @@ export const hasFilterApplied = (filters, filter) => {
export const addFilter = (filter) => (dispatch, getState) => {
filter.value = checkFilterValue(filter.value);
filter.subFilters = filter.subFilters ? filter.subFilters.map(subFilter => ({
...subFilter,
value: checkFilterValue(subFilter.value),
})) : null;
const instance = getState().getIn([ 'search', 'instance']);
if (hasFilterApplied(instance.filters, filter)) {

View file

@ -15,6 +15,7 @@ export enum FilterType {
NUMBER = "NUMBER",
DURATION = "DURATION",
MULTIPLE = "MULTIPLE",
SUB_FILTERS = "SUB_FILTERS",
COUNTRY = "COUNTRY",
DROPDOWN = "DROPDOWN",
MULTIPLE_DROPDOWN = "MULTIPLE_DROPDOWN",
@ -61,4 +62,8 @@ export enum FilterKey {
AVG_CPU_LOAD = "AVG_CPU_LOAD",
AVG_MEMORY_USAGE = "AVG_MEMORY_USAGE",
FETCH_FAILED = "FETCH_FAILED",
FETCH = "FETCH",
FETCH_URL = "FETCH_URL",
FETCH_STATUS = "FETCH_STATUS",
FETCH_METHOD = "FETCH_METHOD",
}

View file

@ -236,22 +236,4 @@ export const operatorOptions = (filter) => {
case KEYS.CLICK_RAGE:
return [{ key: 'onAnything', text: 'on anything', value: 'true' }]
}
}
const NewFilterType = (key, category, label, icon, isEvent = false) => {
return {
key: key,
category: category,
label: label,
icon: icon,
isEvent: isEvent,
operators: operatorOptions({ key }),
value: [""]
}
}
export const newFiltersList = [
NewFilterType(TYPES.CLICK, 'Gear', 'Click', 'filters/click', true),
NewFilterType(TYPES.CLICK, 'Gear', 'Input', 'filters/click', true),
NewFilterType(TYPES.CONSOLE, 'Other', 'Console', 'filters/click', true),
];
}

View file

@ -48,6 +48,11 @@ export const filtersMap = {
[FilterKey.USERANONYMOUSID]: { key: FilterKey.USERANONYMOUSID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User AnonymousId', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/userid' },
// PERFORMANCE
[FilterKey.FETCH]: { key: FilterKey.FETCH, type: FilterType.SUB_FILTERS, category: FilterCategory.PERFORMANCE, label: 'Fetch Request', subFilters: [
{ key: FilterKey.FETCH_URL, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with URL', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' },
{ key: FilterKey.FETCH_STATUS, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with status code', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' },
{ key: FilterKey.FETCH_METHOD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' },
], icon: 'filters/fetch-failed', isEvent: true },
[FilterKey.DOM_COMPLETE]: { key: FilterKey.DOM_COMPLETE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'DOM Complete', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/dom-complete', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators },
[FilterKey.LARGEST_CONTENTFUL_PAINT_TIME]: { key: FilterKey.LARGEST_CONTENTFUL_PAINT_TIME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Largest Contentful Paint', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/lcpt', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators },
[FilterKey.TTFB]: { key: FilterKey.TTFB, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Time to First Byte', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/ttfb', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators },
@ -121,17 +126,19 @@ export default Record({
isEvent: false,
index: 0,
options: [],
subFilters: [],
}, {
keyKey: "_key",
fromJS: ({ value, key, type, ...filter }) => {
// const _filter = filtersMap[key] || filtersMap[type] || {};
const _filter = filtersMap[type];
return {
...filter,
..._filter,
key: _filter.key,
type: _filter.type, // camelCased(filter.type.toLowerCase()),
value: value.length === 0 ? [""] : value, // make sure there an empty value
value: value.length === 0 ? [""] : value,
// subFilters: filter.subFilters.map(this),
}
},
})
@ -142,33 +149,29 @@ export default Record({
* @returns
*/
export const generateFilterOptions = (map) => {
const _options = {};
const filterSection = {};
Object.keys(map).forEach(key => {
const filter = map[key];
if (_options.hasOwnProperty(filter.category)) {
_options[filter.category].push(filter);
if (filterSection.hasOwnProperty(filter.category)) {
filterSection[filter.category].push(filter);
} else {
_options[filter.category] = [filter];
filterSection[filter.category] = [filter];
}
});
return _options;
return filterSection;
}
export const generateLiveFilterOptions = (map) => {
const _options = {};
const filterSection = {};
Object.keys(map).filter(i => map[i].isLive).forEach(key => {
const filter = map[key];
filter.operator = 'contains';
// filter.type = FilterType.STRING;
// filter.type = FilterType.AUTOCOMPLETE_LOCAL;
// filter.options = countryOptions;
// filter.operatorOptions = [{ key: 'contains', text: 'contains', value: 'contains' }]
if (_options.hasOwnProperty(filter.category)) {
_options[filter.category].push(filter);
if (filterSection.hasOwnProperty(filter.category)) {
filterSection[filter.category].push(filter);
} else {
_options[filter.category] = [filter];
filterSection[filter.category] = [filter];
}
});
return _options;
return filterSection;
}