From e5963fbeef8410adccd202899b1f7cdcd18ef04d Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 15 Jun 2022 14:14:48 +0200 Subject: [PATCH 1/4] feat(ui) - assist filters wip --- frontend/app/components/Assist/Assist.tsx | 7 + .../AssistSearchField/AssistSearchField.tsx | 32 ++++ .../Assist/AssistSearchField/index.ts | 1 + .../BugFinder/Filters/SortDropdown.js | 2 +- .../BugFinder/SessionsMenu/SessionsMenu.js | 27 +-- .../Client/CustomFields/CustomFields.js | 2 +- frontend/app/components/Header/Header.js | 35 +--- .../FilterSelection/FilterSelection.tsx | 3 +- .../LiveSessionList/LiveSessionList.tsx | 4 +- .../LiveSessionReloadButton.tsx | 8 +- .../LiveSessionSearch/LiveSessionSearch.tsx | 58 ++++--- .../shared/MainSearchBar/MainSearchBar.tsx | 24 ++- .../shared/SessionItem/SessionItem.tsx | 6 +- .../SessionSearchField/SessionSearchField.tsx | 16 +- .../components/ui/CountryFlag/CountryFlag.js | 2 +- .../components/ui/SavedSearchList/ListItem.js | 20 --- .../ui/SavedSearchList/SavedSearchList.js | 157 ------------------ .../components/ui/SavedSearchList/index.js | 1 - .../ui/SavedSearchList/listItem.module.css | 24 --- .../savedSearchList.module.css | 20 --- frontend/app/components/ui/index.js | 1 - frontend/app/duck/liveSearch.js | 67 ++++++-- frontend/app/duck/search.js | 1 - frontend/app/duck/sessions.js | 50 +----- frontend/app/mstore/types/filter.ts | 2 +- .../MessageDistributor/MessageDistributor.ts | 2 +- 26 files changed, 190 insertions(+), 382 deletions(-) create mode 100644 frontend/app/components/Assist/AssistSearchField/AssistSearchField.tsx create mode 100644 frontend/app/components/Assist/AssistSearchField/index.ts delete mode 100644 frontend/app/components/ui/SavedSearchList/ListItem.js delete mode 100644 frontend/app/components/ui/SavedSearchList/SavedSearchList.js delete mode 100644 frontend/app/components/ui/SavedSearchList/index.js delete mode 100644 frontend/app/components/ui/SavedSearchList/listItem.module.css delete mode 100644 frontend/app/components/ui/SavedSearchList/savedSearchList.module.css diff --git a/frontend/app/components/Assist/Assist.tsx b/frontend/app/components/Assist/Assist.tsx index 0d901337b..f6bcb85c1 100644 --- a/frontend/app/components/Assist/Assist.tsx +++ b/frontend/app/components/Assist/Assist.tsx @@ -4,13 +4,20 @@ import LiveSessionSearch from 'Shared/LiveSessionSearch'; import cn from 'classnames' import withPageTitle from 'HOCs/withPageTitle'; import withPermissions from 'HOCs/withPermissions' +import SessionSearch from '../shared/SessionSearch'; +import MainSearchBar from '../shared/MainSearchBar'; +import AssistSearchField from './AssistSearchField'; function Assist() { return (
+ + {/* */} + + {/* */}
diff --git a/frontend/app/components/Assist/AssistSearchField/AssistSearchField.tsx b/frontend/app/components/Assist/AssistSearchField/AssistSearchField.tsx new file mode 100644 index 000000000..5df3a7d78 --- /dev/null +++ b/frontend/app/components/Assist/AssistSearchField/AssistSearchField.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button } from 'UI'; +import SessionSearchField from 'Shared/SessionSearchField'; +// import { fetchFilterSearch } from 'Duck/search'; +import { connect } from 'react-redux'; +import { edit as editFilter, addFilterByKeyAndValue, clearSearch, fetchFilterSearch } from 'Duck/liveSearch'; +// import { clearSearch } from 'Duck/search'; + +function AssistSearchField(props: any) { + return ( +
+
+ +
+ +
+ ); +} + +export default connect(null, { + fetchFilterSearch, editFilter, addFilterByKeyAndValue, clearSearch +})(AssistSearchField); \ No newline at end of file diff --git a/frontend/app/components/Assist/AssistSearchField/index.ts b/frontend/app/components/Assist/AssistSearchField/index.ts new file mode 100644 index 000000000..8ecf4e244 --- /dev/null +++ b/frontend/app/components/Assist/AssistSearchField/index.ts @@ -0,0 +1 @@ +export { default } from './AssistSearchField' \ No newline at end of file diff --git a/frontend/app/components/BugFinder/Filters/SortDropdown.js b/frontend/app/components/BugFinder/Filters/SortDropdown.js index d75b4222f..398902ec5 100644 --- a/frontend/app/components/BugFinder/Filters/SortDropdown.js +++ b/frontend/app/components/BugFinder/Filters/SortDropdown.js @@ -10,6 +10,7 @@ import stl from './sortDropdown.module.css'; export default class SortDropdown extends React.PureComponent { state = { value: null } sort = ({ value }) => { + value = value.value this.setState({ value: value }) const [ sort, order ] = value.split('-'); const sign = order === 'desc' ? -1 : 1; @@ -25,7 +26,6 @@ export default class SortDropdown extends React.PureComponent { setShowModal(true) } onBlur={ () => setTimeout(setShowModal, 200, false) } onChange={ onSearchChange } - // icon="search" - // iconPosition="left" placeholder={ 'Search sessions using any captured event (click, input, page, error...)'} - // fluid id="search" type="search" autoComplete="off" @@ -57,4 +51,4 @@ function SessionSearchField(props: Props) { ); } -export default connect(null, { fetchFilterSearch, editFilter, addFilterByKeyAndValue })(SessionSearchField); \ No newline at end of file +export default connect(null, { })(SessionSearchField); \ No newline at end of file diff --git a/frontend/app/components/ui/CountryFlag/CountryFlag.js b/frontend/app/components/ui/CountryFlag/CountryFlag.js index 700304dde..5edf8b275 100644 --- a/frontend/app/components/ui/CountryFlag/CountryFlag.js +++ b/frontend/app/components/ui/CountryFlag/CountryFlag.js @@ -12,7 +12,7 @@ const CountryFlag = React.memo(({ country, className, style = {}, label = false return (
{knownCountry - ?
+ ?
: (
diff --git a/frontend/app/components/ui/SavedSearchList/ListItem.js b/frontend/app/components/ui/SavedSearchList/ListItem.js deleted file mode 100644 index d38762c63..000000000 --- a/frontend/app/components/ui/SavedSearchList/ListItem.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { Icon } from 'UI'; -import cn from "classnames"; -import stl from './listItem.module.css'; - -const ListItem = ({icon, label, onClick, onRemove }) => { - return ( -
-
- - { label } -
-
- -
-
- ); -}; - -export default ListItem; diff --git a/frontend/app/components/ui/SavedSearchList/SavedSearchList.js b/frontend/app/components/ui/SavedSearchList/SavedSearchList.js deleted file mode 100644 index 98c4d5208..000000000 --- a/frontend/app/components/ui/SavedSearchList/SavedSearchList.js +++ /dev/null @@ -1,157 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import stl from './savedSearchList.module.css'; -import cn from 'classnames'; -import { Icon, IconButton, Loader, Button } from 'UI'; -import { confirm } from 'UI'; -import { withRouter } from 'react-router-dom'; -import { addFilterByKeyAndValue } from 'Duck/search'; -import { - fetchList as fetchFunnelsList, - remove as deleteSearch, - // clearEvents, - init -} from 'Duck/funnels'; -import { setActiveFlow, clearEvents } from 'Duck/filters'; -import { setActiveTab } from 'Duck/sessions'; -import { funnel as funnelRoute, withSiteId } from 'App/routes'; -import Event, { TYPES } from 'Types/filter/event'; -import FunnelMenuItem from 'Components/Funnels/FunnelMenuItem'; -import FunnelSaveModal from 'Components/Funnels/FunnelSaveModal'; -import { blink as setBlink } from 'Duck/funnels'; -import { FilterKey } from 'Types/filter/filterType'; - -const DEFAULT_VISIBLE = 3; -@withRouter -class SavedSearchList extends React.Component { - state = { showMore: false, showSaveModal: false } - - setFlow = flow => { - this.props.setActiveTab({ name: 'All', type: 'all' }); - this.props.setActiveFlow(flow) - if (flow && flow.type === 'flows') { - this.props.clearEvents() - } - } - - renameHandler = funnel => { - this.props.init(funnel); - this.setState({ showSaveModal: true }) - } - - deleteSearch = async (e, funnel) => { - e.preventDefault(); - e.stopPropagation(); - - if (await confirm({ - header: 'Delete Funnel', - confirmButton: 'Delete', - confirmation: `Are you sure you want to permanently delete "${funnel.name}"?` - })) { - this.props.deleteSearch(funnel.funnelId).then(function() { - this.props.fetchFunnelsList(); - this.setState({ showSaveModal: false }) - }.bind(this)); - } else {} - } - - createHandler = () => { - const { filters } = this.props; - if (filters.size === 0) { - this.props.addFilterByKeyAndValue(FilterKey.LOCATION, ''); - this.props.addFilterByKeyAndValue(FilterKey.LOCATION, ''); - this.props.addFilterByKeyAndValue(FilterKey.CLICK, '') - } else { - this.props.setBlink() - } - } - - onFlowClick = ({ funnelId }) => { - const { siteId, history } = this.props; - history.push(withSiteId(funnelRoute(funnelId), siteId)); - } - - render() { - const { funnels, activeFlow, activeTab, loading } = this.props; - const { showMore, showSaveModal } = this.state; - const shouldLimit = funnels.size > DEFAULT_VISIBLE; - - return ( -
- this.setState({ showSaveModal: false })} - /> - -
-
- Funnels - - { funnels.size > 0 && ( - - )} -
-
- { funnels.size === 0 && -
-
- Funnels makes it easy to uncover the most significant issues that impacted conversions. -
- -
- } - { funnels.size > 0 && - - { funnels.take(showMore ? funnels.size : DEFAULT_VISIBLE).map(filter => ( -
- this.onFlowClick(filter)} - deleteHandler={ (e) => this.deleteSearch(e, filter) } - renameHandler={() => this.renameHandler(filter)} - /> -
- ))} - { shouldLimit && -
this.setState({ showMore: !showMore})} - className={cn(stl.showMore, 'cursor-pointer py-2 flex items-center')} - > - {/* */} - { showMore ? 'SHOW LESS' : 'SHOW MORE' } -
- } -
- } -
-
- ); - } -} - -export default connect(state => ({ - funnels: state.getIn([ 'funnels', 'list' ]), - loading: state.getIn(['funnels', 'fetchListRequest', 'loading']), - activeFlow: state.getIn([ 'filters', 'activeFlow' ]), - activeTab: state.getIn([ 'sessions', 'activeTab' ]), - siteId: state.getIn([ 'site', 'siteId' ]), - events: state.getIn([ 'filters', 'appliedFilter', 'events' ]), - filters: state.getIn([ 'search', 'instance', 'filters' ]), -}), { - deleteSearch, setActiveTab, - setActiveFlow, clearEvents, - addFilterByKeyAndValue, - init, - fetchFunnelsList, - setBlink -})(SavedSearchList) diff --git a/frontend/app/components/ui/SavedSearchList/index.js b/frontend/app/components/ui/SavedSearchList/index.js deleted file mode 100644 index 009c383d7..000000000 --- a/frontend/app/components/ui/SavedSearchList/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SavedSearchList' \ No newline at end of file diff --git a/frontend/app/components/ui/SavedSearchList/listItem.module.css b/frontend/app/components/ui/SavedSearchList/listItem.module.css deleted file mode 100644 index dcf9fdddd..000000000 --- a/frontend/app/components/ui/SavedSearchList/listItem.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.wrapper { - padding: 4px 5px; - cursor: pointer; - border: solid thin transparent; - border-radius: 3px; - margin-left: -5px; - - &.active, - &:hover { - background-color: $active-blue; - border-color: $active-blue-border; - & .actionWrapper { - opacity: 1; - } - } - - & span { - color: $teal - } - - & .actionWrapper { - opacity: 0; - } -} \ No newline at end of file diff --git a/frontend/app/components/ui/SavedSearchList/savedSearchList.module.css b/frontend/app/components/ui/SavedSearchList/savedSearchList.module.css deleted file mode 100644 index 1b6a2aeb4..000000000 --- a/frontend/app/components/ui/SavedSearchList/savedSearchList.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.header { - margin-bottom: 15px; - & .label { - text-transform: uppercase; - color: gray; - letter-spacing: 0.2em; - } -} - -.showMore { - &:hover { - color: $teal; - & svg { - fill: $teal; - } - & .actions { - opacity: 1; - } - } -} \ No newline at end of file diff --git a/frontend/app/components/ui/index.js b/frontend/app/components/ui/index.js index c6b43f5b1..8ac4e8c05 100644 --- a/frontend/app/components/ui/index.js +++ b/frontend/app/components/ui/index.js @@ -30,7 +30,6 @@ export { default as JSONTree } from './JSONTree'; export { default as Tooltip } from './Tooltip'; export { default as CountryFlag } from './CountryFlag'; export { default as RandomElement } from './RandomElement'; -export { default as SavedSearchList } from './SavedSearchList'; export { default as SplitButton } from './SplitButton'; export { default as confirm } from './Confirmation'; export { default as SideMenuitem } from './SideMenuitem'; diff --git a/frontend/app/duck/liveSearch.js b/frontend/app/duck/liveSearch.js index 5a9ffd85b..fce73539a 100644 --- a/frontend/app/duck/liveSearch.js +++ b/frontend/app/duck/liveSearch.js @@ -1,21 +1,24 @@ import { List, Map } from 'immutable'; -import { fetchType, editType } from './funcTools/crud'; +import { fetchListType, fetchType, editType } from './funcTools/crud'; import { createRequestReducer } from './funcTools/request'; -import { mergeReducers } from './funcTools/tools'; +import { mergeReducers, success } from './funcTools/tools'; import Filter from 'Types/filter'; -import { fetchList as fetchSessionList } from './sessions'; -import { liveFiltersMap } from 'Types/filter/newFilter'; +// import { fetchList as fetchSessionList } from './sessions'; +import { liveFiltersMap, filtersMap } from 'Types/filter/newFilter'; import { filterMap, checkFilterValue, hasFilterApplied } from './search'; +import Session from 'Types/session'; const name = "liveSearch"; const idKey = "searchId"; +const FETCH_FILTER_SEARCH = fetchListType(`${name}/FILTER_SEARCH`); const FETCH = fetchType(name); const EDIT = editType(name); const CLEAR_SEARCH = `${name}/CLEAR_SEARCH`; const APPLY = `${name}/APPLY`; const UPDATE_CURRENT_PAGE = `${name}/UPDATE_CURRENT_PAGE`; const UPDATE_SORT = `${name}/UPDATE_SORT`; +const FETCH_SESSION_LIST = fetchListType(`${name}/FETCH_SESSION_LIST`); const initialState = Map({ list: List(), @@ -36,6 +39,23 @@ function reducer(state = initialState, action = {}) { return state.set('currentPage', action.page); case UPDATE_SORT: return state.mergeIn(['sort'], action.sort); + case FETCH_SESSION_LIST: + const { sessions, total } = action.data; + const list = List(sessions).map(Session); + return state + .set('list', list) + .set('total', total); + case success(FETCH_FILTER_SEARCH): + const groupedList = action.data.reduce((acc, item) => { + const { projectId, type, value } = item; + const key = type; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push({ projectId, value }); + return acc; + }, {}); + return state.set('filterSearchList', groupedList); } return state; } @@ -44,21 +64,32 @@ export default mergeReducers( reducer, createRequestReducer({ fetch: FETCH, + fetchList: FETCH_SESSION_LIST, }), ); const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => { dispatch(actionCreator(...args)); - const filter = getState().getIn([ 'search', 'instance']).toData(); + const filter = getState().getIn([ 'liveSearch', 'instance']).toData(); filter.filters = filter.filters.map(filterMap); + filter.limit = 10; + filter.page = getState().getIn([ 'liveSearch', 'currentPage']); + return dispatch(fetchSessionList(filter)); }; -export const edit = (instance) => ({ - type: EDIT, - instance, -}); +export const fetchSessionList = (filter) => { + return { + types: FETCH_SESSION_LIST.array, + call: client => client.post('/assist/sessions', filter), + } +} + +export const edit = reduceThenFetchResource((instance) => ({ + type: EDIT, + instance, +})); export const applyFilter = reduceThenFetchResource((filter, fromUrl=false) => ({ type: APPLY, @@ -67,7 +98,7 @@ export const applyFilter = reduceThenFetchResource((filter, fromUrl=false) => ({ })); export const fetchSessions = (filter) => (dispatch, getState) => { - const _filter = filter ? filter : getState().getIn([ 'search', 'instance']); + const _filter = filter ? filter : getState().getIn([ 'liveSearch', 'instance']); return dispatch(applyFilter(_filter)); }; @@ -93,9 +124,12 @@ export const addFilter = (filter) => (dispatch, getState) => { } } -export const addFilterByKeyAndValue = (key, value) => (dispatch, getState) => { - let defaultFilter = liveFiltersMap[key]; +export const addFilterByKeyAndValue = (key, value, operator = undefined) => (dispatch, getState) => { + let defaultFilter = filtersMap[key]; defaultFilter.value = value; + if (operator) { + defaultFilter.operator = operator; + } dispatch(addFilter(defaultFilter)); } @@ -111,4 +145,13 @@ export function updateSort(sort) { type: UPDATE_SORT, sort, }; +} + +export function fetchFilterSearch(params) { + params.live = true + return { + types: FETCH_FILTER_SEARCH.array, + call: client => client.get('/events/search', params), + params, + }; } \ No newline at end of file diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index a5e1d89c0..9a4498439 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -171,7 +171,6 @@ export const reduceThenFetchResource = actionCreator => (...args) => (dispatch, } } - return isRoute(ERRORS_ROUTE, window.location.pathname) ? dispatch(fetchErrorsList(filter)) : dispatch(fetchSessionList(filter)); diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index 47b960bc2..8887b03f2 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -1,7 +1,7 @@ import { List, Map } from 'immutable'; import Session from 'Types/session'; import ErrorStack from 'Types/session/errorStack'; -import Watchdog, { getSessionWatchdogTypes } from 'Types/watchdog'; +import Watchdog from 'Types/watchdog'; import { clean as cleanParams } from 'App/api_client'; import withRequestState, { RequestTypes } from './requestStateCreator'; import { getRE } from 'App/utils'; @@ -83,55 +83,11 @@ const reducer = (state = initialState, action = {}) => { const { sessions, total } = action.data; const list = List(sessions).map(Session); - const { params } = action; - const eventProperties = { - eventCount: 0, - eventTypes: [], - dateFilter: params.rangeValue, - filterKeys: Object.keys(params) - .filter(key => ![ 'custom', 'startDate', 'endDate', 'strict', 'key', 'events', 'rangeValue' ].includes(key)), - returnedCount: list.size, - totalSearchCount: total, - }; - if (Array.isArray(params.events)) { - eventProperties.eventCount = params.events.length; - params.events.forEach(({ type }) => { - if (!eventProperties.eventTypes.includes(type)) { - eventProperties.eventTypes.push(type); - } - }) - } - - const keyMap = {} - list.forEach(s => { - s.issueTypes.forEach(k => { - if(keyMap[k]) - keyMap[k] += 1 - else - keyMap[k] = 1; - }) - }) - - const wdTypeCount = {} - try{ - list.forEach(s => { - getSessionWatchdogTypes(s).forEach(wdtp => { - wdTypeCount[wdtp] = wdTypeCount[wdtp] ? wdTypeCount[wdtp] + 1 : 1; - }) - }) - } catch(e) { - - } - - const sessionIds = list.map(({ sessionId }) => sessionId ).toJS(); - return state .set('list', list) - .set('sessionIds', sessionIds) + .set('sessionIds', list.map(({ sessionId }) => sessionId ).toJS()) .set('favoriteList', list.filter(({ favorite }) => favorite)) - .set('total', total) - .set('keyMap', keyMap) - .set('wdTypeCount', wdTypeCount); + .set('total', total); case SET_AUTOPLAY_VALUES: { const sessionIds = state.get('sessionIds') const currentSessionId = state.get('current').sessionId diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts index 11434e660..3635993f7 100644 --- a/frontend/app/mstore/types/filter.ts +++ b/frontend/app/mstore/types/filter.ts @@ -70,7 +70,7 @@ export default class Filter implements IFilter { this.filters.splice(index, 1) } - fromJson(json) { + fromJson(json: any) { this.name = json.name this.filters = json.filters.map(i => new FilterItem().fromJson(i)) this.eventsOrder = json.eventsOrder diff --git a/frontend/app/player/MessageDistributor/MessageDistributor.ts b/frontend/app/player/MessageDistributor/MessageDistributor.ts index 59e567a1b..582cd7211 100644 --- a/frontend/app/player/MessageDistributor/MessageDistributor.ts +++ b/frontend/app/player/MessageDistributor/MessageDistributor.ts @@ -133,7 +133,7 @@ export default class MessageDistributor extends StatedScreen { const r = new MFileReader(new Uint8Array(), this.sessionStart) const msgs: Array = [] - loadFiles([this.session.mobsUrl], + loadFiles(this.session.mobsUrl, b => { r.append(b) let next: ReturnType From 2fe2406d0c3be9508cc52a7ab22c5c1fb0838600 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 15 Jun 2022 15:29:29 +0200 Subject: [PATCH 2/4] feat(ui) - assist filters wip --- .../shared/FilterDropdown/FilterDropdown.js | 14 +- .../Filters/FilterModal/FilterModal.tsx | 3 +- .../FilterSelection/FilterSelection.tsx | 10 +- .../SessionSearchField/SessionSearchField.tsx | 13 +- frontend/app/constants/alertMetrics.js | 38 ++--- frontend/app/constants/filterOptions.js | 134 +++++++++--------- frontend/app/constants/platformOptions.js | 6 +- frontend/app/constants/schedule.js | 70 +++------ frontend/app/duck/search.js | 4 +- frontend/app/types/filter/newFilter.js | 115 ++++++++------- 10 files changed, 193 insertions(+), 214 deletions(-) diff --git a/frontend/app/components/shared/FilterDropdown/FilterDropdown.js b/frontend/app/components/shared/FilterDropdown/FilterDropdown.js index fa9165202..aaf0baf70 100644 --- a/frontend/app/components/shared/FilterDropdown/FilterDropdown.js +++ b/frontend/app/components/shared/FilterDropdown/FilterDropdown.js @@ -14,15 +14,15 @@ const COUNTRY = 'country'; const LOCATION = 'location'; const platformOptions = [ - { 'key': PLATFORM, text: 'Desktop', value: 1}, - { 'key': PLATFORM, text: 'Tablet', value: 2 }, - { 'key': PLATFORM, text: 'Tablet Landscape', value: 3 }, - { 'key': PLATFORM, text: 'Mobile', value: 4 }, - { 'key': PLATFORM, text: 'Mobile Landscape', value: 5 } + { 'key': PLATFORM, label: 'Desktop', value: 1}, + { 'key': PLATFORM, label: 'Tablet', value: 2 }, + { 'key': PLATFORM, label: 'Tablet Landscape', value: 3 }, + { 'key': PLATFORM, label: 'Mobile', value: 4 }, + { 'key': PLATFORM, label: 'Mobile Landscape', value: 5 } ]; -const countryOptions = Object.keys(countries).map(c => ({key: COUNTRY, text: countries[c], value: c})); -const locationOptions = Object.keys(regionLabels).map(k => ({ key: LOCATION, text: regionLabels[k], value: k})); +const countryOptions = Object.keys(countries).map(c => ({key: COUNTRY, label: countries[c], value: c})); +const locationOptions = Object.keys(regionLabels).map(k => ({ key: LOCATION, label: regionLabels[k], value: k})); const _filterKeys = [ { key: 'userId', name: 'User ID', icon: 'user-alt', placeholder: 'Search for User ID' }, diff --git a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx index 7f0998d05..5db8bd1ab 100644 --- a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx +++ b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx @@ -51,6 +51,7 @@ function FilterModal(props: Props) { } = props; const hasSearchQuery = searchQuery && searchQuery.length > 0; const showSearchList = isMainSearch && searchQuery.length > 0; + console.log('filters', props.filters) const onFilterSearchClick = (filter) => { const _filter = filtersMap[filter.type]; @@ -123,7 +124,7 @@ function FilterModal(props: Props) { } export default connect(state => ({ - filters: state.getIn([ 'search', 'filterList' ]), + // filters: state.getIn([ 'search', 'filterListLive' ]), filterSearchList: state.getIn([ 'search', 'filterSearchList' ]), metaOptions: state.getIn([ 'customFields', 'list' ]), fetchingFilterSearchList: state.getIn([ 'search', 'fetchFilterSearch', 'loading' ]), diff --git a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx index c92944271..5c2e5c32b 100644 --- a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx +++ b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import FilterModal from '../FilterModal'; -import LiveFilterModal from '../LiveFilterModal'; import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv'; import { Icon } from 'UI'; import { connect } from 'react-redux'; @@ -10,6 +9,8 @@ const ASSIST_ROUTE = assistRoute(); interface Props { filter?: any; // event/filter + filterList: any; + filterListLive: any; onFilterClick: (filter) => void; children?: any; isLive?: boolean; @@ -43,14 +44,15 @@ function FilterSelection(props: Props) { {showModal && (
- {/* { isRoute(ASSIST_ROUTE, window.location.pathname) ? : } */} - +
)}
); } -export default connect(state => ({ +export default connect((state: any) => ({ + filters: state.getIn([ 'search', 'filterList' ]), + liveFilters: state.getIn([ 'search', 'filterListLive' ]), isLive: state.getIn([ 'sessions', 'activeTab' ]).type === 'live', }), { })(FilterSelection); \ No newline at end of file diff --git a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx index a6285c2e0..28d1af364 100644 --- a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx +++ b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx @@ -2,14 +2,15 @@ import React, { useState } from 'react'; import { connect } from 'react-redux'; import { Input } from 'UI'; import FilterModal from 'Shared/Filters/FilterModal'; -// import { fetchFilterSearch } from 'Duck/search'; import { debounce } from 'App/utils'; -import { edit as editFilter, addFilterByKeyAndValue } from 'Duck/search'; +import { assist as assistRoute, isRoute } from "App/routes"; +const ASSIST_ROUTE = assistRoute(); interface Props { fetchFilterSearch: (query: any) => void; - // editFilter: typeof editFilter; addFilterByKeyAndValue: (key: string, value: string) => void; + filterList: any; + filterListLive: any; } function SessionSearchField(props: Props) { const debounceFetchFilterSearch = React.useCallback(debounce(props.fetchFilterSearch, 1000), []); @@ -44,6 +45,7 @@ function SessionSearchField(props: Props) { searchQuery={searchQuery} isMainSearch={true} onFilterClick={onAddFilter} + filters={isRoute(ASSIST_ROUTE, window.location.pathname) ? props.filterListLive : props.filterList } />
)} @@ -51,4 +53,7 @@ function SessionSearchField(props: Props) { ); } -export default connect(null, { })(SessionSearchField); \ No newline at end of file +export default connect((state: any) => ({ + filterList: state.getIn([ 'search', 'filterList' ]), + filterListLive: state.getIn([ 'search', 'filterListLive' ]), +}), { })(SessionSearchField); \ No newline at end of file diff --git a/frontend/app/constants/alertMetrics.js b/frontend/app/constants/alertMetrics.js index e7f0efb30..01fc24acb 100644 --- a/frontend/app/constants/alertMetrics.js +++ b/frontend/app/constants/alertMetrics.js @@ -1,21 +1,21 @@ export default [ - { value: 'performance.dom_content_loaded.average', text: 'performance.dom_content_loaded.average', unit: 'ms' }, - { value: 'performance.first_meaningful_paint.average', text: 'performance.first_meaningful_paint.average', unit: 'ms' }, - { value: 'performance.page_load_time.average', text: 'performance.page_load_time.average', unit: 'ms' }, - { value: 'performance.dom_build_time.average', text: 'performance.dom_build_time.average', unit: 'ms' }, - { value: 'performance.speed_index.average', text: 'performance.speed_index.average', unit: 'ms' }, - { value: 'performance.page_response_time.average', text: 'performance.page_response_time.average', unit: 'ms' }, - { value: 'performance.ttfb.average', text: 'performance.ttfb.average', unit: 'ms' }, - { value: 'performance.time_to_render.average', text: 'performance.time_to_render.average', unit: 'ms' }, - { value: 'performance.image_load_time.average', text: 'performance.image_load_time.average', unit: 'ms' }, - { value: 'performance.request_load_time.average', text: 'performance.request_load_time.average', unit: 'ms' }, - { value: 'resources.load_time.average', text: 'resources.load_time.average', unit: 'ms' }, - { value: 'resources.missing.count', text: 'resources.missing.count', unit: '' }, - { value: 'errors.4xx_5xx.count', text: 'errors.4xx_5xx.count', unit: '' }, - { value: 'errors.4xx.count', text: 'errors.4xx.count', unit: '' }, - { value: 'errors.5xx.count', text: 'errors.5xx.count', unit: '' }, - { value: 'errors.javascript.impacted_sessions.count', text: 'errors.javascript.impacted_sessions.count', unit: '' }, - { value: 'performance.crashes.count', text: 'performance.crashes.count', unit: '' }, - { value: 'errors.javascript.count', text: 'errors.javascript.count', unit: '' }, - { value: 'errors.backend.count', text: 'errors.backend.count', unit: '' }, + { value: 'performance.dom_content_loaded.average', label: 'performance.dom_content_loaded.average', unit: 'ms' }, + { value: 'performance.first_meaningful_paint.average', label: 'performance.first_meaningful_paint.average', unit: 'ms' }, + { value: 'performance.page_load_time.average', label: 'performance.page_load_time.average', unit: 'ms' }, + { value: 'performance.dom_build_time.average', label: 'performance.dom_build_time.average', unit: 'ms' }, + { value: 'performance.speed_index.average', label: 'performance.speed_index.average', unit: 'ms' }, + { value: 'performance.page_response_time.average', label: 'performance.page_response_time.average', unit: 'ms' }, + { value: 'performance.ttfb.average', label: 'performance.ttfb.average', unit: 'ms' }, + { value: 'performance.time_to_render.average', label: 'performance.time_to_render.average', unit: 'ms' }, + { value: 'performance.image_load_time.average', label: 'performance.image_load_time.average', unit: 'ms' }, + { value: 'performance.request_load_time.average', label: 'performance.request_load_time.average', unit: 'ms' }, + { value: 'resources.load_time.average', label: 'resources.load_time.average', unit: 'ms' }, + { value: 'resources.missing.count', label: 'resources.missing.count', unit: '' }, + { value: 'errors.4xx_5xx.count', label: 'errors.4xx_5xx.count', unit: '' }, + { value: 'errors.4xx.count', label: 'errors.4xx.count', unit: '' }, + { value: 'errors.5xx.count', label: 'errors.5xx.count', unit: '' }, + { value: 'errors.javascript.impacted_sessions.count', label: 'errors.javascript.impacted_sessions.count', unit: '' }, + { value: 'performance.crashes.count', label: 'performance.crashes.count', unit: '' }, + { value: 'errors.javascript.count', label: 'errors.javascript.count', unit: '' }, + { value: 'errors.backend.count', label: 'errors.backend.count', unit: '' }, ]; diff --git a/frontend/app/constants/filterOptions.js b/frontend/app/constants/filterOptions.js index 3a7345b61..4e27d6b44 100644 --- a/frontend/app/constants/filterOptions.js +++ b/frontend/app/constants/filterOptions.js @@ -1,34 +1,34 @@ import { FilterKey, IssueType } from 'Types/filter/filterType'; // TODO remove text property from options export const options = [ - { key: 'on', text: 'on', label: 'on', value: 'on' }, - { key: 'notOn', text: 'not on', label: 'not on', value: 'notOn' }, - { key: 'onAny', text: 'on any', label: 'on any', value: 'onAny' }, - { key: 'is', text: 'is', label: 'is', value: 'is' }, - { key: 'isAny', text: 'is any', label: 'is any', value: 'isAny' }, - { key: 'inAnyPage', text: 'in any page', label: 'in any page', value: 'isAny' }, - { key: 'isNot', text: 'is not', label: 'is not', value: 'isNot' }, - { key: 'startsWith', text: 'starts with', label: 'starts with', value: 'startsWith' }, - { key: 'endsWith', text: 'ends with', label: 'ends with', value: 'endsWith' }, - { key: 'contains', text: 'contains', label: 'contains', value: 'contains' }, - { key: 'notContains', text: 'not contains', label: 'not contains', value: 'notContains' }, - { key: 'hasAnyValue', text: 'has any value', label: 'has any value', value: 'hasAnyValue' }, - { key: 'hasNoValue', text: 'has no value', label: 'has no value', value: 'hasNoValue' }, - { key: 'isSignedUp', text: 'is signed up', label: 'is signed up', value: 'isSignedUp' }, - { key: 'notSignedUp', text: 'not signed up', label: 'not signed up', value: 'notSignedUp' }, - { key: 'before', text: 'before', label: 'before', value: 'before' }, - { key: 'after', text: 'after', label: 'after', value: 'after' }, - { key: 'inRage', text: 'in rage', label: 'in rage', value: 'inRage' }, - { key: 'notInRage', text: 'not in rage', label: 'not in rage', value: 'notInRage' }, - { key: 'withinLast', text: 'within last', label: 'within last', value: 'withinLast' }, - { key: 'notWithinLast', text: 'not within last', label: 'not within last', value: 'notWithinLast' }, - { key: 'greaterThan', text: 'greater than', label: 'greater than', value: 'greaterThan' }, - { key: 'lessThan', text: 'less than', label: 'less than', value: 'lessThan' }, - { key: 'equal', text: 'equal', label: 'equal', value: 'equal' }, - { key: 'not equal', text: 'not equal', label: 'not equal', value: 'not equal' }, - { key: 'onSelector', text: 'on selector', label: 'on selector', value: 'onSelector' }, - { key: 'onText', text: 'on text', label: 'on text', value: 'onText' }, - { key: 'onComponent', text: 'on component', label: 'on component', value: 'onComponent' }, + { key: 'on', label: 'on', value: 'on' }, + { key: 'notOn', label: 'not on', value: 'notOn' }, + { key: 'onAny', label: 'on any', value: 'onAny' }, + { key: 'is', label: 'is', value: 'is' }, + { key: 'isAny', label: 'is any', value: 'isAny' }, + { key: 'inAnyPage', label: 'in any page', value: 'isAny' }, + { key: 'isNot', label: 'is not', value: 'isNot' }, + { key: 'startsWith', label: 'starts with', value: 'startsWith' }, + { key: 'endsWith', label: 'ends with', value: 'endsWith' }, + { key: 'contains', label: 'contains', value: 'contains' }, + { key: 'notContains', label: 'not contains', value: 'notContains' }, + { key: 'hasAnyValue', label: 'has any value', value: 'hasAnyValue' }, + { key: 'hasNoValue', label: 'has no value', value: 'hasNoValue' }, + { key: 'isSignedUp', label: 'is signed up', value: 'isSignedUp' }, + { key: 'notSignedUp', label: 'not signed up', value: 'notSignedUp' }, + { key: 'before', label: 'before', value: 'before' }, + { key: 'after', label: 'after', value: 'after' }, + { key: 'inRage', label: 'in rage', value: 'inRage' }, + { key: 'notInRage', label: 'not in rage', value: 'notInRage' }, + { key: 'withinLast', label: 'within last', value: 'withinLast' }, + { key: 'notWithinLast', label: 'not within last', value: 'notWithinLast' }, + { key: 'greaterThan', label: 'greater than', value: 'greaterThan' }, + { key: 'lessThan', label: 'less than', value: 'lessThan' }, + { key: 'equal', label: 'equal', value: 'equal' }, + { key: 'not equal', label: 'not equal', value: 'not equal' }, + { key: 'onSelector', label: 'on selector', value: 'onSelector' }, + { key: 'onText', label: 'on text', value: 'onText' }, + { key: 'onComponent', label: 'on component', value: 'onComponent' }, ]; const filterKeys = ['is', 'isNot']; @@ -47,21 +47,21 @@ export const stringOperators = options.filter(({key}) => stringFilterKeys.includ export const stringOperatorsPerformance = options.filter(({key}) => stringFilterKeysPerformance.includes(key)); export const targetOperators = options.filter(({key}) => targetFilterKeys.includes(key)); export const booleanOperators = [ - { key: 'true', text: 'true', label: 'true', value: 'true' }, - { key: 'false', text: 'false', label: 'false', value: 'false' }, + { key: 'true', label: 'true', value: 'true' }, + { key: 'false', label: 'false', value: 'false' }, ] export const customOperators = [ - { key: '=', text: '=', label: '=', value: '=' }, - { key: '<', text: '<', label: '<', value: '<' }, - { key: '>', text: '>', label: '>', value: '>' }, - { key: '<=', text: '<=', label: '<=', value: '<=' }, - { key: '>=', text: '>=', label: '>=', value: '>=' }, + { key: '=', label: '=', value: '=' }, + { key: '<', label: '<', value: '<' }, + { key: '>', label: '>', value: '>' }, + { key: '<=', label: '<=', value: '<=' }, + { key: '>=', label: '>=', value: '>=' }, ] export const metricTypes = [ - { text: 'Timeseries', label: 'Timeseries', value: 'timeseries' }, - { text: 'Table', label: 'Table', value: 'table' }, + { label: 'Timeseries', value: 'timeseries' }, + { label: 'Table', value: 'table' }, { label: 'Funnel', value: 'funnel' }, // { label: 'Errors', value: 'errors' }, // { label: 'Sessions', value: 'sessions' }, @@ -77,42 +77,42 @@ export const tableColumnName = { } export const metricOf = [ - { text: 'Session Count', label: 'Session Count', value: 'sessionCount', type: 'timeseries' }, - { text: 'Users', label: 'Users', value: FilterKey.USERID, type: 'table' }, - { text: 'Sessions', label: 'Sessions', value: FilterKey.SESSIONS, type: 'table' }, - { text: 'JS Errors', label: 'JS Errors', value: FilterKey.ERRORS, type: 'table' }, - { text: 'Issues', label: 'Issues', value: FilterKey.ISSUE, type: 'table' }, - { text: 'Browsers', label: 'Browsers', value: FilterKey.USER_BROWSER, type: 'table' }, - { text: 'Devices', label: 'Devices', value: FilterKey.USER_DEVICE, type: 'table' }, - { text: 'Countries', label: 'Countries', value: FilterKey.USER_COUNTRY, type: 'table' }, - { text: 'URLs', label: 'URLs', value: FilterKey.LOCATION, type: 'table' }, + { label: 'Session Count', value: 'sessionCount', type: 'timeseries' }, + { label: 'Users', value: FilterKey.USERID, type: 'table' }, + { label: 'Sessions', value: FilterKey.SESSIONS, type: 'table' }, + { label: 'JS Errors', value: FilterKey.ERRORS, type: 'table' }, + { label: 'Issues', value: FilterKey.ISSUE, type: 'table' }, + { label: 'Browsers', value: FilterKey.USER_BROWSER, type: 'table' }, + { label: 'Devices', value: FilterKey.USER_DEVICE, type: 'table' }, + { label: 'Countries', value: FilterKey.USER_COUNTRY, type: 'table' }, + { label: 'URLs', value: FilterKey.LOCATION, type: 'table' }, ] export const methodOptions = [ - { text: 'GET', label: 'GET', value: 'GET' }, - { text: 'POST', label: 'POST', value: 'POST' }, - { text: 'PUT', label: 'PUT', value: 'PUT' }, - { text: 'DELETE', label: 'DELETE', value: 'DELETE' }, - { text: 'PATCH', label: 'PATCH', value: 'PATCH' }, - { text: 'HEAD', label: 'HEAD', value: 'HEAD' }, - { text: 'OPTIONS', label: 'OPTIONS', value: 'OPTIONS' }, - { text: 'TRACE', label: 'TRACE', value: 'TRACE' }, - { text: 'CONNECT', label: 'CONNECT', value: 'CONNECT' }, + { label: 'GET', value: 'GET' }, + { label: 'POST', value: 'POST' }, + { label: 'PUT', value: 'PUT' }, + { label: 'DELETE', value: 'DELETE' }, + { label: 'PATCH', value: 'PATCH' }, + { label: 'HEAD', value: 'HEAD' }, + { label: 'OPTIONS', value: 'OPTIONS' }, + { label: 'TRACE', value: 'TRACE' }, + { label: 'CONNECT', value: 'CONNECT' }, ] export const issueOptions = [ - { text: 'Click Rage', label: 'Click Rage', value: IssueType.CLICK_RAGE }, - { text: 'Dead Click', label: 'Dead Click', value: IssueType.DEAD_CLICK }, - { text: 'Excessive Scrolling', label: 'Excessive Scrolling', value: IssueType.EXCESSIVE_SCROLLING }, - { text: 'Bad Request', label: 'Bad Request', value: IssueType.BAD_REQUEST }, - { text: 'Missing Resource', label: 'Missing Resource', value: IssueType.MISSING_RESOURCE }, - { text: 'Memory', label: 'Memory', value: IssueType.MEMORY }, - { text: 'CPU', label: 'CPU', value: IssueType.CPU }, - { text: 'Slow Resource', label: 'Slow Resource', value: IssueType.SLOW_RESOURCE }, - { text: 'Slow Page Load', label: 'Slow Page Load', value: IssueType.SLOW_PAGE_LOAD }, - { text: 'Crash', label: 'Crash', value: IssueType.CRASH }, - { text: 'Custom', label: 'Custom', value: IssueType.CUSTOM }, - { text: 'Error', label: 'Error', value: IssueType.JS_EXCEPTION }, + { label: 'Click Rage', value: IssueType.CLICK_RAGE }, + { label: 'Dead Click', value: IssueType.DEAD_CLICK }, + { label: 'Excessive Scrolling', value: IssueType.EXCESSIVE_SCROLLING }, + { label: 'Bad Request', value: IssueType.BAD_REQUEST }, + { label: 'Missing Resource', value: IssueType.MISSING_RESOURCE }, + { label: 'Memory', value: IssueType.MEMORY }, + { label: 'CPU', value: IssueType.CPU }, + { label: 'Slow Resource', value: IssueType.SLOW_RESOURCE }, + { label: 'Slow Page Load', value: IssueType.SLOW_PAGE_LOAD }, + { label: 'Crash', value: IssueType.CRASH }, + { label: 'Custom', value: IssueType.CUSTOM }, + { label: 'Error', value: IssueType.JS_EXCEPTION }, ] export default { diff --git a/frontend/app/constants/platformOptions.js b/frontend/app/constants/platformOptions.js index 46747ea2e..2da361a57 100644 --- a/frontend/app/constants/platformOptions.js +++ b/frontend/app/constants/platformOptions.js @@ -1,5 +1,5 @@ export default [ - { value: 'desktop', text: 'Desktop' }, - { value: 'mobile', text: 'Mobile' }, - { value: 'tablet', text: 'Tablet' }, + { value: 'desktop', label: 'Desktop' }, + { value: 'mobile', label: 'Mobile' }, + { value: 'tablet', label: 'Tablet' }, ] \ No newline at end of file diff --git a/frontend/app/constants/schedule.js b/frontend/app/constants/schedule.js index 1b55b8630..8e08a32a8 100644 --- a/frontend/app/constants/schedule.js +++ b/frontend/app/constants/schedule.js @@ -1,49 +1,22 @@ export const MINUTES = [ - { value: 5, text: '5 Minutes' }, - { value: 15, text: '15 Minutes' }, - { value: 30, text: '30 Minutes' }, - { value: 60, text: '60 Minutes' }, + { value: 5, label: '5 Minutes' }, + { value: 15, label: '15 Minutes' }, + { value: 30, label: '30 Minutes' }, + { value: 60, label: '60 Minutes' }, ]; -export const HOURS = [ ...Array(24).keys() ].map(i => ({ value: i, text: `${ i > 9 ? '' : '0' }${ i }:00` })); +export const HOURS = [ ...Array(24).keys() ].map(i => ({ value: i, label: `${ i > 9 ? '' : '0' }${ i }:00` })); export const DAYS = [ - { - value: -2, - text: 'Every', - }, - { - value: -1, - text: 'Everyday', - }, - { - value: 6, - text: 'Sunday', - }, - { - value: 0, - text: 'Monday', - }, - { - value: 1, - text: 'Tuesday', - }, - { - value: 2, - text: 'Wednesday', - }, - { - value: 3, - text: 'Thursday', - }, - { - value: 4, - text: 'Friday', - }, - { - value: 5, - text: 'Saturday', - }, + { value: -2, label: 'Every', }, + { value: -1, label: 'Everyday', }, + { value: 6, label: 'Sunday', }, + { value: 0, label: 'Monday', }, + { value: 1, label: 'Tuesday', }, + { value: 2, label: 'Wednesday', }, + { value: 3, label: 'Thursday', }, + { value: 4, label: 'Friday', }, + { value: 5, label: 'Saturday', }, ]; export const EMAIL = 'email'; @@ -51,16 +24,7 @@ export const SLACK = 'slack'; export const WEBHOOK = 'webhook'; export const CHANNEL = [ - { - value: EMAIL, - text: 'Email' - }, - { - value: SLACK, - text: 'Slack' - }, - { - value: WEBHOOK, - text: 'Webhook' - } + { value: EMAIL, label: 'Email' }, + { value: SLACK, label: 'Slack' }, + { value: WEBHOOK, label: 'Webhook' }, ] \ No newline at end of file diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 9a4498439..a1e6485b6 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -47,7 +47,7 @@ const updateInstance = (state, instance) => state.getIn([ "savedSearch", savedSe const initialState = Map({ filterList: generateFilterOptions(filtersMap), - filterListLive: generateLiveFilterOptions(liveFiltersMap), + filterListLive: generateFilterOptions(liveFiltersMap), list: List(), alertMetricId: null, instance: new Filter({ filters: [] }), @@ -63,7 +63,7 @@ function reducer(state = initialState, action = {}) { switch (action.type) { case REFRESH_FILTER_OPTIONS: return state.set('filterList', generateFilterOptions(filtersMap)) - .set('filterListLive', generateLiveFilterOptions(liveFiltersMap)); + .set('filterListLive', generateFilterOptions(liveFiltersMap)); case EDIT: return state.mergeIn(['instance'], action.instance).set('currentPage', 1); case APPLY: diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 61c0e42bd..45d31e2f1 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -3,67 +3,74 @@ import { FilterType, FilterKey, FilterCategory } from './filterType' import filterOptions, { countries, platformOptions } from 'App/constants'; import { capitalize } from 'App/utils'; -const countryOptions = Object.keys(countries).map(i => ({ text: countries[i], value: i })); +const countryOptions = Object.keys(countries).map(i => ({ label: countries[i], value: i })); const containsFilters = [{ key: 'contains', label: 'contains', text: 'contains', value: 'contains' }] export const metaFilter = { key: FilterKey.METADATA, type: FilterType.MULTIPLE, category: FilterCategory.METADATA, label: 'Metadata', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/metadata' }; -export const filtersMap = { - // EVENTS - [FilterKey.CLICK]: { key: FilterKey.CLICK, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Click', operator: 'on', operatorOptions: filterOptions.targetOperators, icon: 'filters/click', isEvent: true }, - [FilterKey.INPUT]: { key: FilterKey.INPUT, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Input', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/input', isEvent: true }, - [FilterKey.LOCATION]: { key: FilterKey.LOCATION, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Path', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/location', isEvent: true }, - [FilterKey.CUSTOM]: { key: FilterKey.CUSTOM, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Custom Events', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/custom', isEvent: true }, - // [FilterKey.REQUEST]: { key: FilterKey.REQUEST, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Fetch', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', isEvent: true }, - [FilterKey.FETCH]: { 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' }, - { key: FilterKey.FETCH_METHOD, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', options: filterOptions.methodOptions }, - { key: FilterKey.FETCH_DURATION, type: FilterType.NUMBER, category: FilterCategory.PERFORMANCE, label: 'with duration (ms)', operator: '=', operatorOptions: filterOptions.customOperators, icon: 'filters/fetch' }, - { key: FilterKey.FETCH_REQUEST_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with request body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, - { key: FilterKey.FETCH_RESPONSE_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with response body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, - ], icon: 'filters/fetch', isEvent: true }, - [FilterKey.GRAPHQL]: { key: FilterKey.GRAPHQL, type: FilterType.SUB_FILTERS, category: FilterCategory.JAVASCRIPT, label: 'GraphQL', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/graphql', isEvent: true, filters: [ - { key: FilterKey.GRAPHQL_NAME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with name', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, - { key: FilterKey.GRAPHQL_METHOD, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', options: filterOptions.methodOptions }, - { key: FilterKey.GRAPHQL_REQUEST_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with request body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, - { key: FilterKey.GRAPHQL_RESPONSE_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with response body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, - ]}, - [FilterKey.STATEACTION]: { key: FilterKey.STATEACTION, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'StateAction', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/state-action', isEvent: true }, - [FilterKey.ERROR]: { key: FilterKey.ERROR, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Error', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/error', isEvent: true }, - // [FilterKey.METADATA]: { key: FilterKey.METADATA, type: FilterType.MULTIPLE, category: FilterCategory.METADATA, label: 'Metadata', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/metadata', isEvent: true }, +export const filters = [ + { key: FilterKey.CLICK, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Click', operator: 'on', operatorOptions: filterOptions.targetOperators, icon: 'filters/click', isEvent: true }, + { 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.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' }, + { key: FilterKey.FETCH_METHOD, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', options: filterOptions.methodOptions }, + { key: FilterKey.FETCH_DURATION, type: FilterType.NUMBER, category: FilterCategory.PERFORMANCE, label: 'with duration (ms)', operator: '=', operatorOptions: filterOptions.customOperators, icon: 'filters/fetch' }, + { key: FilterKey.FETCH_REQUEST_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with request body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, + { key: FilterKey.FETCH_RESPONSE_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with response body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, + ], icon: 'filters/fetch', isEvent: true }, + { key: FilterKey.GRAPHQL, type: FilterType.SUB_FILTERS, category: FilterCategory.JAVASCRIPT, label: 'GraphQL', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/graphql', isEvent: true, filters: [ + { key: FilterKey.GRAPHQL_NAME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with name', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, + { key: FilterKey.GRAPHQL_METHOD, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', options: filterOptions.methodOptions }, + { key: FilterKey.GRAPHQL_REQUEST_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with request body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, + { key: FilterKey.GRAPHQL_RESPONSE_BODY, type: FilterType.STRING, category: FilterCategory.PERFORMANCE, label: 'with response body', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, + ]}, + { key: FilterKey.STATEACTION, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'StateAction', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/state-action', isEvent: true }, + { key: FilterKey.ERROR, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Error', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/error', isEvent: true }, + { key: FilterKey.METADATA, type: FilterType.MULTIPLE, category: FilterCategory.METADATA, label: 'Metadata', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/metadata', isEvent: true }, + + // FILTERS + { key: FilterKey.USER_OS, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User OS', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/os' }, + { key: FilterKey.USER_BROWSER, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User Browser', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/browser' }, + { key: FilterKey.USER_DEVICE, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User Device', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/device' }, + { key: FilterKey.PLATFORM, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.GEAR, label: 'Platform', operator: 'is', operatorOptions: filterOptions.baseOperators, icon: 'filters/platform', options: platformOptions }, + { key: FilterKey.REVID, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'Version ID', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'collection' }, + { key: FilterKey.REFERRER, type: FilterType.MULTIPLE, category: FilterCategory.RECORDING_ATTRIBUTES, label: 'Referrer', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/arrow-return-right' }, + { key: FilterKey.DURATION, type: FilterType.DURATION, category: FilterCategory.RECORDING_ATTRIBUTES, label: 'Duration', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is']), icon: 'filters/duration' }, + { key: FilterKey.USER_COUNTRY, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.USER, label: 'User Country', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/country', options: countryOptions }, + { key: FilterKey.CONSOLE, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Console', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/console' }, + { key: FilterKey.USERID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User Id', operator: 'is', operatorOptions: filterOptions.stringOperators.concat([{ label: 'is undefined', value: 'isUndefined'}]), icon: 'filters/userid' }, + { key: FilterKey.USERANONYMOUSID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User AnonymousId', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/userid' }, + + // PERFORMANCE + { key: FilterKey.DOM_COMPLETE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'DOM Complete', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/dom-complete', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + { key: FilterKey.LARGEST_CONTENTFUL_PAINT_TIME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Largest Contentful Paint', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/lcpt', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + { key: FilterKey.TTFB, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Time to First Byte', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/ttfb', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + { key: FilterKey.AVG_CPU_LOAD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg CPU Load', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/cpu-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + { key: FilterKey.AVG_MEMORY_USAGE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg Memory Usage', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/memory-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, icon: 'filters/fetch-failed', isEvent: true }, + { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions }, +]; + +export const filtersMap = filters.reduce((acc, filter) => { + acc[filter.key] = filter; + return acc; +}, {}); + +export const liveFiltersMap = filters.reduce((acc, filter) => { + if (filter.category !== FilterCategory.INTERACTIONS && filter.category !== FilterCategory.JAVASCRIPT) { + acc[filter.key] = filter; + } + return acc +}, {}); - // FILTERS - [FilterKey.USER_OS]: { key: FilterKey.USER_OS, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User OS', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/os' }, - [FilterKey.USER_BROWSER]: { key: FilterKey.USER_BROWSER, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User Browser', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/browser' }, - [FilterKey.USER_DEVICE]: { key: FilterKey.USER_DEVICE, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'User Device', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/device' }, - [FilterKey.PLATFORM]: { key: FilterKey.PLATFORM, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.GEAR, label: 'Platform', operator: 'is', operatorOptions: filterOptions.baseOperators, icon: 'filters/platform', options: platformOptions }, - [FilterKey.REVID]: { key: FilterKey.REVID, type: FilterType.MULTIPLE, category: FilterCategory.GEAR, label: 'Version ID', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'collection' }, - [FilterKey.REFERRER]: { key: FilterKey.REFERRER, type: FilterType.MULTIPLE, category: FilterCategory.RECORDING_ATTRIBUTES, label: 'Referrer', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/arrow-return-right' }, - [FilterKey.DURATION]: { key: FilterKey.DURATION, type: FilterType.DURATION, category: FilterCategory.RECORDING_ATTRIBUTES, label: 'Duration', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is']), icon: 'filters/duration' }, - [FilterKey.USER_COUNTRY]: { key: FilterKey.USER_COUNTRY, type: FilterType.MULTIPLE_DROPDOWN, category: FilterCategory.USER, label: 'User Country', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/country', options: countryOptions }, - // [FilterKey.CONSOLE]: { key: FilterKey.CONSOLE, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Console', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/console' }, - [FilterKey.USERID]: { key: FilterKey.USERID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User Id', operator: 'is', operatorOptions: filterOptions.stringOperators.concat([{ text: 'is undefined', value: 'isUndefined'}]), icon: 'filters/userid' }, - [FilterKey.USERANONYMOUSID]: { key: FilterKey.USERANONYMOUSID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User AnonymousId', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/userid' }, - - // PERFORMANCE - [FilterKey.DOM_COMPLETE]: { key: FilterKey.DOM_COMPLETE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'DOM Complete', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, 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.stringOperatorsPerformance, 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.stringOperatorsPerformance, source: [], icon: 'filters/ttfb', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.AVG_CPU_LOAD]: { key: FilterKey.AVG_CPU_LOAD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg CPU Load', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/cpu-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.AVG_MEMORY_USAGE]: { key: FilterKey.AVG_MEMORY_USAGE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg Memory Usage', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/memory-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.FETCH_FAILED]: { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, icon: 'filters/fetch-failed', isEvent: true }, - [FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions }, -} - -export const filterLabelMap = Object.keys(filtersMap).reduce((acc, key) => { - acc[key] = filtersMap[key].label +export const filterLabelMap = filters.reduce((acc, filter) => { + acc[filter.key] = filter.label return acc }, {}) -export const liveFiltersMap = { - [FilterKey.USERID]: { key: FilterKey.USERID, type: FilterType.STRING, category: FilterCategory.USER, label: 'User Id', operator: 'contains', operatorOptions: containsFilters, icon: 'filters/userid', isLive: true }, -} - /** * Add a new filter to the filter list * @param {*} category From aa669d6a86524657b380e8aaee99245a702878d4 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 15 Jun 2022 16:20:35 +0200 Subject: [PATCH 3/4] feat(ui) - assist filters wip --- frontend/app/components/Assist/Assist.tsx | 7 +- .../FilterAutoComplete/FilterAutoComplete.tsx | 14 ++-- .../Filters/FilterValue/FilterValue.tsx | 35 ++++++---- .../LiveSessionList/LiveSessionList.tsx | 68 +++++++++---------- frontend/app/types/filter/newFilter.js | 10 ++- 5 files changed, 70 insertions(+), 64 deletions(-) diff --git a/frontend/app/components/Assist/Assist.tsx b/frontend/app/components/Assist/Assist.tsx index f6bcb85c1..3ef99c573 100644 --- a/frontend/app/components/Assist/Assist.tsx +++ b/frontend/app/components/Assist/Assist.tsx @@ -4,8 +4,8 @@ import LiveSessionSearch from 'Shared/LiveSessionSearch'; import cn from 'classnames' import withPageTitle from 'HOCs/withPageTitle'; import withPermissions from 'HOCs/withPermissions' -import SessionSearch from '../shared/SessionSearch'; -import MainSearchBar from '../shared/MainSearchBar'; +// import SessionSearch from '../shared/SessionSearch'; +// import MainSearchBar from '../shared/MainSearchBar'; import AssistSearchField from './AssistSearchField'; function Assist() { @@ -13,11 +13,8 @@ function Assist() {
- - {/* */} - {/* */}
diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx index fcec61d6e..97699c7eb 100644 --- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx @@ -20,7 +20,7 @@ interface Props { params?: any; headerText?: string; placeholder?: string; - onSelect: (e, item) => void; + onSelect: (e: any, item: any) => void; value: any; icon?: string; } @@ -44,17 +44,17 @@ function FilterAutoComplete(props: Props) { const [options, setOptions] = useState([]); const [query, setQuery] = useState(value); - const requestValues = (q) => { + const requestValues = (q: any) => { setLoading(true); return new APIClient()[method?.toLocaleLowerCase()](endpoint, { ...params, q }) - .then(response => { + .then((response: any) => { if (response.ok) { return response.json(); } throw new Error(response.statusText); }) - .then(({ data }) => { + .then(({ data }: any) => { setOptions(data); }) .finally(() => setLoading(false)); @@ -62,7 +62,7 @@ function FilterAutoComplete(props: Props) { const debouncedRequestValues = React.useCallback(debounce(requestValues, 1000), [params]); - const onInputChange = ({ target: { value } }) => { + const onInputChange = ({ target: { value } }: any) => { setQuery(value); if (!showModal) { setShowModal(true); @@ -85,7 +85,7 @@ function FilterAutoComplete(props: Props) { } } - const onItemClick = (e, item) => { + const onItemClick = (e: any, item: any) => { e.stopPropagation(); e.preventDefault(); @@ -132,7 +132,7 @@ function FilterAutoComplete(props: Props) { ) : (
{ - options.map((item, i) => ( + options.map((item: any, i: any) => (
void; + onUpdate: (filter: any) => void; } function FilterValue(props: Props) { const { filter } = props; @@ -21,13 +24,13 @@ function FilterValue(props: Props) { props.onUpdate({ ...filter, value: newValue }); } - const onRemoveValue = (valueIndex) => { - const newValue = filter.value.filter((_, index) => index !== valueIndex); + const onRemoveValue = (valueIndex: any) => { + const newValue = filter.value.filter((_: any, index: any) => index !== valueIndex); props.onUpdate({ ...filter, value: newValue }); } - const onChange = (e, item, valueIndex) => { - const newValues = filter.value.map((_, _index) => { + const onChange = (e: any, item: any, valueIndex: any) => { + const newValues = filter.value.map((_: any, _index: any) => { if (_index === valueIndex) { return item.value; } @@ -38,11 +41,11 @@ function FilterValue(props: Props) { const debounceOnSelect = React.useCallback(debounce(onChange, 500), [onChange]); - const onDurationChange = (newValues) => { + const onDurationChange = (newValues: any) => { setDurationValues({ ...durationValues, ...newValues }); } - const handleBlur = (e) => { + const handleBlur = (e: any) => { if (filter.type === FilterType.DURATION) { const { maxDuration, minDuration, key } = filter; if (maxDuration || minDuration) return; @@ -53,16 +56,22 @@ function FilterValue(props: Props) { } } - const getParms = (key) => { + const getParms = (key: any) => { + let params = {}; switch (filter.category) { case FilterCategory.METADATA: - return { type: FilterKey.METADATA, key: key }; + params = { type: FilterKey.METADATA, key: key }; default: - return { type: filter.key }; + params = { type: filter.key }; } + + if (isRoute(ASSIST_ROUTE, window.location.pathname)) { + params = { ...params, live: true }; + } + return params; } - const renderValueFiled = (value, valueIndex) => { + const renderValueFiled = (value: any, valueIndex: any) => { const showOrButton = valueIndex === lastIndex && filter.type !== FilterType.NUMBER; switch(filter.type) { case FilterType.STRING: @@ -85,7 +94,7 @@ function FilterValue(props: Props) { filter={filter} options={filter.options} onChange={({ value }) => onChange(null, { value }, valueIndex)} - /> + /> ) case FilterType.ISSUE: case FilterType.MULTIPLE_DROPDOWN: @@ -174,7 +183,7 @@ function FilterValue(props: Props) { { filter.type === FilterType.DURATION ? ( renderValueFiled(filter.value, 0) ) : ( - filter.value && filter.value.map((value, valueIndex) => ( + filter.value && filter.value.map((value: any, valueIndex: any) => (
{renderValueFiled(value, valueIndex)}
diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index 1e669ff9a..606bb54c2 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -1,8 +1,8 @@ import React, { useEffect } from 'react'; import { fetchLiveList } from 'Duck/sessions'; import { connect } from 'react-redux'; -import { NoContent, Loader, LoadMoreButton, Pagination } from 'UI'; -import { List, Map } from 'immutable'; +import { NoContent, Loader, Pagination } from 'UI'; +import { List } from 'immutable'; import SessionItem from 'Shared/SessionItem'; import withPermissions from 'HOCs/withPermissions' import { KEYS } from 'Types/filter/customFilter'; @@ -23,7 +23,7 @@ interface Props { fetchLiveList: () => Promise, applyFilter: () => void, filters: any, - addAttribute: (obj) => void, + addAttribute: (obj: any) => void, addFilterByKeyAndValue: (key: FilterKey, value: string) => void, updateCurrentPage: (page: number) => void, currentPage: number, @@ -34,16 +34,13 @@ interface Props { function LiveSessionList(props: Props) { const { loading, filters, list, currentPage, metaList = [], sort } = props; - var timeoutId; - const hasUserFilter = filters.map(i => i.key).includes(KEYS.USERID); + var timeoutId: any; + const hasUserFilter = filters.map((i: any) => i.key).includes(KEYS.USERID); const [sessions, setSessions] = React.useState(list); - const sortOptions = metaList.map(i => ({ + const sortOptions = metaList.map((i: any) => ({ text: capitalize(i), label: i })).toJS(); - // const displayedCount = Math.min(currentPage * PER_PAGE, sessions.size); - // const addPage = () => props.updateCurrentPage(props.currentPage + 1) - // useEffect(() => { // if (filters.size === 0) { // props.addFilterByKeyAndValue(FilterKey.USERID, ''); @@ -53,31 +50,31 @@ function LiveSessionList(props: Props) { useEffect(() => { if (metaList.size === 0 || !!sort.field) return; - if ( sortOptions[0]) { + if (sortOptions[0]) { props.updateSort({ field: sortOptions[0].value }); } }, [metaList]); - useEffect(() => { - const filteredSessions = filters.size > 0 ? props.list.filter(session => { - let hasValidFilter = true; - filters.forEach(filter => { - if (!hasValidFilter) return; + // useEffect(() => { + // const filteredSessions = filters.size > 0 ? props.list.filter(session => { + // let hasValidFilter = true; + // filters.forEach(filter => { + // if (!hasValidFilter) return; - const _values = filter.value.filter(i => i !== '' && i !== null && i !== undefined).map(i => i.toLowerCase()); - if (filter.key === FilterKey.USERID) { - const _userId = session.userId ? session.userId.toLowerCase() : ''; - hasValidFilter = _values.length > 0 ? (_values.includes(_userId) && hasValidFilter) || _values.some(i => _userId.includes(i)) : hasValidFilter; - } - if (filter.category === FilterCategory.METADATA) { - const _source = session.metadata[filter.key] ? session.metadata[filter.key].toLowerCase() : ''; - hasValidFilter = _values.length > 0 ? (_values.includes(_source) && hasValidFilter) || _values.some(i => _source.includes(i)) : hasValidFilter; - } - }) - return hasValidFilter; - }) : props.list; - setSessions(filteredSessions); - }, [filters, list]); + // const _values = filter.value.filter(i => i !== '' && i !== null && i !== undefined).map(i => i.toLowerCase()); + // if (filter.key === FilterKey.USERID) { + // const _userId = session.userId ? session.userId.toLowerCase() : ''; + // hasValidFilter = _values.length > 0 ? (_values.includes(_userId) && hasValidFilter) || _values.some(i => _userId.includes(i)) : hasValidFilter; + // } + // if (filter.category === FilterCategory.METADATA) { + // const _source = session.metadata[filter.key] ? session.metadata[filter.key].toLowerCase() : ''; + // hasValidFilter = _values.length > 0 ? (_values.includes(_source) && hasValidFilter) || _values.some(i => _source.includes(i)) : hasValidFilter; + // } + // }) + // return hasValidFilter; + // }) : props.list; + // setSessions(filteredSessions); + // }, [filters, list]); useEffect(() => { props.fetchLiveList(); @@ -95,7 +92,8 @@ function LiveSessionList(props: Props) { } } - const onSortChange = (e, { value }) => { + const onSortChange = ({ value }: any) => { + value = value.value props.updateSort({ field: value }); } @@ -145,9 +143,7 @@ function LiveSessionList(props: Props) { show={ !loading && sessions && sessions.size === 0} > - {sessions && sliceListPerPage(sessions.sortBy(i => i.metadata[sort.field]).update(list => { - return sort.order === 'desc' ? list.reverse() : list; - }), currentPage - 1).map(session => ( + {sessions.map(session => ( props.updateCurrentPage(page)} + onPageChange={(page: any) => props.updateCurrentPage(page)} limit={PER_PAGE} />
@@ -173,12 +169,12 @@ function LiveSessionList(props: Props) { } export default withPermissions(['ASSIST_LIVE'])(connect( - (state) => ({ + (state: any) => ({ list: state.getIn(['liveSearch', 'list']), loading: state.getIn([ 'liveSearch', 'fetchList', 'loading' ]), filters: state.getIn([ 'liveSearch', 'instance', 'filters' ]), currentPage: state.getIn(["liveSearch", "currentPage"]), - metaList: state.getIn(['customFields', 'list']).map(i => i.key), + metaList: state.getIn(['customFields', 'list']).map((i: any) => i.key), sort: state.getIn(['liveSearch', 'sort']), }), { diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 45d31e2f1..d5e3859ba 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -53,14 +53,18 @@ export const filters = [ { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, icon: 'filters/fetch-failed', isEvent: true }, { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions }, ]; - + export const filtersMap = filters.reduce((acc, filter) => { acc[filter.key] = filter; return acc; }, {}); - + export const liveFiltersMap = filters.reduce((acc, filter) => { - if (filter.category !== FilterCategory.INTERACTIONS && filter.category !== FilterCategory.JAVASCRIPT) { + if ( + filter.category !== FilterCategory.INTERACTIONS && + filter.category !== FilterCategory.JAVASCRIPT && + filter.category !== FilterCategory.PERFORMANCE + ) { acc[filter.key] = filter; } return acc From 1e78a851c68df91de19ef4ac1ca485233e4f2025 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 15 Jun 2022 16:46:09 +0200 Subject: [PATCH 4/4] feat(ui) - assist filters wip --- .../app/components/shared/LiveSessionList/LiveSessionList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index 606bb54c2..d44dbe2be 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -7,11 +7,11 @@ import SessionItem from 'Shared/SessionItem'; import withPermissions from 'HOCs/withPermissions' import { KEYS } from 'Types/filter/customFilter'; import { applyFilter, addAttribute } from 'Duck/filters'; -import { FilterCategory, FilterKey } from 'App/types/filter/filterType'; +import { FilterKey } from 'App/types/filter/filterType'; import { addFilterByKeyAndValue, updateCurrentPage, updateSort } from 'Duck/liveSearch'; import Select from 'Shared/Select'; import SortOrderButton from 'Shared/SortOrderButton'; -import { capitalize, sliceListPerPage } from 'App/utils'; +import { capitalize } from 'App/utils'; import LiveSessionReloadButton from 'Shared/LiveSessionReloadButton'; const AUTOREFRESH_INTERVAL = .5 * 60 * 1000