2017-05-18 21:21:33 +03:00
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import styled from 'styled-components';
|
2017-07-28 17:25:06 +03:00
|
|
|
import forceArray from 'force-array';
|
|
|
|
import sortBy from 'lodash.sortby';
|
2017-08-14 13:21:45 +03:00
|
|
|
import { isNot } from 'styled-is';
|
2017-08-30 03:22:35 +03:00
|
|
|
import { Col, Row } from 'react-styled-flexboxgrid';
|
|
|
|
import remcalc from 'remcalc';
|
2017-05-18 21:21:33 +03:00
|
|
|
|
2017-08-21 15:39:02 +03:00
|
|
|
import { InstancesIcon, HealthyIcon } from 'joyent-ui-toolkit';
|
2017-07-27 20:45:44 +03:00
|
|
|
import Status from './status';
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
import {
|
2017-08-30 03:22:35 +03:00
|
|
|
Small,
|
|
|
|
MetricGraph,
|
2017-05-18 21:21:33 +03:00
|
|
|
Card,
|
|
|
|
CardView,
|
|
|
|
CardTitle,
|
|
|
|
CardSubTitle,
|
|
|
|
CardDescription,
|
|
|
|
CardGroupView,
|
|
|
|
CardOptions,
|
|
|
|
CardHeader,
|
2017-06-28 20:36:54 +03:00
|
|
|
CardInfo,
|
2017-05-18 21:21:33 +03:00
|
|
|
Anchor
|
|
|
|
} from 'joyent-ui-toolkit';
|
|
|
|
|
2017-06-01 12:28:59 +03:00
|
|
|
const StyledCardHeader = styled(CardHeader)`
|
|
|
|
position: relative;
|
|
|
|
`;
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const TitleInnerContainer = styled.div`
|
|
|
|
display: flex;
|
|
|
|
flex-direction: row;
|
2017-08-14 13:21:45 +03:00
|
|
|
justify-content: left;
|
2017-05-18 21:21:33 +03:00
|
|
|
align-items: center;
|
|
|
|
`;
|
|
|
|
|
2017-08-14 13:21:45 +03:00
|
|
|
const StyledAnchor = styled(Anchor)`
|
|
|
|
${isNot('active')`
|
|
|
|
color: ${props => props.theme.text}
|
|
|
|
`};
|
|
|
|
`;
|
|
|
|
|
2017-08-30 03:22:35 +03:00
|
|
|
const GraphsContainer = styled(Row)`
|
|
|
|
background: #f6f7fe;
|
|
|
|
width: 50%;
|
|
|
|
margin: 0;
|
|
|
|
flex: 1;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const GraphContainer = styled(Col)`
|
|
|
|
position: relative;
|
|
|
|
border-left: ${remcalc(1)} solid #d8d8d8;
|
|
|
|
padding-top: ${remcalc(20)};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const GraphLeftShaddow = styled.div`
|
|
|
|
z-index: 99;
|
|
|
|
position: absolute;
|
|
|
|
margin-left: ${remcalc(-8)};
|
|
|
|
margin-top: ${remcalc(-20)};
|
|
|
|
width: ${remcalc(12)};
|
|
|
|
height: 100%;
|
|
|
|
background-image: linear-gradient(
|
|
|
|
to right,
|
|
|
|
rgba(213, 216, 231, 0.8),
|
|
|
|
rgba(243, 244, 249, 0)
|
|
|
|
);
|
|
|
|
`;
|
|
|
|
|
|
|
|
const GraphTitle = Small.extend`
|
|
|
|
z-index: 99;
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
height: ${remcalc(20)};
|
|
|
|
border-bottom: ${remcalc(1)} solid #d8d8d8;
|
|
|
|
|
|
|
|
font-size: ${remcalc(13)};
|
|
|
|
text-align: center;
|
|
|
|
color: #494949;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const ChildTitle = styled(CardTitle)`
|
|
|
|
padding: 0;
|
|
|
|
flex: 0 1 auto;
|
|
|
|
align-self: stretch;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const ServiceView = styled(CardView)`
|
|
|
|
height: ${remcalc(120)};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const StatusContainer = styled(CardDescription)`
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
justify-content: center;
|
|
|
|
align-content: center;
|
|
|
|
align-items: stretch;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const HealthInfoContainer = styled.div`
|
|
|
|
flex: 0 1 auto;
|
|
|
|
align-self: flex-end;
|
|
|
|
position: absolute;
|
|
|
|
bottom: 0;
|
|
|
|
`;
|
|
|
|
|
2017-05-18 21:21:33 +03:00
|
|
|
const ServiceListItem = ({
|
2017-06-01 12:28:59 +03:00
|
|
|
onQuickActionsClick = () => {},
|
2017-05-18 21:21:33 +03:00
|
|
|
deploymentGroup = '',
|
2017-08-21 15:39:02 +03:00
|
|
|
service,
|
2017-07-19 19:20:22 +03:00
|
|
|
isChild = false
|
2017-05-18 21:21:33 +03:00
|
|
|
}) => {
|
2017-07-28 17:25:06 +03:00
|
|
|
const handleCardOptionsClick = evt => {
|
|
|
|
onQuickActionsClick(evt, service);
|
|
|
|
};
|
2017-05-18 21:21:33 +03:00
|
|
|
|
2017-07-28 19:56:03 +03:00
|
|
|
const children = sortBy(forceArray(service.children), ['slug']);
|
2017-08-14 13:21:45 +03:00
|
|
|
// const isServiceInactive = service.status && service.status !== 'ACTIVE';
|
2017-05-18 21:21:33 +03:00
|
|
|
const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`;
|
|
|
|
|
2017-07-28 17:25:06 +03:00
|
|
|
const instancesCount = children.length
|
|
|
|
? children.reduce((count, child) => count + child.instances.length, 0)
|
|
|
|
: service.instances.length;
|
|
|
|
|
2017-08-21 15:39:02 +03:00
|
|
|
const childrenItems = children.length
|
2017-08-28 22:21:08 +03:00
|
|
|
? children.map(service => (
|
|
|
|
<ServiceListItem
|
|
|
|
key={service.id}
|
|
|
|
deploymentGroup={deploymentGroup}
|
|
|
|
service={service}
|
|
|
|
isChild
|
|
|
|
/>
|
|
|
|
))
|
|
|
|
: null;
|
|
|
|
|
|
|
|
const title = isChild ? (
|
2017-08-30 03:22:35 +03:00
|
|
|
<ChildTitle>{service.name}</ChildTitle>
|
2017-08-28 22:21:08 +03:00
|
|
|
) : (
|
|
|
|
<CardTitle>
|
|
|
|
<TitleInnerContainer>
|
|
|
|
<StyledAnchor to={to} secondary active={service.instancesActive}>
|
|
|
|
{service.name}
|
|
|
|
</StyledAnchor>
|
|
|
|
</TitleInnerContainer>
|
|
|
|
</CardTitle>
|
|
|
|
);
|
2017-05-18 21:21:33 +03:00
|
|
|
|
2017-08-28 22:21:08 +03:00
|
|
|
const header = !isChild ? (
|
|
|
|
<StyledCardHeader>
|
|
|
|
{title}
|
|
|
|
<CardDescription>
|
|
|
|
<CardInfo
|
|
|
|
icon={<InstancesIcon />}
|
|
|
|
iconPosition="left"
|
|
|
|
label={`${instancesCount} ${instancesCount > 1
|
|
|
|
? 'instances'
|
|
|
|
: 'instance'}`}
|
|
|
|
color={!service.instancesActive ? 'disabled' : 'light'}
|
|
|
|
/>
|
|
|
|
</CardDescription>
|
|
|
|
<CardOptions onClick={handleCardOptionsClick} />
|
|
|
|
</StyledCardHeader>
|
|
|
|
) : null;
|
2017-07-05 16:33:16 +03:00
|
|
|
|
2017-08-14 13:21:45 +03:00
|
|
|
let healthyInfo = null;
|
2017-08-28 22:21:08 +03:00
|
|
|
if (service.instancesActive) {
|
2017-08-14 13:21:45 +03:00
|
|
|
const { total, healthy } = service.instancesHealthy;
|
|
|
|
const iconHealthy = total === healthy ? 'HEALTHY' : 'NOT HEALTHY';
|
|
|
|
const icon = <HealthyIcon healthy={iconHealthy} />;
|
|
|
|
const label = `${healthy} of ${total} healthy`;
|
|
|
|
|
|
|
|
healthyInfo = (
|
2017-08-28 22:21:08 +03:00
|
|
|
<CardInfo icon={icon} iconPosition="left" label={label} color="dark" />
|
|
|
|
);
|
2017-08-14 13:21:45 +03:00
|
|
|
}
|
2017-07-12 16:00:54 +03:00
|
|
|
|
2017-08-30 03:22:35 +03:00
|
|
|
const metrics = !children.length
|
|
|
|
? Object.keys(service.metrics).map(key => (
|
|
|
|
<GraphContainer xs={4}>
|
|
|
|
<GraphLeftShaddow />
|
|
|
|
<GraphTitle>{key}</GraphTitle>
|
|
|
|
<MetricGraph
|
|
|
|
key={key}
|
|
|
|
metricsData={service.metrics[key]}
|
|
|
|
graphDurationSeconds={90}
|
|
|
|
/>
|
|
|
|
</GraphContainer>
|
|
|
|
))
|
|
|
|
: null;
|
|
|
|
|
2017-08-28 22:21:08 +03:00
|
|
|
const view = children.length ? (
|
|
|
|
<CardGroupView>{childrenItems}</CardGroupView>
|
|
|
|
) : (
|
2017-08-30 03:22:35 +03:00
|
|
|
<ServiceView>
|
|
|
|
<StatusContainer>
|
|
|
|
{isChild && title}
|
2017-08-28 22:21:08 +03:00
|
|
|
<Status service={service} />
|
2017-08-30 03:22:35 +03:00
|
|
|
<HealthInfoContainer>{healthyInfo}</HealthInfoContainer>
|
|
|
|
</StatusContainer>
|
|
|
|
<GraphsContainer>{metrics}</GraphsContainer>
|
|
|
|
</ServiceView>
|
2017-08-28 22:21:08 +03:00
|
|
|
);
|
2017-05-18 21:21:33 +03:00
|
|
|
|
|
|
|
return (
|
|
|
|
<Card
|
|
|
|
collapsed={service.collapsed}
|
2017-08-14 13:21:45 +03:00
|
|
|
active={service.instancesActive}
|
2017-05-18 21:21:33 +03:00
|
|
|
flat={isChild}
|
|
|
|
headed={!isChild}
|
2017-06-01 17:05:10 +03:00
|
|
|
key={service.id}
|
2017-05-18 21:21:33 +03:00
|
|
|
stacked={isChild && service.instances > 1}
|
|
|
|
>
|
|
|
|
{header}
|
|
|
|
{view}
|
|
|
|
</Card>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
ServiceListItem.propTypes = {
|
2017-06-01 12:28:59 +03:00
|
|
|
onQuickActionsClick: PropTypes.func,
|
2017-05-18 21:21:33 +03:00
|
|
|
deploymentGroup: PropTypes.string,
|
2017-05-25 23:03:39 +03:00
|
|
|
service: PropTypes.object.isRequired // Define better
|
2017-05-18 21:21:33 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
export default ServiceListItem;
|