diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js index 77d72f013..e26e24da2 100644 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js +++ b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js @@ -33,9 +33,6 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) {
-
- Be proactive by monitoring the metrics you care about the most. -
); diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx index b33a13f62..219f4cc98 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx @@ -17,7 +17,7 @@ function CustomMetricPieChart(props: Props) { const { metric, data = { values: [] }, onClick = () => null } = props; const onClickHandler = (event) => { - if (event) { + if (event && !event.payload.group) { const filters = Array(); let filter = { ...filtersMap[metric.metricOf] } filter.value = [event.payload.name] @@ -91,6 +91,8 @@ function CustomMetricPieChart(props: Props) { let x = cx + radius * Math.cos(-midAngle * RADIAN); let y = cy + radius * Math.sin(-midAngle * RADIAN); const percentage = (value / data.values.reduce((a, b) => a + b.sessionCount, 0)) * 100; + let name = data.values[index].name || 'Unidentified'; + name = name.length > 20 ? name.substring(0, 20) + '...' : name; if (percentage<3){ return null; } @@ -105,7 +107,7 @@ function CustomMetricPieChart(props: Props) { dominantBaseline="central" fill='#666' > - {data.values[index].name} - ({value}) + {name || 'Unidentified'} {value} ); }} diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx index 2b85e8839..6a9481077 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx @@ -1,24 +1,26 @@ import React from 'react' import { Table } from '../../common'; import { List } from 'immutable'; -import { FilterKey } from 'Types/filter/filterType'; import { filtersMap } from 'Types/filter/newFilter'; import { NoContent } from 'UI'; +import { tableColumnName } from 'App/constants/filterOptions'; -const cols = [ - { - key: 'name', - title: 'Resource', - toText: name => name || 'Unidentified', - width: '70%', - }, - { - key: 'sessionCount', - title: 'Sessions', - toText: sessions => sessions, - width: '30%', - }, -]; +const getColumns = (metric) => { + return [ + { + key: 'name', + title: tableColumnName[metric.metricOf], + toText: name => name || 'Unidentified', + width: '70%', + }, + { + key: 'sessionCount', + title: 'Sessions', + toText: sessions => sessions, + width: '30%', + }, + ] +} interface Props { metric?: any, @@ -49,7 +51,7 @@ function CustomMetriTable(props: Props) { {isTimeSeries && ( <> - Visualization + Visualization removeSeries(index)} canDelete={metric.series.size > 1} emptyMessage={isTable ? - 'Filter table data by user environment and metadata attributes. Use add step button below to filter.' : + 'Filter data using any event or attribute. Use Add Step button below to do so.' : 'Add user event or filter to define the series by clicking Add Step.' } /> diff --git a/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx b/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx index 96156e6db..542dfce1c 100644 --- a/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx @@ -13,6 +13,8 @@ interface Props { onSelect: (e, item) => void; value: any; icon?: string; + type?: string; + isMultilple?: boolean; } function FilterAutoCompleteLocal(props: Props) { @@ -24,6 +26,8 @@ function FilterAutoCompleteLocal(props: Props) { onAddValue = () => null, value = '', icon = null, + type = "text", + isMultilple = true, } = props; const [showModal, setShowModal] = useState(true) const [query, setQuery] = useState(value); @@ -59,7 +63,7 @@ function FilterAutoCompleteLocal(props: Props) { onFocus={ () => setShowModal(true)} value={ query } autoFocus={ true } - type="text" + type={ type } placeholder={ placeholder } onKeyDown={handleKeyDown} /> @@ -71,7 +75,7 @@ function FilterAutoCompleteLocal(props: Props) { - { !showOrButton &&
or
} + { !showOrButton && isMultilple &&
or
} ); } diff --git a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx index b3bc02305..ba7b8650e 100644 --- a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx +++ b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx @@ -63,7 +63,7 @@ function FilterValue(props: Props) { } const renderValueFiled = (value, valueIndex) => { - const showOrButton = valueIndex === lastIndex; + const showOrButton = valueIndex === lastIndex && filter.type !== FilterType.NUMBER; switch(filter.type) { case FilterType.STRING: return ( @@ -113,17 +113,41 @@ function FilterValue(props: Props) { maxDuration={ durationValues.maxDuration } /> ) - case FilterType.NUMBER: case FilterType.NUMBER_MULTIPLE: return ( - onChange(e, { value: e.target.value }, valueIndex)} + showCloseButton={showCloseButton} + showOrButton={showOrButton} + onAddValue={onAddValue} + onRemoveValue={() => onRemoveValue(valueIndex)} + onSelect={(e, item) => debounceOnSelect(e, item, valueIndex)} + icon={filter.icon} + type="number" /> ) + case FilterType.NUMBER: + return ( + onRemoveValue(valueIndex)} + onSelect={(e, item) => debounceOnSelect(e, item, valueIndex)} + icon={filter.icon} + type="number" + isMultilple={false} + /> + // onChange(e, { value: e.target.value }, valueIndex)} + // /> + ) case FilterType.MULTIPLE: return ( void; onAddValue?: () => void; + isMultilple?: boolean; } function FilterValueDropdown(props: Props) { - const { filter, multiple = false, search = false, options, onChange, value, className = '', showCloseButton = true, showOrButton = true } = props; + const { filter, multiple = false, isMultilple = true, search = false, options, onChange, value, className = '', showCloseButton = true, showOrButton = true } = props; // const options = [] return ( -
- } - /> -
- { showCloseButton &&
} - { showOrButton &&
or
} +
+
+ } + /> +
+ { showCloseButton &&
} + { showOrButton &&
or
} +
+ + { !showOrButton && isMultilple &&
or
}
); } diff --git a/frontend/app/constants/filterOptions.js b/frontend/app/constants/filterOptions.js index 6fa6d0cbf..560ad7912 100644 --- a/frontend/app/constants/filterOptions.js +++ b/frontend/app/constants/filterOptions.js @@ -61,6 +61,15 @@ export const metricTypes = [ { text: 'Table', value: 'table' }, ]; +export const tableColumnName = { + [FilterKey.USERID]: 'User', + [FilterKey.ISSUE]: 'Issue', + [FilterKey.USER_BROWSER]: 'Browser', + [FilterKey.USER_DEVICE]: 'Device', + [FilterKey.USER_COUNTRY]: 'Country', + [FilterKey.LOCATION]: 'URL', +} + export const metricOf = [ { text: 'Session Count', value: 'sessionCount', type: 'timeseries' }, { text: 'Users', value: FilterKey.USERID, type: 'table' }, @@ -71,6 +80,18 @@ export const metricOf = [ { text: 'URL', value: FilterKey.LOCATION, type: 'table' }, ] +export const methodOptions = [ + { text: 'GET', value: 'GET' }, + { text: 'POST', value: 'POST' }, + { text: 'PUT', value: 'PUT' }, + { text: 'DELETE', value: 'DELETE' }, + { text: 'PATCH', value: 'PATCH' }, + { text: 'HEAD', value: 'HEAD' }, + { text: 'OPTIONS', value: 'OPTIONS' }, + { text: 'TRACE', value: 'TRACE' }, + { text: 'CONNECT', value: 'CONNECT' }, +] + export const issueOptions = [ { text: 'Click Rage', value: 'click_rage' }, { text: 'Dead Click', value: 'dead_click' }, @@ -97,4 +118,5 @@ export default { metricTypes, metricOf, issueOptions, + methodOptions, } \ No newline at end of file diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 00799e5d7..4bd05ad2f 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -107,14 +107,15 @@ export const checkFilterValue = (value) => { return Array.isArray(value) ? (value.length === 0 ? [""] : value) : [value]; } -export const filterMap = ({category, value, key, operator, sourceOperator, source, custom, isEvent }) => ({ +export const filterMap = ({category, value, key, operator, sourceOperator, source, custom, isEvent, subFilters }) => ({ value: checkValues(key, value), custom, type: category === FilterCategory.METADATA ? FilterKey.METADATA : key, operator, source: category === FilterCategory.METADATA ? key : source, sourceOperator, - isEvent + isEvent, + filters: subFilters ? subFilters.map(filterMap) : [], }); const reduceThenFetchResource = actionCreator => (...args) => (dispatch, getState) => { diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 6ad2c4c22..7ce792b78 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -33,10 +33,10 @@ export const filtersMap = { [FilterKey.USERANONYMOUSID]: { key: FilterKey.USERANONYMOUSID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User AnonymousId', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/userid' }, // PERFORMANCE - [FilterKey.FETCH]: { key: FilterKey.FETCH, type: FilterType.SUB_FILTERS, category: FilterCategory.PERFORMANCE, label: 'Fetch Request', subFilters: [ + [FilterKey.FETCH]: { key: FilterKey.FETCH, type: FilterType.SUB_FILTERS, category: FilterCategory.PERFORMANCE, operator: 'is', label: 'Network Request', subFilters: [ { 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, category: FilterCategory.PERFORMANCE, label: 'with method', operator: 'is', operatorOptions: filterOptions.stringOperators, 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', 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' },