feat(ui) - dashboard - other widgets

This commit is contained in:
Shekar Siri 2022-04-15 11:50:17 +02:00
parent dc18805699
commit 9cc4cebc5e
11 changed files with 170 additions and 34 deletions

View file

@ -0,0 +1,128 @@
import React from 'react';
import { Loader, NoContent } from 'UI';
import { Styles, AvgLabel } from '../../common';
import {
ComposedChart, Bar, BarChart, CartesianGrid, ResponsiveContainer,
XAxis, YAxis, ReferenceLine, Tooltip, Legend
} from 'recharts';
const PercentileLine = props => {
const {
viewBox: { x, y },
xoffset,
yheight,
height,
label
} = props;
return (
<svg>
<line
x1={x + xoffset}
x2={x + xoffset}
y1={height + 10}
y2={205}
{...props}
/>
<text
x={x + 5}
y={height + 20}
fontSize="8"
fontFamily="Roboto"
fill="#000000"
textAnchor="start"
>
{label}
</text>
</svg>
);
};
interface Props {
data: any
metric?: any
}
function ResponseTimeDistribution(props: Props) {
const { data, metric } = props;
const colors = Styles.colors;
return (
<NoContent
size="small"
show={ metric.data.chart.length === 0 }
style={ { height: '240px' } }
>
<div className="flex items-center justify-end mb-3">
<AvgLabel text="Avg" unit="ms" className="ml-3" count={metric.data.avg} />
</div>
<div className="flex mb-4">
<ResponsiveContainer height={ 240 } width="100%">
<ComposedChart
data={metric.data.chart}
margin={Styles.chartMargins}
barSize={50}
>
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
<XAxis
{...Styles.xaxis}
dataKey="responseTime"
label={{
...Styles.axisLabelLeft,
angle: 0,
offset: 0,
value: "Page Response Time (ms)",
style: { textAnchor: 'middle' },
position: "insideBottom"
}}
/>
<YAxis
{...Styles.yaxis}
allowDecimals={false}
label={{
...Styles.axisLabelLeft,
value: "Number of Calls"
}}
/>
<Bar minPointSize={1} name="Calls" dataKey="count" stackId="a" fill={colors[2]} label="Backend" />
<Tooltip {...Styles.tooltip} labelFormatter={val => 'Page Response Time: ' + val} />
{ metric.data.percentiles.map((item, i) => (
<ReferenceLine
key={i}
label={
<PercentileLine
xoffset={0}
// y={130}
height={i * 20}
stroke={'#000'}
strokeWidth={0.5}
strokeOpacity={1}
label={`${item.percentile}th Percentile (${item.responseTime}ms)`}
/>
}
allowDecimals={false}
x={item.responseTime}
strokeWidth={0}
strokeOpacity={1}
/>
))}
</ComposedChart>
</ResponsiveContainer>
<ResponsiveContainer height={ 240 } width="10%">
<BarChart
data={metric.data.extremeValues}
margin={Styles.chartMargins}
barSize={40}
>
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
<XAxis {...Styles.xaxis} dataKey="time" />
<YAxis hide type="number" domain={[0, metric.data.total]} {...Styles.yaxis} allowDecimals={false} />
<Tooltip {...Styles.tooltip} />
<Bar minPointSize={1} name="Extreme Values" dataKey="count" stackId="a" fill={colors[0]} />
</BarChart>
</ResponsiveContainer>
</div>
</NoContent>
);
}
export default ResponseTimeDistribution;

View file

@ -0,0 +1 @@
export { default } from './ResponseTimeDistribution'

View file

@ -35,7 +35,7 @@ function SessionsImpactedBySlowRequests(props: Props) {
{...Styles.yaxis}
allowDecimals={false}
tickFormatter={val => Styles.tickFormatter(val)}
label={{ ...Styles.axisLabelLeft, value: "Number of Requests" }}
label={{ ...Styles.axisLabelLeft, value: "Number of Sessions" }}
/>
<Tooltip {...Styles.tooltip} />
<Area

View file

@ -63,11 +63,13 @@ function SpeedIndexByLocation(props) {
}
}, [])
// useEffect(() => {
// if (map) {
// map.updateChoropleth(getSeries(metric.data.chart), { reset: true});
// }
// }, [])
useEffect(() => {
if (map && map.updateChoropleth) {
const series = getSeries(metric.data.chart);
// console.log('series', series)
// map.updateChoropleth(series, {reset: true});
}
}, [])
const getDataset = () => {
const { metric } = props;

View file

@ -62,7 +62,12 @@ function DashboardSideMenu(props: Props) {
{item.isPublic && <div className="p-1"><Icon name="user-friends" color="gray-light" size="16" /></div>}
{item.isPinned && <div className="p-1 pointer-events-none"><Icon name="pin-fill" size="16" /></div>}
{!item.isPinned && (
<Tooltip delay={500} arrow title="Set as default dashboard" hideOnClick={true}>
<Tooltip
delay={500}
arrow
title="Set as default dashboard"
hideOnClick={true}
>
<div
className={cn("p-1 invisible group-hover:visible cursor-pointer")}
onClick={() => togglePinned(item)}

View file

@ -26,6 +26,7 @@ import SessionsPerBrowser from 'App/components/Dashboard/Widgets/PredefinedWidge
import CallWithErrors from '../../Widgets/PredefinedWidgets/CallWithErrors';
import SpeedIndexByLocation from '../../Widgets/PredefinedWidgets/SpeedIndexByLocation';
import SlowestResources from '../../Widgets/PredefinedWidgets/SlowestResources';
import ResponseTimeDistribution from '../../Widgets/PredefinedWidgets/ResponseTimeDistribution';
interface Props {
data: any;
@ -54,8 +55,10 @@ function WidgetPredefinedChart(props: Props) {
return <CallWithErrors data={data} metric={metric} />
// PERFORMANCE
// case 'impacted_sessions_by_slow_pages':
// case 'pages_response_time_distribution':
case 'impacted_sessions_by_slow_pages':
return <SessionsImpactedBySlowRequests data={data} metric={metric} />
case 'pages_response_time_distribution':
return <ResponseTimeDistribution data={data} metric={metric} />
case 'speed_location':
return <SpeedIndexByLocation metric={metric} />
case 'cpu':

View file

@ -5,13 +5,9 @@ function TemplateOverlay() {
return (
<div>
<Tooltip
// arrow
// sticky
title="Click to select"
trigger="mouseenter"
hideOnClick={true}
// followCursor={true}
// position="left"
delay={300}
>
<div className="absolute inset-0 cursor-pointer z-10" />

View file

@ -46,28 +46,27 @@ export default class ItemMenu extends React.PureComponent {
{ items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon, disabled = false, disabledMessage = '' }) => (
<div
key={ text }
// className={ cn('') }
onClick={ !disabled ? this.onClick(onClick) : () => {} }
role="menuitem"
tabIndex="-1"
>
<Tooltip
delay={500}
arrow
title={ disabledMessage }
trigger="mouseenter"
position="left"
// disabled={ !disabled }
>
<div className={cn(styles.menuItem, {'disabled' : disabled })}>
{ icon && (
<div className={ styles.iconWrapper }>
<Icon name={ icon } size="13" color="gray-dark" />
</div>
)}
<div>{ text }</div>
</div>
</Tooltip>
<Tooltip
delay={500}
arrow
title={ disabledMessage }
trigger="mouseenter"
position="left"
disabled={ !disabled }
>
<div className={cn(styles.menuItem, {'disabled' : disabled })}>
{ icon && (
<div className={ styles.iconWrapper }>
<Icon name={ icon } size="13" color="gray-dark" />
</div>
)}
<div>{ text }</div>
</div>
</Tooltip>
</div>
))}
</div>

View file

@ -1,7 +1,7 @@
import cn from 'classnames';
import styles from './loader.css';
const Loader = React.memo(({ className, loading = true, children = null, size, style = { minHeight: '150px' } }) => (!loading ? children :
const Loader = React.memo(({ className = '', loading = true, children = null, size, style = { minHeight: '150px' } }) => (!loading ? children :
<div className={ cn(styles.wrapper, className) } style={style}>
<div className={ styles.loader } data-size={ size } />
</div>

View file

@ -1,4 +1,4 @@
export const threeLetter = {'BD':'BGD', 'BE':'BEL', 'BF':'BFA', 'BG':'BGR', 'BA':'BIH', 'BB':'BRB', 'WF':'WLF', 'BL':'BLM', 'BM':'BMU', 'BN':'BRN', 'BO':'BOL', 'BH':'BHR', 'BI':'BDI', 'BJ':'BEN', 'BT':'BTN', 'JM':'JAM', 'BV':'BVT', 'BW':'BWA', 'WS':'WSM', 'BQ':'BES', 'BR':'BRA', 'BS':'BHS', 'JE':'JEY', 'BY':'BLR', 'BZ':'BLZ', 'RU':'RUS', 'RW':'RWA', 'RS':'SRB', 'TL':'TLS', 'RE':'REU', 'TM':'TKM', 'TJ':'TJK', 'RO':'ROU', 'TK':'TKL', 'GW':'GNB', 'GU':'GUM', 'GT':'GTM', 'GS':'SGS', 'GR':'GRC', 'GQ':'GNQ', 'GP':'GLP', 'JP':'JPN', 'GY':'GUY', 'GG':'GGY', 'GF':'GUF', 'GE':'GEO', 'GD':'GRD', 'GB':'GBR', 'GA':'GAB', 'SV':'SLV', 'GN':'GIN', 'GM':'GMB', 'GL':'GRL', 'GI':'GIB', 'GH':'GHA', 'OM':'OMN', 'TN':'TUN', 'JO':'JOR', 'HR':'HRV', 'HT':'HTI', 'HU':'HUN', 'HK':'HKG', 'HN':'HND', 'HM':'HMD', 'VE':'VEN', 'PR':'PRI', 'PS':'PSE', 'PW':'PLW', 'PT':'PRT', 'SJ':'SJM', 'PY':'PRY', 'IQ':'IRQ', 'PA':'PAN', 'PF':'PYF', 'PG':'PNG', 'PE':'PER', 'PK':'PAK', 'PH':'PHL', 'PN':'PCN', 'PL':'POL', 'PM':'SPM', 'ZM':'ZMB', 'EH':'ESH', 'EE':'EST', 'EG':'EGY', 'ZA':'ZAF', 'EC':'ECU', 'IT':'ITA', 'VN':'VNM', 'SB':'SLB', 'ET':'ETH', 'SO':'SOM', 'ZW':'ZWE', 'SA':'SAU', 'ES':'ESP', 'ER':'ERI', 'ME':'MNE', 'MD':'MDA', 'MG':'MDG', 'MF':'MAF', 'MA':'MAR', 'MC':'MCO', 'UZ':'UZB', 'MM':'MMR', 'ML':'MLI', 'MO':'MAC', 'MN':'MNG', 'MH':'MHL', 'MK':'MKD', 'MU':'MUS', 'MT':'MLT', 'MW':'MWI', 'MV':'MDV', 'MQ':'MTQ', 'MP':'MNP', 'MS':'MSR', 'OR':'SEÑMRT', 'IM':'IMN', 'UG':'UGA', 'TZ':'TZA', 'MIS':'MYS', 'MX':'MEX', 'IL':'ISR', 'FR':'FRA', 'IO':'IOT', 'SH':'SHN', 'FI':'FIN', 'FJ':'FJI', 'FK':'FLK', 'FM':'FSM', 'FO':'FRO', 'NI':'NIC', 'NL':'NLD', 'NO':'NOR', 'NA':'NAM', 'VU':'VUT', 'NC':'NCL', 'NE':'NER', 'NF':'NFK', 'NG':'NGA', 'NZ':'NZL', 'NP':'NPL', 'NR':'NRU', 'NU':'NIU', 'CK':'COK', 'XK':'XKX', 'CI':'CIV', 'CH':'CHE', 'CO':'COL', 'CN':'CHN', 'CM':'CMR', 'CL':'CHL', 'CC':'CCK', 'CA':'CAN', 'CG':'COG', 'CF':'CAF', 'CD':'COD', 'CZ':'CZE', 'CY':'CYP', 'CX':'CXR', 'CR':'CRI', 'CW':'CUW', 'CV':'CPV', 'CU':'CUB', 'SZ':'SWZ', 'SY':'SYR', 'SX':'SXM', 'KG':'KGZ', 'KE':'KEN', 'SS':'SSD', 'SR':'SUR', 'KI':'KIR', 'KH':'KHM', 'KN':'KNA', 'KM':'COM', 'ST':'STP', 'SK':'SVK', 'KR':'KOR', 'SI':'SVN', 'KP':'PRK', 'KW':'KWT', 'SN':'SEN', 'SM':'SMR', 'SL':'SLE', 'SC':'SYC', 'KZ':'KAZ', 'KY':'CYM', 'SG':'SGP', 'SE':'SWE', 'SD':'SDN', 'DO':'DOM', 'DM':'DMA', 'DJ':'DJI', 'DK':'DNK', 'VG':'VGB', 'DE':'DEU', 'YE':'YEM', 'DZ':'DZA', 'US':'USA', 'UY':'URY', 'YT':'MYT', 'UM':'UMI', 'LB':'LBN', 'LC':'LCA', 'LA':'LAO', 'TV':'TUV', 'TW':'TWN', 'TT':'TTO', 'TR':'TUR', 'LK':'LKA', 'LI':'LIE', 'LV':'LVA', 'TO':'TON', 'LT':'LTU', 'LU':'LUX', 'LR':'LBR', 'LS':'LSO', 'TH':'THA', 'TF':'ATF', 'TG':'TGO', 'TD':'TCD', 'TC':'TCA', 'LY':'LBY', 'VA':'VAT', 'VC':'VCT', 'AE':'ARE', 'AD':'AND', 'AG':'ATG', 'AF':'AFG', 'AI':'AIA', 'VI':'VIR', 'IS':'ISL', 'IR':'IRN', 'AM':'ARM', 'AL':'ALB', 'AO':'AGO', 'AQ':'ATA', 'AS':'ASM', 'AR':'ARG', 'AU':'AUS', 'AT':'AUT', 'AW':'ABW', 'IN':'IND', 'AX':'ALA', 'AZ':'AZE', 'IE':'IRL', 'ID':'IDN', 'UA':'UKR', 'QA':'QAT', 'MZ':'MOZ'}
export const threeLetter = {UN: 'BGD', BD: 'BGD', BE: 'BEL', BF: 'BFA', BG: 'BGR', BA: 'BIH', BB: 'BRB', WF: 'WLF', BL: 'BLM', BM: 'BMU', BN: 'BRN', BO: 'BOL', BH: 'BHR', BI: 'BDI', BJ: 'BEN', BT: 'BTN', JM: 'JAM', BV: 'BVT', BW: 'BWA', WS: 'WSM', BQ: 'BES', BR: 'BRA', BS: 'BHS', JE: 'JEY', BY: 'BLR', BZ: 'BLZ', RU: 'RUS', RW: 'RWA', RS: 'SRB', TL: 'TLS', RE: 'REU', TM: 'TKM', TJ: 'TJK', RO: 'ROU', TK: 'TKL', GW: 'GNB', GU: 'GUM', GT: 'GTM', GS: 'SGS', GR: 'GRC', GQ: 'GNQ', GP: 'GLP', JP: 'JPN', GY: 'GUY', GG: 'GGY', GF: 'GUF', GE: 'GEO', GD: 'GRD', GB: 'GBR', GA: 'GAB', SV: 'SLV', GN: 'GIN', GM: 'GMB', GL: 'GRL', GI: 'GIB', GH: 'GHA', OM: 'OMN', TN: 'TUN', JO: 'JOR', HR: 'HRV', HT: 'HTI', HU: 'HUN', HK: 'HKG', HN: 'HND', HM: 'HMD', VE: 'VEN', PR: 'PRI', PS: 'PSE', PW: 'PLW', PT: 'PRT', SJ: 'SJM', PY: 'PRY', IQ: 'IRQ', PA: 'PAN', PF: 'PYF', PG: 'PNG', PE: 'PER', PK: 'PAK', PH: 'PHL', PN: 'PCN', PL: 'POL', PM: 'SPM', ZM: 'ZMB', EH: 'ESH', EE: 'EST', EG: 'EGY', ZA: 'ZAF', EC: 'ECU', IT: 'ITA', VN: 'VNM', SB: 'SLB', ET: 'ETH', SO: 'SOM', ZW: 'ZWE', SA: 'SAU', ES: 'ESP', ER: 'ERI', ME: 'MNE', MD: 'MDA', MG: 'MDG', MF: 'MAF', MA: 'MAR', MC: 'MCO', UZ: 'UZB', MM: 'MMR', ML: 'MLI', MO: 'MAC', MN: 'MNG', MH: 'MHL', MK: 'MKD', MU: 'MUS', MT: 'MLT', MW: 'MWI', MV: 'MDV', MQ: 'MTQ', MP: 'MNP', MS: 'MSR', OR: 'SEÑ', IM: 'IMN', UG: 'UGA', TZ: 'TZA', IS: 'MYS', MX: 'MEX', IL: 'ISR', FR: 'FRA', IO: 'IOT', SH: 'SHN', FI: 'FIN', FJ: 'FJI', FK: 'FLK', FM: 'FSM', FO: 'FRO', NI: 'NIC', NL: 'NLD', NO: 'NOR', NA: 'NAM', VU: 'VUT', NC: 'NCL', NE: 'NER', NF: 'NFK', NG: 'NGA', NZ: 'NZL', NP: 'NPL', NR: 'NRU', NU: 'NIU', CK: 'COK', XK: 'XKX', CI: 'CIV', CH: 'CHE', CO: 'COL', CN: 'CHN', CM: 'CMR', CL: 'CHL', CC: 'CCK', CA: 'CAN', CG: 'COG', CF: 'CAF', CD: 'COD', CZ: 'CZE', CY: 'CYP', CX: 'CXR', CR: 'CRI', CW: 'CUW', CV: 'CPV', CU: 'CUB', SZ: 'SWZ', SY: 'SYR', SX: 'SXM', KG: 'KGZ', KE: 'KEN', SS: 'SSD', SR: 'SUR', KI: 'KIR', KH: 'KHM', KN: 'KNA', KM: 'COM', ST: 'STP', SK: 'SVK', KR: 'KOR', SI: 'SVN', KP: 'PRK', KW: 'KWT', SN: 'SEN', SM: 'SMR', SL: 'SLE', SC: 'SYC', KZ: 'KAZ', KY: 'CYM', SG: 'SGP', SE: 'SWE', SD: 'SDN', DO: 'DOM', DM: 'DMA', DJ: 'DJI', DK: 'DNK', VG: 'VGB', DE: 'DEU', YE: 'YEM', DZ: 'DZA', US: 'USA', UY: 'URY', YT: 'MYT', UM: 'UMI', LB: 'LBN', LC: 'LCA', LA: 'LAO', TV: 'TUV', TW: 'TWN', TT: 'TTO', TR: 'TUR', LK: 'LKA', LI: 'LIE', LV: 'LVA', TO: 'TON', LT: 'LTU', LU: 'LUX', LR: 'LBR', LS: 'LSO', TH: 'THA', TF: 'ATF', TG: 'TGO', TD: 'TCD', TC: 'TCA', LY: 'LBY', VA: 'VAT', VC: 'VCT', AE: 'ARE', AD: 'AND', AG: 'ATG', AF: 'AFG', AI: 'AIA', VI: 'VIR', IS: 'ISL', IR: 'IRN', AM: 'ARM', AL: 'ALB', AO: 'AGO', AQ: 'ATA', AS: 'ASM', AR: 'ARG', AU: 'AUS', AT: 'AUT', AW: 'ABW', IN: 'IND', AX: 'ALA', AZ: 'AZE', IE: 'IRL', ID: 'IDN', UA: 'UKR', QA: 'QAT', MZ: 'MOZ'}
export default {
AC: 'Ascension Island',
AD: 'Andorra',

View file

@ -72,7 +72,9 @@ export default class Widget implements IWidget {
position: number = 0
data: any = {
chart: [],
namesMap: {}
namesMap: {},
avg: 0,
percentiles: [],
}
isLoading: boolean = false
isValid: boolean = false