diff --git a/frontend/package.json b/frontend/package.json
index c374607f..467054c7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -18,6 +18,7 @@
"react-router-dom": "^4.1.1",
"redux": "^3.6.0",
"redux-actions": "^2.0.3",
+ "redux-batched-actions": "^0.2.0",
"redux-form": "^6.7.0",
"reselect": "^3.0.1",
"simple-statistics": "^4.1.0",
diff --git a/frontend/src/components/navigation/header.js b/frontend/src/components/navigation/header.js
index b83567b1..c1513de6 100644
--- a/frontend/src/components/navigation/header.js
+++ b/frontend/src/components/navigation/header.js
@@ -1,4 +1,5 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
@@ -8,6 +9,7 @@ import { colors } from '@ui/shared/constants';
import Logo from '@assets/triton_logo.png';
import Row from '@ui/components/row';
import Column from '@ui/components/column';
+import { P } from '@ui/components/base-elements';
const StyledHeader = styled.div`
background-color: ${colors.base.primaryDarkBrand};
@@ -19,14 +21,36 @@ const StyledLogo = styled.img`
height: ${remcalc(25)};
`;
-export default () => (
+const StyledP = styled(P)`
+ color: ${colors.base.white};
+ font-weight: 600;
+ margin: ${unitcalc(0.5)} 0 0 0;
+`;
+
+const Header = ({
+ datacenter,
+ username
+}) => (
-
+
+
+ {datacenter}
+
+
+ {username}
+
);
+
+Header.propTypes = {
+ datacenter: PropTypes.string,
+ username: PropTypes.string
+}
+
+export default Header;
diff --git a/frontend/src/containers/instances/list.js b/frontend/src/containers/instances/list.js
index 5996570a..917674cc 100644
--- a/frontend/src/containers/instances/list.js
+++ b/frontend/src/containers/instances/list.js
@@ -1,7 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo';
-import PortalQuery from '@graphql/Portal.gql';
import InstancesQuery from '@graphql/Instances.gql';
import { LayoutContainer } from '@components/layout';
@@ -58,8 +57,6 @@ class InstanceList extends Component {
}
}
-const PortalGql = graphql(PortalQuery, {});
-
const InstanceListGql = graphql(InstancesQuery, {
options(props) {
const params = props.match.params;
@@ -82,7 +79,6 @@ const InstanceListGql = graphql(InstancesQuery, {
});
const InstanceListWithData = compose(
- PortalGql,
InstanceListGql
)(InstanceList);
diff --git a/frontend/src/containers/navigation/header.js b/frontend/src/containers/navigation/header.js
new file mode 100644
index 00000000..ad36b1e6
--- /dev/null
+++ b/frontend/src/containers/navigation/header.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { graphql } from 'react-apollo';
+import PortalQuery from '@graphql/Portal.gql';
+import { Header as HeaderComponent } from '@components/navigation';
+
+const Header = ({
+ portal = {
+ datacenter: {
+ region: ''
+ },
+ user: {
+ firstName: ''
+ }
+ },
+ loading,
+ error
+}) => {
+
+ return (
+
+ );
+};
+
+const HeaderWithData = graphql(PortalQuery, {
+ props: ({ data: { portal, loading, error }}) => ({
+ portal,
+ loading,
+ error
+ })
+})(Header);
+
+export default HeaderWithData;
diff --git a/frontend/src/containers/navigation/index.js b/frontend/src/containers/navigation/index.js
index 4e5c76e1..570ccc4d 100644
--- a/frontend/src/containers/navigation/index.js
+++ b/frontend/src/containers/navigation/index.js
@@ -1,2 +1,3 @@
+export { default as Header } from './header';
export { default as Breadcrumb } from './breadcrumb';
export { default as Menu } from './menu';
diff --git a/frontend/src/containers/services/list.js b/frontend/src/containers/services/list.js
index 83f7efc4..dccafa9c 100644
--- a/frontend/src/containers/services/list.js
+++ b/frontend/src/containers/services/list.js
@@ -2,8 +2,6 @@ import React, { Component } from 'react';
import { compose, graphql } from 'react-apollo';
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 { processServices } from '@root/state/selectors';
@@ -69,8 +67,6 @@ class ServiceList extends Component {
}
}
-const PortalGql = graphql(PortalQuery, {});
-
const ServicesGql = graphql(ServicesQuery, {
options(props) {
return {
@@ -89,7 +85,6 @@ const ServicesGql = graphql(ServicesQuery, {
});
const ServiceListWithData = compose(
- PortalGql,
ServicesGql
)(ServiceList);
diff --git a/frontend/src/containers/services/topology.js b/frontend/src/containers/services/topology.js
index 3dcf9250..b958cf02 100644
--- a/frontend/src/containers/services/topology.js
+++ b/frontend/src/containers/services/topology.js
@@ -2,10 +2,10 @@ import React from 'react';
import { compose, graphql } from 'react-apollo';
import { connect } from 'react-redux';
import styled from 'styled-components';
-import PortalQuery from '@graphql/Portal.gql';
import ServicesQuery from '@graphql/ServicesTopology.gql';
import { processServices } from '@root/state/selectors';
+import { toggleServicesQuickActions } from '@root/state/actions';
import { LayoutContainer } from '@components/layout';
import { Loader, ErrorMessage } from '@components/messaging';
@@ -14,25 +14,26 @@ import { ServicesTooltip } from '@components/services';
import { colors } from '@ui/shared/constants';
import { unitcalc } from '@ui/shared/functions';
import { TopologyGraph } from '@ui/components/topology';
-/*import ServicesTooltip from '@components/services/tooltip';
-
-import { toggleTooltip } from '@state/actions';*/
const StyledBackground = styled.div`
+
background-color: ${colors.base.whiteActive};
+ padding: ${unitcalc(4)};
`;
const StyledContainer = styled.div`
position: relative;
- padding: ${unitcalc(4)};
`;
const ServicesTopology = ({
+ url,
push,
services,
datacenter,
loading,
- error
+ error,
+ servicesQuickActions,
+ toggleServicesQuickActions
}) => {
if(loading) {
@@ -52,18 +53,57 @@ const ServicesTopology = ({
)
}
+ const handleQuickActions = (evt, tooltipData) => {
+ toggleServicesQuickActions(tooltipData);
+ };
+
+ const handleTooltipBlur = (evt) => {
+ toggleServicesQuickActions({
+ show: false,
+ service: servicesQuickActions.service,
+ position: servicesQuickActions.position
+ });
+ }
+
+ const handleNodeTitleClick = (evt, { service }) => {
+ push(
+ `${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`
+ );
+ };
+
return (
+
);
}
-const PortalGql = graphql(PortalQuery, {});
+const mapStateToProps = (state, ownProps) => ({
+ servicesQuickActions: state.ui.services.quickActions,
+ url: ownProps.match.url,
+ push: ownProps.history.push
+})
+
+const mapDispatchToProps = (dispatch) => ({
+ toggleServicesQuickActions: (data) =>
+ dispatch(toggleServicesQuickActions(data))
+});
+
+const UiConnect = connect(
+ mapStateToProps,
+ mapDispatchToProps
+);
const ServicesGql = graphql(ServicesQuery, {
options(props) {
@@ -82,8 +122,8 @@ const ServicesGql = graphql(ServicesQuery, {
});
const ServicesTopologyWithData = compose(
- PortalGql,
- ServicesGql
+ ServicesGql,
+ UiConnect
)(ServicesTopology);
export default ServicesTopologyWithData;
diff --git a/frontend/src/graphql/Portal.gql b/frontend/src/graphql/Portal.gql
index 9918426c..4d5e20c9 100644
--- a/frontend/src/graphql/Portal.gql
+++ b/frontend/src/graphql/Portal.gql
@@ -1,6 +1,8 @@
query Portal {
portal {
- username
+ user {
+ firstName
+ }
datacenter {
uuid
region
diff --git a/frontend/src/router.js b/frontend/src/router.js
index e7aa63a2..a6c8713f 100644
--- a/frontend/src/router.js
+++ b/frontend/src/router.js
@@ -6,9 +6,7 @@ import {
Switch
} from 'react-router-dom';
-import { Header } from '@components/navigation';
-
-import { Breadcrumb, Menu } from '@containers/navigation';
+import { Header, Breadcrumb, Menu } from '@containers/navigation';
import { DeploymentGroupList } from '@containers/deployment-groups';
import { ServiceList, ServicesTopology, ServicesMenu } from '@containers/services';
diff --git a/frontend/src/state/actions.js b/frontend/src/state/actions.js
index fa428d71..58ce94aa 100644
--- a/frontend/src/state/actions.js
+++ b/frontend/src/state/actions.js
@@ -5,5 +5,5 @@ const APP = constantCase(process.env['APP_NAME']);
/******************************* UI *******************************/
-export const addMemberToProject =
- createAction(`${APP}/PROJECT_ADD_MEMBER`);
+export const toggleServicesQuickActions =
+ createAction(`${APP}/TOGGLE_SERVICES_QUICK_ACTIONS`);
diff --git a/frontend/src/state/reducers/index.js b/frontend/src/state/reducers/index.js
index e69de29b..570fa5d0 100644
--- a/frontend/src/state/reducers/index.js
+++ b/frontend/src/state/reducers/index.js
@@ -0,0 +1 @@
+export { default as ui } from './ui';
diff --git a/frontend/src/state/reducers/ui.js b/frontend/src/state/reducers/ui.js
new file mode 100644
index 00000000..95ad051e
--- /dev/null
+++ b/frontend/src/state/reducers/ui.js
@@ -0,0 +1,34 @@
+import { handleActions } from 'redux-actions';
+import { toggleServicesQuickActions } from '@state/actions';
+
+export default handleActions({
+ [toggleServicesQuickActions.toString()]: (state, action) => {
+ const {
+ position,
+ service,
+ show
+ } = action.payload;
+
+ const s = show !== undefined ? show :
+ !state.services.quickActions.service ||
+ service.uuid !== state.services.quickActions.service.uuid;
+
+ const quickActions = s ? {
+ show: s,
+ position,
+ service
+ } : {
+ show: false
+ }
+
+ return {
+ ...state,
+ services: {
+ ...state.services,
+ quickActions
+ }
+ }
+
+ return state;
+ }
+}, {});
diff --git a/frontend/src/state/state.js b/frontend/src/state/state.js
index 02e5421a..32472fe0 100644
--- a/frontend/src/state/state.js
+++ b/frontend/src/state/state.js
@@ -15,6 +15,11 @@ const state = {
pathname: "instances",
name: "Instances"
}]
+ },
+ services: {
+ quickActions: {
+ show: false
+ }
}
}
}
diff --git a/frontend/src/state/store.js b/frontend/src/state/store.js
index 1b54bd0c..3e6ec108 100644
--- a/frontend/src/state/store.js
+++ b/frontend/src/state/store.js
@@ -1,6 +1,8 @@
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
+import { enableBatching } from 'redux-batched-actions';
import { ApolloClient, createNetworkInterface } from 'react-apollo';
import state from './state';
+import { ui } from './reducers';
export const client = new ApolloClient({
dataIdFromObject: o => {
@@ -14,12 +16,10 @@ export const client = new ApolloClient({
export const store = createStore(
combineReducers({
- ui: (s) => {
- return s ? s : state.ui
- },
+ ui: ui,
apollo: client.reducer(),
}),
- {}, // initial state
+ state, // initial state
compose(
applyMiddleware(client.middleware()),
// If you are using the devToolsExtension, you can add it here also
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 509fe11b..8bd6e3c2 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -5263,6 +5263,10 @@ redux-actions@^2.0.3:
lodash-es "^4.17.4"
reduce-reducers "^0.1.0"
+redux-batched-actions@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/redux-batched-actions/-/redux-batched-actions-0.2.0.tgz#da0000c882b0e6c861a96d5823bd36adf5d9c0dd"
+
redux-form@^6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-6.7.0.tgz#9fb0769e76f14febf1dd7686e02c4ab2d6f953aa"
diff --git a/spikes/graphql/graphql-server/src/resolvers.js b/spikes/graphql/graphql-server/src/resolvers.js
index 5c751c6b..d35d67fd 100644
--- a/spikes/graphql/graphql-server/src/resolvers.js
+++ b/spikes/graphql/graphql-server/src/resolvers.js
@@ -7,7 +7,14 @@ const datacenter = {
uuid: 'datacenter-uuid',
region: 'us-east-1'
};
-const portal = { username: 'juditgreskovits', host: 'dockerhost', datacenter};
+const user = {
+ uuid: 'unique-user-id',
+ firstName: 'Judit',
+ lastName: 'Greskovits',
+ email: 'name@email.com',
+ login: 'juditgreskovits'
+}
+const portal = { user, host: 'dockerhost', datacenter};
const deploymentGroups = data.projects.data.map(p => {
p.slug = p.id;
return p;
diff --git a/spikes/graphql/graphql-server/src/schema.gql b/spikes/graphql/graphql-server/src/schema.gql
index 73cf8ef0..82f28374 100644
--- a/spikes/graphql/graphql-server/src/schema.gql
+++ b/spikes/graphql/graphql-server/src/schema.gql
@@ -3,11 +3,19 @@
scalar Object
type Portal {
- username: String!
+ user: User!
datacenter: Datacenter!
deploymentGroups: [DeploymentGroup]!
}
+ type User {
+ uuid: ID!
+ firstName: String!
+ lastName: String!
+ email: String!
+ login: String!
+ }
+
type DeploymentGroup {
uuid: ID!
name: String!
@@ -163,6 +171,7 @@
# but this way we expose the entire db through gql
type Query {
portal: Portal
+ user: User
deploymentGroups(name: String, slug: String): [DeploymentGroup]
deploymentGroup(uuid: ID, name: String, slug: String): DeploymentGroup
serviceScales(serviceName: String, versionUuid: ID): [ServiceScale]
diff --git a/ui/src/components/topology/graph-node/button.js b/ui/src/components/topology/graph-node/button.js
index 52e2d799..9b1a78e7 100644
--- a/ui/src/components/topology/graph-node/button.js
+++ b/ui/src/components/topology/graph-node/button.js
@@ -39,6 +39,7 @@ const GraphNodeButton = ({
y2={height}
connected={connected}
/>
+ {buttonCircles}
- {buttonCircles}
);
};
diff --git a/ui/src/components/topology/graph-node/index.js b/ui/src/components/topology/graph-node/index.js
index 2c406867..f4334e3e 100644
--- a/ui/src/components/topology/graph-node/index.js
+++ b/ui/src/components/topology/graph-node/index.js
@@ -40,7 +40,6 @@ const GraphNode = ({
}
const onButtonClick = (evt) => {
-
const tooltipPosition = {
x: data.x + Constants.buttonRect.x + Constants.buttonRect.width/2,
y: data.y + Constants.buttonRect.y + Constants.buttonRect.height
@@ -52,18 +51,19 @@ const GraphNode = ({
}
const d = {
- service: data.uuid,
+ service: data,
position: {
left: tooltipPosition.x,
top: tooltipPosition.y
- }
+ },
+ rect: Constants.buttonRect
};
onQuickActions(evt, d);
};
- const onTitleClick = () =>
- onNodeTitleClick(data.uuid);
+ const onTitleClick = (evt) =>
+ onNodeTitleClick(evt, { service: data });
const onStart = (evt) => {
evt.preventDefault();
diff --git a/ui/src/components/topology/graph-node/shapes.js b/ui/src/components/topology/graph-node/shapes.js
index 8ad486c7..5e4890f8 100644
--- a/ui/src/components/topology/graph-node/shapes.js
+++ b/ui/src/components/topology/graph-node/shapes.js
@@ -27,6 +27,7 @@ export const GraphTitle = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 16px;
font-weight: 600;
+ cursor: pointer;
`;
export const GraphSubtitle = styled.text`
diff --git a/ui/src/components/topology/topology-graph.js b/ui/src/components/topology/topology-graph.js
index 2c60d00b..70751d52 100644
--- a/ui/src/components/topology/topology-graph.js
+++ b/ui/src/components/topology/topology-graph.js
@@ -220,8 +220,8 @@ class TopologyGraph extends React.Component {
this.setDragInfo(false);
};
- const onTitleClick = (serviceUUID) =>
- this.props.onNodeTitleClick(serviceUUID);
+ const onTitleClick = (evt, data) =>
+ this.props.onNodeTitleClick(evt, data);
const renderedNode = (n, index) => (