mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 06:00:06 +02:00
feat(cp-gql-mock-server, cp-frontend): Add missing dg and service error messaging
This commit is contained in:
parent
0917d67b07
commit
2eb7f4197f
@ -1,3 +1,4 @@
|
|||||||
export { default as Loader } from './loader';
|
export { default as Loader } from './loader';
|
||||||
export { default as ErrorMessage } from './error';
|
export { default as ErrorMessage } from './error';
|
||||||
export { default as WarningMessage } from './warning';
|
export { default as WarningMessage } from './warning';
|
||||||
|
export { default as ModalErrorMessage } from './modal-error';
|
||||||
|
24
packages/cp-frontend/src/components/messaging/modal-error.js
Normal file
24
packages/cp-frontend/src/components/messaging/modal-error.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { ModalHeading, ModalText, Button } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
const StyledHeading = styled(ModalHeading)`
|
||||||
|
color: ${props => props.theme.red};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ModalErrorMessage = ({ title, message, onCloseClick }) =>
|
||||||
|
<div>
|
||||||
|
<StyledHeading>{title}</StyledHeading>
|
||||||
|
<ModalText marginBottom="3">{message}
|
||||||
|
</ModalText>
|
||||||
|
<Button onClick={onCloseClick} secondary>Close </Button>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
ModalErrorMessage.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
message: PropTypes.string.isRequired,
|
||||||
|
onCloseClick: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalErrorMessage;
|
@ -2,3 +2,4 @@ export { default as Breadcrumb } from './breadcrumb';
|
|||||||
export { default as Menu } from './menu';
|
export { default as Menu } from './menu';
|
||||||
export { default as Header } from './header';
|
export { default as Header } from './header';
|
||||||
export { default as Title } from './title';
|
export { default as Title } from './title';
|
||||||
|
export { default as NotFound } from './not-found';
|
||||||
|
44
packages/cp-frontend/src/components/navigation/not-found.js
Normal file
44
packages/cp-frontend/src/components/navigation/not-found.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import { H1, H2, P, Button } from 'joyent-ui-toolkit';
|
||||||
|
import { LayoutContainer } from '@components/layout';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
margin-top: ${remcalc(60)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTitle = styled(H1)`
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: ${remcalc(32)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledP = styled(P)`
|
||||||
|
margin-bottom: ${remcalc(30)};
|
||||||
|
max-width: ${remcalc(490)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const NotFound = ({
|
||||||
|
title = 'I have no memory of this place',
|
||||||
|
message = 'HTTP 404: We can’t find what you are looking for. Next time, always follow your nose.',
|
||||||
|
link = 'Back to dashboard',
|
||||||
|
to = '/deployment-groups'
|
||||||
|
}) => (
|
||||||
|
<LayoutContainer>
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledTitle>{title}</StyledTitle>
|
||||||
|
<StyledP>{message}</StyledP>
|
||||||
|
<Button to={to}>{link}</Button>
|
||||||
|
</StyledContainer>
|
||||||
|
</LayoutContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
NotFound.propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
message: PropTypes.string,
|
||||||
|
link: PropTypes.string,
|
||||||
|
to: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFound;
|
@ -3,9 +3,10 @@ import PropTypes from 'prop-types';
|
|||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutation.gql';
|
import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutation.gql';
|
||||||
import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql';
|
import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ModalErrorMessage } from '@components/messaging';
|
||||||
import { DeploymentGroupDelete as DeploymentGroupDeleteComponent } from '@components/deployment-group';
|
import { DeploymentGroupDelete as DeploymentGroupDeleteComponent } from '@components/deployment-group';
|
||||||
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
|
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit'
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
class DeploymentGroupDelete extends Component {
|
class DeploymentGroupDelete extends Component {
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ class DeploymentGroupDelete extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, error } = this.props;
|
const { location, history, match, loading, error } = this.props;
|
||||||
|
|
||||||
const handleCloseClick = evt => {
|
const handleCloseClick = evt => {
|
||||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||||
@ -36,32 +37,26 @@ class DeploymentGroupDelete extends Component {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ErrorMessage
|
<ModalErrorMessage
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occurred while loading your deployment group.' />
|
message='An error occurred while loading your deployment group.'
|
||||||
|
onCloseClick={handleCloseClick} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
deploymentGroup,
|
deploymentGroup,
|
||||||
deleteDeploymentGroup,
|
deleteDeploymentGroup
|
||||||
history,
|
|
||||||
match
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ModalHeading>
|
<ModalErrorMessage
|
||||||
Deleting a deployment group: <br /> {deploymentGroup.name}
|
|
||||||
</ModalHeading>
|
|
||||||
<ErrorMessage
|
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occurred while attempting to delete your deployment group.' />
|
message={`An error occured while attempting to delete the ${deploymentGroup.name} deployment group.`}
|
||||||
<Button onClick={handleCloseClick} secondary>
|
onCloseClick={handleCloseClick} />
|
||||||
Ok
|
|
||||||
</Button>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -70,6 +65,7 @@ class DeploymentGroupDelete extends Component {
|
|||||||
deleteDeploymentGroup(deploymentGroup.id)
|
deleteDeploymentGroup(deploymentGroup.id)
|
||||||
.then(() => handleCloseClick())
|
.then(() => handleCloseClick())
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
console.log('err = ', err);
|
||||||
this.setState({ error: err });
|
this.setState({ error: err });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -120,7 +116,8 @@ const DeploymentGroupGql = graphql(DeploymentGroupQuery, {
|
|||||||
|
|
||||||
const DeploymentGroupDeleteWithData = compose(
|
const DeploymentGroupDeleteWithData = compose(
|
||||||
DeleteDeploymentGroupGql,
|
DeleteDeploymentGroupGql,
|
||||||
DeploymentGroupGql
|
DeploymentGroupGql,
|
||||||
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
)(DeploymentGroupDelete);
|
)(DeploymentGroupDelete);
|
||||||
|
|
||||||
export default DeploymentGroupDeleteWithData;
|
export default DeploymentGroupDeleteWithData;
|
||||||
|
@ -13,6 +13,7 @@ import { ErrorMessage, Loader } from '@components/messaging';
|
|||||||
import DeploymentGroupsQuery from '@graphql/DeploymentGroups.gql';
|
import DeploymentGroupsQuery from '@graphql/DeploymentGroups.gql';
|
||||||
import DeploymentGroupsImportableQuery from '@graphql/DeploymentGroupsImportable.gql';
|
import DeploymentGroupsImportableQuery from '@graphql/DeploymentGroupsImportable.gql';
|
||||||
import { H2, H3, Small, IconButton, BinIcon } from 'joyent-ui-toolkit';
|
import { H2, H3, Small, IconButton, BinIcon } from 'joyent-ui-toolkit';
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
const DGsRows = Row.extend`
|
const DGsRows = Row.extend`
|
||||||
margin-top: ${remcalc(-7)};
|
margin-top: ${remcalc(-7)};
|
||||||
@ -216,5 +217,6 @@ export default compose(
|
|||||||
props: ({ data: { importableDeploymentGroups } }) => ({
|
props: ({ data: { importableDeploymentGroups } }) => ({
|
||||||
importable: importableDeploymentGroups
|
importable: importableDeploymentGroups
|
||||||
})
|
})
|
||||||
})
|
}),
|
||||||
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
)(DeploymentGroupList);
|
)(DeploymentGroupList);
|
||||||
|
@ -11,6 +11,8 @@ import { Title } from '@components/navigation';
|
|||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ErrorMessage } from '@components/messaging';
|
||||||
import { InstanceListItem, EmptyInstances } from '@components/instances';
|
import { InstanceListItem, EmptyInstances } from '@components/instances';
|
||||||
|
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
|
const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
|
||||||
const _title = <Title>Instances</Title>;
|
const _title = <Title>Instances</Title>;
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ const InstanceListGql = graphql(InstancesQuery, {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: ({ data: { deploymentGroup, loading, error } }) => ({
|
props: ({ data: { deploymentGroup, loading, error }}) => ({
|
||||||
deploymentGroup,
|
deploymentGroup,
|
||||||
instances: sortBy(
|
instances: sortBy(
|
||||||
forceArray(
|
forceArray(
|
||||||
@ -92,4 +94,10 @@ const InstanceListGql = graphql(InstancesQuery, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export default compose(InstanceListGql)(InstanceList);
|
export default compose(
|
||||||
|
InstanceListGql,
|
||||||
|
withNotFound([
|
||||||
|
GqlPaths.DEPLOYMENT_GROUP,
|
||||||
|
GqlPaths.SERVICES
|
||||||
|
])
|
||||||
|
)(InstanceList);
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { compose } from 'react-apollo';
|
||||||
import { Breadcrumb as BreadcrumbComponent } from '@components/navigation';
|
import { Breadcrumb as BreadcrumbComponent } from '@components/navigation';
|
||||||
|
import withNotFound from './not-found-hoc';
|
||||||
import {
|
import {
|
||||||
deploymentGroupBySlugSelector,
|
deploymentGroupBySlugSelector,
|
||||||
serviceBySlugSelector
|
serviceBySlugSelector
|
||||||
} from '@root/state/selectors';
|
} from '@root/state/selectors';
|
||||||
|
|
||||||
const Breadcrumb = ({ deploymentGroup, service, location }) => {
|
const Breadcrumb = ({ deploymentGroup, service, location }) => {
|
||||||
|
|
||||||
const path = location.pathname.split('/');
|
const path = location.pathname.split('/');
|
||||||
|
|
||||||
const links = [
|
const links = [
|
||||||
@ -33,7 +36,7 @@ const Breadcrumb = ({ deploymentGroup, service, location }) => {
|
|||||||
return <BreadcrumbComponent links={links} />;
|
return <BreadcrumbComponent links={links} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConnectedBreadcrumb = connect(
|
const connectBreadcrumb = connect(
|
||||||
(state, ownProps) => {
|
(state, ownProps) => {
|
||||||
const params = ownProps.match.params;
|
const params = ownProps.match.params;
|
||||||
const deploymentGroupSlug = params.deploymentGroup;
|
const deploymentGroupSlug = params.deploymentGroup;
|
||||||
@ -47,6 +50,9 @@ const ConnectedBreadcrumb = connect(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
dispatch => ({})
|
dispatch => ({})
|
||||||
)(Breadcrumb);
|
);
|
||||||
|
|
||||||
export default ConnectedBreadcrumb;
|
export default compose(
|
||||||
|
connectBreadcrumb,
|
||||||
|
withNotFound()
|
||||||
|
)(Breadcrumb);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export { default as Header } from './header';
|
export { default as Header } from './header';
|
||||||
export { default as Breadcrumb } from './breadcrumb';
|
export { default as Breadcrumb } from './breadcrumb';
|
||||||
export { default as Menu } from './menu';
|
export { default as Menu } from './menu';
|
||||||
|
export { default as withNotFound, GqlPaths } from './not-found-hoc';
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { compose } from 'react-apollo';
|
||||||
|
import withNotFound from './not-found-hoc';
|
||||||
import { Menu as MenuComponent } from '@components/navigation';
|
import { Menu as MenuComponent } from '@components/navigation';
|
||||||
|
|
||||||
const Menu = ({ match, sections }) => {
|
const Menu = ({ location, match, sections }) => {
|
||||||
|
|
||||||
if (!sections || !sections.length) {
|
if (!sections || !sections.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -16,7 +19,7 @@ const Menu = ({ match, sections }) => {
|
|||||||
return <MenuComponent links={sectionsWithPathnames} />;
|
return <MenuComponent links={sectionsWithPathnames} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConnectedMenu = connect(
|
const connectMenu = connect(
|
||||||
(state, ownProps) => {
|
(state, ownProps) => {
|
||||||
const params = ownProps.match.params;
|
const params = ownProps.match.params;
|
||||||
const deploymentGroupSlug = params.deploymentGroup;
|
const deploymentGroupSlug = params.deploymentGroup;
|
||||||
@ -35,6 +38,9 @@ const ConnectedMenu = connect(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
dispatch => ({})
|
dispatch => ({})
|
||||||
)(Menu);
|
);
|
||||||
|
|
||||||
export default ConnectedMenu;
|
export default compose(
|
||||||
|
connectMenu,
|
||||||
|
withNotFound()
|
||||||
|
)(Menu);
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { NotFound } from '@components/navigation';
|
||||||
|
|
||||||
|
export const GqlPaths = {
|
||||||
|
DEPLOYMENT_GROUP: 'deploymentGroup',
|
||||||
|
SERVICES: 'services'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (paths) => {
|
||||||
|
return (WrappedComponent) => {
|
||||||
|
|
||||||
|
return class extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if(paths) {
|
||||||
|
const {
|
||||||
|
error,
|
||||||
|
location,
|
||||||
|
history,
|
||||||
|
match
|
||||||
|
} = nextProps;
|
||||||
|
|
||||||
|
if (error && (!location.state || !location.state.notFound)) {
|
||||||
|
if(error.graphQLErrors && error.graphQLErrors.length) {
|
||||||
|
const graphQLError = error.graphQLErrors[0];
|
||||||
|
if(graphQLError.message === 'Not Found') {
|
||||||
|
const notFound = graphQLError.path.pop();
|
||||||
|
if(paths.indexOf(notFound) > -1) {
|
||||||
|
history.replace(location.pathname, { notFound });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const {
|
||||||
|
error,
|
||||||
|
location,
|
||||||
|
match
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if(location.state && location.state.notFound) {
|
||||||
|
const notFound = location.state.notFound;
|
||||||
|
if(paths && paths.indexOf(notFound) > -1) {
|
||||||
|
let title;
|
||||||
|
let to;
|
||||||
|
let link;
|
||||||
|
if(notFound === 'services' || notFound === 'service') {
|
||||||
|
title = 'This service doesn’t exist';
|
||||||
|
to = match.url.split('/').slice(0, 3).join('/');
|
||||||
|
link = 'Back to services';
|
||||||
|
}
|
||||||
|
else if(notFound === 'deploymentGroup') {
|
||||||
|
title = 'This deployment group doesn’t exist';
|
||||||
|
to = '/deployment-group';
|
||||||
|
link = 'Back to dashboard';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<NotFound
|
||||||
|
title={title}
|
||||||
|
message='Sorry, but our princess is in another castle.'
|
||||||
|
to={to}
|
||||||
|
link={link}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <WrappedComponent {...this.props} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import ServicesDeleteMutation from '@graphql/ServicesDeleteMutation.gql';
|
import ServicesDeleteMutation from '@graphql/ServicesDeleteMutation.gql';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ModalErrorMessage } from '@components/messaging';
|
||||||
import { ServiceDelete as ServiceDeleteComponent } from '@components/service';
|
import { ServiceDelete as ServiceDeleteComponent } from '@components/service';
|
||||||
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
|
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
|
||||||
import ServiceGql from './service-gql';
|
import ServiceGql from './service-gql';
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
class ServiceDelete extends Component {
|
class ServiceDelete extends Component {
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ class ServiceDelete extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, error } = this.props;
|
const { loading, error, match, history, location } = this.props;
|
||||||
|
|
||||||
const handleCloseClick = evt => {
|
const handleCloseClick = evt => {
|
||||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||||
@ -36,27 +37,23 @@ class ServiceDelete extends Component {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ErrorMessage
|
<ModalErrorMessage
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occured while loading your service.' />
|
message='An error occured while loading your service.'
|
||||||
|
onCloseClick={handleCloseClick} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { service, deleteServices, history, match } = this.props;
|
const { service, deleteServices } = this.props;
|
||||||
|
|
||||||
if(this.state.error) {
|
if(this.state.error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ModalHeading>
|
<ModalErrorMessage
|
||||||
Deleting a service: <br /> {service.name}
|
|
||||||
</ModalHeading>
|
|
||||||
<ErrorMessage
|
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occurred while attempting to delete your service.' />
|
message={`An error occured while attempting to delete the ${service.name} service.`}
|
||||||
<Button onClick={handleCloseClick} secondary>
|
onCloseClick={handleCloseClick} />
|
||||||
Ok
|
|
||||||
</Button>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -96,4 +93,8 @@ const DeleteServicesGql = graphql(ServicesDeleteMutation, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export default compose(DeleteServicesGql, ServiceGql)(ServiceDelete);
|
export default compose(
|
||||||
|
DeleteServicesGql,
|
||||||
|
ServiceGql,
|
||||||
|
withNotFound([ GqlPaths.SERVICES ])
|
||||||
|
)(ServiceDelete);
|
||||||
|
@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
|
|||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import { reduxForm } from 'redux-form';
|
import { reduxForm } from 'redux-form';
|
||||||
import ServiceScaleMutation from '@graphql/ServiceScale.gql';
|
import ServiceScaleMutation from '@graphql/ServiceScale.gql';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ModalErrorMessage } from '@components/messaging';
|
||||||
import { ServiceScale as ServiceScaleComponent } from '@components/service';
|
import { ServiceScale as ServiceScaleComponent } from '@components/service';
|
||||||
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
|
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
|
||||||
import ServiceGql from './service-gql';
|
import ServiceGql from './service-gql';
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
class ServiceScale extends Component {
|
class ServiceScale extends Component {
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ class ServiceScale extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, error } = this.props;
|
const { loading, error, match, history, location } = this.props;
|
||||||
|
|
||||||
const handleCloseClick = evt => {
|
const handleCloseClick = evt => {
|
||||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||||
@ -37,27 +38,23 @@ class ServiceScale extends Component {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ErrorMessage
|
<ModalErrorMessage
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occured while loading your service.' />
|
message='An error occured while loading your service.'
|
||||||
|
onCloseClick={handleCloseClick} />
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { service, scale, history, match } = this.props;
|
const { service, scale } = this.props;
|
||||||
|
|
||||||
if(this.state.error) {
|
if(this.state.error) {
|
||||||
return (
|
return (
|
||||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||||
<ModalHeading>
|
<ModalErrorMessage
|
||||||
Scaling a service: <br /> {service.name}
|
|
||||||
</ModalHeading>
|
|
||||||
<ErrorMessage
|
|
||||||
title='Ooops!'
|
title='Ooops!'
|
||||||
message='An error occurred while attempting to scale your service.' />
|
message={`An error occured while attempting to scale the ${service.name} service.`}
|
||||||
<Button onClick={handleCloseClick} secondary>
|
onCloseClick={handleCloseClick} />
|
||||||
Ok
|
|
||||||
</Button>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -124,4 +121,8 @@ const ServiceScaleGql = graphql(ServiceScaleMutation, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export default compose(ServiceScaleGql, ServiceGql)(ServiceScale);
|
export default compose(
|
||||||
|
ServiceScaleGql,
|
||||||
|
ServiceGql,
|
||||||
|
withNotFound([ GqlPaths.SERVICES ])
|
||||||
|
)(ServiceScale);
|
||||||
|
@ -21,6 +21,8 @@ import { ServicesQuickActions } from '@components/services';
|
|||||||
|
|
||||||
import { Message } from 'joyent-ui-toolkit';
|
import { Message } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
@ -55,7 +57,8 @@ class ServiceList extends Component {
|
|||||||
push,
|
push,
|
||||||
restartServices,
|
restartServices,
|
||||||
stopServices,
|
stopServices,
|
||||||
startServices
|
startServices,
|
||||||
|
location
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (loading && !forceArray(services).length) {
|
if (loading && !forceArray(services).length) {
|
||||||
@ -77,6 +80,7 @@ class ServiceList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
deploymentGroup &&
|
||||||
deploymentGroup.status === 'PROVISIONING' &&
|
deploymentGroup.status === 'PROVISIONING' &&
|
||||||
!forceArray(services).length
|
!forceArray(services).length
|
||||||
) {
|
) {
|
||||||
@ -222,7 +226,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: ({ data: { deploymentGroup, loading, error } }) => ({
|
props: ({ data: { deploymentGroup, loading, error }}) => ({
|
||||||
deploymentGroup,
|
deploymentGroup,
|
||||||
services: deploymentGroup
|
services: deploymentGroup
|
||||||
? processServices(deploymentGroup.services, null)
|
? processServices(deploymentGroup.services, null)
|
||||||
@ -256,7 +260,8 @@ const ServiceListWithData = compose(
|
|||||||
ServicesStopGql,
|
ServicesStopGql,
|
||||||
ServicesStartGql,
|
ServicesStartGql,
|
||||||
ServicesGql,
|
ServicesGql,
|
||||||
UiConnect
|
UiConnect,
|
||||||
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
)(ServiceList);
|
)(ServiceList);
|
||||||
|
|
||||||
export default ServiceListWithData;
|
export default ServiceListWithData;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { compose } from 'react-apollo';
|
||||||
import { Col, Row } from 'react-styled-flexboxgrid';
|
import { Col, Row } from 'react-styled-flexboxgrid';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import unitcalc from 'unitcalc';
|
import unitcalc from 'unitcalc';
|
||||||
import { LayoutContainer } from '@components/layout';
|
import { LayoutContainer } from '@components/layout';
|
||||||
import { Title } from '@components/navigation';
|
import { Title } from '@components/navigation';
|
||||||
|
import { withNotFound } from '@containers/navigation';
|
||||||
|
|
||||||
import { H2, FormGroup, Toggle, ToggleList, Legend } from 'joyent-ui-toolkit';
|
import { H2, FormGroup, Toggle, ToggleList, Legend } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
@ -19,6 +21,11 @@ const PaddedRow = Row.extend`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const ServicesMenu = ({ location, history: { push } }) => {
|
const ServicesMenu = ({ location, history: { push } }) => {
|
||||||
|
|
||||||
|
if(location.state && location.state.notFound) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const toggleValue = location.pathname.split('-').pop();
|
const toggleValue = location.pathname.split('-').pop();
|
||||||
|
|
||||||
const handleToggle = evt => {
|
const handleToggle = evt => {
|
||||||
@ -57,4 +64,4 @@ ServicesMenu.propTypes = {
|
|||||||
history: PropTypes.object.isRequired
|
history: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ServicesMenu;
|
export default compose(withNotFound())(ServicesMenu);
|
||||||
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import forceArray from 'force-array';
|
||||||
import ServicesQuery from '@graphql/Services.gql';
|
import ServicesQuery from '@graphql/Services.gql';
|
||||||
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
|
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
|
||||||
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
|
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
|
||||||
@ -17,6 +18,8 @@ import { ServicesQuickActions } from '@components/services';
|
|||||||
|
|
||||||
import { Topology } from 'joyent-ui-toolkit';
|
import { Topology } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
const StyledBackground = styled.div`
|
const StyledBackground = styled.div`
|
||||||
padding: ${unitcalc(4)};
|
padding: ${unitcalc(4)};
|
||||||
background-color: ${props => props.theme.whiteActive};
|
background-color: ${props => props.theme.whiteActive};
|
||||||
@ -50,10 +53,11 @@ class ServicesTopology extends Component {
|
|||||||
toggleServicesQuickActions,
|
toggleServicesQuickActions,
|
||||||
restartServices,
|
restartServices,
|
||||||
stopServices,
|
stopServices,
|
||||||
startServices
|
startServices,
|
||||||
|
location
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (loading) {
|
if (loading && !forceArray(services).length) {
|
||||||
return (
|
return (
|
||||||
<LayoutContainer center>
|
<LayoutContainer center>
|
||||||
<Loader />
|
<Loader />
|
||||||
@ -62,7 +66,6 @@ class ServicesTopology extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutContainer>
|
<LayoutContainer>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
@ -73,6 +76,7 @@ class ServicesTopology extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
deploymentGroup &&
|
||||||
deploymentGroup.status === 'PROVISIONING' &&
|
deploymentGroup.status === 'PROVISIONING' &&
|
||||||
!forceArray(services).length
|
!forceArray(services).length
|
||||||
) {
|
) {
|
||||||
@ -232,7 +236,8 @@ const ServicesTopologyWithData = compose(
|
|||||||
ServicesStopGql,
|
ServicesStopGql,
|
||||||
ServicesStartGql,
|
ServicesStartGql,
|
||||||
ServicesGql,
|
ServicesGql,
|
||||||
UiConnect
|
UiConnect,
|
||||||
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
)(ServicesTopology);
|
)(ServicesTopology);
|
||||||
|
|
||||||
export default ServicesTopologyWithData;
|
export default ServicesTopologyWithData;
|
||||||
|
@ -21,6 +21,8 @@ import {
|
|||||||
|
|
||||||
import { DeploymentGroupDelete } from '@containers/deployment-group';
|
import { DeploymentGroupDelete } from '@containers/deployment-group';
|
||||||
|
|
||||||
|
import { NotFound } from '@components/navigation';
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
@ -30,25 +32,23 @@ const Container = styled.div`
|
|||||||
|
|
||||||
const rootRedirect = p => <Redirect to="/deployment-groups" />;
|
const rootRedirect = p => <Redirect to="/deployment-groups" />;
|
||||||
|
|
||||||
const deploymentGroupRedirect = p =>
|
const servicesListRedirect = p =>
|
||||||
<Redirect
|
<Redirect
|
||||||
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`}
|
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
|
const servicesTopologyRedirect = p =>
|
||||||
|
<Redirect
|
||||||
|
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-topology`}
|
||||||
|
/>;
|
||||||
|
|
||||||
const serviceRedirect = p =>
|
const serviceRedirect = p =>
|
||||||
<Redirect
|
<Redirect
|
||||||
to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match
|
to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match
|
||||||
.params.service}/instances`}
|
.params.service}/instances`}
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
// TODO component to be designed
|
const App = p => (
|
||||||
const notFound = p => {
|
|
||||||
return <p>
|
|
||||||
NOT FOUND
|
|
||||||
</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const APP = p => (
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
@ -141,7 +141,7 @@ const APP = p => (
|
|||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/deployment-groups/:deploymentGroup"
|
path="/deployment-groups/:deploymentGroup"
|
||||||
component={deploymentGroupRedirect}
|
component={servicesListRedirect}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
||||||
@ -171,11 +171,11 @@ const APP = p => (
|
|||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/deployment-groups/:deploymentGroup/services-list"
|
path="/deployment-groups/:deploymentGroup/services-list"
|
||||||
component={deploymentGroupRedirect}
|
component={servicesListRedirect}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/deployment-groups/:deploymentGroup/services-topology"
|
path="/deployment-groups/:deploymentGroup/services-topology"
|
||||||
component={deploymentGroupRedirect}
|
component={servicesTopologyRedirect}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
@ -186,9 +186,9 @@ const Router = (
|
|||||||
<Container>
|
<Container>
|
||||||
<Route path="/" component={Header} />
|
<Route path="/" component={Header} />
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/deployment-groups" component={APP} />
|
<Route path="/deployment-groups" component={App} />
|
||||||
<Route path="/" exact component={rootRedirect} />
|
<Route path="/" exact component={rootRedirect} />
|
||||||
<Route path="/*" component={notFound} />
|
<Route path="/*" component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</Container>
|
</Container>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
@ -9,6 +9,7 @@ const random = require('lodash.random');
|
|||||||
const uniq = require('lodash.uniq');
|
const uniq = require('lodash.uniq');
|
||||||
const yaml = require('js-yaml');
|
const yaml = require('js-yaml');
|
||||||
const hasha = require('hasha');
|
const hasha = require('hasha');
|
||||||
|
const Boom = require('boom');
|
||||||
|
|
||||||
const wpData = require('./wp-data.json');
|
const wpData = require('./wp-data.json');
|
||||||
const cpData = require('./cp-data.json');
|
const cpData = require('./cp-data.json');
|
||||||
@ -96,7 +97,13 @@ const getServices = query => {
|
|||||||
).map(serviceId => lfind(services, ['id', serviceId]));
|
).map(serviceId => lfind(services, ['id', serviceId]));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.resolve(services);
|
return Promise.resolve(services)
|
||||||
|
.then((services) => {
|
||||||
|
if(!services || !services.length) {
|
||||||
|
throw Boom.notFound();
|
||||||
|
}
|
||||||
|
return services;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDeploymentGroups = query => {
|
const getDeploymentGroups = query => {
|
||||||
@ -114,7 +121,12 @@ const getDeploymentGroups = query => {
|
|||||||
|
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
deploymentGroups.filter(find(cleanQuery(query))).map(addNestedResolvers)
|
deploymentGroups.filter(find(cleanQuery(query))).map(addNestedResolvers)
|
||||||
);
|
).then((deploymentGroups) => {
|
||||||
|
if(!deploymentGroups || !deploymentGroups.length) {
|
||||||
|
throw Boom.notFound();
|
||||||
|
}
|
||||||
|
return deploymentGroups;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPortal = () =>
|
const getPortal = () =>
|
||||||
|
Loading…
Reference in New Issue
Block a user