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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import Card from './card';
const StyledCard = Card.extend` const StyledCard = Card.extend`
position: absolute; position: absolute;
flex-direction: row;
background-color: ${props => props.theme.primary}; background-color: ${props => props.theme.primary};
border: solid ${remcalc(1)} ${props => props.theme.primaryDesaturatedActive}; 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 CardSubTitle } from './subtitle.js';
export { default as CardTitle } from './title.js'; export { default as CardTitle } from './title.js';
export { default as CardView } from './view.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; height: auto;
padding-top: 0; padding-top: 0;
min-width: auto; min-width: auto;
flex-direction: row;
${is('headed')` /*${is('headed')`
padding-top: ${remcalc(47)}; padding-top: ${remcalc(47)};
`}; `};
@ -23,7 +24,7 @@ const StyledView = Row.extend`
${is('fromHeader')` ${is('fromHeader')`
padding-top: 0; padding-top: 0;
`}; `};*/
`; `;
const View = ({ children, ...rest }) => { 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 MinusIcon } from './minus';
export { default as ArrowIcon } from './arrow'; export { default as ArrowIcon } from './arrow';
export { default as TickIcon } from './tick'; 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> <title>icon: instances</title>
<desc>Created with Sketch.</desc> <desc>Created with Sketch.</desc>
<defs></defs> <defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd">
<g id="topology" transform="translate(-1046.000000, -447.000000)" fill="#494949"> <g id="topology" transform="translate(-1046.000000, -447.000000)">
<g id="services" transform="translate(0.000000, 348.000000)"> <g id="services" transform="translate(0.000000, 348.000000)">
<g id="service:-consul" transform="translate(1003.000000, 36.000000)"> <g id="service:-consul" transform="translate(1003.000000, 36.000000)">
<g id="metric"> <g id="metric">
@ -17,4 +17,4 @@
</g> </g>
</g> </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, CardOutlet,
CardSubTitle, CardSubTitle,
CardTitle, CardTitle,
CardView CardView,
CardInfo
} from './card'; } from './card';
export { export {
@ -91,3 +92,12 @@ export {
Link as SectionListLink, Link as SectionListLink,
NavLink as SectionListNavLink NavLink as SectionListNavLink
} from './section-list'; } from './section-list';
export {
CloseIcon,
PlusIcon,
MinusIcon,
ArrowIcon,
InstancesIcon,
HealthyIcon
} from './icons';