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 {