feat(ui-toolkit, cp-frontend): Display statuses on topology view
This commit is contained in:
parent
6651208b2e
commit
05d0420813
@ -15,24 +15,18 @@ const StyledStatusContainer = styled.div`
|
|||||||
height: ${remcalc(54)}
|
height: ${remcalc(54)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InstanceStatuses = ({ instances }) => {
|
const InstanceStatuses = ({ instanceStatuses }) => {
|
||||||
const statuses = instances.reduce((statuses, instance) => {
|
|
||||||
if (instance.status !== 'RUNNING') {
|
const statuses = instanceStatuses.map(instanceStatus => {
|
||||||
if (statuses[instance.status]) {
|
const {
|
||||||
statuses[instance.status]++;
|
status,
|
||||||
} else {
|
count
|
||||||
statuses[instance.status] = 1;
|
} = instanceStatus;
|
||||||
}
|
|
||||||
}
|
|
||||||
return statuses;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const instanceStatuses = Object.keys(statuses).map(status => {
|
|
||||||
const instances = statuses[status];
|
|
||||||
return (
|
return (
|
||||||
<StyledStatus>
|
<StyledStatus>
|
||||||
{`${instances}
|
{`${count}
|
||||||
${instances > 1 ? 'instances' : 'instance'}
|
${count > 1 ? 'instances' : 'instance'}
|
||||||
${status.toLowerCase()}`}
|
${status.toLowerCase()}`}
|
||||||
</StyledStatus>
|
</StyledStatus>
|
||||||
);
|
);
|
||||||
@ -40,13 +34,13 @@ const InstanceStatuses = ({ instances }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledStatusContainer>
|
<StyledStatusContainer>
|
||||||
{instanceStatuses}
|
{statuses}
|
||||||
</StyledStatusContainer>
|
</StyledStatusContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
InstanceStatuses.propTypes = {
|
InstanceStatuses.propTypes = {
|
||||||
instances: PropTypes.array.isRequired
|
instanceStatuses: PropTypes.array.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InstanceStatuses;
|
export default InstanceStatuses;
|
||||||
|
@ -96,7 +96,8 @@ const ServiceListItem = ({
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
<CardOptions onClick={handleCardOptionsClick} />
|
<CardOptions onClick={handleCardOptionsClick} />
|
||||||
</StyledCardHeader>;
|
</StyledCardHeader>;
|
||||||
|
console.log('*** service = ', service);
|
||||||
|
console.log('*** service.instanceStatuses = ', service.instanceStatuses);
|
||||||
const view = children
|
const view = children
|
||||||
? <CardGroupView>
|
? <CardGroupView>
|
||||||
{children}
|
{children}
|
||||||
@ -105,7 +106,7 @@ const ServiceListItem = ({
|
|||||||
{isChild && title}
|
{isChild && title}
|
||||||
{isChild && subtitle}
|
{isChild && subtitle}
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
<InstanceStatuses instances={service.instances} />
|
<InstanceStatuses instanceStatuses={service.instanceStatuses} />
|
||||||
<CardInfo
|
<CardInfo
|
||||||
icon={<HealthyIcon />}
|
icon={<HealthyIcon />}
|
||||||
iconPosition="left"
|
iconPosition="left"
|
||||||
|
@ -73,10 +73,20 @@ const ServicesTopology = ({
|
|||||||
startServices(service.id);
|
startServices(service.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleScaleClick = (evt, service) => {
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
push(`${url}/${service.slug}/delete`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteClick = (evt, service) => {
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
push(`${url}/${service.slug}/scale`);
|
||||||
|
};
|
||||||
|
|
||||||
const handleNodeTitleClick = (evt, { service }) => {
|
const handleNodeTitleClick = (evt, { service }) => {
|
||||||
push(`${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`);
|
push(`${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`);
|
||||||
};
|
};
|
||||||
|
console.log('ServicesTopology services = ', services);
|
||||||
return (
|
return (
|
||||||
<StyledBackground>
|
<StyledBackground>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
@ -89,11 +99,12 @@ const ServicesTopology = ({
|
|||||||
service={servicesQuickActions.service}
|
service={servicesQuickActions.service}
|
||||||
show={servicesQuickActions.show}
|
show={servicesQuickActions.show}
|
||||||
position={servicesQuickActions.position}
|
position={servicesQuickActions.position}
|
||||||
url={url}
|
|
||||||
onBlur={handleTooltipBlur}
|
onBlur={handleTooltipBlur}
|
||||||
onRestartClick={handleRestartClick}
|
onRestartClick={handleRestartClick}
|
||||||
onStopClick={handleStopClick}
|
onStopClick={handleStopClick}
|
||||||
onStartClick={handleStartClick}
|
onStartClick={handleStartClick}
|
||||||
|
onScaleClick={handleScaleClick}
|
||||||
|
onDeleteClick={handleDeleteClick}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</StyledBackground>
|
</StyledBackground>
|
||||||
|
@ -10,6 +10,7 @@ query Instances($deploymentGroupSlug: String!){
|
|||||||
connections
|
connections
|
||||||
instances {
|
instances {
|
||||||
id
|
id
|
||||||
|
status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,10 +59,30 @@ const instancesByServiceId = serviceId =>
|
|||||||
const findService = (services, uuid) =>
|
const findService = (services, uuid) =>
|
||||||
services.reduce((service, s) => (s.uuid === uuid ? s : service), null);
|
services.reduce((service, s) => (s.uuid === uuid ? s : service), null);
|
||||||
|
|
||||||
|
const getInstanceStatuses = (service) => {
|
||||||
|
|
||||||
|
const instanceStatuses = service.instances.reduce((statuses, instance) => {
|
||||||
|
if (instance.status !== 'RUNNING') {
|
||||||
|
if (statuses[instance.status]) {
|
||||||
|
statuses[instance.status]++;
|
||||||
|
} else {
|
||||||
|
statuses[instance.status] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statuses;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.keys(instanceStatuses).map(status => ({
|
||||||
|
status,
|
||||||
|
count: instanceStatuses[status]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
const getService = (service, index, datacenter) => ({
|
const getService = (service, index, datacenter) => ({
|
||||||
index,
|
index,
|
||||||
...service,
|
...service,
|
||||||
datacenter,
|
datacenter,
|
||||||
|
instanceStatuses: getInstanceStatuses(service),
|
||||||
isConsul: service.slug === 'consul'
|
isConsul: service.slug === 'consul'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const Lengths = {
|
const Lengths = {
|
||||||
paddingLeft: 12,
|
paddingLeft: 12,
|
||||||
nodeWidth: 180
|
nodeWidth: 180,
|
||||||
|
statusHeight: 18
|
||||||
};
|
};
|
||||||
|
|
||||||
const Sizes = {
|
const Sizes = {
|
||||||
@ -68,12 +69,14 @@ const Rects = {
|
|||||||
},
|
},
|
||||||
// Top, bottom, left, right - from 'centre'
|
// Top, bottom, left, right - from 'centre'
|
||||||
nodeRect: {
|
nodeRect: {
|
||||||
|
...Sizes.nodeSize,
|
||||||
left: -Sizes.nodeSize.width / 2,
|
left: -Sizes.nodeSize.width / 2,
|
||||||
right: Sizes.nodeSize.width / 2,
|
right: Sizes.nodeSize.width / 2,
|
||||||
top: -Sizes.nodeSize.height / 2,
|
top: -Sizes.nodeSize.height / 2,
|
||||||
bottom: Sizes.nodeSize.height / 2
|
bottom: Sizes.nodeSize.height / 2
|
||||||
},
|
},
|
||||||
nodeRectWithChildren: {
|
nodeRectWithChildren: {
|
||||||
|
...Sizes.nodeSizeWithChildren,
|
||||||
left: -Sizes.nodeSizeWithChildren.width / 2,
|
left: -Sizes.nodeSizeWithChildren.width / 2,
|
||||||
right: Sizes.nodeSizeWithChildren.width / 2,
|
right: Sizes.nodeSizeWithChildren.width / 2,
|
||||||
top: -Sizes.nodeSizeWithChildren.height / 2 + Sizes.contentSize.height / 3,
|
top: -Sizes.nodeSizeWithChildren.height / 2 + Sizes.contentSize.height / 3,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Constants from '../constants';
|
import Constants from './constants';
|
||||||
|
|
||||||
const getAngleFromPoints = (source, target) => {
|
const getAngleFromPoints = (source, target) => {
|
||||||
const lineAngle = Math.atan2(target.y - source.y, target.x - source.x);
|
const lineAngle = Math.atan2(target.y - source.y, target.x - source.x);
|
||||||
@ -70,23 +70,20 @@ const getPositions = (rect, halfCorner = 0) => [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const getRect = data =>
|
/* const getRect = data =>
|
||||||
data.children ? Constants.nodeRectWithChildren : Constants.nodeRect;
|
data.children ? Constants.nodeRectWithChildren : Constants.nodeRect; */
|
||||||
|
|
||||||
const calculateLineLayout = ({ source, target }) => {
|
const calculateLineLayout = ({ source, target }) => {
|
||||||
// Actually, this will need to be got dynamically, in case them things are different sizes
|
// Actually, this will need to be got dynamically, in case them things are different sizes
|
||||||
// yeah right, now you'll get to do exactly that
|
// yeah right, now you'll get to do exactly that
|
||||||
|
|
||||||
const sourceRect = getRect(source);
|
|
||||||
const targetRect = getRect(target);
|
|
||||||
|
|
||||||
const halfCorner = 2;
|
const halfCorner = 2;
|
||||||
|
|
||||||
const sourcePositions = getPositions(sourceRect, halfCorner);
|
const sourcePositions = getPositions(source.nodeRect, halfCorner);
|
||||||
const sourceAngle = getAngleFromPoints(source, target);
|
const sourceAngle = getAngleFromPoints(source, target);
|
||||||
const sourcePosition = getPosition(sourceAngle, sourcePositions, source);
|
const sourcePosition = getPosition(sourceAngle, sourcePositions, source);
|
||||||
|
|
||||||
const targetPositions = getPositions(targetRect, halfCorner);
|
const targetPositions = getPositions(target.nodeRect, halfCorner);
|
||||||
const targetAngle = getAngleFromPoints(target, sourcePosition);
|
const targetAngle = getAngleFromPoints(target, sourcePosition);
|
||||||
const targetPosition = getPosition(targetAngle, targetPositions, target); // , true);
|
const targetPosition = getPosition(targetAngle, targetPositions, target); // , true);
|
||||||
|
|
||||||
@ -101,4 +98,30 @@ const calculateLineLayout = ({ source, target }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export { calculateLineLayout };
|
const getNodeRect = (data) => {
|
||||||
|
const nodeSize = data.children
|
||||||
|
? Constants.nodeSizeWithChildren
|
||||||
|
: Constants.nodeSize;
|
||||||
|
|
||||||
|
const statuses = data.children
|
||||||
|
? data.children.reduce((statuses, child) =>
|
||||||
|
statuses + child.instanceStatuses.length, 0)
|
||||||
|
: data.instanceStatuses.length;
|
||||||
|
|
||||||
|
const { width, height } = nodeSize;
|
||||||
|
|
||||||
|
const nodeHeight = statuses
|
||||||
|
? height + Constants.statusHeight*statuses + 6
|
||||||
|
: height;
|
||||||
|
|
||||||
|
return ({
|
||||||
|
left: -width / 2,
|
||||||
|
right: width / 2,
|
||||||
|
top: -height / 2,
|
||||||
|
bottom: nodeHeight - height / 2,
|
||||||
|
width,
|
||||||
|
height: nodeHeight
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getNodeRect, calculateLineLayout };
|
@ -8,7 +8,7 @@ import { createSimulation } from './simulation';
|
|||||||
import TopologyNode from './node';
|
import TopologyNode from './node';
|
||||||
import TopologyLink from './link';
|
import TopologyLink from './link';
|
||||||
import TopologyLinkArrow from './link/arrow';
|
import TopologyLinkArrow from './link/arrow';
|
||||||
import { calculateLineLayout } from './link/functions';
|
import { getNodeRect, calculateLineLayout } from './functions';
|
||||||
|
|
||||||
const StyledSvg = Svg.extend`
|
const StyledSvg = Svg.extend`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -124,10 +124,7 @@ class Topology extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findNodeData(nodesData, nodeId) {
|
findNodeData(nodesData, nodeId) {
|
||||||
return nodesData.reduce(
|
return nodesData.filter(nodeData => nodeData.id === nodeId).shift();
|
||||||
(acc, nodeData, index) => (nodeData.id === nodeId ? nodeData : acc),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDragInfo(dragging, nodeId = null, position = {}) {
|
setDragInfo(dragging, nodeId = null, position = {}) {
|
||||||
@ -148,9 +145,12 @@ class Topology extends React.Component {
|
|||||||
? this.getConsulNodePosition()
|
? this.getConsulNodePosition()
|
||||||
: this.getConstrainedNodePosition(service.id, service.children);
|
: this.getConstrainedNodePosition(service.id, service.children);
|
||||||
|
|
||||||
|
const nodeRect = getNodeRect(service);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...service,
|
...service,
|
||||||
...nodePosition
|
...nodePosition,
|
||||||
|
nodeRect
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Baseline from '../../baseline';
|
import Baseline from '../../baseline';
|
||||||
import Constants from '../constants';
|
import Constants from '../constants';
|
||||||
import { GraphLine, GraphSubtitle } from './shapes';
|
import { GraphLine, GraphSubtitle, GraphText } from './shapes';
|
||||||
import GraphNodeInfo from './info';
|
import GraphNodeInfo from './info';
|
||||||
import GraphNodeMetrics from './metrics';
|
import GraphNodeMetrics from './metrics';
|
||||||
|
|
||||||
@ -48,6 +48,7 @@ const GraphNodeContent = ({ connected, child = false, data, index = 0 }) => {
|
|||||||
<GraphNodeInfo
|
<GraphNodeInfo
|
||||||
datacenter={data.datacenter}
|
datacenter={data.datacenter}
|
||||||
instances={data.instances}
|
instances={data.instances}
|
||||||
|
instanceStatuses={data.instanceStatuses}
|
||||||
healthy
|
healthy
|
||||||
connected={connected}
|
connected={connected}
|
||||||
pos={nodeInfoPos}
|
pos={nodeInfoPos}
|
||||||
|
@ -15,13 +15,7 @@ const GraphNode = ({
|
|||||||
onNodeTitleClick,
|
onNodeTitleClick,
|
||||||
onQuickActions
|
onQuickActions
|
||||||
}) => {
|
}) => {
|
||||||
const { width, height } = data.children
|
const { left, top, width, height } = data.nodeRect;
|
||||||
? Constants.nodeSizeWithChildren
|
|
||||||
: Constants.nodeSize;
|
|
||||||
|
|
||||||
const { left, top } = data.children
|
|
||||||
? Constants.nodeRectWithChildren
|
|
||||||
: Constants.nodeRect;
|
|
||||||
|
|
||||||
let x = data.x;
|
let x = data.x;
|
||||||
let y = data.y;
|
let y = data.y;
|
||||||
|
@ -25,9 +25,16 @@ const StyledDataCentresIcon = styled(DataCentresIcon)`
|
|||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const GraphNodeInfo = ({ connected, datacenter, instances, healthy, pos }) => {
|
const GraphNodeInfo = ({ connected, datacenter, instances, instanceStatuses, healthy, pos }) => {
|
||||||
const { x, y } = pos;
|
const { x, y } = pos;
|
||||||
|
|
||||||
|
const statuses = instanceStatuses.map((instanceStatus, index) =>
|
||||||
|
<GraphText key={index} connected={connected}>
|
||||||
|
{`${instanceStatus.count}
|
||||||
|
${instanceStatus.status.toLowerCase()}`}
|
||||||
|
</GraphText>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g transform={`translate(${x}, ${y})`}>
|
<g transform={`translate(${x}, ${y})`}>
|
||||||
<g transform={`translate(0, 0)`}>
|
<g transform={`translate(0, 0)`}>
|
||||||
@ -40,6 +47,9 @@ const GraphNodeInfo = ({ connected, datacenter, instances, healthy, pos }) => {
|
|||||||
<GraphText x={54} y={14} connected={connected}>
|
<GraphText x={54} y={14} connected={connected}>
|
||||||
{`${instances.length} inst.`}
|
{`${instances.length} inst.`}
|
||||||
</GraphText>
|
</GraphText>
|
||||||
|
<g transform={'translate(54, 36)'}>
|
||||||
|
{ statuses }
|
||||||
|
</g>
|
||||||
{/* <g transform={'translate(82, 0)'}>
|
{/* <g transform={'translate(82, 0)'}>
|
||||||
<StyledDataCentresIcon connected={connected} />
|
<StyledDataCentresIcon connected={connected} />
|
||||||
</g>
|
</g>
|
||||||
@ -54,7 +64,8 @@ GraphNodeInfo.propTypes = {
|
|||||||
connected: PropTypes.bool,
|
connected: PropTypes.bool,
|
||||||
datacenter: PropTypes.string,
|
datacenter: PropTypes.string,
|
||||||
healthy: PropTypes.bool,
|
healthy: PropTypes.bool,
|
||||||
instances: PropTypes.number,
|
instances: PropTypes.array,
|
||||||
|
instanceStatuses: PropTypes.array,
|
||||||
pos: Point.isRequired
|
pos: Point.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user