diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx index c9287c4ed..ffbbc6b88 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx @@ -19,6 +19,7 @@ function CustomMetriLineChart(props: Props) { margin={Styles.chartMargins} // syncId={ showSync ? "domainsErrors_4xx" : undefined } onClick={onClick} + isAnimationActive={ false } > void; } function CustomMetriPercentage(props: Props) { - const { data } = props; + const { data = {} } = props; return (
{data.count}
-
{`${data.previousCount} ( ${data.countProgress}% ) from previous hour`}
+
{`${data.previousCount} ( ${data.countProgress}% ) from previous period.`}
) } diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx index b0f7dc36f..99c054fae 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx @@ -1,7 +1,36 @@ import React from 'react' import { ResponsiveContainer, XAxis, YAxis, CartesianGrid, Area, Tooltip } from 'recharts'; -import { LineChart, Line, Legend, PieChart, Pie } from 'recharts'; +import { LineChart, Line, Legend, PieChart, Pie, Cell } from 'recharts'; import { Styles } from '../../common'; + + +function renderCustomizedLabel({ + cx, cy, midAngle, innerRadius, outerRadius, value, color, startAngle, endAngle}) { + const RADIAN = Math.PI / 180; + const diffAngle = endAngle - startAngle; + const delta = ((360-diffAngle)/15)-1; + const radius = innerRadius + (outerRadius - innerRadius); + const x = cx + (radius+delta) * Math.cos(-midAngle * RADIAN); + const y = cy + (radius+(delta*delta)) * Math.sin(-midAngle * RADIAN); + return ( + cx ? 'start' : 'end'} dominantBaseline="central" fontSize={12} fontWeight="normal"> + {value} + + ); +}; +function renderCustomizedLabelLine(props){ + let { cx, cy, midAngle, innerRadius, outerRadius, color, startAngle, endAngle } = props; + const RADIAN = Math.PI / 180; + const diffAngle = endAngle - startAngle; + const radius = 10 + innerRadius + (outerRadius - innerRadius); + let path=''; + for(let i=0;i<((360-diffAngle)/15);i++){ + path += `${(cx + (radius+i) * Math.cos(-midAngle * RADIAN))},${(cy + (radius+i*i) * Math.sin(-midAngle * RADIAN))} ` + } + return ( + + ); +} interface Props { data: any; params: any; @@ -9,35 +38,114 @@ interface Props { colors: any; onClick?: (event, index) => void; } + function CustomMetricPieChart(props: Props) { - const { data, params, colors, onClick = () => null } = props; - const data01 = [ - { "name": "Group A", "value": 400 }, - { "name": "Group B", "value": 300 }, - { "name": "Group C", "value": 300 }, - { "name": "Group D", "value": 200 }, - { "name": "Group E", "value": 278 }, - { "name": "Group F", "value": 189 } - ]; + const { data = { values: [] }, params, colors, onClick = () => null } = props; return ( - //
- //
0%
- //
0 ( 0.0% ) from previous hour
- //
- + + labelLine={({ + cx, + cy, + midAngle, + innerRadius, + outerRadius, + value, + index + }) => { + const RADIAN = Math.PI / 180; + let radius1 = 15 + innerRadius + (outerRadius - innerRadius); + let radius2 = innerRadius + (outerRadius - innerRadius); + let x2 = cx + radius1 * Math.cos(-midAngle * RADIAN); + let y2 = cy + radius1 * Math.sin(-midAngle * RADIAN); + let x1 = cx + radius2 * Math.cos(-midAngle * RADIAN); + let y1 = cy + radius2 * Math.sin(-midAngle * RADIAN); + + const percentage = value * 100 / data.values.reduce((a, b) => a + b.sessionCount, 0); + + if (percentage<3){ + return null; + } + + return( + + ) + }} + label={({ + cx, + cy, + midAngle, + innerRadius, + outerRadius, + value, + index + }) => { + const RADIAN = Math.PI / 180; + let radius = 20 + innerRadius + (outerRadius - innerRadius); + 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; + if (percentage<3){ + return null; + } + return ( + cx ? "start" : "end"} + dominantBaseline="central" + fill='#3EAAAF' + > + {data.values[index].name} - ({value}) + + ); + }} + // label={({ + // cx, + // cy, + // midAngle, + // innerRadius, + // outerRadius, + // value, + // index + // }) => { + // const RADIAN = Math.PI / 180; + // const radius = 30 + innerRadius + (outerRadius - innerRadius); + // const x = cx + radius * Math.cos(-midAngle * RADIAN); + // const y = cy + radius * Math.sin(-midAngle * RADIAN); + + // return ( + // cx ? "start" : "end"} + // dominantBaseline="top" + // fontSize={10} + // > + // {data.values[index].name} ({value}) + // + // ); + // }} + > + {data.values.map((entry, index) => ( + + ))} + diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx index 9ab0d72eb..be94acc57 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx @@ -6,11 +6,11 @@ const cols = [ { key: 'name', title: 'Resource', - toText: name => name, + toText: name => name || 'Unidentified', width: '70%', }, { - key: 'sessions', + key: 'sessionCount', title: 'Sessions', toText: sessions => sessions, width: '30%', @@ -19,18 +19,13 @@ const cols = [ interface Props { data: any; + onClick?: (event, index) => void; } function CustomMetriTable(props: Props) { - const { data } = props; - const rows = List([ - { name: 'one', sessions: 2 }, - { name: 'two', sessions: 3 }, - { name: 'three', sessions: 4 }, - { name: 'four', sessions: 1 }, - { name: 'five', sessions: 6 }, - ]) + const { data = { values: [] }, onClick = () => null } = props; + const rows = List(data.values); return ( -
+
{ const params = { density: 70 } @@ -101,7 +102,7 @@ function CustomMetricWidget(props: Props) { return (
-
+
{metric.name}
@@ -109,7 +110,7 @@ function CustomMetricWidget(props: Props) { updateActiveState(metric.metricId, false)} />
-
+
)} + + {metric.viewType === 'table' && ( + + )} diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.css b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.css index 1d1ef3ee4..42444d934 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.css +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.css @@ -1,6 +1,13 @@ .wrapper { - background-color: white; + background-color: $gray-light; /* border: solid thin $gray-medium; */ border-radius: 3px; - padding: 10px; + padding: 20px; +} + +.innerWapper { + border-radius: 3px; + width: 70%; + margin: 0 auto; + background-color: white; } \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx index 7aa43f94b..eb37268fc 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx @@ -106,8 +106,8 @@ function CustomMetricWidget(props: Props) { onSelect={ chagneViewType } value={{ value: metric.viewType }} list={ [ - { value: 'lineChart', icon: 'graph-up-arrow' }, - { value: 'progress', icon: 'hash' }, + { value: 'lineChart', name: 'Chart', icon: 'graph-up-arrow' }, + { value: 'progress', name: 'Progress', icon: 'hash' }, ]} /> )} @@ -121,8 +121,8 @@ function CustomMetricWidget(props: Props) { onSelect={ chagneViewType } value={{ value: metric.viewType }} list={[ - { value: 'table', icon: 'table' }, - { value: 'pieChart', icon: 'graph-up-arrow' }, + { value: 'table', name: 'Table', icon: 'table' }, + { value: 'pieChart', name: 'Chart', icon: 'graph-up-arrow' }, ]} /> )} @@ -139,45 +139,50 @@ function CustomMetricWidget(props: Props) {
-
+
+
+ {metric.name} +
+
{ isTimeSeries && ( - <> - { metric.viewType === 'progress' && ( - - )} - { metric.viewType === 'lineChart' && ( - - )} - - )} - - { isTable && ( -
- { metric.viewType === 'table' ? ( - - ) : ( - + { metric.viewType === 'progress' && ( + - )} -
- )} + )} + { metric.viewType === 'lineChart' && ( + + )} + + )} + + { isTable && ( + <> + { metric.viewType === 'table' ? ( + + ) : ( + + )} + + )} +
diff --git a/frontend/app/components/Dashboard/Widgets/common/Styles.js b/frontend/app/components/Dashboard/Widgets/common/Styles.js index 7b763f698..f071127dd 100644 --- a/frontend/app/components/Dashboard/Widgets/common/Styles.js +++ b/frontend/app/components/Dashboard/Widgets/common/Styles.js @@ -5,6 +5,7 @@ const colorsx = ['#256669', '#38999e', '#3eaaaf', '#51b3b7', '#78c4c7', '#9fd5d7 const compareColors = ['#394EFF', '#4D5FFF', '#808DFF', '#B3BBFF', '#E5E8FF']; const compareColorsx = ["#222F99", "#2E3ECC", "#394EFF", "#6171FF", "#8895FF", "#B0B8FF", "#D7DCFF"].reverse(); const customMetricColors = ['#3EAAAF', '#394EFF', '#666666']; +const colorsPie = colors.concat(["#DDDDDD"]); const countView = count => { const isMoreThanK = count >= 1000; @@ -14,6 +15,7 @@ const countView = count => { export default { customMetricColors, colors, + colorsPie, colorsx, compareColors, compareColorsx, diff --git a/frontend/app/components/Dashboard/Widgets/common/Table.js b/frontend/app/components/Dashboard/Widgets/common/Table.js index 73192a501..8fcef4315 100644 --- a/frontend/app/components/Dashboard/Widgets/common/Table.js +++ b/frontend/app/components/Dashboard/Widgets/common/Table.js @@ -45,14 +45,14 @@ export default class Table extends React.PureComponent { )) }
{ rows.size > (small ? 3 : 5) && !showAll && -
+
} diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx index b10b1dfe4..ea923a6e1 100644 --- a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx +++ b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx @@ -25,6 +25,10 @@ function CustomMetricForm(props: Props) { // const metricOfOptions = metricOf.filter(i => i.key === metric.metricType); const timeseriesOptions = metricOf.filter(i => i.key === 'timeseries'); const tableOptions = metricOf.filter(i => i.key === 'table'); + const isTable = metric.metricType === 'table'; + const isTimeSeries = metric.metricType === 'timeseries'; + const _issueOptions = [{ text: 'All', value: '' }].concat(issueOptions); + const addSeries = () => { props.addSeries(); @@ -44,7 +48,7 @@ function CustomMetricForm(props: Props) { if (name === 'metricOf') { if (value === 'ISSUES') { - props.editMetric({ metricValue: [issueOptions[0].value] }, false); + props.editMetric({ metricValue: [''] }, false); } } @@ -140,7 +144,7 @@ function CustomMetricForm(props: Props) { issue type @@ -165,9 +169,10 @@ function CustomMetricForm(props: Props) {
- {metric.series && metric.series.size > 0 && metric.series.map((series: any, index: number) => ( + {metric.series && metric.series.size > 0 && metric.series.take(isTable ? 1 : metric.series.size).map((series: any, index: number) => (
removeSeries(index)} @@ -177,9 +182,11 @@ function CustomMetricForm(props: Props) { ))}
-
2})}> - -
+ { isTimeSeries && ( +
2})}> + +
+ )}
diff --git a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx index 7054e4516..aea20aea1 100644 --- a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx @@ -25,10 +25,12 @@ interface Props { editSeriesFilterFilter: typeof editSeriesFilterFilter; editSeriesFilter: typeof editSeriesFilter; removeSeriesFilterFilter: typeof removeSeriesFilterFilter; + hideHeader?: boolean; + emptyMessage?: any; } function FilterSeries(props: Props) { - const { canDelete } = props; + const { canDelete, hideHeader = false, emptyMessage = 'Add user event or filter to define the series by clicking Add Step.' } = props; const [expanded, setExpanded] = useState(true) const { series, seriesIndex } = props; @@ -51,7 +53,7 @@ function FilterSeries(props: Props) { return (
-
+
props.updateSeries(seriesIndex, { name }) } />
@@ -78,7 +80,7 @@ function FilterSeries(props: Props) { onChangeEventsOrder={onChangeEventsOrder} /> ): ( -
Add user event or filter to define the series by clicking Add Step.
+
{emptyMessage}
)}
diff --git a/frontend/app/components/ui/SegmentSelection/SegmentSelection.js b/frontend/app/components/ui/SegmentSelection/SegmentSelection.js index e18faf096..74335fddd 100644 --- a/frontend/app/components/ui/SegmentSelection/SegmentSelection.js +++ b/frontend/app/components/ui/SegmentSelection/SegmentSelection.js @@ -28,8 +28,8 @@ class SegmentSelection extends React.Component { data-active={ this.props.value && this.props.value.value === item.value } onClick={ () => !item.disabled && this.setActiveItem(item) } > - { item.icon && } -
{ item.name }
+ { item.icon && } +
{ item.name }
} disabled={!item.disabled} diff --git a/frontend/app/components/ui/SegmentSelection/segmentSelection.css b/frontend/app/components/ui/SegmentSelection/segmentSelection.css index f63559c0c..907f81e37 100644 --- a/frontend/app/components/ui/SegmentSelection/segmentSelection.css +++ b/frontend/app/components/ui/SegmentSelection/segmentSelection.css @@ -72,7 +72,7 @@ } .extraSmall .item { - padding: 0 4px !important; + padding: 2px 4px !important; font-size: 12px; } diff --git a/frontend/app/types/customMetric.js b/frontend/app/types/customMetric.js index 9ce5841f0..0686af87d 100644 --- a/frontend/app/types/customMetric.js +++ b/frontend/app/types/customMetric.js @@ -31,7 +31,7 @@ export default Record({ metricOf: 'USERID', metricValue: ['sessionCount'], metricFormat: 'sessionCount', - viewType: 'table', + viewType: 'pieChart', series: List(), isPublic: true, startDate: '', diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index db0248458..154db3f23 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -44,7 +44,7 @@ export const filtersMap = { [FilterKey.AVG_CPU_LOAD]: { key: FilterKey.AVG_CPU_LOAD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg CPU Load', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/cpu-load', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, [FilterKey.AVG_MEMORY_USAGE]: { key: FilterKey.AVG_MEMORY_USAGE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg Memory Usage', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/memory-load', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, [FilterKey.FETCH_FAILED]: { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch-failed', isEvent: true }, - [FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.baseOperators, icon: 'filters/click', options: filterOptions.issueOptions }, + [FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions }, } export const liveFiltersMap = {