feat(ui-toolkit, cp-frontend): Display statuses on topology view

This commit is contained in:
JUDIT GRESKOVITS 2017-06-30 11:52:55 +01:00 committed by Judit Greskovits
parent 6651208b2e
commit 05d0420813
11 changed files with 114 additions and 55 deletions

View File

@ -15,24 +15,18 @@ const StyledStatusContainer = styled.div`
height: ${remcalc(54)}
`;
const InstanceStatuses = ({ instances }) => {
const statuses = instances.reduce((statuses, instance) => {
if (instance.status !== 'RUNNING') {
if (statuses[instance.status]) {
statuses[instance.status]++;
} else {
statuses[instance.status] = 1;
}
}
return statuses;
}, {});
const InstanceStatuses = ({ instanceStatuses }) => {
const statuses = instanceStatuses.map(instanceStatus => {
const {
status,
count
} = instanceStatus;
const instanceStatuses = Object.keys(statuses).map(status => {
const instances = statuses[status];
return (
<StyledStatus>
{`${instances}
${instances > 1 ? 'instances' : 'instance'}
{`${count}
${count > 1 ? 'instances' : 'instance'}
${status.toLowerCase()}`}
</StyledStatus>
);
@ -40,13 +34,13 @@ const InstanceStatuses = ({ instances }) => {
return (
<StyledStatusContainer>
{instanceStatuses}
{statuses}
</StyledStatusContainer>
);
};
InstanceStatuses.propTypes = {
instances: PropTypes.array.isRequired
instanceStatuses: PropTypes.array.isRequired
};
export default InstanceStatuses;

View File

@ -96,7 +96,8 @@ const ServiceListItem = ({
</CardDescription>
<CardOptions onClick={handleCardOptionsClick} />
</StyledCardHeader>;
console.log('*** service = ', service);
console.log('*** service.instanceStatuses = ', service.instanceStatuses);
const view = children
? <CardGroupView>
{children}
@ -105,7 +106,7 @@ const ServiceListItem = ({
{isChild && title}
{isChild && subtitle}
<CardDescription>
<InstanceStatuses instances={service.instances} />
<InstanceStatuses instanceStatuses={service.instanceStatuses} />
<CardInfo
icon={<HealthyIcon />}
iconPosition="left"

View File

@ -73,10 +73,20 @@ const ServicesTopology = ({
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 }) => {
push(`${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`);
};
console.log('ServicesTopology services = ', services);
return (
<StyledBackground>
<StyledContainer>
@ -89,11 +99,12 @@ const ServicesTopology = ({
service={servicesQuickActions.service}
show={servicesQuickActions.show}
position={servicesQuickActions.position}
url={url}
onBlur={handleTooltipBlur}
onRestartClick={handleRestartClick}
onStopClick={handleStopClick}
onStartClick={handleStartClick}
onScaleClick={handleScaleClick}
onDeleteClick={handleDeleteClick}
/>
</StyledContainer>
</StyledBackground>

View File

@ -10,6 +10,7 @@ query Instances($deploymentGroupSlug: String!){
connections
instances {
id
status
}
}
}

View File

@ -59,10 +59,30 @@ const instancesByServiceId = serviceId =>
const findService = (services, uuid) =>
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) => ({
index,
...service,
datacenter,
instanceStatuses: getInstanceStatuses(service),
isConsul: service.slug === 'consul'
});

View File

@ -1,6 +1,7 @@
const Lengths = {
paddingLeft: 12,
nodeWidth: 180
nodeWidth: 180,
statusHeight: 18
};
const Sizes = {
@ -68,12 +69,14 @@ const Rects = {
},
// Top, bottom, left, right - from 'centre'
nodeRect: {
...Sizes.nodeSize,
left: -Sizes.nodeSize.width / 2,
right: Sizes.nodeSize.width / 2,
top: -Sizes.nodeSize.height / 2,
bottom: Sizes.nodeSize.height / 2
},
nodeRectWithChildren: {
...Sizes.nodeSizeWithChildren,
left: -Sizes.nodeSizeWithChildren.width / 2,
right: Sizes.nodeSizeWithChildren.width / 2,
top: -Sizes.nodeSizeWithChildren.height / 2 + Sizes.contentSize.height / 3,

View File

@ -1,4 +1,4 @@
import Constants from '../constants';
import Constants from './constants';
const getAngleFromPoints = (source, target) => {
const lineAngle = Math.atan2(target.y - source.y, target.x - source.x);
@ -70,23 +70,20 @@ const getPositions = (rect, halfCorner = 0) => [
}
];
const getRect = data =>
data.children ? Constants.nodeRectWithChildren : Constants.nodeRect;
/* const getRect = data =>
data.children ? Constants.nodeRectWithChildren : Constants.nodeRect; */
const calculateLineLayout = ({ source, target }) => {
// 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
const sourceRect = getRect(source);
const targetRect = getRect(target);
const halfCorner = 2;
const sourcePositions = getPositions(sourceRect, halfCorner);
const sourcePositions = getPositions(source.nodeRect, halfCorner);
const sourceAngle = getAngleFromPoints(source, target);
const sourcePosition = getPosition(sourceAngle, sourcePositions, source);
const targetPositions = getPositions(targetRect, halfCorner);
const targetPositions = getPositions(target.nodeRect, halfCorner);
const targetAngle = getAngleFromPoints(target, sourcePosition);
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 };

View File

@ -8,7 +8,7 @@ import { createSimulation } from './simulation';
import TopologyNode from './node';
import TopologyLink from './link';
import TopologyLinkArrow from './link/arrow';
import { calculateLineLayout } from './link/functions';
import { getNodeRect, calculateLineLayout } from './functions';
const StyledSvg = Svg.extend`
width: 100%;
@ -124,10 +124,7 @@ class Topology extends React.Component {
}
findNodeData(nodesData, nodeId) {
return nodesData.reduce(
(acc, nodeData, index) => (nodeData.id === nodeId ? nodeData : acc),
{}
);
return nodesData.filter(nodeData => nodeData.id === nodeId).shift();
}
setDragInfo(dragging, nodeId = null, position = {}) {
@ -148,9 +145,12 @@ class Topology extends React.Component {
? this.getConsulNodePosition()
: this.getConstrainedNodePosition(service.id, service.children);
const nodeRect = getNodeRect(service);
return {
...service,
...nodePosition
...nodePosition,
nodeRect
};
});

View File

@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Baseline from '../../baseline';
import Constants from '../constants';
import { GraphLine, GraphSubtitle } from './shapes';
import { GraphLine, GraphSubtitle, GraphText } from './shapes';
import GraphNodeInfo from './info';
import GraphNodeMetrics from './metrics';
@ -44,14 +44,15 @@ const GraphNodeContent = ({ connected, child = false, data, index = 0 }) => {
/>
: null; */
const nodeInfo =
<GraphNodeInfo
datacenter={data.datacenter}
instances={data.instances}
healthy
connected={connected}
pos={nodeInfoPos}
/>;
const nodeInfo =
<GraphNodeInfo
datacenter={data.datacenter}
instances={data.instances}
instanceStatuses={data.instanceStatuses}
healthy
connected={connected}
pos={nodeInfoPos}
/>;
return (
<g transform={`translate(${x}, ${contentY})`}>

View File

@ -15,13 +15,7 @@ const GraphNode = ({
onNodeTitleClick,
onQuickActions
}) => {
const { width, height } = data.children
? Constants.nodeSizeWithChildren
: Constants.nodeSize;
const { left, top } = data.children
? Constants.nodeRectWithChildren
: Constants.nodeRect;
const { left, top, width, height } = data.nodeRect;
let x = data.x;
let y = data.y;

View File

@ -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 statuses = instanceStatuses.map((instanceStatus, index) =>
<GraphText key={index} connected={connected}>
{`${instanceStatus.count}
${instanceStatus.status.toLowerCase()}`}
</GraphText>
);
return (
<g transform={`translate(${x}, ${y})`}>
<g transform={`translate(0, 0)`}>
@ -40,6 +47,9 @@ const GraphNodeInfo = ({ connected, datacenter, instances, healthy, pos }) => {
<GraphText x={54} y={14} connected={connected}>
{`${instances.length} inst.`}
</GraphText>
<g transform={'translate(54, 36)'}>
{ statuses }
</g>
{/* <g transform={'translate(82, 0)'}>
<StyledDataCentresIcon connected={connected} />
</g>
@ -54,7 +64,8 @@ GraphNodeInfo.propTypes = {
connected: PropTypes.bool,
datacenter: PropTypes.string,
healthy: PropTypes.bool,
instances: PropTypes.number,
instances: PropTypes.array,
instanceStatuses: PropTypes.array,
pos: Point.isRequired
};