From 8dd75781690d3cc00ad614c6a6a54b55b05ff793 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 10 Feb 2022 22:55:03 +0100 Subject: [PATCH] feat(ui) - assist filter auto focus --- .../LiveSessionList/LiveSessionList.tsx | 2 +- .../app/components/Dashboard/Dashboard.js | 9 +++-- .../Dashboard/SideMenu/SideMenuSection.js | 8 ++++ .../shared/CustomMetrics/CustomMetrics.tsx | 31 ++------------- .../CustomMetricsModal/CustomMetricsModal.tsx | 38 +++++++++++++++++++ .../CustomMetricsModal/index.tsx | 1 + .../FilterAutoComplete/FilterAutoComplete.tsx | 28 +++++++++----- .../SessionCopyLink/SessionCopyLink.tsx | 13 +++++++ .../SharePopup/SessionCopyLink/index.ts | 1 + .../shared/SharePopup/SharePopup.js | 15 ++++++-- frontend/app/duck/customField.js | 1 - frontend/app/duck/liveSearch.js | 4 +- frontend/app/duck/search.js | 6 +-- frontend/app/types/filter/newFilter.js | 19 ++++++---- 14 files changed, 117 insertions(+), 59 deletions(-) create mode 100644 frontend/app/components/shared/CustomMetrics/CustomMetricsModal/CustomMetricsModal.tsx create mode 100644 frontend/app/components/shared/CustomMetrics/CustomMetricsModal/index.tsx create mode 100644 frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx create mode 100644 frontend/app/components/shared/SharePopup/SessionCopyLink/index.ts diff --git a/frontend/app/components/BugFinder/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/BugFinder/LiveSessionList/LiveSessionList.tsx index 13617a8c2..2c13d8d86 100644 --- a/frontend/app/components/BugFinder/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/BugFinder/LiveSessionList/LiveSessionList.tsx @@ -84,7 +84,7 @@ function LiveSessionList(props: Props) { title={"No live sessions."} subtext={ - See how to {'enable Assist'} if you haven't yet done so. + See how to {'enable Assist'} and ensure you're using tracker-assist v3.5.0 or higher. } image={ {comparing && ( -
+
Custom Metrics are not supported for comparison.
)} - + {/* */}
} > @@ -297,6 +298,8 @@ export default class Dashboard extends React.PureComponent { + + ); } diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js index 977989559..77d72f013 100644 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js +++ b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js @@ -5,6 +5,7 @@ import stl from './sideMenuSection.css'; import { connect } from 'react-redux'; import { NavLink } from 'react-router-dom'; import { withSiteId } from 'App/routes'; +import CustomMetrics from 'Shared/CustomMetrics'; function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) { return ( @@ -29,6 +30,13 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) { onClick={() => setShowAlerts(true)} /> +
+
+ +
+ Be proactive by monitoring the metrics you care about the most. +
+
); } diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx index 9393099e6..aedd4a097 100644 --- a/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx +++ b/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx @@ -1,42 +1,17 @@ -import React, { useState } from 'react'; -import { IconButton, SlideModal } from 'UI'; -import CustomMetricForm from './CustomMetricForm'; +import React from 'react'; +import { IconButton } from 'UI'; import { connect } from 'react-redux'; import { edit, init } from 'Duck/customMetrics'; interface Props { - metric: any; - edit: (metric) => void; - instance: any; init: (instance?, setDefault?) => void; } function CustomMetrics(props: Props) { - const { metric } = props; - return (
props.init()} /> - - - { metric && metric.exists() ? 'Update Custom Metric' : 'Create Custom Metric' } -
- } - isDisplayed={ !!metric } - onClose={ () => props.init(null, true)} - content={ (!!metric) && ( -
- props.init(null, true)} /> -
- )} - />
); } -export default connect(state => ({ - metric: state.getIn(['customMetrics', 'instance']), - alertInstance: state.getIn(['alerts', 'instance']), - showModal: state.getIn(['customMetrics', 'showModal']), -}), { edit, init })(CustomMetrics); \ No newline at end of file +export default connect(null, { edit, init })(CustomMetrics); \ No newline at end of file diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/CustomMetricsModal.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/CustomMetricsModal.tsx new file mode 100644 index 000000000..9783ceca0 --- /dev/null +++ b/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/CustomMetricsModal.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { IconButton, SlideModal } from 'UI'; +import CustomMetricForm from '../CustomMetricForm'; +import { connect } from 'react-redux' +import { init } from 'Duck/customMetrics'; + +interface Props { + metric: any; + init: (instance?, setDefault?) => void; +} +function CustomMetricsModal(props: Props) { + const { metric } = props; + return ( + <> + + { metric && metric.exists() ? 'Update Custom Metric' : 'Create Custom Metric' } + + } + isDisplayed={ !!metric } + onClose={ () => props.init(null, true)} + content={ (!!metric) && ( +
+ props.init(null, true)} /> +
+ )} + /> + + ) +} + + +export default connect(state => ({ + metric: state.getIn(['customMetrics', 'instance']), + alertInstance: state.getIn(['alerts', 'instance']), + showModal: state.getIn(['customMetrics', 'showModal']), + }), { init })(CustomMetricsModal); \ No newline at end of file diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/index.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/index.tsx new file mode 100644 index 000000000..251375d3b --- /dev/null +++ b/frontend/app/components/shared/CustomMetrics/CustomMetricsModal/index.tsx @@ -0,0 +1 @@ +export { default } from './CustomMetricsModal'; \ No newline at end of file diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx index b6c1c4608..dd82cb1b7 100644 --- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx @@ -39,7 +39,7 @@ function FilterAutoComplete(props: Props) { value = '', icon = null, } = props; - const [showModal, setShowModal] = useState(true) + const [showModal, setShowModal] = useState(false) const [loading, setLoading] = useState(false) const [options, setOptions] = useState([]); const [query, setQuery] = useState(value); @@ -64,15 +64,23 @@ function FilterAutoComplete(props: Props) { const onInputChange = ({ target: { value } }) => { setQuery(value); - } - - useEffect(() => { - if (query === '' || query === ' ') { - return + if (!showModal) { + setShowModal(true); } - debouncedRequestValues(query) - }, [query]) + if (value === '' || value === ' ') { + return + } + debouncedRequestValues(value); + } + + // useEffect(() => { + // if (query === '' || query === ' ') { + // return + // } + + // debouncedRequestValues(query) + // }, [query]) useEffect(() => { setQuery(value); @@ -106,7 +114,7 @@ function FilterAutoComplete(props: Props) { name="query" onChange={ onInputChange } onBlur={ onBlur } - onFocus={ () => setShowModal(true)} + // onFocus={ () => setShowModal(true)} value={ query } autoFocus={ true } type="text" @@ -130,7 +138,7 @@ function FilterAutoComplete(props: Props) { {/* */} - { showModal && (options.length > 0 || loading) && + { showModal &&
{ headerText && headerText } diff --git a/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx b/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx new file mode 100644 index 000000000..61deb3eff --- /dev/null +++ b/frontend/app/components/shared/SharePopup/SessionCopyLink/SessionCopyLink.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { IconButton } from 'UI' + +function SessionCopyLink() { + return ( +
+ +
Copied to Clipboard
+
+ ) +} + +export default SessionCopyLink \ No newline at end of file diff --git a/frontend/app/components/shared/SharePopup/SessionCopyLink/index.ts b/frontend/app/components/shared/SharePopup/SessionCopyLink/index.ts new file mode 100644 index 000000000..c7c88f6de --- /dev/null +++ b/frontend/app/components/shared/SharePopup/SessionCopyLink/index.ts @@ -0,0 +1 @@ + export { default } from './SessionCopyLink'; \ No newline at end of file diff --git a/frontend/app/components/shared/SharePopup/SharePopup.js b/frontend/app/components/shared/SharePopup/SharePopup.js index a1655e6e1..482b04e17 100644 --- a/frontend/app/components/shared/SharePopup/SharePopup.js +++ b/frontend/app/components/shared/SharePopup/SharePopup.js @@ -5,6 +5,7 @@ import { Popup, Dropdown, Icon, IconButton } from 'UI'; import { pause } from 'Player'; import styles from './sharePopup.css'; import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton'; +import SessionCopyLink from './SessionCopyLink'; @connect(state => ({ channels: state.getIn([ 'slack', 'list' ]), @@ -62,9 +63,14 @@ export default class SharePopup extends React.PureComponent {
{ 'Comment' }
{ options.length === 0 ? -
- -
+ <> +
+ +
+
+ +
+ :
@@ -97,7 +103,10 @@ export default class SharePopup extends React.PureComponent { { loading ? 'Sharing...' : 'Share' }
+ +
+ } diff --git a/frontend/app/duck/customField.js b/frontend/app/duck/customField.js index 53f98ec7f..3edc557da 100644 --- a/frontend/app/duck/customField.js +++ b/frontend/app/duck/customField.js @@ -36,7 +36,6 @@ const initialState = Map({ const reducer = (state = initialState, action = {}) => { switch(action.type) { case FETCH_SUCCESS: - console.log('FETCH_SUCCESS', action.data); action.data.forEach(item => { addElementToFiltersMap(FilterCategory.METADATA, item.key); }); diff --git a/frontend/app/duck/liveSearch.js b/frontend/app/duck/liveSearch.js index c0de6a189..7b24ea6b5 100644 --- a/frontend/app/duck/liveSearch.js +++ b/frontend/app/duck/liveSearch.js @@ -5,7 +5,7 @@ import { mergeReducers } from './funcTools/tools'; import Filter from 'Types/filter'; import SavedFilter from 'Types/filter/savedFilter'; import { fetchList as fetchSessionList } from './sessions'; -import { filtersMap } from 'Types/filter/newFilter'; +import { liveFiltersMap } from 'Types/filter/newFilter'; import { filterMap, checkFilterValue, hasFilterApplied } from './search'; const name = "liveSearch"; @@ -86,7 +86,7 @@ export const addFilter = (filter) => (dispatch, getState) => { } export const addFilterByKeyAndValue = (key, value) => (dispatch, getState) => { - let defaultFilter = filtersMap[key]; + let defaultFilter = liveFiltersMap[key]; defaultFilter.value = value; dispatch(addFilter(defaultFilter)); } diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 1b6e84a3f..6cec66a27 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -8,7 +8,7 @@ import { errors as errorsRoute, isRoute } from "App/routes"; import { fetchList as fetchSessionList } from './sessions'; import { fetchList as fetchErrorsList } from './errors'; import { FilterCategory, FilterKey } from '../types/filter/filterType'; -import { filtersMap, generateFilterOptions, generateLiveFilterOptions } from 'Types/filter/newFilter'; +import { filtersMap, liveFiltersMap, generateFilterOptions, generateLiveFilterOptions } from 'Types/filter/newFilter'; const ERRORS_ROUTE = errorsRoute(); @@ -43,7 +43,7 @@ const updateInstance = (state, instance) => state.getIn([ "savedSearch", savedSe const initialState = Map({ filterList: generateFilterOptions(filtersMap), - filterListLive: generateLiveFilterOptions(filtersMap), + filterListLive: generateLiveFilterOptions(liveFiltersMap), list: List(), alertMetricId: null, instance: new Filter({ filters: [] }), @@ -56,7 +56,7 @@ function reducer(state = initialState, action = {}) { switch (action.type) { case REFRESH_FILTER_OPTIONS: return state.set('filterList', generateFilterOptions(filtersMap)) - .set('filterListLive', generateLiveFilterOptions(filtersMap)); + .set('filterListLive', generateLiveFilterOptions(liveFiltersMap)); case EDIT: return state.mergeIn(['instance'], action.instance); case APPLY: diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 6ae761f58..abe17dd24 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -43,7 +43,7 @@ export const filtersMap = { [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.RECORDING_ATTRIBUTES, 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, icon: 'filters/userid', isLive: true }, + [FilterKey.USERID]: { key: FilterKey.USERID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User Id', operator: 'is', operatorOptions: filterOptions.stringOperators, 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 @@ -56,6 +56,10 @@ export const filtersMap = { [FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.baseOperators, icon: 'filters/click', options: ISSUE_OPTIONS }, } +export const liveFiltersMap = { + [FilterKey.USERID]: { key: FilterKey.USERID, type: FilterType.STRING, category: FilterCategory.USER, label: 'User Id', operator: 'contains', operatorOptions: [{ key: 'contains', text: 'contains', value: 'contains' }], icon: 'filters/userid', isLive: true }, +} + /** * Add a new filter to the filter list * @param {*} category @@ -138,17 +142,16 @@ export const generateFilterOptions = (filtersMap) => { return _options; } -export const generateLiveFilterOptions = (filtersMap) => { +export const generateLiveFilterOptions = (map) => { const _options = {}; - Object.keys(filtersMap).filter(i => filtersMap[i].isLive).forEach(key => { - const filter = filtersMap[key]; + + Object.keys(map).filter(i => map[i].isLive).forEach(key => { + const filter = map[key]; filter.operator = 'contains'; - filter.type = FilterType.STRING; + // filter.type = FilterType.STRING; // filter.type = FilterType.AUTOCOMPLETE_LOCAL; // filter.options = countryOptions; - filter.operatorOptions = [ - { key: 'contains', text: 'contains', value: 'contains' }, - ] + // filter.operatorOptions = [{ key: 'contains', text: 'contains', value: 'contains' }] if (_options.hasOwnProperty(filter.category)) { _options[filter.category].push(filter); } else {