diff --git a/frontend/app/components/BugFinder/EventFilter/EventFilter.js b/frontend/app/components/BugFinder/EventFilter/EventFilter.js index 21467c382..5bc1d1b32 100644 --- a/frontend/app/components/BugFinder/EventFilter/EventFilter.js +++ b/frontend/app/components/BugFinder/EventFilter/EventFilter.js @@ -141,21 +141,21 @@ export default class EventFilter extends React.PureComponent { { hasFilters &&
-
Operator
- -
+
Operator
+ +
{ events.size > 0 && <> diff --git a/frontend/app/components/Dashboard/Dashboard.js b/frontend/app/components/Dashboard/Dashboard.js index 6af004136..81d7a1de9 100644 --- a/frontend/app/components/Dashboard/Dashboard.js +++ b/frontend/app/components/Dashboard/Dashboard.js @@ -39,6 +39,7 @@ import SideMenuSection from './SideMenu/SideMenuSection'; import styles from './dashboard.css'; import WidgetSection from 'Shared/WidgetSection/WidgetSection'; import OverviewWidgets from './Widgets/OverviewWidgets/OverviewWidgets'; +import CustomMetricsWidgets from './Widgets/CustomMetricsWidgets/CustomMetricsWidgets'; import WidgetHolder from './WidgetHolder/WidgetHolder'; import MetricsFilters from 'Shared/MetricsFilters/MetricsFilters'; import { withRouter } from 'react-router'; @@ -47,6 +48,7 @@ const OVERVIEW = 'overview'; const PERFORMANCE = 'performance'; const ERRORS_N_CRASHES = 'errors_n_crashes'; const RESOURCES = 'resources'; +const CUSTOM_METRICS = 'custom_metrics'; const menuList = [ { @@ -201,6 +203,12 @@ export default class Dashboard extends React.PureComponent { + +
+ +
+
+
{ dashboardAppearance.impactedSessionsByJsErrors && } diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx new file mode 100644 index 000000000..9de11f57e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { Loader, NoContent } from 'UI'; +import { widgetHOC, Styles } from '../../common'; +import { ResponsiveContainer, AreaChart, XAxis, YAxis, CartesianGrid, Area, Tooltip } from 'recharts'; +import { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period'; +import CustomMetricWidgetHoc from '../../common/CustomMetricWidgetHoc'; + +const customParams = rangeName => { + const params = { density: 70 } + + if (rangeName === LAST_24_HOURS) params.density = 70 + if (rangeName === LAST_30_MINUTES) params.density = 70 + if (rangeName === YESTERDAY) params.density = 70 + if (rangeName === LAST_7_DAYS) params.density = 70 + + return params +} + +interface Period { + rangeName: string; +} + +interface Props { + widget: any; + loading?: boolean; + data?: any; + showSync?: boolean; + compare?: boolean; + period?: Period; +} +function CustomMetricWidget(props: Props) { + const { widget, loading = false, data = { chart: []}, showSync, compare, period = { rangeName: ''} } = props; + + const colors = compare ? Styles.compareColors : Styles.colors; + const params = customParams(period.rangeName) + const gradientDef = Styles.gradientDef(); + return ( + + + + + {gradientDef} + + + + + + + + + + ); +} + +export default CustomMetricWidgetHoc(CustomMetricWidget); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/index.ts new file mode 100644 index 000000000..4a6d9b653 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricWidget'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx new file mode 100644 index 000000000..3ea473f61 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricsWidgets.tsx @@ -0,0 +1,29 @@ +import React, { useEffect } from 'react'; +import { connect } from 'react-redux'; +import { fetchList } from 'Duck/customMetrics'; +import { list } from 'App/components/BugFinder/CustomFilters/filterModal.css'; +import CustomMetricWidget from './CustomMetricWidget'; + +interface Props { + fetchList: Function; + list: any; +} +function CustomMetricsWidgets(props: Props) { + const { list } = props; + + useEffect(() => { + props.fetchList() + }, []) + + return ( + <> + {list.map((item: any) => ( + + ))} + + ); +} + +export default connect(state => ({ + list: state.getIn(['customMetrics', 'list']), +}), { fetchList })(CustomMetricsWidgets); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/index.ts new file mode 100644 index 000000000..54d9a4192 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricsWidgets'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.css b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.css new file mode 100644 index 000000000..1d1ef3ee4 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.css @@ -0,0 +1,6 @@ +.wrapper { + background-color: white; + /* border: solid thin $gray-medium; */ + border-radius: 3px; + padding: 10px; +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.tsx b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.tsx new file mode 100644 index 000000000..ba4a2726a --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/CustomMetricWidgetHoc.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import stl from './CustomMetricWidgetHoc.css'; +import { Icon } from 'UI'; + +interface Props { +} +const CustomMetricWidgetHoc = ({ ...rest }: Props) => BaseComponent => { + + console.log('CustomMetricWidgetHoc', rest); + return ( +
+
+
Widget Name
+
+
+ +
+
+
+ {/* */} +
+ ); +} + +export default CustomMetricWidgetHoc; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/index.ts b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/index.ts new file mode 100644 index 000000000..0be8a5be5 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/common/CustomMetricWidgetHoc/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricWidgetHoc'; \ No newline at end of file diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx index 20337cbc7..243a51ced 100644 --- a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx +++ b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Form, SegmentSelection, Button } from 'UI'; +import { Form, SegmentSelection, Button, IconButton } from 'UI'; import FilterSeries from '../FilterSeries'; import { connect } from 'react-redux'; import { edit as editMetric, save } from 'Duck/customMetrics'; @@ -68,13 +68,13 @@ function CustomMetricForm(props: Props) {
- Timeseries - of -
+ Timeseries + of +
- + {metric.series && metric.series.size > 0 && metric.series.map((series: any, index: number) => (
- +
+ +
diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx index ae7102202..c11682513 100644 --- a/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx +++ b/frontend/app/components/shared/CustomMetrics/CustomMetrics.tsx @@ -1,38 +1,29 @@ -import React from 'react'; +import React, { useState } from 'react'; import { IconButton, SlideModal } from 'UI' import CustomMetricForm from './CustomMetricForm'; interface Props {} function CustomMetrics(props: Props) { + const [showModal, setShowModal] = useState(true); + return ( -
- +
+ setShowModal(true)} /> { 'Custom Metric' } - {/* toggleForm({}, true) } - /> */}
} - isDisplayed={ true } - // onClose={ () => { - // toggleForm({}, false); - // setShowAlerts(false); - // } } + isDisplayed={ showModal } + onClose={ () => setShowModal(false)} // size="medium" - content={ -
+ content={ showModal && ( +
- } + )} />
); diff --git a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx index f29301644..69a1637d5 100644 --- a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import FilterList from 'Shared/Filters/FilterList'; import { edit, updateSeries } from 'Duck/customMetrics'; import { connect } from 'react-redux'; -import { IconButton, Button, Icon } from 'UI'; +import { IconButton, Button, Icon, SegmentSelection } from 'UI'; import FilterSelection from '../../Filters/FilterSelection'; interface Props { @@ -14,6 +14,7 @@ interface Props { } function FilterSeries(props: Props) { + const [expanded, setExpanded] = useState(false) const { series, seriesIndex } = props; const onAddFilter = (filter) => { @@ -46,6 +47,16 @@ function FilterSeries(props: Props) { }); } + const onChangeEventsOrder = (e, { name, value }) => { + props.updateSeries(seriesIndex, { + ...series.toData(), + filter: { + ...series.filter, + eventsOrder: value, + } + }); + } + const onRemoveFilter = (filterIndex) => { const newFilters = series.filter.filters.filter((_filter, i) => { return i !== filterIndex; @@ -62,35 +73,43 @@ function FilterSeries(props: Props) { return (
-
- { series.name } -
- +
+
{ series.name }
+ +
+
+ +
+ +
setExpanded(!expanded)} className="ml-3"> + +
+
-
- { series.filter.filters.size > 0 ? ( - - ): ( -
Add user event or filter to build the series.
- )} -
-
- - {/* */} - - -
+ { expanded && ( + <> +
+ { series.filter.filters.size > 0 ? ( + + ): ( +
Add user event or filter to build the series.
+ )} +
+
+ + + +
+ + )}
); } diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.css b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.css index ba7359239..e2ce40ac0 100644 --- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.css +++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.css @@ -16,20 +16,36 @@ & .right { height: 28px; display: flex; - align-items: center; - padding: 0 5px; + align-items: stretch; + padding: 0; background-color: $gray-lightest; - border-left: solid thin $gray-light !important; border-top-right-radius: 3px; border-bottom-right-radius: 3px; - cursor: pointer; + + & div { + /* background-color: red; */ + border-left: solid thin $gray-light !important; + width: 28px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + &:last-child { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } + &:hover { + background-color: $gray-light; + } + } } } .menu { border-radius: 0 0 3px 3px; - box-shadow: 0 2px 10px 0 $gray-light; - padding: 20px; + border: solid thin $gray-light !important; + /* box-shadow: 0 2px 10px 0 $gray-light; */ + /* padding: 20px; */ background-color: white; max-height: 350px; overflow-y: auto; @@ -43,7 +59,7 @@ .filterItem { display: flex; align-items: center; - padding: 8px; + padding: 8px 10px; cursor: pointer; border-radius: 3px; transition: all 0.4s; diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx index 068b255f7..fd215c11e 100644 --- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx @@ -12,6 +12,7 @@ const hiddenStyle = { interface Props { showOrButton?: boolean; + showCloseButton?: boolean; onRemoveValue?: () => void; onAddValue?: () => void; endpoint?: string; @@ -25,6 +26,7 @@ interface Props { function FilterAutoComplete(props: Props) { const { + showCloseButton = false, placeholder = 'Type to search', method = 'GET', showOrButton = false, @@ -94,7 +96,7 @@ function FilterAutoComplete(props: Props) { } return ( -
+
- { !showOrButton && } - { showOrButton && or} + { showCloseButton &&
} + { showOrButton &&
or
}
+ { !showOrButton &&
or
} + {/* */} { showModal && (options.length > 0 || loading) && diff --git a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx index 66aa08ff3..c1e35f250 100644 --- a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx +++ b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx @@ -44,26 +44,29 @@ function FitlerItem(props: Props) { } return ( -
+
{filterIndex+1}
- +
{filter.value && filter.value.map((value, valueIndex) => ( 1} showOrButton={valueIndex === filter.value.length - 1} - key={valueIndex} + // filter={filter} + // key={valueIndex} value={value} + key={filter.key} index={valueIndex} onAddValue={onAddValue} onRemoveValue={() => onRemoveValue(valueIndex)} - onSelect={(e, item) => onSelect(e, valueIndex, item)} + onSelect={(e, item) => onSelect(e, item, valueIndex)} /> ))}
-
+
+
+
EVENTS
+
+
Events Order
+ null } + // value={{ value: series.filter.eventsOrder }} + value={{ value: 'and' }} + list={ [ + { name: 'AND', value: 'and' }, + { name: 'OR', value: 'or' }, + { name: 'THEN', value: 'then' }, + ]} + /> +
+
{filters.map((filter, filterIndex) => ( onRemoveFilter(filterIndex) } /> ))} + + {/*
Filters
+ {filters.filter(f => !f.isEvent).map((filter, filterIndex) => ( + props.onUpdateFilter(filterIndex, filter)} + onRemoveFilter={() => onRemoveFilter(filterIndex) } + /> + ))} */}
); } diff --git a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx index 4a16b6511..b2299b2e2 100644 --- a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx +++ b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx @@ -9,14 +9,14 @@ interface Props { function FilterModal(props: Props) { const { filters, onFilterClick = () => null } = props; return ( -
+
{filters && Object.keys(filters).map((key) => (
{key}
{filters[key].map((filter: any) => ( -
onFilterClick(filter)}> +
onFilterClick(filter)}> {filter.label}
diff --git a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx index e899f6566..17cc7d288 100644 --- a/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx +++ b/frontend/app/components/shared/Filters/FilterSelection/FilterSelection.tsx @@ -10,19 +10,20 @@ interface Props { } function FilterSelection(props: Props) { const { filter, onFilterClick, children } = props; - const [showModal, setShowModal] = useState(false) + const [showModal, setShowModal] = useState(false); + return ( -
+
setTimeout(function() { setShowModal(false) - }, 20)} + }, 50)} > { children ? React.cloneElement(children, { onClick: () => setShowModal(true)}) : (
setShowModal(true)} > {filter.label} diff --git a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx index 65e123ba4..3483b3340 100644 --- a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx +++ b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx @@ -4,25 +4,29 @@ import FilterAutoComplete from '../FilterAutoComplete'; interface Props { index: number; value: any; // event/filter + // type: string; + key: string; onRemoveValue?: () => void; onAddValue?: () => void; + showCloseButton: boolean; showOrButton: boolean; onSelect: (e, item) => void; } function FilterValue(props: Props) { - const { index, value, showOrButton, onRemoveValue , onAddValue } = props; + const { index, value, key, showOrButton, showCloseButton, onRemoveValue , onAddValue } = props; return ( ); diff --git a/frontend/app/components/ui/SegmentSelection/segmentSelection.css b/frontend/app/components/ui/SegmentSelection/segmentSelection.css index a74d7d77b..e33ec3b73 100644 --- a/frontend/app/components/ui/SegmentSelection/segmentSelection.css +++ b/frontend/app/components/ui/SegmentSelection/segmentSelection.css @@ -3,16 +3,16 @@ align-items: center; justify-content: space-around; border: solid thin $gray-light; - border-radius: 5px; + border-radius: 3px; overflow: hidden; & .item { color: $gray-medium; font-weight: medium; padding: 10px; - flex: 1; + /* flex: 1; */ text-align: center; - border-right: solid thin $gray-light; + border-right: solid thin $teal; cursor: pointer; background-color: $gray-lightest; display: flex; @@ -64,6 +64,6 @@ } .extraSmall .item { - padding: 2px 4px; + padding: 0 4px; font-size: 12px; } \ No newline at end of file diff --git a/frontend/app/duck/customMetrics.js b/frontend/app/duck/customMetrics.js index 0909ff3b1..f6a7b0b88 100644 --- a/frontend/app/duck/customMetrics.js +++ b/frontend/app/duck/customMetrics.js @@ -48,6 +48,7 @@ function reducer(state = initialState, action = {}) { case EDIT: return state.mergeIn([ 'instance' ], CustomMetric(action.instance)); case UPDATE_SERIES: + console.log('update series', action.series); return state.setIn(['instance', 'series', action.index], FilterSeries(action.series)); case success(SAVE): return state.set([ 'instance' ], CustomMetric(action.data)); @@ -55,11 +56,7 @@ function reducer(state = initialState, action = {}) { return state.set("instance", ErrorInfo(action.data)); case success(FETCH_LIST): const { data } = action; - return state - .set("totalCount", data ? data.total : 0) - .set("list", List(data && data.errors).map(CustomMetric) - .filter(e => e.parentErrorId == null) - .map(e => e.update("chart", chartWrapper))); + return state.set("list", List(data.map(CustomMetric))); } return state; } @@ -95,11 +92,9 @@ export function save(instance) { }; } -export function fetchList(params = {}, clear = false) { +export function fetchList() { return { types: array(FETCH_LIST), - call: client => client.post('/errors/search', params), - clear, - params: cleanParams(params), + call: client => client.get(`/${name}s`), }; } \ No newline at end of file diff --git a/frontend/app/duck/filters.js b/frontend/app/duck/filters.js index 290a4f1cb..9122fb9c2 100644 --- a/frontend/app/duck/filters.js +++ b/frontend/app/duck/filters.js @@ -14,11 +14,9 @@ import { newFiltersList } from 'Types/filter' import NewFilter, { filtersMap } from 'Types/filter/newFilter'; const filterOptions = {} -// newFiltersList.forEach(filter => { -// filterOptions[filter.category] = filter -// }) Object.keys(filtersMap).forEach(key => { + // const filter = NewFilter(filtersMap[key]); const filter = filtersMap[key]; if (filterOptions.hasOwnProperty(filter.category)) { filterOptions[filter.category].push(filter); diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index 077ec9904..8915fd341 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -107,8 +107,9 @@ } .form-group { - margin-bottom: 20px; + margin-bottom: 25px; & label { + display: inline-block; margin-bottom: 5px; } } \ No newline at end of file diff --git a/frontend/app/types/customMetric.js b/frontend/app/types/customMetric.js index 32c4386b6..1bfaebbd6 100644 --- a/frontend/app/types/customMetric.js +++ b/frontend/app/types/customMetric.js @@ -17,6 +17,7 @@ export const FilterSeries = Record({ methods: { toData() { const js = this.toJS(); + delete js.key; // js.filter = js.filter.toData(); return js; }, @@ -42,6 +43,7 @@ export default Record({ toData() { const js = this.toJS(); + js.series = js.series.map(series => { series.filter.filters = series.filter.filters.map(filter => { delete filter.operatorOptions @@ -51,6 +53,8 @@ export default Record({ return series; }); + delete js.key; + return js; }, }, diff --git a/frontend/app/types/filter/filter.js b/frontend/app/types/filter/filter.js index 41c3421f8..e3663e860 100644 --- a/frontend/app/types/filter/filter.js +++ b/frontend/app/types/filter/filter.js @@ -46,6 +46,7 @@ export default Record({ suspicious: undefined, consoleLevel: undefined, strict: false, + eventsOrder: 'and', }, { idKey: 'searchId', methods: { @@ -53,10 +54,12 @@ export default Record({ const js = this.toJS(); js.filters = js.filters.map(filter => { delete filter.operatorOptions + delete filter._key return filter; }); delete js.createdAt; + delete js.key; return js; } }, diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 2c1a49f48..091b16277 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -219,41 +219,41 @@ export const booleanOptions = [ ] export const filtersMap = { - [TYPES.CLICK]: { category: 'interactions', label: 'Click', operator: 'on', operatorOptions: targetFilterOptions, icon: 'filters/click' }, - [TYPES.INPUT]: { category: 'interactions', label: 'Input', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.LOCATION]: { category: 'interactions', label: 'Page', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.CLICK]: { key: TYPES.CLICK, type: 'multiple', category: 'interactions', label: 'Click', operator: 'on', operatorOptions: targetFilterOptions, icon: 'filters/click', isEvent: true }, + [TYPES.INPUT]: { key: TYPES.INPUT, type: 'multiple', category: 'interactions', label: 'Input', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click', isEvent: true }, + [TYPES.LOCATION]: { key: TYPES.LOCATION, type: 'multiple', category: 'interactions', label: 'Page', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click', isEvent: true }, - [TYPES.USER_OS]: { category: 'gear', label: 'User OS', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.USER_BROWSER]: { category: 'gear', label: 'User Browser', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.USER_DEVICE]: { category: 'gear', label: 'User Device', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.PLATFORM]: { category: 'gear', label: 'Platform', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.REVID]: { category: 'gear', label: 'RevId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USER_OS]: { key: TYPES.USER_OS, type: 'multiple', category: 'gear', label: 'User OS', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USER_BROWSER]: { key: TYPES.USER_BROWSER, type: 'multiple', category: 'gear', label: 'User Browser', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USER_DEVICE]: { key: TYPES.USER_DEVICE, type: 'multiple', category: 'gear', label: 'User Device', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.PLATFORM]: { key: TYPES.PLATFORM, type: 'multiple', category: 'gear', label: 'Platform', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.REVID]: { key: TYPES.REVID, type: 'multiple', category: 'gear', label: 'RevId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.REFERRER]: { category: 'recording_attributes', label: 'Referrer', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.DURATION]: { category: 'recording_attributes', label: 'Duration', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.USER_COUNTRY]: { category: 'recording_attributes', label: 'User Country', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.REFERRER]: { key: TYPES.REFERRER, type: 'multiple', category: 'recording_attributes', label: 'Referrer', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.DURATION]: { key: TYPES.DURATION, type: 'number', category: 'recording_attributes', label: 'Duration', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USER_COUNTRY]: { key: TYPES.USER_COUNTRY, type: 'multiple', category: 'recording_attributes', label: 'User Country', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.CONSOLE]: { category: 'javascript', label: 'Console', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.ERROR]: { category: 'javascript', label: 'Error', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.FETCH]: { category: 'javascript', label: 'Fetch', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.GRAPHQL]: { category: 'javascript', label: 'GraphQL', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.STATEACTION]: { category: 'javascript', label: 'StateAction', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.CONSOLE]: { key: TYPES.CONSOLE, type: 'multiple', category: 'javascript', label: 'Console', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.ERROR]: { key: TYPES.ERROR, type: 'multiple', category: 'javascript', label: 'Error', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.FETCH]: { key: TYPES.FETCH, type: 'multiple', category: 'javascript', label: 'Fetch', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.GRAPHQL]: { key: TYPES.GRAPHQL, type: 'multiple', category: 'javascript', label: 'GraphQL', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.STATEACTION]: { key: TYPES.STATEACTION, type: 'multiple', category: 'javascript', label: 'StateAction', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.USERID]: { category: 'user', label: 'UserId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.USERANONYMOUSID]: { category: 'user', label: 'UserAnonymousId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USERID]: { key: TYPES.USERID, type: 'multiple', category: 'user', label: 'UserId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.USERANONYMOUSID]: { key: TYPES.USERANONYMOUSID, type: 'multiple', category: 'user', label: 'UserAnonymousId', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.DOM_COMPLETE]: { category: 'new', label: 'DOM Complete', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.LARGEST_CONTENTFUL_PAINT_TIME]: { category: 'new', label: 'Largest Contentful Paint Time', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.TIME_BETWEEN_EVENTS]: { category: 'new', label: 'Time Between Events', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.TTFB]: { category: 'new', label: 'TTFB', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.AVG_CPU_LOAD]: { category: 'new', label: 'Avg CPU Load', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.AVG_MEMORY_USAGE]: { category: 'new', label: 'Avg Memory Usage', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, - [TYPES.SLOW_SESSION]: { category: 'new', label: 'Slow Session', operator: 'true', operatorOptions: [{ key: 'true', text: 'true', value: 'true' }], icon: 'filters/click' }, - [TYPES.MISSING_RESOURCE]: { category: 'new', label: 'Missing Resource', operator: 'true', operatorOptions: [{ key: 'inImages', text: 'in images', value: 'true' }], icon: 'filters/click' }, - [TYPES.CLICK_RAGE]: { category: 'new', label: 'Click Rage', operator: 'onAnything', operatorOptions: [{ key: 'onAnything', text: 'on anything', value: 'true' }], icon: 'filters/click' }, - // [TYPES.URL]: { category: 'interactions', label: 'URL', operator: 'is', operatorOptions: stringFilterOptions }, - // [TYPES.CUSTOM]: { category: 'interactions', label: 'Custom', operator: 'is', operatorOptions: stringFilterOptions }, - // [TYPES.METADATA]: { category: 'interactions', label: 'Metadata', operator: 'is', operatorOptions: stringFilterOptions }, + [TYPES.DOM_COMPLETE]: { key: TYPES.DOM_COMPLETE, type: 'multiple', category: 'new', label: 'DOM Complete', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.LARGEST_CONTENTFUL_PAINT_TIME]: { key: TYPES.LARGEST_CONTENTFUL_PAINT_TIME, type: 'number', category: 'new', label: 'Largest Contentful Paint Time', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.TIME_BETWEEN_EVENTS]: { key: TYPES.TIME_BETWEEN_EVENTS, type: 'number', category: 'new', label: 'Time Between Events', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.TTFB]: { key: TYPES.TTFB, type: 'time', category: 'new', label: 'TTFB', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.AVG_CPU_LOAD]: { key: TYPES.AVG_CPU_LOAD, type: 'number', category: 'new', label: 'Avg CPU Load', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.AVG_MEMORY_USAGE]: { key: TYPES.AVG_MEMORY_USAGE, type: 'number', category: 'new', label: 'Avg Memory Usage', operator: 'is', operatorOptions: stringFilterOptions, icon: 'filters/click' }, + [TYPES.SLOW_SESSION]: { key: TYPES.SLOW_SESSION, type: 'boolean', category: 'new', label: 'Slow Session', operator: 'true', operatorOptions: [{ key: 'true', text: 'true', value: 'true' }], icon: 'filters/click' }, + [TYPES.MISSING_RESOURCE]: { key: TYPES.MISSING_RESOURCE, type: 'boolean', category: 'new', label: 'Missing Resource', operator: 'true', operatorOptions: [{ key: 'inImages', text: 'in images', value: 'true' }], icon: 'filters/click' }, + [TYPES.CLICK_RAGE]: { key: TYPES.CLICK_RAGE, type: 'boolean', category: 'new', label: 'Click Rage', operator: 'onAnything', operatorOptions: [{ key: 'onAnything', text: 'on anything', value: 'true' }], icon: 'filters/click' }, + // [TYPES.URL]: { / [TYPES,TYPES. category: 'interactions', label: 'URL', operator: 'is', operatorOptions: stringFilterOptions }, + // [TYPES.CUSTOM]: { / [TYPES,TYPES. category: 'interactions', label: 'Custom', operator: 'is', operatorOptions: stringFilterOptions }, + // [TYPES.METADATA]: { / [TYPES,TYPES. category: 'interactions', label: 'Metadata', operator: 'is', operatorOptions: stringFilterOptions }, } export default Record({ @@ -263,6 +263,7 @@ export default Record({ icon: '', type: '', value: [""], + category: '', custom: '', // target: Target(), @@ -274,10 +275,13 @@ export default Record({ operator: 'is', operatorOptions: [], + isEvent: false, + index: 0, }, { keyKey: "_key", fromJS: ({ ...filter }) => ({ ...filter, + key: filter.type, type: filter.type, // camelCased(filter.type.toLowerCase()), // key: filter.type === METADATA ? filter.label : filter.key || filter.type, // || camelCased(filter.type.toLowerCase()), // label: getLabel(filter), @@ -299,10 +303,4 @@ export default Record({ // operators: filterMap[key].operatorOptions, // value: [""] // } -// } - -// export const newFiltersList = [ -// NewFilterType(TYPES.CLICK, 'Click', 'filters/click', true), -// NewFilterType(TYPES.CLICK, 'Input', 'filters/click', true), -// NewFilterType(TYPES.CONSOLE, 'Console', 'filters/click', true), -// ]; \ No newline at end of file +// } \ No newline at end of file