diff --git a/packages/cp-frontend/src/components/instances/list-item.js b/packages/cp-frontend/src/components/instances/list-item.js index 3a711528..41d3cf77 100644 --- a/packages/cp-frontend/src/components/instances/list-item.js +++ b/packages/cp-frontend/src/components/instances/list-item.js @@ -85,6 +85,8 @@ const StatusBadge = ({ status }) => { }; const StyledCard = Card.extend` + flex-direction: row; + &:not(:last-child) { margin-bottom: 0; box-shadow: none; diff --git a/packages/cp-frontend/src/components/services/instance-statuses.js b/packages/cp-frontend/src/components/services/instance-statuses.js new file mode 100644 index 00000000..412f316e --- /dev/null +++ b/packages/cp-frontend/src/components/services/instance-statuses.js @@ -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 ( + + {`${instances} + ${instances > 1 ? 'instances' : 'instance'} + ${status.toLowerCase()}`} + + ); + }); + + return ( + + {instanceStatuses} + + ); +}; + +InstanceStatuses.propTypes = { + instances: PropTypes.array.isRequired +}; + +export default InstanceStatuses; diff --git a/packages/cp-frontend/src/components/services/list-item.js b/packages/cp-frontend/src/components/services/list-item.js index ca2a91a0..414668f8 100644 --- a/packages/cp-frontend/src/components/services/list-item.js +++ b/packages/cp-frontend/src/components/services/list-item.js @@ -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 = ({ ; const subtitle = ( - {service.instances.length} instances + + {service.instances.length}{' '} + {service.instances.length > 1 ? 'instances' : 'instance'} + ); const handleCardOptionsClick = evt => { onQuickActionsClick(evt, service); }; - const statuses = service.instances.map(instance => -

1 instance {instance.status}

- ); + const instancesCount = service.children + ? service.children.reduce( + (count, child) => count + child.instances.length, + 0 + ) + : service.instances.length; const header = isChild ? null : - - {title} - - {/* } - iconPosition="top" - label={`${service.instances} ${service.instances > 1 ? 'instances' : 'instance'}`} - /> */} - {/* } - label={service.datacenters[0].id} - /> */} - - + {title} + + } + iconPosition="left" + label={`${instancesCount} ${instancesCount > 1 + ? 'instances' + : 'instance'}`} + color="light" + /> + ; @@ -100,17 +102,17 @@ const ServiceListItem = ({ {children} : - - {isChild && title} - {isChild && subtitle} - - {/* } label="Healthy" /> */} - {statuses} - - - {/* */} + {isChild && title} + {isChild && subtitle} + + + } + iconPosition="left" + label="Healthy" + color="dark" + /> + ; return ( diff --git a/packages/cp-frontend/src/components/services/quick-actions.js b/packages/cp-frontend/src/components/services/quick-actions.js index 4c341201..23f1efd9 100644 --- a/packages/cp-frontend/src/components/services/quick-actions.js +++ b/packages/cp-frontend/src/components/services/quick-actions.js @@ -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 ( - Scale + Scale Restart {startService} {stopService} - Delete + Delete ); }; 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; diff --git a/packages/cp-frontend/src/containers/services/list.js b/packages/cp-frontend/src/containers/services/list.js index 690a6974..270d5b4d 100644 --- a/packages/cp-frontend/src/containers/services/list.js +++ b/packages/cp-frontend/src/containers/services/list.js @@ -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} /> @@ -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 => ({ diff --git a/packages/cp-frontend/src/router.js b/packages/cp-frontend/src/router.js index 41458742..5676e7b6 100644 --- a/packages/cp-frontend/src/router.js +++ b/packages/cp-frontend/src/router.js @@ -67,11 +67,28 @@ const Router = ( + + + + + + - + + - - - - ); diff --git a/packages/ui-toolkit/src/card/card.js b/packages/ui-toolkit/src/card/card.js index 6b13cc29..14a98963 100644 --- a/packages/ui-toolkit/src/card/card.js +++ b/packages/ui-toolkit/src/card/card.js @@ -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; diff --git a/packages/ui-toolkit/src/card/description.js b/packages/ui-toolkit/src/card/description.js index 0db7b155..bf3161e3 100644 --- a/packages/ui-toolkit/src/card/description.js +++ b/packages/ui-toolkit/src/card/description.js @@ -15,10 +15,7 @@ const StyledTitle = Title.extend` flex-grow: 2; ${isNot('collapsed')` - position: absolute; - bottom: 0; padding-bottom: ${remcalc(12)}; - padding-top: 0; `}; `; diff --git a/packages/ui-toolkit/src/card/group-view.js b/packages/ui-toolkit/src/card/group-view.js index 8fe287a3..a7ede584 100644 --- a/packages/ui-toolkit/src/card/group-view.js +++ b/packages/ui-toolkit/src/card/group-view.js @@ -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}; `; diff --git a/packages/ui-toolkit/src/card/header.js b/packages/ui-toolkit/src/card/header.js index 21065c8a..ea0c8d6b 100644 --- a/packages/ui-toolkit/src/card/header.js +++ b/packages/ui-toolkit/src/card/header.js @@ -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}; diff --git a/packages/ui-toolkit/src/card/index.js b/packages/ui-toolkit/src/card/index.js index 8b4b0709..2ddc34b4 100644 --- a/packages/ui-toolkit/src/card/index.js +++ b/packages/ui-toolkit/src/card/index.js @@ -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'; diff --git a/packages/ui-toolkit/src/card/info.js b/packages/ui-toolkit/src/card/info.js new file mode 100644 index 00000000..b6abcdc0 --- /dev/null +++ b/packages/ui-toolkit/src/card/info.js @@ -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 ( +
+ + {icon} + + + {label} + +
+ ); +}; + +CardInfo.propTypes = { + label: PropTypes.string.isRequired, + icon: PropTypes.node.isRequired, + iconPosition: PropTypes.string, + color: PropTypes.oneOf(['dark', 'light']) +}; + +export default CardInfo; diff --git a/packages/ui-toolkit/src/card/view.js b/packages/ui-toolkit/src/card/view.js index 67acf644..566dc247 100644 --- a/packages/ui-toolkit/src/card/view.js +++ b/packages/ui-toolkit/src/card/view.js @@ -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 }) => { diff --git a/packages/ui-toolkit/src/icons/healthy.js b/packages/ui-toolkit/src/icons/healthy.js new file mode 100644 index 00000000..c8124e89 --- /dev/null +++ b/packages/ui-toolkit/src/icons/healthy.js @@ -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); diff --git a/packages/ui-toolkit/src/icons/index.js b/packages/ui-toolkit/src/icons/index.js index 4a0a5a90..b11b14f8 100644 --- a/packages/ui-toolkit/src/icons/index.js +++ b/packages/ui-toolkit/src/icons/index.js @@ -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'; diff --git a/packages/ui-toolkit/src/icons/instances.js b/packages/ui-toolkit/src/icons/instances.js new file mode 100644 index 00000000..b184a672 --- /dev/null +++ b/packages/ui-toolkit/src/icons/instances.js @@ -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); diff --git a/packages/ui-toolkit/src/icons/svg/icon_instances.svg b/packages/ui-toolkit/src/icons/svg/icon_instances.svg index fe15312e..642dd9e3 100755 --- a/packages/ui-toolkit/src/icons/svg/icon_instances.svg +++ b/packages/ui-toolkit/src/icons/svg/icon_instances.svg @@ -4,8 +4,8 @@ icon: instances Created with Sketch. - - + + @@ -17,4 +17,4 @@ - \ No newline at end of file + diff --git a/packages/ui-toolkit/src/index.js b/packages/ui-toolkit/src/index.js index 375a2ab3..33aeab97 100644 --- a/packages/ui-toolkit/src/index.js +++ b/packages/ui-toolkit/src/index.js @@ -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';