feat(ui) - dashboard - wip

This commit is contained in:
Shekar Siri 2022-04-08 18:11:55 +02:00
parent 351d1749e9
commit 1b9df5e6bd
25 changed files with 610 additions and 58 deletions

View file

@ -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,
} }

View file

@ -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

View file

@ -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;

View file

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

View 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;

View file

@ -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

View file

@ -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;

View file

@ -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>
);
}
}

View file

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

View file

@ -0,0 +1,10 @@
.name {
letter-spacing: -.04em;
font-size: .9rem;
cursor: pointer;
}
.timings {
color: $gray-medium;
font-size: 12px;
}

View file

@ -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;

View file

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

View file

@ -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;

View file

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

View file

@ -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)

View file

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

View file

@ -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;

View file

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

View file

@ -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 }

View file

@ -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) => (

View file

@ -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} />
}

View file

@ -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>
}
}

View file

@ -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 } />
}

View file

@ -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)

View file

@ -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