From dc18805699ba614ce81ec8cddffcc36f4c621b58 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 14 Apr 2022 19:23:08 +0200 Subject: [PATCH] feat(ui) - dashboard - other widgets --- .../CallsErrors4xx/CallsErrors4xx.tsx | 1 + .../CallsErrors5xx/CallsErrors5xx.tsx | 1 + .../DomBuildingTime/DomBuildingTime.tsx | 4 +- .../ErrorsByOrigin/ErrorsByOrigin.tsx | 1 + .../ErrorsByType/ErrorsByType.tsx | 1 + .../ResourceLoadingTime.tsx | 4 +- .../ResponseTime/ResponseTime.tsx | 4 +- .../SessionsAffectedByJSErrors.tsx | 1 + .../SlowestResources/Chart.js | 15 +++ .../SlowestResources/CopyPath.js | 23 ++++ .../SlowestResources/ImageInfo.js | 27 +++++ .../SlowestResources/ResourceType.js | 12 ++ .../SlowestResources/SlowestResources.tsx | 81 +++++++++++++ .../SlowestResources/imageInfo.css | 52 ++++++++ .../SlowestResources/index.ts | 1 + .../SpeedIndexByLocation/Scale.js | 24 ++++ .../SpeedIndexByLocation.js | 112 ++++++++++++++++++ .../SpeedIndexByLocation/index.ts | 1 + .../SpeedIndexByLocation/scale.css | 11 ++ .../TimeToRender/TimeToRender.tsx | 4 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 3 +- .../WidgetPredefinedChart.tsx | 8 +- 22 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/Chart.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx index e0f721078..afaaeb37d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx @@ -17,6 +17,7 @@ function CallsErrors4xx(props: Props) { <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx index 8e1a125f0..87fd42eb3 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx @@ -17,6 +17,7 @@ function ErrorsByOrigin(props: Props) { <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx index d1af7d2b4..a9ef6dcac 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx @@ -38,13 +38,13 @@ function ResponseTime(props: Props) { > <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx index a2206f2f9..0c077e747 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx @@ -17,6 +17,7 @@ function SessionsAffectedByJSErrors(props: Props) { { + const colors = compare ? Styles.compareColors : Styles.colors; + return ( + + + + ); +} + +Chart.displayName = 'Chart'; + +export default Chart; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js new file mode 100644 index 000000000..6b7e709e7 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js @@ -0,0 +1,23 @@ +import React from 'react' +import copy from 'copy-to-clipboard' +import { useState } from 'react' + +const CopyPath = ({ data }) => { + const [copied, setCopied] = useState(false) + + const copyHandler = () => { + copy(data.url); + setCopied(true); + setTimeout(function() { + setCopied(false) + }, 500); + } + + return ( +
+ { copied ? 'Copied' : 'Copy Path'} +
+ ) +} + +export default CopyPath diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js new file mode 100644 index 000000000..fed6b71b6 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js @@ -0,0 +1,27 @@ +import { Popup } from 'UI'; +import cn from 'classnames'; +import styles from './imageInfo.css'; + +const supportedTypes = ['png', 'jpg', 'jpeg', 'svg']; + +const ImageInfo = ({ data }) => { + const canPreview = supportedTypes.includes(data.type); + return ( +
+ +
{data.name}
+
+ } + disabled={!canPreview} + content={ One of the slowest images } + /> + + ) +}; + +ImageInfo.displayName = 'ImageInfo'; + +export default ImageInfo; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js new file mode 100644 index 000000000..9803a050f --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js @@ -0,0 +1,12 @@ +import React from 'react' +import cn from 'classnames' + +const ResourceType = ({ data : { type = 'js' }, compare }) => { + return ( +
+ { type.toUpperCase() } +
+ ) +} + +export default ResourceType diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx new file mode 100644 index 000000000..ca62855b0 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, Table } from '../../common'; +import { List } from 'immutable'; +import { numberWithCommas } from 'App/utils'; + +import Chart from './Chart'; +import ImageInfo from './ImageInfo'; +import ResourceType from './ResourceType'; +import CopyPath from './CopyPath'; + +export const RESOURCE_OPTIONS = [ + { text: 'All', value: 'ALL', }, + { text: 'CSS', value: 'STYLESHEET', }, + { text: 'JS', value: 'SCRIPT', }, +]; + +const cols = [ + { + key: 'type', + title: 'Type', + Component: ResourceType, + className: 'text-center justify-center', + cellClass: 'ml-2', + width: '8%', + }, + { + key: 'name', + title: 'File Name', + Component: ImageInfo, + cellClass: '-ml-2', + width: '40%', + }, + { + key: 'avg', + title: 'Load Time', + toText: avg => `${ avg ? numberWithCommas(Math.trunc(avg)) : 0} ms`, + className: 'justify-center', + width: '15%', + }, + { + key: 'trend', + title: 'Trend', + Component: Chart, + width: '15%', + }, + { + key: 'copy-path', + title: '', + Component: CopyPath, + cellClass: 'invisible group-hover:visible text-right', + width: '15%', + } +]; + +interface Props { + data: any + metric?: any +} +function MissingResources(props: Props) { + const { data, metric } = props; + + return ( + +
+ + + + ); +} + +export default MissingResources; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css new file mode 100644 index 000000000..1de36b529 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css @@ -0,0 +1,52 @@ +.name { + display: flex; + align-items: center; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 60%; + } + + & .label { + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.hasPreview { + /* text-decoration: underline; */ + border-bottom: 1px dotted; + cursor: pointer; +} + +.imagePreview { + max-width: 200px; + max-height: 200px; +} + +.imageWrapper { + display: flex; + flex-flow: column; + align-items: center; + width: 40px; + text-align: center; + margin-right: 10px; + & > span { + height: 16px; + } + & .label { + font-size: 9px; + color: $gray-light; + } +} + +.popup { + background-color: #f5f5f5 !important; + &:before { + background-color: #f5f5f5 !important; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts new file mode 100644 index 000000000..ca907e9f0 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts @@ -0,0 +1 @@ +export { default } from './SlowestResources' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js new file mode 100644 index 000000000..2171c432e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js @@ -0,0 +1,24 @@ +import React from 'react' +import { Styles } from '../../common'; +import cn from 'classnames'; +import stl from './scale.css'; + +function Scale({ colors }) { + const lastIndex = (Styles.colors.length - 1) + return ( +
+ {colors.map((c, i) => ( +
+ { i === 0 &&
Slow
} + { i === lastIndex &&
Fast
} +
+ ))} +
+ ) +} + +export default Scale diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js new file mode 100644 index 000000000..411e47030 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js @@ -0,0 +1,112 @@ +import React, { useEffect } from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import Scale from './Scale'; +import { threeLetter } from 'App/constants/countries'; +import { colorScale } from 'App/utils'; +import { observer } from 'mobx-react-lite'; +import * as DataMap from "datamaps"; +import { numberWithCommas } from 'App/utils'; + +// interface Props { +// metric?: any +// } +function SpeedIndexByLocation(props) { + const { metric } = props; + const wrapper: any = React.useRef(null); + let map: any = null; + + const getSeries = data => { + const series: any[] = []; + data.forEach(item => { + const d = [threeLetter[item.userCountry], Math.round(item.avg)] + series.push(d) + }) + + return series; + } + + useEffect(() => { + if (wrapper.current && !map && metric.data.chart.length > 0) { + const dataset = getDataset(); + map = new DataMap({ + element: wrapper.current, + fills: { defaultFill: '#E8E8E8' }, + data: dataset, + // responsive: true, + // height: null, //if not null, datamaps will grab the height of 'element' + // width: null, //if not null, datamaps will grab the width of 'element' + geographyConfig: { + borderColor: '#FFFFFF', + borderWidth: 0.5, + highlightBorderWidth: 1, + popupOnHover: true, + // don't change color on mouse hover + highlightFillColor: function(geo) { + return '#999999'; + // return geo['fillColor'] || '#F5F5F5'; + }, + // only change border + highlightBorderColor: '#B7B7B7', + // show desired information in tooltip + popupTemplate: function(geo, data) { + // don't show tooltip if country don't present in dataset + if (!data) { return ; } + // tooltip content + return ['
', + '', geo.properties.name, '', + 'Avg: ', numberWithCommas(data.numberOfThings), '', + '
'].join(''); + } + } + }); + } + }, []) + + // useEffect(() => { + // if (map) { + // map.updateChoropleth(getSeries(metric.data.chart), { reset: true}); + // } + // }, []) + + const getDataset = () => { + const { metric } = props; + const colors = Styles.colors; + + var dataset = {}; + const series = getSeries(metric.data.chart); + var onlyValues = series.map(function(obj){ return obj[1]; }); + const paletteScale = colorScale(onlyValues, [...colors].reverse()); + + // fill dataset in appropriate format + series.forEach(function(item){ + var iso = item[0], value = item[1]; + dataset[iso] = { numberOfThings: value, fillColor: paletteScale(value) }; + }); + return dataset; + } + + return ( + +
+ +
+ +
+ + ); +} + +export default observer(SpeedIndexByLocation); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts new file mode 100644 index 000000000..1cbdfe2f8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts @@ -0,0 +1 @@ +export { default } from './SpeedIndexByLocation' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css new file mode 100644 index 000000000..5aa34f966 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css @@ -0,0 +1,11 @@ +.bars { + & div:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + + & div:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx index 7e3b928bb..e0da69b8b 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx @@ -38,13 +38,13 @@ function TimeToRender(props: Props) { > <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 0d04ad363..6cd0c19e1 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -117,5 +117,4 @@ function DashboardSideMenu(props: Props) { )); } -export default connect((state) => { -}, { setShowAlerts })(withRouter(DashboardSideMenu)); \ No newline at end of file +export default connect(null, { setShowAlerts })(withRouter(DashboardSideMenu)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx index 151825b80..e61510760 100644 --- a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -24,6 +24,8 @@ import MissingResources from 'App/components/Dashboard/Widgets/PredefinedWidgets import ResourceLoadedVsResponseEnd from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd'; import SessionsPerBrowser from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser'; import CallWithErrors from '../../Widgets/PredefinedWidgets/CallWithErrors'; +import SpeedIndexByLocation from '../../Widgets/PredefinedWidgets/SpeedIndexByLocation'; +import SlowestResources from '../../Widgets/PredefinedWidgets/SlowestResources'; interface Props { data: any; @@ -54,7 +56,8 @@ function WidgetPredefinedChart(props: Props) { // PERFORMANCE // case 'impacted_sessions_by_slow_pages': // case 'pages_response_time_distribution': - // case 'speed_location': + case 'speed_location': + return case 'cpu': return case 'crashes': @@ -85,7 +88,8 @@ function WidgetPredefinedChart(props: Props) { return case 'resources_loading_time': return - // case 'slowest_resources': + case 'slowest_resources': + return default: return
Widget not supported