Add services list view

This commit is contained in:
JUDIT GRESKOVITS 2017-05-18 22:04:29 +01:00 committed by Sérgio Ramos
parent 7d39da51ed
commit db4ac69452
7 changed files with 262 additions and 76 deletions

View File

@ -1 +1,2 @@
export { default as EmptyServices } from './empty'; export { default as EmptyServices } from './empty';
export { default as ServiceListItem } from './list-item';

View File

@ -0,0 +1,145 @@
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 { Link } from '@ui/components/anchor';
import { Checkbox, FormGroup } from '@ui/components/form';
import {
DataCentersIcon,
HealthyIcon,
InstancesMultipleIcon
} from '@ui/components/icons';
import {
ListItem,
ListItemView,
ListItemMeta,
ListItemTitle,
ListItemSubTitle,
ListItemDescription,
ListItemGroupView,
ListItemOptions,
ListItemHeader,
ListItemInfo
} from '@ui/components/list';
const StyledFormGroup = styled(FormGroup)`
width: auto;
`;
const TitleInnerContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
`;
const ServiceListItem = ({
// onQuickActions=() => {},
deploymentGroup = '',
service = {}
}) => {
const isChild = !!service.parent;
const children = service.children ?
service.children.map((service) => (
<ServiceListItem
key={service.uuid}
deploymentGroup={deploymentGroup}
service={service}
/>
)) : null;
const to = `/deploymentGroups/${deploymentGroup}/services/${service.id}`;
const title = service.parent ? (
<ListItemTitle>
{service.name}
</ListItemTitle>
) : (
<ListItemTitle>
<TitleInnerContainer>
<StyledFormGroup>
<Checkbox />
</StyledFormGroup>
<Link secondary to={to}>
{service.name}
</Link>
</TitleInnerContainer>
</ListItemTitle>
);
const subtitle = (
<ListItemSubTitle>{service.instances} instances</ListItemSubTitle>
);
const onOptionsClick = (evt) => {
// onQuickActions(evt, service.uuid);
};
const header = isChild ? null : (
<ListItemHeader>
<ListItemMeta>
{title}
<ListItemDescription>
<ListItemInfo
icon={<InstancesMultipleIcon />}
iconPosition='top'
label={`${service.instances} ${service.instances > 1 ?
'instances' : 'instance' }`}
/>
{ /* <ListItemInfo
icon={<DataCentersIcon />}
label={service.datacenters[0].id}
/>*/ }
</ListItemDescription>
</ListItemMeta>
<ListItemOptions onClick={onOptionsClick} />
</ListItemHeader>
);
const view = children ? (
<ListItemGroupView>
{children}
</ListItemGroupView>
) : (
<ListItemView>
<ListItemMeta>
{isChild && title}
{isChild && subtitle}
<ListItemDescription>
<ListItemInfo
icon={<HealthyIcon />}
label='Healthy'
/>
</ListItemDescription>
</ListItemMeta>
{ /* <ItemMetricGroup
datasets={service.metrics}
/> */ }
</ListItemView>
);
return (
<ListItem
collapsed={service.collapsed}
flat={isChild}
headed={!isChild}
key={service.uuid}
stacked={isChild && (service.instances > 1)}
>
{header}
{view}
</ListItem>
);
};
ServiceListItem.propTypes = {
// onQuickActions: React.PropTypes.func,
deploymentGroup: React.PropTypes.string,
service: PropTypes.object.isRequired // define better
};
export default ServiceListItem;

View File

@ -1,43 +1,67 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import { Link } from 'react-router-dom'; import { connect } from 'react-redux';
import styled from 'styled-components';
// import { Link } from 'react-router-dom';
import PortalQuery from '@graphql/Portal.gql';
import ServicesQuery from '@graphql/Services.gql'; import ServicesQuery from '@graphql/Services.gql';
import { processServices } from '@root/state/selectors';
import { LayoutContainer } from '@components/layout';
import { ServiceListItem } from '@components/services';
const StyledContainer = styled.div`
position: relative;
`;
class ServiceList extends Component { class ServiceList extends Component {
render() { render() {
const { const {
location, location,
deploymentGroup,
services, services,
loading, loading,
error error
} = this.props; } = this.props;
const serviceList = console.log('services = ', services);
loading ? <p>Loading...</p> :
error ? <p>Error!!!</p> : if(loading || error) {
services.map((service, index) => return (
<p key={index}> <p>Loading OR error</p>
<Link );
to={`${location.pathname.replace('services-list', 'services')}/${service.slug}/instances`} }
>
{service.name} const serviceList = services.map((service) => (
</Link> <ServiceListItem
</p>); key={service.uuid}
onQuickActions={null /*onQuickActions*/}
deploymentGroup={deploymentGroup.slug}
service={service}
uiTooltip={null /* uiTooltip */}
/>
));
return ( return (
<LayoutContainer>
<StyledContainer>
<div> <div>
<div> { /* <div ref={this.ref('container')}> */ }
<h2>Service List</h2> {serviceList}
</div> { /* <ServicesTooltip {...uiTooltip} onBlur={handleTooltipBlur} /> */ }
{ serviceList }
</div> </div>
</StyledContainer>
</LayoutContainer>
); );
} }
} }
const ServiceListWithData = graphql(ServicesQuery, { const PortalGql = graphql(PortalQuery, {});
const ServicesGql = graphql(ServicesQuery, {
options(props) { options(props) {
return { return {
variables: { variables: {
@ -46,10 +70,17 @@ const ServiceListWithData = graphql(ServicesQuery, {
} }
}, },
props: ({ data: { deploymentGroup, loading, error }}) => ({ props: ({ data: { deploymentGroup, loading, error }}) => ({
services: deploymentGroup ? deploymentGroup.services : null, deploymentGroup,
services: deploymentGroup ?
processServices(deploymentGroup.services, null) : null,
loading, loading,
error error
}) })
})(ServiceList); });
const ServiceListWithData = compose(
PortalGql,
ServicesGql
)(ServiceList);
export default ServiceListWithData; export default ServiceListWithData;

View File

@ -5,6 +5,8 @@ import styled from 'styled-components';
import PortalQuery from '@graphql/Portal.gql'; import PortalQuery from '@graphql/Portal.gql';
import ServicesQuery from '@graphql/ServicesTopology.gql'; import ServicesQuery from '@graphql/ServicesTopology.gql';
import { processServices } from '@root/state/selectors';
import { LayoutContainer } from '@components/layout'; import { LayoutContainer } from '@components/layout';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ErrorMessage } from '@components/messaging';
@ -49,61 +51,18 @@ const ServicesTopology = ({
) )
} }
const findService = (ss, uuid) =>
ss.reduce((s, service) => service.uuid === uuid ?
service : s, null);
const getService = (s, i) => ({
index: i,
...s,
metrics: [1, 2, 3].map((m) => ({
name: `${m}`,
value: `${m}`
})),
instances: s.instances.length,
datacenter: datacenter
});
// Selector???
const ss = services.reduce((ss, s, i) => {
// check whether it exits in thing, if so, add as child
// if not, create and add as child
if(s.parent) {
let parent = findService(ss, s.parent);
if(!parent) {
parent = { uuid: s.parent };
ss.push(parent);
}
if(!parent.children) {
parent.children = [];
}
parent.children.push(getService(s, i));
}
if(!s.parent) {
ss.push(getService(s, i));
}
return ss;
}, []);
return ( return (
<StyledBackground> <StyledBackground>
<StyledContainer> <StyledContainer>
<TopologyGraph <TopologyGraph
services={ss} services={services}
/> />
</StyledContainer> </StyledContainer>
</StyledBackground> </StyledBackground>
); );
} }
const PortalGql = graphql(PortalQuery, { const PortalGql = graphql(PortalQuery, {});
props: ({ data: { portal, loading, error }}) => ({
datacenter: portal ? portal.datacenter.id : null,
loading,
error
})
})
const ServicesGql = graphql(ServicesQuery, { const ServicesGql = graphql(ServicesQuery, {
options(props) { options(props) {
@ -114,7 +73,8 @@ const ServicesGql = graphql(ServicesQuery, {
} }
}, },
props: ({ data: { deploymentGroup, loading, error }}) => ({ props: ({ data: { deploymentGroup, loading, error }}) => ({
services: deploymentGroup ? deploymentGroup.services : null, services: deploymentGroup ?
processServices(deploymentGroup.services, null) : null,
loading, loading,
error error
}) })

View File

@ -6,6 +6,10 @@ query Services($deploymentGroupSlug: String!){
...DeploymentGroupInfo ...DeploymentGroupInfo
services { services {
...ServiceInfo ...ServiceInfo
parent
instances {
uuid
}
} }
} }
} }

View File

@ -45,11 +45,11 @@ const Router = (
<Route path='/deployment-groups/:deploymentGroup/services' exact component={deploymentGroupRedirect} /> <Route path='/deployment-groups/:deploymentGroup/services' exact component={deploymentGroupRedirect} />
<Route path='/deployment-groups/:deploymentGroup/instances' exact component={InstanceList} /> <Route path='/deployment-groups/:deploymentGroup/instances' exact component={InstanceList} />
<Route path={`/deployment-groups/:deploymentGroup/services-list`} exact component={ServicesMenu} /> <Route path='/deployment-groups/:deploymentGroup/services-list' exact component={ServicesMenu} />
<Route path='/deployment-groups/:deploymentGroup/services-list' exact component={ServiceList} /> <Route path='/deployment-groups/:deploymentGroup/services-list' exact component={ServiceList} />
<Route path={`/deployment-groups/:deploymentGroup/services-topology`} exact component={ServicesMenu} /> <Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesMenu} />
<Route path={`/deployment-groups/:deploymentGroup/services-topology`} exact component={ServicesTopology} /> <Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesTopology} />
<Route path='/deployment-groups/:deploymentGroup/services/:service/instances' exact component={InstanceList} /> <Route path='/deployment-groups/:deploymentGroup/services/:service/instances' exact component={InstanceList} />

View File

@ -2,7 +2,9 @@ import { createSelector } from 'reselect';
const apollo = (state) => state.apollo; const apollo = (state) => state.apollo;
const deploymentGroupById = (deploymentGroupSlug) => createSelector( // redux selectors //
const deploymentGroupBySlug = (deploymentGroupSlug) => createSelector(
[apollo], [apollo],
(apollo) => apollo ? Object.keys(apollo).reduce((dg, k) => (apollo) => apollo ? Object.keys(apollo).reduce((dg, k) =>
apollo[k].__typename === 'DeploymentGroup' && apollo[k].__typename === 'DeploymentGroup' &&
@ -10,7 +12,7 @@ const deploymentGroupById = (deploymentGroupSlug) => createSelector(
apollo[k] : dg, {}) : null apollo[k] : dg, {}) : null
); );
const servicesById = (serviceSlug) => createSelector( const servicesBySlug = (serviceSlug) => createSelector(
[apollo], [apollo],
(apollo) => apollo ? Object.keys(apollo).reduce((s, k) => (apollo) => apollo ? Object.keys(apollo).reduce((s, k) =>
apollo[k].__typename === 'Service' && apollo[k].__typename === 'Service' &&
@ -18,7 +20,50 @@ const servicesById = (serviceSlug) => createSelector(
apollo[k] : s, {}) : null apollo[k] : s, {}) : null
); );
export { // apollo gql utils //
deploymentGroupById as deploymentGroupByIdSelector,
servicesById as servicesByIdSelector const findService = (services, uuid) =>
services.reduce((service, s) => s.uuid === uuid ?
s : service, null);
const getService = (service, index, datacenter) => ({
index,
...service,
// tmp for topology
metrics: [1, 2, 3].map((m) => ({
name: `${m}`,
value: `${m}`
})),
instances: service.instances.length,
datacenter
});
const processServices = (services, datacenter) => {
console.log('services = ', services);
return services.reduce((ss, s, i) => {
// check whether it exits in thing, if so, add as child
// if not, create and add as child
if(s.parent) {
let parent = findService(ss, s.parent);
if(!parent) {
parent = { uuid: s.parent };
ss.push(parent);
}
if(!parent.children) {
parent.children = [];
}
parent.children.push(getService(s, i, datacenter));
}
if(!s.parent) {
ss.push(getService(s, i, datacenter));
}
return ss;
}, []);
}
export {
deploymentGroupBySlug as deploymentGroupBySlugSelector,
servicesBySlug as servicesBySlugSelector,
processServices as processServices
} }