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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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';
@ -44,14 +44,15 @@ const GraphNodeContent = ({ connected, child = false, data, index = 0 }) => {
/> />
: null; */ : null; */
const nodeInfo = const nodeInfo =
<GraphNodeInfo <GraphNodeInfo
datacenter={data.datacenter} datacenter={data.datacenter}
instances={data.instances} instances={data.instances}
healthy instanceStatuses={data.instanceStatuses}
connected={connected} healthy
pos={nodeInfoPos} connected={connected}
/>; pos={nodeInfoPos}
/>;
return ( return (
<g transform={`translate(${x}, ${contentY})`}> <g transform={`translate(${x}, ${contentY})`}>

View File

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

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 { 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
}; };