feat(ui-toolkit, cp-frontend): Fix services list
This commit is contained in:
parent
40f666adc2
commit
ef6c166c56
@ -85,6 +85,8 @@ const StatusBadge = ({ status }) => {
|
||||
};
|
||||
|
||||
const StyledCard = Card.extend`
|
||||
flex-direction: row;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
|
@ -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;
|
@ -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 (
|
||||
|
@ -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;
|
||||
|
@ -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 => ({
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -15,10 +15,7 @@ const StyledTitle = Title.extend`
|
||||
flex-grow: 2;
|
||||
|
||||
${isNot('collapsed')`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding-bottom: ${remcalc(12)};
|
||||
padding-top: 0;
|
||||
`};
|
||||
`;
|
||||
|
||||
|
@ -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};
|
||||
`;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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';
|
||||
|
40
packages/ui-toolkit/src/card/info.js
Normal file
40
packages/ui-toolkit/src/card/info.js
Normal 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;
|
@ -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 }) => {
|
||||
|
7
packages/ui-toolkit/src/icons/healthy.js
Normal file
7
packages/ui-toolkit/src/icons/healthy.js
Normal 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);
|
@ -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';
|
||||
|
7
packages/ui-toolkit/src/icons/instances.js
Normal file
7
packages/ui-toolkit/src/icons/instances.js
Normal 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);
|
@ -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">
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user