From a8760279b949d7dd762d7941b313043a76901b84 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:08:24 +0200 Subject: [PATCH] feat(ui) - url search - read query params --- .../shared/SessionSearch/SessionSearch.tsx | 134 ++++++------------ .../SessionSearchQueryParamHandler.tsx | 111 +++++++++++++++ .../SessionSearchQueryParamHandler/index.ts | 1 + frontend/app/types/filter/filterType.ts | 97 ++++++++++++- frontend/app/types/filter/newFilter.js | 2 +- 5 files changed, 252 insertions(+), 93 deletions(-) create mode 100644 frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx create mode 100644 frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index 156c42312..8fc9f16c5 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -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 ? ( -
-
- -
+ return ( + <> + + {hasEvents || hasFilters ? ( +
+
+ +
-
-
- - {/* */} - - +
+
+ + {/* */} + + +
+
+ +
+
-
- -
-
-
- ) : ( - <> + ) : ( + <> + )} + ); } -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); diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx new file mode 100644 index 000000000..bc75e0195 --- /dev/null +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -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); +} diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts b/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts new file mode 100644 index 000000000..c13bb493d --- /dev/null +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts @@ -0,0 +1 @@ +export { default } from './SessionSearchQueryParamHandler'; \ No newline at end of file diff --git a/frontend/app/types/filter/filterType.ts b/frontend/app/types/filter/filterType.ts index 17c84ece3..9915a3aa5 100644 --- a/frontend/app/types/filter/filterType.ts +++ b/frontend/app/types/filter/filterType.ts @@ -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; } }; diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 9a87da2c6..452c4f1c9 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -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' },