diff --git a/frontend/app/components/BugFinder/BugFinder.js b/frontend/app/components/BugFinder/BugFinder.js index 24952ac61..93ee7cd68 100644 --- a/frontend/app/components/BugFinder/BugFinder.js +++ b/frontend/app/components/BugFinder/BugFinder.js @@ -26,6 +26,7 @@ import TrackerUpdateMessage from 'Shared/TrackerUpdateMessage'; import LiveSessionList from './LiveSessionList' import SessionSearch from 'Shared/SessionSearch'; import MainSearchBar from 'Shared/MainSearchBar'; +import LiveSearchBar from 'Shared/LiveSearchBar'; import { clearSearch } from 'Duck/search'; const weakEqual = (val1, val2) => { @@ -168,15 +169,28 @@ export default class BugFinder extends React.PureComponent {
-
- - -
- { activeTab.type !== 'live' && } - { activeTab.type === 'live' && } + + {/* Recorde Sessions */} + { activeTab.type !== 'live' && ( + <> +
+ + +
+ { activeTab.type !== 'live' && } + + )} + + {/* Live Sessions */} + { activeTab.type === 'live' && ( + <> +
+ + +
+ { activeTab.type === 'live' && } + + )}
setActiveSeries(value); + const getListSessionsBySeries = (seriesId) => { + const arr: any = [] + list.forEach(element => { + if (seriesId === 'all') { + const sessionIds = arr.map(i => i.sessionId); + arr.push(...element.sessions.filter(i => !sessionIds.includes(i.sessionId))); + } else { + if (element.seriesId === seriesId) { + arr.push(...element.sessions) + } + } + }); + return arr; + } - const filteredSessions = activeSeries === 'all' ? list.reduce((a, b) => a.concat(b.sessions), []) : list.filter(item => item.seriesId === activeSeries).reduce((a, b) => a.concat(b.sessions), []); - const startTime = new DateTime(activeWidget.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); - const endTime = new DateTime(activeWidget.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const writeOption = (e, { name, value }) => setActiveSeries(value); + const filteredSessions = getListSessionsBySeries(activeSeries); + + const startTime = DateTime.fromMillis(activeWidget.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const endTime = DateTime.fromMillis(activeWidget.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); return ( Series */} - - { filteredSessions.map(session => ) } - + + + { filteredSessions.map(session => ) } + + )} /> @@ -94,7 +111,7 @@ function SessionListModal(props: Props) { } export default connect(state => ({ - loading: state.getIn(['customMetrics', 'sessionListRequest', 'loading']), + loading: state.getIn(['customMetrics', 'fetchSessionList', 'loading']), list: state.getIn(['customMetrics', 'sessionList']), // activeWidget: state.getIn(['customMetrics', 'activeWidget']), }), { fetchSessionList, setActiveWidget })(SessionListModal); diff --git a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx index bc7c6917d..ab5e76157 100644 --- a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx +++ b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx @@ -4,7 +4,6 @@ import { connect } from 'react-redux'; import cn from 'classnames'; import stl from './FilterModal.css'; import { filtersMap, getMetaDataFilter } from 'Types/filter/newFilter'; -// import { FilterKey, FilterType } from 'Types/filter/filterType'; interface Props { filters: any, @@ -25,7 +24,6 @@ function FilterModal(props: Props) { fetchingFilterSearchList, searchQuery = '', } = props; - // const hasFilerSearchList = filterSearchList && Object.keys(filterSearchList).length > 0; const hasSearchQuery = searchQuery && searchQuery.length > 0; const showSearchList = isMainSearch && searchQuery.length > 0; diff --git a/frontend/app/components/shared/LiveSearchBar/LiveSearchBar.tsx b/frontend/app/components/shared/LiveSearchBar/LiveSearchBar.tsx new file mode 100644 index 000000000..afe300c31 --- /dev/null +++ b/frontend/app/components/shared/LiveSearchBar/LiveSearchBar.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import SessionSearchField from 'Shared/SessionSearchField'; +import SavedSearch from 'Shared/SavedSearch'; +import { Button, Popup } from 'UI'; +import { clearSearch } from 'Duck/search'; +import { connect } from 'react-redux'; + +interface Props { + clearSearch: () => void; + appliedFilter: any; +} +const LiveSearchBar = (props: Props) => { + const { appliedFilter } = props; + const hasFilters = appliedFilter && appliedFilter.filters && appliedFilter.filters.size > 0; + return ( +
+
+ +
+
+ props.clearSearch()} + > + Clear + + } + content={'Clear Steps'} + size="tiny" + inverted + position="top right" + /> +
+
+ ) +} +export default connect(state => ({ + appliedFilter: state.getIn(['search', 'instance']), +}), { clearSearch })(LiveSearchBar); \ No newline at end of file diff --git a/frontend/app/components/shared/LiveSearchBar/index.ts b/frontend/app/components/shared/LiveSearchBar/index.ts new file mode 100644 index 000000000..32cdf44ce --- /dev/null +++ b/frontend/app/components/shared/LiveSearchBar/index.ts @@ -0,0 +1 @@ +export { default } from './LiveSearchBar'; \ 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 b4b619f04..c8741ec9d 100644 --- a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx +++ b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx @@ -1,26 +1,18 @@ -import React, { useRef, useState } from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import stl from './SessionSearchField.css'; import { Input } from 'UI'; import FilterModal from 'Shared/Filters/FilterModal'; -// import { fetchList as fetchFilterSearch } from 'Duck/events'; import { fetchFilterSearch } from 'Duck/search'; import { debounce } from 'App/utils'; -import { edit as editFilter } from 'Duck/search'; -import { - addEvent, applyFilter, moveEvent, clearEvents, - addCustomFilter, addAttribute, setActiveFlow, setFilterOption -} from 'Duck/filters'; +import { edit as editFilter, addFilterByKeyAndValue } from 'Duck/search'; interface Props { - // setSearchQuery: (query: string) => void; fetchFilterSearch: (query: any) => void; - // searchQuery: string; - appliedFilter: any; editFilter: typeof editFilter; + addFilterByKeyAndValue: (key: string, value: string) => void; } function SessionSearchField(props: Props) { - const { appliedFilter } = props; const debounceFetchFilterSearch = debounce(props.fetchFilterSearch, 1000) const [showModal, setShowModal] = useState(false) const [searchQuery, setSearchQuery] = useState('') @@ -31,12 +23,7 @@ function SessionSearchField(props: Props) { } const onAddFilter = (filter) => { - filter.value = filter.value ? filter.value : [""] - const newFilters = appliedFilter.filters.concat(filter); - props.editFilter({ - ...appliedFilter.filter, - filters: newFilters, - }); + props.addFilterByKeyAndValue(filter.key, filter.value) } return ( @@ -46,10 +33,7 @@ function SessionSearchField(props: Props) { className={stl.searchField} onFocus={ () => setShowModal(true) } onBlur={ () => setTimeout(setShowModal, 200, false) } - // ref={ this.inputRef } onChange={ onSearchChange } - // onKeyUp={this.onKeyUp} - // value={props.searchQuery} icon="search" iconPosition="left" placeholder={ 'Search sessions using any captured event (click, input, page, error...)'} @@ -72,14 +56,4 @@ function SessionSearchField(props: Props) { ); } -export default connect(state => ({ - events: state.getIn([ 'filters', 'appliedFilter', 'events' ]), - // searchQuery: state.getIn([ 'filters', 'searchQuery' ]), - appliedFilterKeys: state.getIn([ 'filters', 'appliedFilter', 'filters' ]) - .map(({type}) => type).toJS(), - searchedEvents: state.getIn([ 'events', 'list' ]), - loading: state.getIn([ 'events', 'loading' ]), - strict: state.getIn([ 'filters', 'appliedFilter', 'strict' ]), - blink: state.getIn([ 'funnels', 'blink' ]), - appliedFilter: state.getIn(['search', 'instance']), -}), { fetchFilterSearch, editFilter })(SessionSearchField); \ No newline at end of file +export default connect(null, { fetchFilterSearch, editFilter, addFilterByKeyAndValue })(SessionSearchField); \ No newline at end of file diff --git a/frontend/app/components/ui/SegmentSelection/SegmentSelection.js b/frontend/app/components/ui/SegmentSelection/SegmentSelection.js index 44219c1ec..a862394ee 100644 --- a/frontend/app/components/ui/SegmentSelection/SegmentSelection.js +++ b/frontend/app/components/ui/SegmentSelection/SegmentSelection.js @@ -20,9 +20,9 @@ class SegmentSelection extends React.Component { > { list.map(item => ( !item.disabled && this.setActiveItem(item) } diff --git a/frontend/app/types/dashboard/helper.js b/frontend/app/types/dashboard/helper.js index b19b787e3..05e50d757 100644 --- a/frontend/app/types/dashboard/helper.js +++ b/frontend/app/types/dashboard/helper.js @@ -43,7 +43,7 @@ export const getChartFormatter = period => (data = []) => export const getStartAndEndTimestampsByDensity = (current, start, end, density) => { const diff = end - start; - const step = diff / density; + const step = Math.floor(diff / density); const currentIndex = Math.floor((current - start) / step); const startTimestamp = parseInt(start + currentIndex * step); const endTimestamp = parseInt(startTimestamp + step); diff --git a/frontend/app/types/session/issue.js b/frontend/app/types/session/issue.js index 87878b6bb..31214ae3e 100644 --- a/frontend/app/types/session/issue.js +++ b/frontend/app/types/session/issue.js @@ -6,8 +6,8 @@ export const issues_types = List([ { 'type': 'js_exception', 'visible': true, 'order': 0, 'name': 'Errors', 'icon': 'funnel/exclamation-circle' }, { 'type': 'bad_request', 'visible': true, 'order': 1, 'name': 'Bad Requests', 'icon': 'funnel/file-medical-alt' }, { 'type': 'missing_resource', 'visible': true, 'order': 2, 'name': 'Missing Images', 'icon': 'funnel/image' }, - { 'type': 'click_rage', 'visible': true, 'order': 3, 'name': 'Click Rage', 'icon': 'funnel/dizzy' }, - { 'type': 'dead_click', 'visible': true, 'order': 4, 'name': 'Dead Clicks', 'icon': 'funnel/emoji-angry' }, + { 'type': 'click_rage', 'visible': true, 'order': 3, 'name': 'Click Rage', 'icon': 'funnel/emoji-angry' }, + { 'type': 'dead_click', 'visible': true, 'order': 4, 'name': 'Dead Clicks', 'icon': 'funnel/dizzy' }, { 'type': 'memory', 'visible': true, 'order': 5, 'name': 'High Memory', 'icon': 'funnel/sd-card' }, { 'type': 'cpu', 'visible': true, 'order': 6, 'name': 'High CPU', 'icon': 'funnel/cpu' }, { 'type': 'crash', 'visible': true, 'order': 7, 'name': 'Crashes', 'icon': 'funnel/file-earmark-break' },