change(ui): speed index example, slowest domains example and card type change

This commit is contained in:
Shekar Siri 2024-07-01 16:59:37 +02:00
parent f35456b3f8
commit b3ee59c6e3
10 changed files with 307 additions and 219 deletions

View file

@ -0,0 +1,65 @@
import React from 'react';
import { List, Progress, Typography } from 'antd';
import cn from 'classnames';
interface ListItem {
icon?: any;
title: string;
progress: number;
value?: number;
}
interface Props {
list: ListItem[];
}
function ListWithIcons({ list = [] }: Props) {
return (
<List
dataSource={list}
split={false}
renderItem={(row: any) => (
<List.Item
key={row.domain}
// onClick={(e) => onClickHandler(e, row)} // Remove onClick handler to disable click interaction
style={{
borderBottom: '1px dotted rgba(0, 0, 0, 0.05)',
padding: '4px 10px',
lineHeight: '1px'
}}
className={cn('rounded')} // Remove hover:bg-active-blue and cursor-pointer
>
<List.Item.Meta
className="m-0"
avatar={row.icon ? row.icon : null}
title={(
<div className="m-0">
<div className="flex justify-between m-0 p-0">
<Typography.Text>{row.name}</Typography.Text>
<Typography.Text type="secondary"> {row.value}</Typography.Text>
</div>
<Progress
percent={row.progress}
showInfo={false}
strokeColor={{
'0%': '#394EFF',
'100%': '#394EFF'
}}
size={['small', 2]}
style={{
padding: '0 0px',
margin: '0 0px',
height: 4
}}
/>
</div>
)}
/>
</List.Item>
)}
/>
);
}
export default ListWithIcons;

View file

@ -1,12 +1,7 @@
import React from 'react';
import { Icon, NoContent } from 'UI';
import { Styles } from '../../common';
import { numberWithCommas } from 'App/utils';
import Bar from './Bar';
import { NO_METRIC_DATA } from 'App/constants/messages';
import CardSessionsByList from 'Components/Dashboard/Widgets/CardSessionsByList';
import { List, Progress, Typography } from 'antd';
import cn from 'classnames';
import ListWithIcons from 'Components/Dashboard/Widgets/ListWithIcons';
interface Props {
data: any;
@ -15,7 +10,7 @@ interface Props {
function SlowestDomains(props: Props) {
const { data } = props;
// TODO - move this to the store
const highest = data.chart[0].value;
const highest = data.chart[0]?.value;
const list = data.chart.slice(0, 4).map((item: any) => ({
name: item.domain,
icon: <Icon name="link-45deg" size={24} />,
@ -31,50 +26,7 @@ function SlowestDomains(props: Props) {
title={NO_METRIC_DATA}
>
<div className="w-full" style={{ height: '240px' }}>
<List
dataSource={list}
split={false}
renderItem={(row: any) => (
<List.Item
key={row.domain}
// onClick={(e) => onClickHandler(e, row)} // Remove onClick handler to disable click interaction
style={{
borderBottom: '1px dotted rgba(0, 0, 0, 0.05)',
padding: '4px 10px',
lineHeight: '1px'
}}
className={cn('rounded')} // Remove hover:bg-active-blue and cursor-pointer
>
<List.Item.Meta
className="m-0"
avatar={row.icon ? row.icon : null}
title={(
<div className="m-0">
<div className="flex justify-between m-0 p-0">
<Typography.Text>{row.name}</Typography.Text>
<Typography.Text type="secondary"> {row.value}</Typography.Text>
</div>
<Progress
percent={row.progress}
showInfo={false}
strokeColor={{
'0%': '#394EFF',
'100%': '#394EFF'
}}
size={['small', 2]}
style={{
padding: '0 0px',
margin: '0 0px',
height: 4
}}
/>
</div>
)}
/>
</List.Item>
)}
/>
<ListWithIcons list={list} />
</div>
</NoContent>
);

View file

@ -1,25 +1,25 @@
import React from 'react'
import React from 'react';
import { Styles } from '../../common';
import cn from 'classnames';
import stl from './scale.module.css';
function Scale({ colors }) {
const lastIndex = (Styles.colorsTeal.length - 1)
const lastIndex = (Styles.compareColors.length - 1);
return (
<div className={ cn(stl.bars, 'absolute bottom-0 mb-4')}>
{Styles.colorsTeal.map((c, i) => (
<div className={cn(stl.bars, 'absolute bottom-0 mb-4')}>
{Styles.compareColors.map((c, i) => (
<div
key={i}
style={{ backgroundColor: c, width: '6px', height: '15px', marginBottom: '1px' }}
className="flex items-center justify-center"
>
{ i === 0 && <div className="text-xs pl-12">Slow</div>}
{ i === lastIndex && <div className="text-xs pl-12">Fast</div>}
{i === 0 && <div className="text-xs pl-12">Slow</div>}
{i === lastIndex && <div className="text-xs pl-12">Fast</div>}
</div>
))}
</div>
)
);
}
export default Scale
export default Scale;

View file

@ -15,7 +15,7 @@
&:focus,
&:hover {
fill: $teal !important;
fill: #2E3ECC !important;
outline: 0;
}
}
@ -25,23 +25,23 @@
}
.heat_index5 {
fill: #3EAAAF !important;
fill: #B0B8FF !important;
}
.heat_index4 {
fill:#5FBABF !important;
fill:#6171FF !important;
}
.heat_index3 {
fill: #7BCBCF !important;
fill: #394EFF !important;
}
.heat_index2 {
fill: #96DCDF !important;
fill: #2E3ECC !important;
}
.heat_index1 {
fill: #ADDCDF !important;
fill: #222F99 !important;
}
.tooltip {
@ -52,4 +52,4 @@
background-color: white;
font-size: 12px;
line-height: 1.2;
}
}

View file

@ -8,99 +8,105 @@ import WorldMap from '@svg-maps/world';
import { SVGMap } from 'react-svg-map';
import stl from './SpeedIndexByLocation.module.css';
import cn from 'classnames';
import { NO_METRIC_DATA } from 'App/constants/messages'
import { NO_METRIC_DATA } from 'App/constants/messages';
interface Props {
metric?: any;
data?: any;
}
function SpeedIndexByLocation(props: Props) {
const { metric } = props;
const wrapper: any = React.useRef(null);
let map: any = null;
const [tooltipStyle, setTooltipStyle] = React.useState({ display: 'none' });
const [pointedLocation, setPointedLocation] = React.useState<any>(null);
const dataMap: any = React.useMemo(() => {
const data: any = {};
const max = metric.data.chart.reduce((acc: any, item: any) => Math.max(acc, item.value), 0);
const min = metric.data.chart.reduce((acc: any, item: any) => Math.min(acc, item.value), 0);
metric.data.chart.forEach((item: any) => {
if (!item || !item.userCountry) { return }
item.perNumber = positionOfTheNumber(min, max, item.value, 5);
data[item.userCountry.toLowerCase()] = item;
});
return data;
}, []);
const { data } = props;
console.log('data', data);
const wrapper: any = React.useRef(null);
let map: any = null;
const [tooltipStyle, setTooltipStyle] = React.useState({ display: 'none' });
const [pointedLocation, setPointedLocation] = React.useState<any>(null);
const getLocationClassName = (location: any) => {
const i = dataMap[location.id] ? dataMap[location.id].perNumber : 0;
const cls = stl['heat_index' + i];
return cn(stl.location, cls);
const dataMap: any = React.useMemo(() => {
const _data: any = {};
const max = data.chart?.reduce((acc: any, item: any) => Math.max(acc, item.value), 0);
const min = data.chart?.reduce((acc: any, item: any) => Math.min(acc, item.value), 0);
data.chart?.forEach((item: any) => {
console.log('item', item);
if (!item || !item.userCountry) {
return;
}
item.perNumber = positionOfTheNumber(min, max, item.value, 5);
_data[item.userCountry.toLowerCase()] = item;
});
return _data;
}, [data.chart]);
const getLocationClassName = (location: any) => {
const i = dataMap[location.id] ? dataMap[location.id].perNumber : 0;
const cls = stl['heat_index' + i];
return cn(stl.location, cls);
};
const getLocationName = (event: any) => {
if (!event) return null;
const id = event.target.attributes.id.value;
const name = event.target.attributes.name.value;
const percentage = dataMap[id] ? dataMap[id].perNumber : 0;
return { name, id, percentage };
};
const handleLocationMouseOver = (event: any) => {
const pointedLocation = getLocationName(event);
setPointedLocation(pointedLocation);
};
const handleLocationMouseOut = () => {
setTooltipStyle({ display: 'none' });
setPointedLocation(null);
};
const handleLocationMouseMove = (event: any) => {
const tooltipStyle = {
display: 'block',
top: event.clientY + 10,
left: event.clientX - 100
};
setTooltipStyle(tooltipStyle);
};
const getLocationName = (event: any) => {
if (!event) return null;
const id = event.target.attributes.id.value;
const name = event.target.attributes.name.value;
const percentage = dataMap[id] ? dataMap[id].perNumber : 0;
return { name, id, percentage };
};
const handleLocationMouseOver = (event: any) => {
const pointedLocation = getLocationName(event);
setPointedLocation(pointedLocation);
};
const handleLocationMouseOut = () => {
setTooltipStyle({ display: 'none' });
setPointedLocation(null);
};
const handleLocationMouseMove = (event: any) => {
const tooltipStyle = {
display: 'block',
top: event.clientY + 10,
left: event.clientX - 100,
};
setTooltipStyle(tooltipStyle);
};
return (
<NoContent size="small" show={false} style={{ height: '240px' }} title={NO_METRIC_DATA}>
<div className="absolute right-0 mr-4 top=0 w-full flex justify-end">
<AvgLabel text="Avg" count={Math.round(metric.data.value)} unit="ms" />
return (
<NoContent size="small" show={false} style={{ height: '240px' }} title={NO_METRIC_DATA}>
<div className="absolute right-0 mr-4 top=0 w-full flex justify-end">
<AvgLabel text="Avg" count={Math.round(data.value)} unit="ms" />
</div>
<Scale colors={Styles.compareColors} />
<div className="map-target"></div>
<div
style={{
height: '234px',
width: '100%',
margin: '0 auto',
display: 'flex'
}}
ref={wrapper}
>
<SVGMap
map={WorldMap}
className={stl.maps}
locationClassName={getLocationClassName}
onLocationMouseOver={handleLocationMouseOver}
onLocationMouseOut={handleLocationMouseOut}
onLocationMouseMove={handleLocationMouseMove}
/>
</div>
<div className={stl.tooltip} style={tooltipStyle}>
{pointedLocation && (
<>
<div>{pointedLocation.name}</div>
<div>
Avg: <strong>{dataMap[pointedLocation.id] ? numberWithCommas(parseInt(dataMap[pointedLocation.id].value)) : 0}</strong>
</div>
<Scale colors={Styles.colors} />
<div className="map-target"></div>
<div
style={{
height: '234px',
width: '100%',
margin: '0 auto',
display: 'flex',
}}
ref={wrapper}
>
<SVGMap
map={WorldMap}
className={stl.maps}
locationClassName={getLocationClassName}
onLocationMouseOver={handleLocationMouseOver}
onLocationMouseOut={handleLocationMouseOut}
onLocationMouseMove={handleLocationMouseMove}
/>
</div>
<div className={stl.tooltip} style={tooltipStyle}>
{pointedLocation && (
<>
<div>{pointedLocation.name}</div>
<div>
Avg: <strong>{dataMap[pointedLocation.id] ? numberWithCommas(parseInt(dataMap[pointedLocation.id].value)) : 0}</strong>
</div>
</>
)}
</div>
</NoContent>
);
</>
)}
</div>
</NoContent>
);
}
export default observer(SpeedIndexByLocation);

View file

@ -23,6 +23,8 @@ import SessionsPerBrowserExample
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsPerBrowserExample';
import SlowestDomains
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SessionsBy/SlowestDomains';
import SpeedIndexByLocationExample
from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/SpeedIndexByLocationExample';
export const CARD_CATEGORY = {
PRODUCT_ANALYTICS: 'product-analytics',
@ -234,7 +236,8 @@ export const CARD_LIST: CardType[] = [
category: CARD_CATEGORIES[1].key,
data: {
chart: generateAreaData(),
label: "Number of Calls",
label: "Page Response Time (ms)",
unit: 'ms',
namesMap: [
"Series 1"
]
@ -289,7 +292,7 @@ export const CARD_LIST: CardType[] = [
"Series 1"
]
},
example: AreaChartCard,
example: SpeedIndexByLocationExample,
},
{

View file

@ -46,7 +46,7 @@ function AreaChartCard(props: Props) {
{/*<div className="flex items-center justify-end mb-3">*/}
{/* <AvgLabel text="Avg" className="ml-3" count={data?.value}/>*/}
{/*</div>*/}
<ResponsiveContainer height={207} width="100%">
<ResponsiveContainer width="100%">
<AreaChart
data={data?.chart}
margin={Styles.chartMargins}

View file

@ -1,67 +1,35 @@
import ExCard from '../ExCard'
import React from 'react'
import CardSessionsByList from "Components/Dashboard/Widgets/CardSessionsByList";
import ExCard from '../ExCard';
import React from 'react';
import CardSessionsByList from 'Components/Dashboard/Widgets/CardSessionsByList';
function ByComponent({title, rows, lineWidth, onCard, type}: {
title: string
rows: {
label: string
progress: number
value: string
icon: React.ReactNode
}[]
onCard: (card: string) => void
type: string
lineWidth: number
function ByComponent({ title, rows, lineWidth, onCard, type }: {
title: string
rows: {
label: string
progress: number
value: string
icon: React.ReactNode
}[]
onCard: (card: string) => void
type: string
lineWidth: number
}) {
const _rows = rows.map((r) => ({
...r,
name: r.label,
sessionCount: r.value,
})).slice(0, 4)
return (
<ExCard
title={title}
onCard={onCard}
type={type}
>
<div className={'flex gap-1 flex-col'}>
<CardSessionsByList list={_rows} selected={''} onClickHandler={() => null}/>
{/*{rows.map((r) => (*/}
{/* <div*/}
{/* className={*/}
{/* 'flex items-center gap-2 border-b border-dotted py-2 last:border-0 first:pt-0 last:pb-0'*/}
{/* }*/}
{/* >*/}
{/* <div>{r.icon}</div>*/}
{/* <div>{r.label}</div>*/}
{/* <div*/}
{/* style={{marginLeft: 'auto', marginRight: 20, display: 'flex'}}*/}
{/* >*/}
{/* <div*/}
{/* style={{*/}
{/* height: 2,*/}
{/* width: lineWidth * (0.01 * r.progress),*/}
{/* background: '#394EFF',*/}
{/* }}*/}
{/* className={'rounded-l'}*/}
{/* />*/}
{/* <div*/}
{/* style={{*/}
{/* height: 2,*/}
{/* width: lineWidth - lineWidth * (0.01 * r.progress),*/}
{/* background: '#E2E4F6',*/}
{/* }}*/}
{/* className={'rounded-r'}*/}
{/* />*/}
{/* </div>*/}
{/* <div className={'min-w-8'}>{r.value}</div>*/}
{/* </div>*/}
{/*))}*/}
</div>
</ExCard>
)
const _rows = rows.map((r) => ({
...r,
name: r.label,
sessionCount: r.value
})).slice(0, 4);
return (
<ExCard
title={title}
onCard={onCard}
type={type}
>
<div className={'flex gap-1 flex-col'}>
<CardSessionsByList list={_rows} selected={''} onClickHandler={() => null} />
</div>
</ExCard>
);
}
export default ByComponent
export default ByComponent;

View file

@ -0,0 +1,94 @@
import React from 'react';
import ExCard from 'Components/Dashboard/components/DashboardList/NewDashModal/Examples/ExCard';
import InsightsCard from 'Components/Dashboard/Widgets/CustomMetricsWidgets/InsightsCard';
import { InsightIssue } from 'App/mstore/types/widget';
import SessionsPerBrowser from 'Components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser';
import SpeedIndexByLocation from 'Components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation';
interface Props {
title: string;
type: string;
onCard: (card: string) => void;
}
function SpeedIndexByLocationExample(props: Props) {
const data = {
'value': 1480,
'chart': [
{
'userCountry': 'AT',
'value': 415
},
{
'userCountry': 'PL',
'value': 433.1666666666667
},
{
'userCountry': 'FR',
'value': 502
},
{
'userCountry': 'IT',
'value': 540.4117647058823
},
{
'userCountry': 'TH',
'value': 662.0
},
{
'userCountry': 'ES',
'value': 740.5454545454545
},
{
'userCountry': 'SG',
'value': 889.6666666666666
},
{
'userCountry': 'TW',
'value': 1008.0
},
{
'userCountry': 'HU',
'value': 1027.0
},
{
'userCountry': 'DE',
'value': 1054.4583333333333
},
{
'userCountry': 'BE',
'value': 1126.0
},
{
'userCountry': 'TR',
'value': 1174.0
},
{
'userCountry': 'US',
'value': 1273.3015873015872
},
{
'userCountry': 'GB',
'value': 1353.8095238095239
},
{
'userCountry': 'VN',
'value': 1473.8181818181818
},
{
'userCountry': 'HK',
'value': 1654.6666666666667
},
],
'unit': 'ms'
};
return (
<ExCard
{...props}
>
<SpeedIndexByLocation data={data} />
</ExCard>
);
}
export default SpeedIndexByLocationExample;

View file

@ -60,7 +60,7 @@ function WidgetPredefinedChart(props: Props) {
case FilterKey.PAGES_RESPONSE_TIME_DISTRIBUTION:
return <ResponseTimeDistribution data={data} metric={metric} />
case FilterKey.SPEED_LOCATION:
return <SpeedIndexByLocation metric={metric} />
return <SpeedIndexByLocation data={data} />
case FilterKey.CPU:
return <CPULoad data={data} metric={metric} />
case FilterKey.CRASHES: