diff --git a/frontend/src/components/metrics-row/index.js b/frontend/src/components/metrics-row/index.js
new file mode 100644
index 00000000..ff842e52
--- /dev/null
+++ b/frontend/src/components/metrics-row/index.js
@@ -0,0 +1,56 @@
+const React = require('react');
+const Styled = require('styled-components');
+
+const Column = require('@ui/components/column');
+const MiniMetric = require('@ui/components/mini-metric');
+const PropTypes = require('@root/prop-types');
+const Row = require('@ui/components/row');
+
+const {
+ default: styled
+} = Styled;
+
+const {
+ MiniMetricGraph,
+ MiniMetricMeta,
+ MiniMetricTitle,
+ MiniMetricSubtitle,
+ MiniMetricView
+} = MiniMetric;
+
+const StyledRow = styled(Row)`
+ margin: 0;
+
+ & > div {
+ padding-left: 0;
+ padding-right: 0;
+ }
+`;
+
+const MetricsRow = ({
+ datasets = []
+}) => {
+ const _datasets = datasets.map((metric, i) => (
+
+
+
+ Memory: 54%
+ (1280/3000 MB)
+
+
+
+
+ ));
+
+ return (
+
+ {_datasets}
+
+ );
+};
+
+MetricsRow.propTypes = {
+ datasets: React.PropTypes.arrayOf(PropTypes.dataset)
+};
+
+module.exports = MetricsRow;
diff --git a/frontend/src/components/service-item/index.js b/frontend/src/components/service-item/index.js
index c3e6e528..3fe83741 100644
--- a/frontend/src/components/service-item/index.js
+++ b/frontend/src/components/service-item/index.js
@@ -1,23 +1,16 @@
const forceArray = require('force-array');
const React = require('react');
const ReactRouter = require('react-router');
-const Styled = require('styled-components');
const Anchor = require('@ui/components/anchor');
-const Column = require('@ui/components/column');
const List = require('@ui/components/list');
-const MiniMetric = require('@ui/components/mini-metric');
+const MetricsRow = require('@components/metrics-row');
const PropTypes = require('@root/prop-types');
-const Row = require('@ui/components/row');
const {
Link
} = ReactRouter;
-const {
- default: styled
-} = Styled;
-
const {
ListItem,
ListItemView,
@@ -31,23 +24,6 @@ const {
ListItemHeader
} = List;
-const {
- MiniMetricGraph,
- MiniMetricMeta,
- MiniMetricTitle,
- MiniMetricSubtitle,
- MiniMetricView
-} = MiniMetric;
-
-const MetricsRow = styled(Row)`
- margin: 0;
-
- & > div {
- padding-left: 0;
- padding-right: 0;
- }
-`;
-
const ServiceItem = ({
org = '',
project = '',
@@ -99,18 +75,6 @@ const ServiceItem = ({
);
- const metrics = service.metrics.map((metric, i) => (
-
-
-
- Memory: 54%
- (1280/3000 MB)
-
-
-
-
- ));
-
const view = childs.length ? (
{childs}
@@ -123,9 +87,7 @@ const ServiceItem = ({
{description}
-
- {metrics}
-
+
);
diff --git a/frontend/src/containers/service/instances.js b/frontend/src/containers/service/instances.js
index c062a507..6572c529 100644
--- a/frontend/src/containers/service/instances.js
+++ b/frontend/src/containers/service/instances.js
@@ -1,11 +1,17 @@
const React = require('react');
const ReactRedux = require('react-redux');
+const actions = require('@state/actions');
const EmptyInstances = require('@components/empty/instances');
const PropTypes = require('@root/prop-types');
const List = require('@ui/components/list');
+const DatasetsRow = require('@components/metrics-row');
const selectors = require('@state/selectors');
+const {
+ toggleInstanceCollapsed
+} = actions;
+
const {
connect
} = ReactRedux;
@@ -19,22 +25,29 @@ const {
ListItemView,
ListItemMeta,
ListItemTitle,
- ListItemOptions
+ ListItemOptions,
+ ListItemOutlet
} = List;
const Instances = ({
- instances = []
+ instances = [],
+ toggleCollapsed = () => null
}) => {
+ const onClick = (uuid) => () => toggleCollapsed(uuid);
+
const empty = instances.length ? null : (
);
- const instanceList = instances.map((service) => (
-
+ const instanceList = instances.map((instance) => (
+
-
- {service.name}
+
+ {instance.name}
+
+
+
…
@@ -51,7 +64,8 @@ const Instances = ({
};
Instances.propTypes = {
- instances: React.PropTypes.arrayOf(PropTypes.instance)
+ instances: React.PropTypes.arrayOf(PropTypes.instance),
+ toggleCollapsed: React.PropTypes.func
};
const mapStateToProps = (state, {
@@ -60,6 +74,11 @@ const mapStateToProps = (state, {
instances: instancesByServiceIdSelector(params.serviceId)(state)
});
+const mapDispatchToProps = (dispatch) => ({
+ toggleCollapsed: (uuid) => dispatch(toggleInstanceCollapsed(uuid))
+});
+
module.exports = connect(
- mapStateToProps
+ mapStateToProps,
+ mapDispatchToProps
)(Instances);
diff --git a/frontend/src/mock-state.json b/frontend/src/mock-state.json
index 49e5f94c..48be8b2d 100644
--- a/frontend/src/mock-state.json
+++ b/frontend/src/mock-state.json
@@ -378,72 +378,130 @@
}]
},
"instances": {
+ "ui": {
+ "collapsed": []
+ },
"data": [{
"uuid": "309ecd9f-ac03-474b-aff7-4bd2e743296c",
"name": "wordpress_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "0db6db53-de6f-4378-839e-5d5b452fbaf2",
"name": "nfs_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6a0eee76-c019-413b-9d5f-44712b55b993",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "250c8a6c-7d02-49a9-8abd-e1c22773041d",
"name": "consul",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "97c68055-db88-45c9-ad49-f26da4264777",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "2c921f3a-8bc3-4f57-9cd7-789ebae72061",
"name": "memcache_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "68d3046e-8e34-4f5d-a0e5-db3795a250fd",
"name": "memcache_02",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "2ea99763-3b44-4179-8393-d66d94961051",
"name": "memcache_03",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "25f6bc62-63b8-4959-908e-1f6d7ff6341d",
"name": "memcache_04",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "8be01042-0281-4a77-a357-25979e87bf3d",
"name": "memcache_05",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "3d652e9d-73e8-4a6f-8171-84fa83740662",
"name": "nginx",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "081a792c-47e0-4439-924b-2efa9788ae9e",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "c3ec7633-a02b-4615-86a0-9e6faeaae94b",
"name": "percona-primary",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "4ee4103e-1a52-4099-a48e-01588f597c70",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}, {
"uuid": "c2b5fec2-31e2-41a7-b7fc-cd0bb1822e76",
"name": "percona-secundary",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "4ee4103e-1a52-4099-a48e-01588f597c70",
- "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
+ "project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
+ "metrics": [
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
+ "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
+ ]
}]
}
}
diff --git a/frontend/src/prop-types.js b/frontend/src/prop-types.js
index 611097f0..c3422655 100644
--- a/frontend/src/prop-types.js
+++ b/frontend/src/prop-types.js
@@ -39,6 +39,20 @@ const Metric = React.PropTypes.shape({
...BaseObject
});
+const Dataset = React.PropTypes.shape({
+ uuid: React.PropTypes.string,
+ type: React.PropTypes.string,
+ data: React.PropTypes.arrayOf(
+ React.PropTypes.shape({
+ firstQuartile: React.PropTypes.string,
+ thirdQuartile: React.PropTypes.string,
+ median: React.PropTypes.string,
+ max: React.PropTypes.string,
+ min: React.PropTypes.string
+ })
+ )
+});
+
const Sections = React.PropTypes.arrayOf(
React.PropTypes.string
);
@@ -58,5 +72,6 @@ module.exports = {
instance: Instance,
metric: Metric,
// consinder renaming this to 'Types' as it could be used for any
- metricTypes: MetricTypes
+ metricTypes: MetricTypes,
+ dataset: Dataset
};
diff --git a/frontend/src/state/actions.js b/frontend/src/state/actions.js
index 739bc96a..35e0629d 100644
--- a/frontend/src/state/actions.js
+++ b/frontend/src/state/actions.js
@@ -11,5 +11,6 @@ module.exports = {
...require('@state/thunks'),
updateRouter: createAction(`${APP}/APP/UPDATE_ROUTER`),
toggleHeaderTooltip: createAction(`${APP}/APP/TOGGLE_HEADER_TOOLTIP`),
- toggleServiceCollapsed: createAction(`${APP}/APP/TOGGLE_SERVICE_COLLAPSED`)
+ toggleServiceCollapsed: createAction(`${APP}/APP/TOGGLE_SERVICE_COLLAPSED`),
+ toggleInstanceCollapsed: createAction(`${APP}/APP/TOGGLE_INSTANCE_COLLAPSED`)
};
diff --git a/frontend/src/state/reducers/common.js b/frontend/src/state/reducers/common.js
new file mode 100644
index 00000000..d192085c
--- /dev/null
+++ b/frontend/src/state/reducers/common.js
@@ -0,0 +1,25 @@
+const toggleCollapsed = (state, action) => {
+ const {
+ ui
+ } = state;
+
+ const {
+ collapsed = []
+ } = ui;
+
+ const _collapsed = collapsed.indexOf(action.payload) >= 0
+ ? collapsed.filter((uuid) => uuid !== action.payload)
+ : [...collapsed, action.payload];
+
+ return {
+ ...state,
+ ui: {
+ ...ui,
+ collapsed: _collapsed
+ }
+ };
+};
+
+module.exports = {
+ toggleCollapsed
+};
diff --git a/frontend/src/state/reducers/instances.js b/frontend/src/state/reducers/instances.js
index 7fec06fb..4f04e2f4 100644
--- a/frontend/src/state/reducers/instances.js
+++ b/frontend/src/state/reducers/instances.js
@@ -1,9 +1,20 @@
const ReduxActions = require('redux-actions');
+const actions = require('@state/actions');
+const common = require('@state/reducers/common');
+
const {
handleActions
} = ReduxActions;
+const {
+ toggleInstanceCollapsed
+} = actions;
+
+const {
+ toggleCollapsed
+} = common;
+
module.exports = handleActions({
- 'x': (state) => state // somehow handleActions needs at least one reducer
+ [toggleInstanceCollapsed.toString()]: toggleCollapsed
}, {});
diff --git a/frontend/src/state/reducers/services.js b/frontend/src/state/reducers/services.js
index 4a6311db..d67ffb59 100644
--- a/frontend/src/state/reducers/services.js
+++ b/frontend/src/state/reducers/services.js
@@ -1,6 +1,7 @@
const ReduxActions = require('redux-actions');
const actions = require('@state/actions');
+const common = require('@state/reducers/common');
const {
handleActions
@@ -10,14 +11,10 @@ const {
toggleServiceCollapsed
} = actions;
+const {
+ toggleCollapsed
+} = common;
+
module.exports = handleActions({
- [toggleServiceCollapsed.toString()]: (state, action) => ({
- ...state,
- ui: {
- ...state.ui,
- collapsed: state.ui.collapsed.indexOf(action.payload) >= 0
- ? state.ui.collapsed.filter((uuid) => uuid !== action.payload)
- : [...state.ui.collapsed, action.payload]
- }
- })
+ [toggleServiceCollapsed.toString()]: toggleCollapsed
}, {});
diff --git a/frontend/src/state/selectors.js b/frontend/src/state/selectors.js
index 0e2d9225..4ef35fbd 100644
--- a/frontend/src/state/selectors.js
+++ b/frontend/src/state/selectors.js
@@ -16,6 +16,7 @@ const orgs = (state) => get(state, 'orgs.data', []);
const projects = (state) => get(state, 'projects.data', []);
const services = (state) => get(state, 'services.data', []);
const collapsedServices = (state) => get(state, 'services.ui.collapsed', []);
+const collapsedInstances = (state) => get(state, 'instances.ui.collapsed', []);
const instances = (state) => get(state, 'instances.data', []);
const metricDatasets = (state) => get(state, 'metrics.data.datasets', []);
@@ -46,6 +47,13 @@ const orgSections = (orgId) => createSelector(
)
);
+const isCollapsed = (collapsed, uuid) => collapsed.indexOf(uuid) >= 0;
+
+const datasets = (metrics, uuids) => uuids.map((uuid) => find(metrics, [
+ 'uuid',
+ uuid
+]));
+
const servicesByProjectId = (projectId) => createSelector(
[services, projectById(projectId), collapsedServices, metricDatasets],
(services, project, collapsed, metrics) =>
@@ -55,31 +63,27 @@ const servicesByProjectId = (projectId) => createSelector(
services: services.filter((s) => s.parent === service.uuid)
}))
.filter((s) => !s.parent)
- .map((service) => {
- const isCollapsed = (uuid) => collapsed.indexOf(uuid) >= 0;
-
- const datasets = (uuids) => uuids.map((uuid) => find(metrics, [
- 'uuid',
- uuid
- ]));
-
- return {
+ .map((service) => ({
+ ...service,
+ metrics: datasets(metrics, service.metrics),
+ collapsed: isCollapsed(collapsed, service.uuid),
+ services: service.services.map((service) => ({
...service,
- metrics: datasets(service.metrics),
- collapsed: isCollapsed(service.uuid),
- services: service.services.map((service) => ({
- ...service,
- metrics: datasets(service.metrics),
- collapsed: isCollapsed(service.uuid)
- }))
- };
- })
+ metrics: datasets(metrics, service.metrics),
+ collapsed: isCollapsed(collapsed, service.uuid)
+ }))
+ }))
);
const instancesByServiceId = (serviceId) => createSelector(
- [instances, serviceById(serviceId)],
- (instances, service) =>
+ [instances, serviceById(serviceId), collapsedInstances, metricDatasets],
+ (instances, service, collapsed, metrics) =>
instances.filter((i) => i.service === service.uuid)
+ .map((instance) => ({
+ ...instance,
+ metrics: datasets(metrics, instance.metrics),
+ collapsed: isCollapsed(collapsed, instance.uuid)
+ }))
);