feat(ui) - dashboard - wip
This commit is contained in:
parent
351d1749e9
commit
1b9df5e6bd
25 changed files with 610 additions and 58 deletions
|
|
@ -8,26 +8,18 @@ import { numberWithCommas } from 'App/utils';
|
|||
|
||||
interface Props {
|
||||
data: any;
|
||||
params: any;
|
||||
seriesMap: any;
|
||||
colors: any;
|
||||
onClick?: (event, index) => void;
|
||||
// onClick?: (event, index) => void;
|
||||
}
|
||||
function CustomMetricOverviewChart(props: Props) {
|
||||
const { data, params, seriesMap, colors, onClick = () => null } = props;
|
||||
const { data } = props;
|
||||
console.log('data', data)
|
||||
const gradientDef = Styles.gradientDef();
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className="relative -mx-4">
|
||||
<div className="absolute flex items-start flex-col justify-center inset-0 p-3">
|
||||
<div className="mb-2 flex items-center" >
|
||||
{/* <div className={ cn("text-lg") }>{ 'test' }</div> */}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{/* {prefix} */}
|
||||
{/* <div className="h-2 w-2 bg-red mr-2" />
|
||||
<div className="h-2 w-2 bg-green mr-2 rounded-full" />
|
||||
<div className="mr-2" style={{ borderWidth: "0 5px 7px 5px", borderColor: "transparent transparent #007bff transparent" }} /> */}
|
||||
<CountBadge
|
||||
// title={subtext}
|
||||
count={ countView(Math.round(data.value), data.unit) }
|
||||
|
|
@ -40,7 +32,6 @@ function CustomMetricOverviewChart(props: Props) {
|
|||
<ResponsiveContainer height={ 100 } width="100%">
|
||||
<AreaChart
|
||||
data={ data.chart }
|
||||
// syncId={syncId}
|
||||
margin={ {
|
||||
top: 85, right: 0, left: 0, bottom: 5,
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function CustomMetricPieChart(props: Props) {
|
|||
}
|
||||
}
|
||||
return (
|
||||
<NoContent size="small" show={!data.values || data.values.length === 0} >
|
||||
<NoContent size="small" show={!data.values || data.values.length === 0} style={{ minHeight: '240px'}}>
|
||||
<ResponsiveContainer height={ 220 } width="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Styles } from '../../common';
|
||||
import {
|
||||
AreaChart, Area,
|
||||
BarChart, Bar, CartesianGrid, Tooltip,
|
||||
LineChart, Line, Legend, ResponsiveContainer,
|
||||
XAxis, YAxis
|
||||
} from 'recharts';
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
}
|
||||
function BreakdownOfLoadedResources(props: Props) {
|
||||
const { data } = props;
|
||||
const gradientDef = Styles.gradientDef();
|
||||
const params = { density: 28 }
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<ResponsiveContainer height={ 240 } width="100%">
|
||||
<BarChart
|
||||
data={ data.chart }
|
||||
margin={ Styles.chartMargins }
|
||||
>
|
||||
{gradientDef}
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
|
||||
<XAxis {...Styles.xaxis} dataKey="time" interval={params.density/7} />
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
label={{ ...Styles.axisLabelLeft, value: "Number of Resources" }}
|
||||
/>
|
||||
<Legend />
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Bar minPointSize={1} name="CSS" dataKey="stylesheet" stackId="a" fill={Styles.colors[0]} />
|
||||
<Bar name="Images" dataKey="img" stackId="a" fill={Styles.colors[2]} />
|
||||
<Bar name="Scripts" dataKey="script" stackId="a" fill={Styles.colors[3]} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default BreakdownOfLoadedResources;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './BreakdownOfLoadedResources'
|
||||
16
frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js
vendored
Normal file
16
frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { AreaChart, Area } from 'recharts';
|
||||
import { Styles } from '../../common';
|
||||
|
||||
const Chart = ({ data, compare }) => {
|
||||
const colors = compare ? Styles.compareColors : Styles.colors;
|
||||
|
||||
return (
|
||||
<AreaChart width={ 90 } height={ 30 } data={ data.chart } >
|
||||
<Area type="monotone" dataKey="count" stroke={colors[0]} fill={colors[3]} fillOpacity={ 0.5 } />
|
||||
</AreaChart>
|
||||
);
|
||||
}
|
||||
|
||||
Chart.displayName = 'Chart';
|
||||
|
||||
export default Chart;
|
||||
|
|
@ -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 (
|
||||
<div className="cursor-pointer color-teal" onClick={copyHandler}>
|
||||
{ copied ? 'Copied' : 'Copy Path'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CopyPath
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Styles, Table } from '../../common';
|
||||
import { List } from 'immutable';
|
||||
|
||||
import Chart from './Chart';
|
||||
import ResourceInfo from './ResourceInfo';
|
||||
import CopyPath from './CopyPath';
|
||||
|
||||
const cols = [
|
||||
{
|
||||
key: 'resource',
|
||||
title: 'Resource',
|
||||
Component: ResourceInfo,
|
||||
width: '40%',
|
||||
},
|
||||
{
|
||||
key: 'sessions',
|
||||
title: 'Sessions',
|
||||
toText: count => `${ count > 1000 ? Math.trunc(count / 1000) : count }${ count > 1000 ? 'k' : '' }`,
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
key: 'trend',
|
||||
title: 'Trend',
|
||||
Component: Chart,
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
key: 'copy-path',
|
||||
title: '',
|
||||
Component: CopyPath,
|
||||
cellClass: 'invisible group-hover:visible text-right',
|
||||
width: '20%',
|
||||
}
|
||||
];
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
}
|
||||
function MissingResources(props: Props) {
|
||||
const { data } = props;
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
title="No resources missing."
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<div style={{ height: '240px'}}>
|
||||
<Table
|
||||
small
|
||||
cols={ cols }
|
||||
rows={ List(data.chart) }
|
||||
rowClass="group"
|
||||
/>
|
||||
</div>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default MissingResources;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { diffFromNowString } from 'App/date';
|
||||
import { TextEllipsis } from 'UI';
|
||||
|
||||
import styles from './resourceInfo.css';
|
||||
|
||||
export default class ResourceInfo extends React.PureComponent {
|
||||
render() {
|
||||
const { data } = this.props;
|
||||
return (
|
||||
<div className="flex flex-col" >
|
||||
<TextEllipsis className={ styles.name } text={ data.name } hintText={ data.url } />
|
||||
<div className={ styles.timings }>
|
||||
{ data.endedAt && data.startedAt && `${ diffFromNowString(data.endedAt) } ago - ${ diffFromNowString(data.startedAt) } old` }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './MissingResources'
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
.name {
|
||||
letter-spacing: -.04em;
|
||||
font-size: .9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timings {
|
||||
color: $gray-medium;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Styles } from '../../common';
|
||||
import {
|
||||
ComposedChart, Bar, CartesianGrid, Line, Legend, ResponsiveContainer,
|
||||
XAxis, YAxis, Tooltip
|
||||
} from 'recharts';
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
}
|
||||
function ResourceLoadedVsResponseEnd(props: Props) {
|
||||
const { data } = props;
|
||||
const params = { density: 70 }
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<ResponsiveContainer height={ 240 } width="100%">
|
||||
<ComposedChart
|
||||
data={data.chart}
|
||||
margin={ Styles.chartMargins}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
|
||||
<XAxis
|
||||
{...Styles.xaxis}
|
||||
dataKey="time"
|
||||
// interval={3}
|
||||
interval={(params.density / 7)}
|
||||
/>
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
label={{ ...Styles.axisLabelLeft, value: "Number of Resources" }}
|
||||
yAxisId="left"
|
||||
tickFormatter={val => Styles.tickFormatter(val, 'ms')}
|
||||
/>
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
label={{
|
||||
...Styles.axisLabelLeft,
|
||||
value: "Response End (ms)",
|
||||
position: "insideRight",
|
||||
offset: 0
|
||||
}}
|
||||
yAxisId="right"
|
||||
orientation="right"
|
||||
tickFormatter={val => Styles.tickFormatter(val, 'ms')}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Legend />
|
||||
<Bar minPointSize={1} yAxisId="left" name="XHR" dataKey="xhr" stackId="a" fill={Styles.colors[0]} />
|
||||
<Bar yAxisId="left" name="Other" dataKey="total" stackId="a" fill={Styles.colors[2]} />
|
||||
<Line
|
||||
yAxisId="right"
|
||||
strokeWidth={2}
|
||||
name="Response End"
|
||||
type="monotone"
|
||||
dataKey="avgResponseEnd"
|
||||
stroke={Styles.lineColor}
|
||||
dot={false}
|
||||
/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResourceLoadedVsResponseEnd;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './ResourceLoadedVsResponseEnd'
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Styles } from '../../common';
|
||||
import {
|
||||
ComposedChart, Bar, CartesianGrid, Line, Legend, ResponsiveContainer,
|
||||
XAxis, YAxis, Tooltip
|
||||
} from 'recharts';
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
}
|
||||
function ResourceLoadedVsVisuallyComplete(props: Props) {
|
||||
const { data } = props;
|
||||
const gradientDef = Styles.gradientDef();
|
||||
const params = { density: 70 }
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<ResponsiveContainer height={ 240 } width="100%">
|
||||
<ComposedChart
|
||||
data={data.chart}
|
||||
margin={ Styles.chartMargins}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
|
||||
<XAxis
|
||||
{...Styles.xaxis}
|
||||
dataKey="time"
|
||||
// interval={3}
|
||||
interval={(params.density / 7)}
|
||||
/>
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
label={{ ...Styles.axisLabelLeft, value: "Visually Complete (ms)" }}
|
||||
yAxisId="left"
|
||||
tickFormatter={val => Styles.tickFormatter(val, 'ms')}
|
||||
/>
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
label={{
|
||||
...Styles.axisLabelLeft,
|
||||
value: "Number of Resources",
|
||||
position: "insideRight",
|
||||
offset: 0
|
||||
}}
|
||||
yAxisId="right"
|
||||
orientation="right"
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Legend />
|
||||
<Bar minPointSize={1} yAxisId="right" name="Images" type="monotone" dataKey="types.img" stackId="a" fill={Styles.colors[0]} />
|
||||
<Bar yAxisId="right" name="Scripts" type="monotone" dataKey="types.script" stackId="a" fill={Styles.colors[2]} />
|
||||
<Bar yAxisId="right" name="CSS" type="monotone" dataKey="types.stylesheet" stackId="a" fill={Styles.colors[4]} />
|
||||
<Line
|
||||
yAxisId="left"
|
||||
name="Visually Complete"
|
||||
type="monotone"
|
||||
dataKey="avgTimeToRender"
|
||||
stroke={Styles.lineColor }
|
||||
dot={false}
|
||||
unit=" ms"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResourceLoadedVsVisuallyComplete;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './ResourceLoadedVsVisuallyComplete'
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
import React from 'react';
|
||||
import { NoContent, DropdownPlain } from 'UI';
|
||||
import { Styles, AvgLabel } from '../../common';
|
||||
import { withRequest } from 'HOCs'
|
||||
import {
|
||||
AreaChart, Area,
|
||||
BarChart, Bar, CartesianGrid, Tooltip,
|
||||
LineChart, Line, Legend, ResponsiveContainer,
|
||||
XAxis, YAxis
|
||||
} from 'recharts';
|
||||
import WidgetAutoComplete from 'Shared/WidgetAutoComplete';
|
||||
import { toUnderscore } from 'App/utils';
|
||||
|
||||
const WIDGET_KEY = 'resourcesLoadingTime';
|
||||
export const RESOURCE_OPTIONS = [
|
||||
{ text: 'All', value: 'all', },
|
||||
{ text: 'JS', value: "SCRIPT", },
|
||||
{ text: 'CSS', value: "STYLESHEET", },
|
||||
{ text: 'Fetch', value: "REQUEST", },
|
||||
{ text: 'Image', value: "IMG", },
|
||||
{ text: 'Media', value: "MEDIA", },
|
||||
{ text: 'Other', value: "OTHER", },
|
||||
];
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
optionsLoading: any
|
||||
fetchOptions: any
|
||||
options: any
|
||||
}
|
||||
function ResourceLoadingTime(props: Props) {
|
||||
const { data, optionsLoading } = props;
|
||||
const gradientDef = Styles.gradientDef();
|
||||
const params = { density: 70 }
|
||||
const [autoCompleteSelected, setSutoCompleteSelected] = React.useState('');
|
||||
const [type, setType] = React.useState('');
|
||||
|
||||
const onSelect = (params) => {
|
||||
const _params = { density: 70 }
|
||||
setSutoCompleteSelected(params.value);
|
||||
console.log('params', params) // TODO reload the data with new params;
|
||||
// this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value })
|
||||
}
|
||||
|
||||
const writeOption = (e, { name, value }) => {
|
||||
// this.setState({ [name]: value })
|
||||
setType(value);
|
||||
const _params = { density: 70 } // TODO reload the data with new params;
|
||||
// this.props.fetchWidget(WIDGET_KEY, this.props.period, this.props.platform, { ..._params, [ name ]: value === 'all' ? null : value })
|
||||
}
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<>
|
||||
<div className="flex items-center mb-3">
|
||||
<WidgetAutoComplete
|
||||
loading={optionsLoading}
|
||||
fetchOptions={props.fetchOptions}
|
||||
options={props.options}
|
||||
onSelect={onSelect}
|
||||
placeholder="Search for Page"
|
||||
/>
|
||||
<DropdownPlain
|
||||
disabled={!!autoCompleteSelected}
|
||||
name="type"
|
||||
label="Resource"
|
||||
options={ RESOURCE_OPTIONS }
|
||||
onChange={ writeOption }
|
||||
defaultValue={'all'}
|
||||
wrapperStyle={{
|
||||
position: 'absolute',
|
||||
top: '12px',
|
||||
left: '170px',
|
||||
}}
|
||||
/>
|
||||
<AvgLabel className="ml-auto" text="Avg" count={Math.round(data.avg)} unit="ms" />
|
||||
</div>
|
||||
<ResponsiveContainer height={ 200 } width="100%">
|
||||
<AreaChart
|
||||
data={ data.chart }
|
||||
margin={ Styles.chartMargins }
|
||||
>
|
||||
{gradientDef}
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
|
||||
<XAxis {...Styles.xaxis} dataKey="time" interval={(params.density/7)} />
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Area
|
||||
name="Avg"
|
||||
unit=" ms"
|
||||
type="monotone"
|
||||
dataKey="avg"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
strokeOpacity={ 0.8 }
|
||||
fill={'url(#colorCount)'}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default withRequest({
|
||||
dataName: "options",
|
||||
initialData: [],
|
||||
dataWrapper: data => data,
|
||||
loadingName: 'optionsLoading',
|
||||
requestName: "fetchOptions",
|
||||
endpoint: '/dashboard/' + toUnderscore(WIDGET_KEY) + '/search',
|
||||
method: 'GET'
|
||||
})(ResourceLoadingTime)
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './ResourceLoadingTime'
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import React from 'react';
|
||||
import { NoContent } from 'UI';
|
||||
import { Styles } from '../../common';
|
||||
import {
|
||||
AreaChart, Area,
|
||||
BarChart, Bar, CartesianGrid, Tooltip,
|
||||
LineChart, Line, Legend, ResponsiveContainer,
|
||||
XAxis, YAxis
|
||||
} from 'recharts';
|
||||
|
||||
interface Props {
|
||||
data: any
|
||||
}
|
||||
function SessionsImpactedBySlowRequests(props: Props) {
|
||||
const { data } = props;
|
||||
const gradientDef = Styles.gradientDef();
|
||||
const params = { density: 70 }
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
size="small"
|
||||
show={ data.chart.length === 0 }
|
||||
>
|
||||
<ResponsiveContainer height={ 240 } width="100%">
|
||||
<AreaChart
|
||||
data={ data.chart }
|
||||
margin={ Styles.chartMargins }
|
||||
>
|
||||
{gradientDef}
|
||||
<CartesianGrid strokeDasharray="3 3" vertical={ false } stroke="#EEEEEE" />
|
||||
<XAxis {...Styles.xaxis} dataKey="time" interval={(params.density/7)} />
|
||||
<YAxis
|
||||
{...Styles.yaxis}
|
||||
allowDecimals={false}
|
||||
tickFormatter={val => Styles.tickFormatter(val)}
|
||||
label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }}
|
||||
/>
|
||||
<Tooltip {...Styles.tooltip} />
|
||||
<Area
|
||||
name="Sessions"
|
||||
type="monotone"
|
||||
dataKey="count"
|
||||
stroke={Styles.colors[0]}
|
||||
fillOpacity={ 1 }
|
||||
strokeWidth={ 2 }
|
||||
strokeOpacity={ 0.8 }
|
||||
fill={'url(#colorCount)'}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default SessionsImpactedBySlowRequests;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './SessionsImpactedBySlowRequests'
|
||||
|
|
@ -47,7 +47,7 @@ function TimeToRender(props: Props) {
|
|||
/>
|
||||
<AvgLabel className="ml-auto" text="Avg" count={Math.round(data.avg)} unit="ms" />
|
||||
</div>
|
||||
<ResponsiveContainer height={ 207 } width="100%">
|
||||
<ResponsiveContainer height={ 200 } width="100%">
|
||||
<AreaChart
|
||||
data={ data.chart }
|
||||
margin={ Styles.chartMargins }
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ function DashboardMetricSelection(props) {
|
|||
</div>
|
||||
<div className="col-span-9">
|
||||
<div
|
||||
className="grid grid-cols-4 gap-4 -mx-4 px-4 pb-20 items-start"
|
||||
className="grid grid-cols-4 gap-4 -mx-4 px-4 pb-40 items-start"
|
||||
style={{ height: "calc(100vh - 165px)", overflowY: 'auto' }}
|
||||
>
|
||||
{activeCategory && activeCategory.widgets.map((widget: any) => (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { observer, useObserver, useLocalObservable } from 'mobx-react-lite';
|
|||
import { Loader } from 'UI';
|
||||
import { useStore } from 'App/mstore';
|
||||
import WidgetPredefinedChart from '../WidgetPredefinedChart';
|
||||
import CustomMetricOverviewChart from '../../Widgets/CustomMetricsWidgets/CustomMetricOverviewChart';
|
||||
interface Props {
|
||||
metric: any;
|
||||
isWidget?: boolean
|
||||
|
|
@ -45,6 +46,9 @@ function WidgetChart(props: Props) {
|
|||
const { metricType, viewType, predefinedKey } = metric;
|
||||
|
||||
if (metricType === 'predefined') {
|
||||
if (viewType === 'overview') {
|
||||
return <CustomMetricOverviewChart data={data} />
|
||||
}
|
||||
return <WidgetPredefinedChart data={data} predefinedKey={metric.predefinedKey} />
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,23 @@ import ErrorsByType from 'App/components/Dashboard/Widgets/PredefinedWidgets/Err
|
|||
import ErrorsByOrigin from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin';
|
||||
import ErrorsPerDomain from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain';
|
||||
import { useObserver } from 'mobx-react-lite';
|
||||
import SessionsAffectedByJSErrors from '../../Widgets/PredefinedWidgets/SessionsAffectedByJSErrors';
|
||||
import CallsErrors4xx from '../../Widgets/PredefinedWidgets/CallsErrors4xx';
|
||||
import CallsErrors5xx from '../../Widgets/PredefinedWidgets/CallsErrors5xx';
|
||||
import CPULoad from '../../Widgets/PredefinedWidgets/CPULoad';
|
||||
import Crashes from '../../Widgets/PredefinedWidgets/Crashes';
|
||||
import DomBuildingTime from '../../Widgets/PredefinedWidgets/DomBuildingTime';
|
||||
import FPS from '../../Widgets/PredefinedWidgets/FPS';
|
||||
import MemoryConsumption from '../../Widgets/PredefinedWidgets/MemoryConsumption';
|
||||
import ResponseTime from '../../Widgets/PredefinedWidgets/ResponseTime';
|
||||
import TimeToRender from '../../Widgets/PredefinedWidgets/TimeToRender';
|
||||
import SlowestDomains from '../../Widgets/PredefinedWidgets/SlowestDomains';
|
||||
import SessionsAffectedByJSErrors from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors';
|
||||
import CallsErrors4xx from 'App/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx';
|
||||
import CallsErrors5xx from 'App/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx';
|
||||
import CPULoad from 'App/components/Dashboard/Widgets/PredefinedWidgets/CPULoad';
|
||||
import Crashes from 'App/components/Dashboard/Widgets/PredefinedWidgets/Crashes';
|
||||
import DomBuildingTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime';
|
||||
import FPS from 'App/components/Dashboard/Widgets/PredefinedWidgets/FPS';
|
||||
import MemoryConsumption from 'App/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption';
|
||||
import ResponseTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime';
|
||||
import TimeToRender from 'App/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender';
|
||||
import SlowestDomains from 'App/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains';
|
||||
import ResourceLoadedVsVisuallyComplete from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete';
|
||||
import SessionsImpactedBySlowRequests from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests';
|
||||
import ResourceLoadingTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime';
|
||||
import BreakdownOfLoadedResources from 'App/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources';
|
||||
import MissingResources from 'App/components/Dashboard/Widgets/PredefinedWidgets/MissingResources';
|
||||
import ResourceLoadedVsResponseEnd from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd';
|
||||
|
||||
interface Props {
|
||||
data: any;
|
||||
|
|
@ -23,8 +29,6 @@ interface Props {
|
|||
}
|
||||
function WidgetPredefinedChart(props: Props) {
|
||||
const { data, predefinedKey } = props;
|
||||
// const { viewType } = data;
|
||||
const params = { density: 70 }
|
||||
|
||||
const renderWidget = () => {
|
||||
switch (predefinedKey) {
|
||||
|
|
@ -43,6 +47,9 @@ function WidgetPredefinedChart(props: Props) {
|
|||
return <CallsErrors5xx data={data} />
|
||||
|
||||
// PERFORMANCE
|
||||
// case 'impacted_sessions_by_slow_pages':
|
||||
// case 'pages_response_time_distribution':
|
||||
// case 'speed_location':
|
||||
case 'cpu':
|
||||
return <CPULoad data={data} />
|
||||
case 'crashes':
|
||||
|
|
@ -55,19 +62,28 @@ function WidgetPredefinedChart(props: Props) {
|
|||
return <MemoryConsumption data={data} />
|
||||
case 'pages_response_time':
|
||||
return <ResponseTime data={data} />
|
||||
// case 'pages_response_time_distribution':
|
||||
// case 'resources_vs_visually_complete':
|
||||
// case 'impacted_sessions_by_slow_pages':
|
||||
// case 'sessions_per_browser':
|
||||
case 'resources_vs_visually_complete':
|
||||
return <ResourceLoadedVsVisuallyComplete data={data} />
|
||||
case 'sessions_per_browser':
|
||||
return <SessionsImpactedBySlowRequests data={data} />
|
||||
case 'slowest_domains':
|
||||
return <SlowestDomains data={data} />
|
||||
// case 'speed_location':
|
||||
case 'time_to_render':
|
||||
return <TimeToRender data={data} />
|
||||
|
||||
// Resources
|
||||
case 'resources_count_by_type':
|
||||
return <BreakdownOfLoadedResources data={data} />
|
||||
case 'missing_resources':
|
||||
return <MissingResources data={data} />
|
||||
case 'resource_type_vs_response_end':
|
||||
return <ResourceLoadedVsResponseEnd data={data} />
|
||||
case 'resources_loading_time':
|
||||
return <ResourceLoadingTime data={data} />
|
||||
// case 'slowest_resources':
|
||||
|
||||
default:
|
||||
return <div>No widget found</div>
|
||||
return <div className="h-40 color-red">Widget not supported</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ export default ({
|
|||
show = true,
|
||||
children = null,
|
||||
empty = false,
|
||||
image = null
|
||||
image = null,
|
||||
style = {},
|
||||
}) => (!show ? children :
|
||||
<div className={ `${ styles.wrapper } ${ size && styles[ size ] }` }>
|
||||
<div className={ `${ styles.wrapper } ${ size && styles[ size ] }` } style={style}>
|
||||
{
|
||||
icon && <div className={ empty ? styles.emptyIcon : styles.icon } />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,19 +193,25 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
|
||||
dashboard.metrics = this.selectedWidgets.map(w => w.metricId)
|
||||
|
||||
return dashboardService.saveDashboard(dashboard).then(_dashboard => {
|
||||
runInAction(() => {
|
||||
if (isCreating) {
|
||||
toast.success('Dashboard created successfully')
|
||||
this.addDashboard(_dashboard)
|
||||
} else {
|
||||
toast.success('Dashboard updated successfully')
|
||||
this.updateDashboard(_dashboard)
|
||||
}
|
||||
})
|
||||
}).finally(() => {
|
||||
runInAction(() => {
|
||||
this.isSaving = false
|
||||
return new Promise((resolve, reject) => {
|
||||
dashboardService.saveDashboard(dashboard).then(_dashboard => {
|
||||
runInAction(() => {
|
||||
if (isCreating) {
|
||||
toast.success('Dashboard created successfully')
|
||||
this.addDashboard(_dashboard)
|
||||
} else {
|
||||
toast.success('Dashboard updated successfully')
|
||||
this.updateDashboard(_dashboard)
|
||||
}
|
||||
resolve(_dashboard)
|
||||
})
|
||||
}).catch(error => {
|
||||
toast.error('Error saving dashboard')
|
||||
reject()
|
||||
}).finally(() => {
|
||||
runInAction(() => {
|
||||
this.isSaving = false
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -406,12 +412,12 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
metric.setData(_data)
|
||||
resolve(_data);
|
||||
} else {
|
||||
if (metric.predefinedKey === 'errors_per_domains') {
|
||||
console.log('errors_per_domains', data)
|
||||
data.chart = data
|
||||
} else {
|
||||
data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart)
|
||||
}
|
||||
// if (metric.predefinedKey === 'errors_per_domains') {
|
||||
// console.log('errors_per_domains', data)
|
||||
// data.chart = data
|
||||
// } else {
|
||||
// data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart)
|
||||
// }
|
||||
data.namesMap = Array.isArray(data) ? data
|
||||
.map(i => Object.keys(i))
|
||||
.flat()
|
||||
|
|
@ -422,10 +428,40 @@ export default class DashboardStore implements IDashboardSotre {
|
|||
}
|
||||
return unique;
|
||||
}, []) : data.chart;
|
||||
console.log('map', data.namesMap)
|
||||
const _data = { namesMap: data.namesMap, chart: data.chart }
|
||||
// console.log('map', data.namesMap)
|
||||
// const _data = { ...data, namesMap: data.namesMap, chart: data.chart }
|
||||
// metric.setData(_data)
|
||||
// resolve(_data);
|
||||
|
||||
const _data = {}
|
||||
if (data.hasOwnProperty('chart')) {
|
||||
_data['chart'] = getChartFormatter(this.period)(data.chart)
|
||||
_data['namesMap'] = data.chart
|
||||
.map(i => Object.keys(i))
|
||||
.flat()
|
||||
.filter(i => i !== 'time' && i !== 'timestamp')
|
||||
.reduce((unique: any, item: any) => {
|
||||
if (!unique.includes(item)) {
|
||||
unique.push(item);
|
||||
}
|
||||
return unique;
|
||||
}, [])
|
||||
} else {
|
||||
_data['chart'] = data
|
||||
_data['namesMap'] = data
|
||||
.map(i => Object.keys(i))
|
||||
.flat()
|
||||
.filter(i => i !== 'time' && i !== 'timestamp')
|
||||
.reduce((unique: any, item: any) => {
|
||||
if (!unique.includes(item)) {
|
||||
unique.push(item);
|
||||
}
|
||||
return unique;
|
||||
}, [])
|
||||
}
|
||||
|
||||
metric.setData(_data)
|
||||
resolve(_data);
|
||||
resolve({ ...data, ..._data });
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log('err', err)
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ export default class Widget implements IWidget {
|
|||
}
|
||||
|
||||
fromJson(json: any) {
|
||||
json.config = json.config || {}
|
||||
runInAction(() => {
|
||||
this.metricId = json.metricId
|
||||
this.widgetId = json.widgetId
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue