feat(ui) - url search - read query params

This commit is contained in:
Shekar Siri 2022-08-17 18:08:24 +02:00
parent f0d5238b65
commit a8760279b9
5 changed files with 252 additions and 93 deletions

View file

@ -1,68 +1,23 @@
import React, { useEffect } from 'react';
import React from 'react';
import FilterList from 'Shared/Filters/FilterList';
import FilterSelection from 'Shared/Filters/FilterSelection';
import SaveFilterButton from 'Shared/SaveFilterButton';
import { connect } from 'react-redux';
import { Button } from 'UI';
import { edit, addFilter } from 'Duck/search';
import { withRouter } from 'react-router';
import { clearSearch, fetchSessions, addFilterByKeyAndValue } from 'Duck/search';
import { FilterKey, getFilterKeyTypeByKey } from 'Types/filter/filterType';
const allowedQueryKeys = [
'userOs',
'userId',
'userBrowser',
'userDevice',
'userCountry',
'startDate',
'endDate',
'minDuration',
'maxDuration',
'referrer',
'sort',
'order',
];
import SessionSearchQueryParamHandler from 'Shared/SessionSearchQueryParamHandler';
interface Props {
appliedFilter: any;
edit: typeof edit;
addFilter: typeof addFilter;
saveRequestPayloads: boolean;
location: any;
addFilterByKeyAndValue: typeof addFilterByKeyAndValue;
}
function SessionSearch(props: Props) {
const {
appliedFilter,
saveRequestPayloads = false,
location: { search },
} = props;
const { appliedFilter, saveRequestPayloads = false } = props;
const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).size > 0;
const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).size > 0;
useEffect(() => {
const queryParams = Object.fromEntries(
Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) =>
allowedQueryKeys.includes(key)
)
);
const entires = Object.entries(queryParams);
if (entires.length > 0) {
entires.forEach(([key, value]) => {
if (value !== '') {
const filterKey = getFilterKeyTypeByKey(key);
const valueArr = value.split('|');
const operator = valueArr.shift();
console.log('operator', operator, valueArr);
if (filterKey) {
props.addFilterByKeyAndValue(filterKey, valueArr, operator);
}
}
});
}
}, []);
const onAddFilter = (filter: any) => {
props.addFilter(filter);
};
@ -98,48 +53,51 @@ function SessionSearch(props: Props) {
});
};
return hasEvents || hasFilters ? (
<div className="border bg-white rounded mt-4">
<div className="p-5">
<FilterList
filter={appliedFilter}
onUpdateFilter={onUpdateFilter}
onRemoveFilter={onRemoveFilter}
onChangeEventsOrder={onChangeEventsOrder}
saveRequestPayloads={saveRequestPayloads}
/>
</div>
return (
<>
<SessionSearchQueryParamHandler />
{hasEvents || hasFilters ? (
<div className="border bg-white rounded mt-4">
<div className="p-5">
<FilterList
filter={appliedFilter}
onUpdateFilter={onUpdateFilter}
onRemoveFilter={onRemoveFilter}
onChangeEventsOrder={onChangeEventsOrder}
saveRequestPayloads={saveRequestPayloads}
/>
</div>
<div className="border-t px-5 py-1 flex items-center -mx-2">
<div>
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
{/* <IconButton primaryText label="ADD STEP" icon="plus" /> */}
<Button
variant="text-primary"
className="mr-2"
// onClick={() => setshowModal(true)}
icon="plus"
>
ADD STEP
</Button>
</FilterSelection>
<div className="border-t px-5 py-1 flex items-center -mx-2">
<div>
<FilterSelection filter={undefined} onFilterClick={onAddFilter}>
{/* <IconButton primaryText label="ADD STEP" icon="plus" /> */}
<Button
variant="text-primary"
className="mr-2"
// onClick={() => setshowModal(true)}
icon="plus"
>
ADD STEP
</Button>
</FilterSelection>
</div>
<div className="ml-auto flex items-center">
<SaveFilterButton />
</div>
</div>
</div>
<div className="ml-auto flex items-center">
<SaveFilterButton />
</div>
</div>
</div>
) : (
<></>
) : (
<></>
)}
</>
);
}
export default withRouter(
connect(
(state: any) => ({
saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']),
appliedFilter: state.getIn(['search', 'instance']),
}),
{ edit, addFilter, addFilterByKeyAndValue }
)(SessionSearch)
);
export default connect(
(state: any) => ({
saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']),
appliedFilter: state.getIn(['search', 'instance']),
}),
{ edit, addFilter }
)(SessionSearch);

View file

@ -0,0 +1,111 @@
import React, { useEffect } from 'react';
import { useHistory } from 'react-router';
import { connect } from 'react-redux';
import { addFilterByKeyAndValue } from 'Duck/search';
import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType';
const allowedQueryKeys = [
'userId',
'userid',
'uid',
'usera',
'clk',
'inp',
'loc',
'os',
'browser',
'device',
'platform',
'revid',
'country',
// 'startDate',
// 'endDate',
// 'minDuration',
// 'maxDuration',
'ref',
'sort',
'order',
'ce',
'sa',
'err',
'iss',
// PERFORMANCE
'domc',
'lcp',
'ttfb',
'acpu',
'amem',
'ff',
];
interface Props {
appliedFilter: any;
addFilterByKeyAndValue: typeof addFilterByKeyAndValue;
}
function SessionSearchQueryParamHandler(props: Props) {
const { appliedFilter } = props;
const history = useHistory();
const createUrlQuery = (filters: any) => {
const query: any = {};
filters.forEach((filter: any) => {
if (filter.value.length > 0) {
const _key = setQueryParamKeyFromFilterkey(filter.key);
query[_key] = `${filter.operator}|${filter.value.join('|')}`;
}
});
return query;
};
const addFilter = ([key, value]: [string, string]): void => {
if (value !== '') {
const filterKey = getFilterKeyTypeByKey(key);
const valueArr = value.split('|');
const operator = valueArr.shift();
// TODO validate operator
if (filterKey) {
props.addFilterByKeyAndValue(filterKey, valueArr, operator);
}
}
};
const applyFilterFromQuery = () => {
const entires = getQueryObject(history.location.search);
console.log('entires', entires);
if (entires.length > 0) {
entires.forEach(addFilter);
}
};
useEffect(() => {
console.log('rerender');
applyFilterFromQuery();
}, []);
useEffect(() => {
const query: any = createUrlQuery(appliedFilter.filters);
history.replace({ search: new URLSearchParams(query).toString() });
}, [appliedFilter]);
return <></>;
}
export default connect(
(state: any) => ({
appliedFilter: state.getIn(['search', 'instance']),
}),
{ addFilterByKeyAndValue }
)(SessionSearchQueryParamHandler);
function getQueryObject(search: any) {
const queryParams = Object.fromEntries(
Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) =>
allowedQueryKeys.includes(key)
)
);
return Object.entries(queryParams);
}

View file

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

View file

@ -8,18 +8,107 @@ export enum FilterCategory {
PERFORMANCE = 'Performance',
}
export const setQueryParamKeyFromFilterkey = (filterKey: string) => {
switch (filterKey) {
case FilterKey.USERID:
return 'uid';
case FilterKey.USERANONYMOUSID:
return 'usera';
case FilterKey.CLICK:
return 'clk';
case FilterKey.INPUT:
return 'inp';
case FilterKey.LOCATION:
return 'loc';
case FilterKey.USER_OS:
return 'os';
case FilterKey.USER_BROWSER:
return 'browser';
case FilterKey.USER_DEVICE:
return 'device';
case FilterKey.PLATFORM:
return 'platform';
case FilterKey.REVID:
return 'revid';
case FilterKey.USER_COUNTRY:
return 'country';
case FilterKey.REFERRER:
return 'ref';
case FilterKey.CUSTOM:
return 'ce';
case FilterKey.STATEACTION:
return 'sa';
case FilterKey.ERROR:
return 'err';
case FilterKey.ISSUE:
return 'iss';
// PERFORMANCE
case FilterKey.DOM_COMPLETE:
return 'domc';
case FilterKey.LARGEST_CONTENTFUL_PAINT_TIME:
return 'lcp';
case FilterKey.TTFB:
return 'ttfb';
case FilterKey.AVG_CPU_LOAD:
return 'acpu';
case FilterKey.AVG_MEMORY_USAGE:
return 'amem';
case FilterKey.FETCH_FAILED:
return 'ff';
}
};
export const getFilterKeyTypeByKey = (key: string) => {
switch (key) {
case 'userId':
case 'uid':
case 'userid':
return FilterKey.USERID;
case 'userOs':
case 'usera':
return FilterKey.USERANONYMOUSID;
case 'clk':
return FilterKey.CLICK;
case 'inp':
return FilterKey.INPUT;
case 'loc':
return FilterKey.LOCATION;
case 'os':
return FilterKey.USER_OS;
case 'userBrowser':
case 'browser':
return FilterKey.USER_BROWSER;
case 'userDevice':
case 'device':
return FilterKey.USER_DEVICE;
case 'userCountry':
case 'platform':
return FilterKey.PLATFORM;
case 'revid':
return FilterKey.REVID;
case 'country':
return FilterKey.USER_COUNTRY;
case 'ref':
return FilterKey.REFERRER;
case 'ce':
return FilterKey.CUSTOM;
case 'sa':
return FilterKey.STATEACTION;
case 'err':
return FilterKey.ERROR;
case 'iss':
return FilterKey.ISSUE;
// PERFORMANCE
case 'domc':
return FilterKey.DOM_COMPLETE;
case 'lcp':
return FilterKey.LARGEST_CONTENTFUL_PAINT_TIME;
case 'ttfb':
return FilterKey.TTFB;
case 'acpu':
return FilterKey.AVG_CPU_LOAD;
case 'amem':
return FilterKey.AVG_MEMORY_USAGE;
case 'ff':
return FilterKey.FETCH_FAILED;
}
};

View file

@ -11,7 +11,7 @@ export const filters = [
{ key: FilterKey.INPUT, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Input', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/input', isEvent: true },
{ key: FilterKey.LOCATION, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Path', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/location', isEvent: true },
{ key: FilterKey.CUSTOM, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Custom Events', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/custom', isEvent: true },
{ key: FilterKey.REQUEST, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Fetch', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', isEvent: true },
// { key: FilterKey.REQUEST, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Fetch', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', isEvent: true },
{ key: FilterKey.FETCH, type: FilterType.SUB_FILTERS, category: FilterCategory.JAVASCRIPT, operator: 'is', label: 'Network Request', filters: [
{ 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_CODE, type: FilterType.NUMBER_MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with status code', operator: '=', operatorOptions: filterOptions.customOperators, icon: 'filters/fetch' },