feat(ui-toolkit, cp-frontend): Fix services list

This commit is contained in:
JUDIT GRESKOVITS 2017-06-28 18:36:54 +01:00 committed by Sérgio Ramos
parent 40f666adc2
commit ef6c166c56
18 changed files with 218 additions and 72 deletions

View File

@ -85,6 +85,8 @@ const StatusBadge = ({ status }) => {
};
const StyledCard = Card.extend`
flex-direction: row;
&:not(:last-child) {
margin-bottom: 0;
box-shadow: none;

View File

@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import remcalc from 'remcalc';
import { P } from 'joyent-ui-toolkit';
const StyledStatus = P.extend`
margin: 0;
font-size: ${remcalc(13)};
line-height: ${remcalc(13)};
`;
const StyledStatusContainer = styled.div`
margin: ${remcalc(6)} 0 ${remcalc(12)} 0;
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 = Object.keys(statuses).map(status => {
const instances = statuses[status];
return (
<StyledStatus>
{`${instances}
${instances > 1 ? 'instances' : 'instance'}
${status.toLowerCase()}`}
</StyledStatus>
);
});
return (
<StyledStatusContainer>
{instanceStatuses}
</StyledStatusContainer>
);
};
InstanceStatuses.propTypes = {
instances: PropTypes.array.isRequired
};
export default InstanceStatuses;

View File

@ -1,9 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
// Import forceArray from 'force-array';
// import ItemMetricGroup from '@components/item-metric-group';
import {
Card,
CardView,
@ -14,13 +12,14 @@ import {
CardGroupView,
CardOptions,
CardHeader,
// CardInfo,
CardInfo,
Anchor
// DataCentersIcon,
// HealthyIcon,
// InstancesMultipleIcon
} from 'joyent-ui-toolkit';
import { InstancesIcon, HealthyIcon, P } from 'joyent-ui-toolkit';
import InstanceStatuses from './instance-statuses';
const StyledCardHeader = styled(CardHeader)`
position: relative;
`;
@ -64,34 +63,37 @@ const ServiceListItem = ({
</CardTitle>;
const subtitle = (
<CardSubTitle>{service.instances.length} instances</CardSubTitle>
<CardSubTitle>
{service.instances.length}{' '}
{service.instances.length > 1 ? 'instances' : 'instance'}
</CardSubTitle>
);
const handleCardOptionsClick = evt => {
onQuickActionsClick(evt, service);
};
const statuses = service.instances.map(instance =>
<p>1 instance {instance.status}</p>
);
const instancesCount = service.children
? service.children.reduce(
(count, child) => count + child.instances.length,
0
)
: service.instances.length;
const header = isChild
? null
: <StyledCardHeader>
<CardMeta>
{title}
<CardDescription>
{/* <CardInfo
icon={<InstancesMultipleIcon />}
iconPosition="top"
label={`${service.instances} ${service.instances > 1 ? 'instances' : 'instance'}`}
/> */}
{/* <CardInfo
icon={<DataCentersIcon />}
label={service.datacenters[0].id}
/> */}
</CardDescription>
</CardMeta>
{title}
<CardDescription>
<CardInfo
icon={<InstancesIcon />}
iconPosition="left"
label={`${instancesCount} ${instancesCount > 1
? 'instances'
: 'instance'}`}
color="light"
/>
</CardDescription>
<CardOptions onClick={handleCardOptionsClick} />
</StyledCardHeader>;
@ -100,17 +102,17 @@ const ServiceListItem = ({
{children}
</CardGroupView>
: <CardView>
<CardMeta>
{isChild && title}
{isChild && subtitle}
<CardDescription>
{/* <CardInfo icon={<HealthyIcon />} label="Healthy" /> */}
{statuses}
</CardDescription>
</CardMeta>
{/* <ItemMetricGroup
datasets={service.metrics}
/> */}
{isChild && title}
{isChild && subtitle}
<CardDescription>
<InstanceStatuses instances={service.instances} />
<CardInfo
icon={<HealthyIcon />}
iconPosition="left"
label="Healthy"
color="dark"
/>
</CardDescription>
</CardView>;
return (

View File

@ -10,7 +10,9 @@ const ServicesQuickActions = ({
onBlur,
onRestartClick,
onStopClick,
onStartClick
onStartClick,
onScaleClick,
onDeleteClick
}) => {
if (!show) {
return null;
@ -25,9 +27,6 @@ const ServicesQuickActions = ({
return p;
}, {});
const scaleUrl = `${url}/${service.slug}/scale`;
const deleteUrl = `${url}/${service.slug}/delete`;
const handleRestartClick = evt => {
onRestartClick(evt, service);
};
@ -40,6 +39,14 @@ const ServicesQuickActions = ({
onStopClick(evt, service);
};
const handleScaleClick = evt => {
onScaleClick(evt, service);
};
const handleDeleteClick = evt => {
onDeleteClick(evt, service);
};
const status = service.instances.reduce((status, instance) => {
return status
? instance.status === status ? status : 'MIXED'
@ -56,25 +63,26 @@ const ServicesQuickActions = ({
return (
<Tooltip {...p} onBlur={onBlur}>
<TooltipButton to={scaleUrl}>Scale</TooltipButton>
<TooltipButton onClick={handleScaleClick}>Scale</TooltipButton>
<TooltipButton onClick={handleRestartClick}>Restart</TooltipButton>
{startService}
{stopService}
<TooltipDivider />
<TooltipButton to={deleteUrl}>Delete</TooltipButton>
<TooltipButton onClick={handleDeleteClick}>Delete</TooltipButton>
</Tooltip>
);
};
ServicesQuickActions.propTypes = {
service: PropTypes.object.isRequired,
url: PropTypes.string.isRequired,
position: PropTypes.object,
show: PropTypes.bool,
onBlur: PropTypes.func,
onRestartClick: PropTypes.func,
onStopClick: PropTypes.func,
onStartClick: PropTypes.func
onStartClick: PropTypes.func,
onScaleClick: PropTypes.func,
onDeleteClick: PropTypes.func
};
export default ServicesQuickActions;

View File

@ -38,6 +38,7 @@ class ServiceList extends Component {
servicesQuickActions,
toggleServicesQuickActions,
url,
push,
restartServices,
stopServices,
startServices
@ -89,6 +90,16 @@ class ServiceList extends Component {
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 handleQuickActionsBlur = o => {
toggleServicesQuickActions({ show: false });
};
@ -113,11 +124,12 @@ class ServiceList extends Component {
position={servicesQuickActions.position}
service={servicesQuickActions.service}
show={servicesQuickActions.show}
url={url}
onBlur={handleQuickActionsBlur}
onRestartClick={handleRestartClick}
onStopClick={handleStopClick}
onStartClick={handleStartClick}
onScaleClick={handleScaleClick}
onDeleteClick={handleDeleteClick}
/>
</div>
</StyledContainer>
@ -128,7 +140,8 @@ class ServiceList extends Component {
const mapStateToProps = (state, ownProps) => ({
servicesQuickActions: state.ui.services.quickActions,
url: ownProps.match.url.replace(/\/$/, '')
url: ownProps.match.url.replace(/\/$/, ''),
push: ownProps.history.push
});
const mapDispatchToProps = dispatch => ({

View File

@ -67,11 +67,28 @@ const Router = (
<Route path="/" exact component={rootRedirect} />
<Route path="/deployment-groups" exact component={DeploymentGroupList} />
<Route
path="/deployment-groups/:deploymentGroup"
exact
component={deploymentGroupRedirect}
/>
<Route
path="/deployment-groups/:deploymentGroup/services/:service"
exact
component={serviceRedirect}
/>
<Route
path="/deployment-groups/:deploymentGroup/services-list"
component={ServicesMenu}
/>
<Route
path="/deployment-groups/:deploymentGroup/services-topology"
component={ServicesMenu}
/>
<Switch>
<Route
path="/deployment-groups/~create/:stage?"
@ -83,11 +100,7 @@ const Router = (
exact
component={DeploymentGroupImport}
/>
<Route
path="/deployment-groups/:deploymentGroup"
exact
component={deploymentGroupRedirect}
/>
<Route
path="/deployment-groups/:deploymentGroup/services"
exact
@ -128,17 +141,13 @@ const Router = (
exact
component={ServiceScale}
/>
<Route
path="/deployment-groups/:deploymentGroup/services-list/:service/delete"
exact
component={ServiceDelete}
/>
<Route
path="/deployment-groups/:deploymentGroup/services-topology"
component={ServicesMenu}
/>
<Route
path="/deployment-groups/:deploymentGroup/services-topology/:service/scale"
exact
@ -150,12 +159,6 @@ const Router = (
component={ServiceDelete}
/>
<Route
path="/deployment-groups/:deploymentGroup/services/:service"
exact
component={serviceRedirect}
/>
</Container>
</BrowserRouter>
);

View File

@ -16,6 +16,7 @@ const StyledCard = Row.extend`
border: ${remcalc(1)} solid ${props => props.theme.grey};
background-color: ${props => props.theme.white};
box-shadow: ${bottomShaddow};
flex-direction: column;
${is('collapsed')`
min-height: auto;

View File

@ -15,10 +15,7 @@ const StyledTitle = Title.extend`
flex-grow: 2;
${isNot('collapsed')`
position: absolute;
bottom: 0;
padding-bottom: ${remcalc(12)};
padding-top: 0;
`};
`;

View File

@ -5,7 +5,7 @@ import React from 'react';
const StyledView = View.extend`
display: block;
padding: ${remcalc(62, 23, 5, 23)};
padding: ${remcalc(12, 24, 6, 24)};
background-color: ${props => props.grey};
`;

View File

@ -7,6 +7,7 @@ import Card from './card';
const StyledCard = Card.extend`
position: absolute;
flex-direction: row;
background-color: ${props => props.theme.primary};
border: solid ${remcalc(1)} ${props => props.theme.primaryDesaturatedActive};

View File

@ -8,4 +8,4 @@ export { default as CardOutlet } from './outlet.js';
export { default as CardSubTitle } from './subtitle.js';
export { default as CardTitle } from './title.js';
export { default as CardView } from './view.js';
// Export { default as CardInfo } from './info.js';
export { default as CardInfo } from './info.js';

View File

@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import remcalc from 'remcalc';
import Label from '../label';
const StyledLabel = Label.extend`
${props => (props.color === 'light' ? `color: ${props.theme.white};` : '')};
margin-left: ${props => (props.iconPosition === 'left' ? remcalc(24) : 0)}
`;
const StyledIconContainer = styled.div`
position: absolute;
> svg {
${props => (props.color === 'light' ? `fill: ${props.theme.white};` : '')};
}
`;
const CardInfo = ({ label, icon, iconPosition = 'left', color = 'light' }) => {
return (
<div>
<StyledIconContainer iconPosition={iconPosition} color={color}>
{icon}
</StyledIconContainer>
<StyledLabel iconPosition={iconPosition} color={color}>
{label}
</StyledLabel>
</div>
);
};
CardInfo.propTypes = {
label: PropTypes.string.isRequired,
icon: PropTypes.node.isRequired,
iconPosition: PropTypes.string,
color: PropTypes.oneOf(['dark', 'light'])
};
export default CardInfo;

View File

@ -12,8 +12,9 @@ const StyledView = Row.extend`
height: auto;
padding-top: 0;
min-width: auto;
flex-direction: row;
${is('headed')`
/*${is('headed')`
padding-top: ${remcalc(47)};
`};
@ -23,7 +24,7 @@ const StyledView = Row.extend`
${is('fromHeader')`
padding-top: 0;
`};
`};*/
`;
const View = ({ children, ...rest }) => {

View File

@ -0,0 +1,7 @@
import Baseline from '../baseline';
// eslint-disable-next-line no-unused-vars
import React from 'react';
import HealthyIcon from './svg/icon_healthy.svg';
export default Baseline(HealthyIcon);

View File

@ -3,3 +3,5 @@ export { default as PlusIcon } from './plus';
export { default as MinusIcon } from './minus';
export { default as ArrowIcon } from './arrow';
export { default as TickIcon } from './tick';
export { default as InstancesIcon } from './instances';
export { default as HealthyIcon } from './healthy';

View File

@ -0,0 +1,7 @@
import Baseline from '../baseline';
// eslint-disable-next-line no-unused-vars
import React from 'react';
import InstancesIcon from './svg/icon_instances.svg';
export default Baseline(InstancesIcon);

View File

@ -4,8 +4,8 @@
<title>icon: instances</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="topology" transform="translate(-1046.000000, -447.000000)" fill="#494949">
<g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd">
<g id="topology" transform="translate(-1046.000000, -447.000000)">
<g id="services" transform="translate(0.000000, 348.000000)">
<g id="service:-consul" transform="translate(1003.000000, 36.000000)">
<g id="metric">
@ -17,4 +17,4 @@
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -58,7 +58,8 @@ export {
CardOutlet,
CardSubTitle,
CardTitle,
CardView
CardView,
CardInfo
} from './card';
export {
@ -91,3 +92,12 @@ export {
Link as SectionListLink,
NavLink as SectionListNavLink
} from './section-list';
export {
CloseIcon,
PlusIcon,
MinusIcon,
ArrowIcon,
InstancesIcon,
HealthyIcon
} from './icons';