mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:19:02 +08:00
UX updates
This commit is contained in:
parent
325ca434d4
commit
9ac2dece11
14
frontend/package-lock.json
generated
14
frontend/package-lock.json
generated
@ -46,16 +46,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ant-design/icons": {
|
"@ant-design/icons": {
|
||||||
"version": "4.5.0",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.6.2.tgz",
|
||||||
"integrity": "sha512-ZAKJcmr4DBV3NWr8wm2dCxNKN4eFrX+qCaPsuFejP6FRsf+m5OKxvCVi9bSp1lmKWeOI5yECAx5s0uFm4QHuPw==",
|
"integrity": "sha512-QsBG2BxBYU/rxr2eb8b2cZ4rPKAPBpzAR+0v6rrZLp/lnyvflLH3tw1vregK+M7aJauGWjIGNdFmUfpAOtw25A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ant-design/colors": "^6.0.0",
|
"@ant-design/colors": "^6.0.0",
|
||||||
"@ant-design/icons-svg": "^4.0.0",
|
"@ant-design/icons-svg": "^4.0.0",
|
||||||
"@babel/runtime": "^7.11.2",
|
"@babel/runtime": "^7.11.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"insert-css": "^2.0.0",
|
"rc-util": "^5.9.4"
|
||||||
"rc-util": "^5.0.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ant-design/icons-svg": {
|
"@ant-design/icons-svg": {
|
||||||
@ -11076,11 +11075,6 @@
|
|||||||
"css-in-js-utils": "^2.0.0"
|
"css-in-js-utils": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"insert-css": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ="
|
|
||||||
},
|
|
||||||
"internal-ip": {
|
"internal-ip": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^4.6.2",
|
||||||
"@auth0/auth0-react": "^1.2.0",
|
"@auth0/auth0-react": "^1.2.0",
|
||||||
"@babel/core": "7.12.3",
|
"@babel/core": "7.12.3",
|
||||||
"@material-ui/core": "^4.0.0",
|
"@material-ui/core": "^4.0.0",
|
||||||
|
@ -1,8 +1,27 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { servicesItem } from "Src/store/actions";
|
import { servicesItem } from "Src/store/actions";
|
||||||
|
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||||
import { Select } from "antd";
|
import { Select } from "antd";
|
||||||
|
import styled from "styled-components";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
.info {
|
||||||
|
display:flex;
|
||||||
|
font-family: Roboto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #4F4F4F;
|
||||||
|
font-size: 14px;
|
||||||
|
.anticon-info-circle {
|
||||||
|
margin-top: 22px;
|
||||||
|
margin-right: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
interface SelectServiceProps {
|
interface SelectServiceProps {
|
||||||
services: servicesItem[];
|
services: servicesItem[];
|
||||||
zoomToService: (arg0: string) => void;
|
zoomToService: (arg0: string) => void;
|
||||||
@ -16,18 +35,28 @@ const SelectService = (props: SelectServiceProps) => {
|
|||||||
zoomToService(value);
|
zoomToService(value);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Select
|
<Container>
|
||||||
style={{ width: 270, marginBottom: "56px" }}
|
<Select
|
||||||
placeholder="Select a service"
|
style={{ width: 270, marginBottom: "56px" }}
|
||||||
onChange={handleSelect}
|
placeholder="Select a service"
|
||||||
value={selectedVal}
|
onChange={handleSelect}
|
||||||
>
|
value={selectedVal}
|
||||||
{services.map(({ serviceName }) => (
|
>
|
||||||
<Option key={serviceName} value={serviceName}>
|
{services.map(({ serviceName }) => (
|
||||||
{serviceName}
|
<Option key={serviceName} value={serviceName}>
|
||||||
</Option>
|
{serviceName}
|
||||||
))}
|
</Option>
|
||||||
</Select>
|
))}
|
||||||
|
</Select>
|
||||||
|
<div className='info'>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div>-> Size of circles is proportial to the number of requests served by each node </div>
|
||||||
|
<div>-> Click on node name to reposition the node</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,11 +8,28 @@ import {
|
|||||||
getDetailedServiceMapItems,
|
getDetailedServiceMapItems,
|
||||||
} from "Src/store/actions";
|
} from "Src/store/actions";
|
||||||
import { Spin } from "antd";
|
import { Spin } from "antd";
|
||||||
|
import styled from "styled-components";
|
||||||
import { StoreState } from "../../store/reducers";
|
import { StoreState } from "../../store/reducers";
|
||||||
import { getGraphData } from "./utils";
|
import { getGraphData } from "./utils";
|
||||||
import SelectService from "./SelectService";
|
import SelectService from "./SelectService";
|
||||||
import { ForceGraph2D } from "react-force-graph";
|
import { ForceGraph2D } from "react-force-graph";
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
.force-graph-container .graph-tooltip {
|
||||||
|
background: black;
|
||||||
|
padding: 1px;
|
||||||
|
.keyval {
|
||||||
|
display: flex;
|
||||||
|
.key {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
interface ServiceMapProps extends RouteComponentProps<any> {
|
interface ServiceMapProps extends RouteComponentProps<any> {
|
||||||
serviceMap: serviceMapStore;
|
serviceMap: serviceMapStore;
|
||||||
globalTime: GlobalTime;
|
globalTime: GlobalTime;
|
||||||
@ -52,13 +69,13 @@ const ServiceMap = (props: ServiceMapProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const zoomToService = (value: string) => {
|
const zoomToService = (value: string) => {
|
||||||
fgRef && fgRef.current.zoomToFit(700, 480, (e) => e.id === value);
|
fgRef && fgRef.current.zoomToFit(700, 280, (e) => e.id === value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodes, links } = getGraphData(serviceMap);
|
const { nodes, links } = getGraphData(serviceMap);
|
||||||
const graphData = { nodes, links };
|
const graphData = { nodes, links };
|
||||||
return (
|
return (
|
||||||
<div>
|
<Container>
|
||||||
<SelectService
|
<SelectService
|
||||||
services={serviceMap.services}
|
services={serviceMap.services}
|
||||||
zoomToService={zoomToService}
|
zoomToService={zoomToService}
|
||||||
@ -66,7 +83,7 @@ const ServiceMap = (props: ServiceMapProps) => {
|
|||||||
<ForceGraph2D
|
<ForceGraph2D
|
||||||
ref={fgRef}
|
ref={fgRef}
|
||||||
cooldownTicks={100}
|
cooldownTicks={100}
|
||||||
onEngineStop={() => fgRef.current.zoomToFit(100, 200)}
|
onEngineStop={() => fgRef.current.zoomToFit(100, 120)}
|
||||||
graphData={graphData}
|
graphData={graphData}
|
||||||
nodeLabel="id"
|
nodeLabel="id"
|
||||||
linkAutoColorBy={(d) => d.target}
|
linkAutoColorBy={(d) => d.target}
|
||||||
@ -75,46 +92,46 @@ const ServiceMap = (props: ServiceMapProps) => {
|
|||||||
nodeCanvasObject={(node, ctx, globalScale) => {
|
nodeCanvasObject={(node, ctx, globalScale) => {
|
||||||
const label = node.id;
|
const label = node.id;
|
||||||
const fontSize = node.fontSize;
|
const fontSize = node.fontSize;
|
||||||
ctx.font = `${fontSize}px Sans-Serif`;
|
ctx.font = `${fontSize}px Roboto`;
|
||||||
const textWidth = ctx.measureText(label).width;
|
const width = node.width;
|
||||||
const width = textWidth > node.width ? textWidth : node.width;
|
|
||||||
const bckgDimensions = [width, node.height].map((n) => n + fontSize); // some padding
|
|
||||||
ctx.fillStyle = node.color;
|
ctx.fillStyle = node.color;
|
||||||
ctx.fillRect(
|
ctx.beginPath();
|
||||||
node.x - bckgDimensions[0] / 2,
|
ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
|
||||||
node.y - bckgDimensions[1] / 2,
|
ctx.fill();
|
||||||
...bckgDimensions,
|
|
||||||
);
|
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "#333333";
|
||||||
ctx.fillText(label, node.x, node.y);
|
ctx.fillText(label, node.x, node.y);
|
||||||
|
|
||||||
node.__bckgDimensions = bckgDimensions; // to re-use in nodePointerAreaPaint
|
|
||||||
}}
|
}}
|
||||||
onNodeClick={(node) => {
|
onNodeClick={(node) => {
|
||||||
const tooltip = document.querySelector(".graph-tooltip");
|
const tooltip = document.querySelector(".graph-tooltip");
|
||||||
if (tooltip && node) {
|
if (tooltip && node) {
|
||||||
tooltip.innerHTML = `<div style="padding:12px;background: black;border: 1px solid #BDBDBD;border-radius: 2px;">
|
tooltip.innerHTML = `<div style="color:#333333;padding:12px;background: white;border-radius: 2px;">
|
||||||
<div style="color:white; font-weight:bold; margin-bottom:8px;">${node.id}</div>
|
<div style="font-weight:bold; margin-bottom:16px;">${node.id}</div>
|
||||||
<div style="color:white">P99 latency: ${node.p99 / 1000000}</div>
|
<div class="keyval">
|
||||||
<div style="color:white">Error Rate: ${node.errorRate}%</div>
|
<div class="key">P99 latency:</div>
|
||||||
<div style="color:white">Request Per Sec: ${node.callRate}</div>
|
<div class="val">${node.p99 / 1000000}</div>
|
||||||
|
</div>
|
||||||
|
<div class="keyval">
|
||||||
|
<div class="key">Request:</div>
|
||||||
|
<div class="val">${node.callRate}/sec</div>
|
||||||
|
</div>
|
||||||
|
<div class="keyval">
|
||||||
|
<div class="key">Error Rate:</div>
|
||||||
|
<div class="val">${node.errorRate}%</div>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
nodePointerAreaPaint={(node, color, ctx) => {
|
nodePointerAreaPaint={(node, color, ctx) => {
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
const bckgDimensions = node.__bckgDimensions;
|
ctx.beginPath();
|
||||||
bckgDimensions &&
|
ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
|
||||||
ctx.fillRect(
|
ctx.fill();
|
||||||
node.x - bckgDimensions[0] / 2,
|
|
||||||
node.y - bckgDimensions[1] / 2,
|
|
||||||
...bckgDimensions,
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,20 +2,16 @@ import { uniqBy, uniq, maxBy, cloneDeep, find } from "lodash";
|
|||||||
import { serviceMapStore } from "Src/store/actions";
|
import { serviceMapStore } from "Src/store/actions";
|
||||||
import { graphDataType } from "./ServiceMap";
|
import { graphDataType } from "./ServiceMap";
|
||||||
|
|
||||||
const MIN_WIDTH = 25;
|
const MIN_WIDTH = 12;
|
||||||
const MAX_WIDTH = 50;
|
const MAX_WIDTH = 20;
|
||||||
const MIN_HEIGHT = 10;
|
const DEFAULT_FONT_SIZE = 6;
|
||||||
const MAX_HEIGHT = 15;
|
|
||||||
const DEFAULT_FONT_SIZE = 4;
|
|
||||||
export const getDimensions = (num, highest) => {
|
export const getDimensions = (num, highest) => {
|
||||||
const percentage = (num / highest) * 100;
|
const percentage = (num / highest) * 100;
|
||||||
const width = (percentage * (MAX_WIDTH - MIN_WIDTH)) / 100 + MIN_WIDTH;
|
const width = (percentage * (MAX_WIDTH - MIN_WIDTH)) / 100 + MIN_WIDTH;
|
||||||
const height = (percentage * (MAX_HEIGHT - MIN_HEIGHT)) / 100 + MIN_HEIGHT;
|
|
||||||
const fontSize = DEFAULT_FONT_SIZE;
|
const fontSize = DEFAULT_FONT_SIZE;
|
||||||
return {
|
return {
|
||||||
fontSize,
|
fontSize,
|
||||||
width,
|
width,
|
||||||
height,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,14 +36,13 @@ export const getGraphData = (serviceMap: serviceMapStore): graphDataType => {
|
|||||||
const uniqNodes = uniq([...uniqParent, ...uniqChild]);
|
const uniqNodes = uniq([...uniqParent, ...uniqChild]);
|
||||||
const nodes = uniqNodes.map((node, i) => {
|
const nodes = uniqNodes.map((node, i) => {
|
||||||
const service = find(services, (service) => service.serviceName === node);
|
const service = find(services, (service) => service.serviceName === node);
|
||||||
let color = "#84ff00";
|
let color = "#88CEA5";
|
||||||
if (!service) {
|
if (!service) {
|
||||||
return {
|
return {
|
||||||
id: node,
|
id: node,
|
||||||
group: i + 1,
|
group: i + 1,
|
||||||
fontSize: DEFAULT_FONT_SIZE,
|
fontSize: DEFAULT_FONT_SIZE,
|
||||||
width: MIN_WIDTH,
|
width: MIN_WIDTH,
|
||||||
height: MIN_HEIGHT,
|
|
||||||
color,
|
color,
|
||||||
nodeVal: MIN_WIDTH,
|
nodeVal: MIN_WIDTH,
|
||||||
callRate: 0,
|
callRate: 0,
|
||||||
@ -56,17 +51,16 @@ export const getGraphData = (serviceMap: serviceMapStore): graphDataType => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (service.errorRate > 0) {
|
if (service.errorRate > 0) {
|
||||||
color = "#f00a0a";
|
color = "#F98989";
|
||||||
} else if (service.fourXXRate > 0) {
|
} else if (service.fourXXRate > 0) {
|
||||||
color = "#ebeb15";
|
color = "#F9DA7B";
|
||||||
}
|
}
|
||||||
const { fontSize, width, height } = getDimensions(service.callRate, highestCallRate);
|
const { fontSize, width } = getDimensions(service.callRate, highestCallRate);
|
||||||
return {
|
return {
|
||||||
id: node,
|
id: node,
|
||||||
group: i + 1,
|
group: i + 1,
|
||||||
fontSize,
|
fontSize,
|
||||||
width,
|
width,
|
||||||
height,
|
|
||||||
color,
|
color,
|
||||||
nodeVal: width,
|
nodeVal: width,
|
||||||
callRate: service.callRate,
|
callRate: service.callRate,
|
||||||
|
@ -44,36 +44,36 @@ export const getServiceMapItems = (globalTime: GlobalTime) => {
|
|||||||
"&end=" +
|
"&end=" +
|
||||||
globalTime.maxTime;
|
globalTime.maxTime;
|
||||||
|
|
||||||
const response = await api.get<servicesMapItem[]>(apiV1 + request_string);
|
// const response = await api.get<servicesMapItem[]>(apiV1 + request_string);
|
||||||
// const response = {
|
const response = {
|
||||||
// data: [
|
data: [
|
||||||
// {
|
{
|
||||||
// parent: "driver",
|
parent: "driver",
|
||||||
// child: "redis",
|
child: "redisredisredisredisredisredisredisredisre",
|
||||||
// callCount: 17050,
|
callCount: 17050,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// parent: "frontend",
|
parent: "frontend",
|
||||||
// child: "driver",
|
child: "driver",
|
||||||
// callCount: 1263,
|
callCount: 1263,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// parent: "customer",
|
parent: "customer",
|
||||||
// child: "mysql",
|
child: "mysql",
|
||||||
// callCount: 1262,
|
callCount: 1262,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// parent: "frontend",
|
parent: "frontend",
|
||||||
// child: "customer",
|
child: "customer",
|
||||||
// callCount: 1262,
|
callCount: 1262,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// parent: "frontend",
|
parent: "frontend",
|
||||||
// child: "route",
|
child: "route",
|
||||||
// callCount: 12636,
|
callCount: 12636,
|
||||||
// },
|
},
|
||||||
// ],
|
],
|
||||||
// };
|
};
|
||||||
dispatch<serviceMapItemAction>({
|
dispatch<serviceMapItemAction>({
|
||||||
type: ActionTypes.getServiceMapItems,
|
type: ActionTypes.getServiceMapItems,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
@ -86,77 +86,77 @@ export const getDetailedServiceMapItems = (globalTime: GlobalTime) => {
|
|||||||
let request_string =
|
let request_string =
|
||||||
"/services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime;
|
"/services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime;
|
||||||
|
|
||||||
const response = await api.get<servicesItem[]>(apiV1 + request_string);
|
// const response = await api.get<servicesItem[]>(apiV1 + request_string);
|
||||||
// const response = {
|
const response = {
|
||||||
// data: [
|
data: [
|
||||||
// {
|
{
|
||||||
// serviceName: "redis",
|
serviceName: "redisredisredisredisredisredisredisredisre",
|
||||||
// p99: 1179518000,
|
p99: 1179518000,
|
||||||
// avgDuration: 742158850,
|
avgDuration: 742158850,
|
||||||
// numCalls: 1898,
|
numCalls: 1898,
|
||||||
// callRate: 0.019097411,
|
callRate: 0.019097411,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 3,
|
errorRate: 3,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0,
|
fourXXRate: 0,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// serviceName: "mysql",
|
serviceName: "mysql",
|
||||||
// p99: 1179518000,
|
p99: 1179518000,
|
||||||
// avgDuration: 742158850,
|
avgDuration: 742158850,
|
||||||
// numCalls: 1898,
|
numCalls: 1898,
|
||||||
// callRate: 0.019097411,
|
callRate: 0.019097411,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 0,
|
errorRate: 0,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0,
|
fourXXRate: 0,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// serviceName: "frontend",
|
serviceName: "frontend",
|
||||||
// p99: 1179518000,
|
p99: 1179518000,
|
||||||
// avgDuration: 742158850,
|
avgDuration: 742158850,
|
||||||
// numCalls: 1898,
|
numCalls: 1898,
|
||||||
// callRate: 0.000019097411,
|
callRate: 0.000019097411,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 0,
|
errorRate: 0,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0,
|
fourXXRate: 0,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// serviceName: "customer",
|
serviceName: "customer",
|
||||||
// p99: 728385000,
|
p99: 728385000,
|
||||||
// avgDuration: 342475680,
|
avgDuration: 342475680,
|
||||||
// numCalls: 1897,
|
numCalls: 1897,
|
||||||
// callRate: 0.000019087349,
|
callRate: 0.000019087349,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 0,
|
errorRate: 0,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0.6325778,
|
fourXXRate: 0.6325778,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// serviceName: "driver",
|
serviceName: "driver",
|
||||||
// p99: 243808000,
|
p99: 243808000,
|
||||||
// avgDuration: 204640670,
|
avgDuration: 204640670,
|
||||||
// numCalls: 1898,
|
numCalls: 1898,
|
||||||
// callRate: 0.000019097411,
|
callRate: 0.000019097411,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 0,
|
errorRate: 0,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0.63224447,
|
fourXXRate: 0.63224447,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// serviceName: "route",
|
serviceName: "route",
|
||||||
// p99: 79419000,
|
p99: 79419000,
|
||||||
// avgDuration: 50748804,
|
avgDuration: 50748804,
|
||||||
// numCalls: 18979,
|
numCalls: 18979,
|
||||||
// callRate: 0.00019096404,
|
callRate: 0.00019096404,
|
||||||
// numErrors: 0,
|
numErrors: 0,
|
||||||
// errorRate: 3,
|
errorRate: 3,
|
||||||
// num4XX: 0,
|
num4XX: 0,
|
||||||
// fourXXRate: 0,
|
fourXXRate: 0,
|
||||||
// },
|
},
|
||||||
// ],
|
],
|
||||||
// };
|
};
|
||||||
dispatch<servicesAction>({
|
dispatch<servicesAction>({
|
||||||
type: ActionTypes.getServices,
|
type: ActionTypes.getServices,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user