diff --git a/packages/unleash-frontend-next/.eslintrc b/packages/unleash-frontend-next/.eslintrc
index 8f68bb4a87..ee0b68514f 100644
--- a/packages/unleash-frontend-next/.eslintrc
+++ b/packages/unleash-frontend-next/.eslintrc
@@ -2,5 +2,8 @@
"extends": [
"finn",
"finn/node"
- ]
+ ],
+ "rules": {
+ "no-shadow": 0
+ }
}
diff --git a/packages/unleash-frontend-next/src/component/metrics/metrics-component.js b/packages/unleash-frontend-next/src/component/metrics/metrics-component.js
new file mode 100644
index 0000000000..4b47bf2b42
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/metrics/metrics-component.js
@@ -0,0 +1,31 @@
+import React, { Component } from 'react';
+import { List, ListItem, ListSubHeader, ListDivider } from 'react-toolbox/lib/list';
+import Chip from 'react-toolbox/lib/chip';
+
+class Metrics extends Component {
+
+ componentDidMount () {
+ this.props.fetchMetrics();
+ }
+
+ render () {
+ const { globalCount, apps, clientList } = this.props;
+
+ return (
+
+ Total of {globalCount} toggles checked from {apps.length} apps ({apps.join(', ')})} />
+
+ {clientList.map(({ name, count, ping, appName }, i) =>
+ {count}]}
+ key={name + i}
+ caption={appName}
+ legend={`${name} pinged ${ping}`} />
+ )}
+
+ );
+ }
+}
+
+
+export default Metrics;
diff --git a/packages/unleash-frontend-next/src/component/metrics/metrics-container.js b/packages/unleash-frontend-next/src/component/metrics/metrics-container.js
new file mode 100644
index 0000000000..e4ab36af79
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/metrics/metrics-container.js
@@ -0,0 +1,39 @@
+import { connect } from 'react-redux';
+import Metrics from './metrics-component';
+import { fetchMetrics } from '../../store/metrics-actions';
+
+const mapStateToProps = (state) => {
+ const globalCount = state.metrics.get('globalCount');
+ const apps = state.metrics.get('apps').toArray();
+ const clients = state.metrics.get('clients').toJS();
+
+ const clientList = Object
+ .keys(clients)
+ .map((k) => {
+ const client = clients[k];
+ return {
+ name: k,
+ appName: client.appName,
+ count: client.count,
+ ping: new Date(client.ping),
+ };
+ })
+ .sort((a, b) => (a.ping > b.ping ? -1 : 1));
+
+
+ /*
+ Possible stuff to ask/answer:
+ * toggles in use but not in unleash-server
+ * nr of toggles using fallbackValue
+ * strategies implemented but not used
+ */
+ return {
+ globalCount,
+ apps,
+ clientList,
+ };
+};
+
+const MetricsContainer = connect(mapStateToProps, { fetchMetrics })(Metrics);
+
+export default MetricsContainer;
diff --git a/packages/unleash-frontend-next/src/component/nav.jsx b/packages/unleash-frontend-next/src/component/nav.jsx
index bded15c70b..41a7e1e5b3 100644
--- a/packages/unleash-frontend-next/src/component/nav.jsx
+++ b/packages/unleash-frontend-next/src/component/nav.jsx
@@ -14,10 +14,11 @@ export default class UnleashNav extends Component {
return (
- {createListItem('/features', 'Feature Toggles')}
+ {createListItem('/features', 'Feature toggles')}
{createListItem('/strategies', 'Strategies')}
- {createListItem('/history', 'Event History')}
- {createListItem('/archive', 'Archived Toggles')}
+ {createListItem('/history', 'Event history')}
+ {createListItem('/archive', 'Archived toggles')}
+ {createListItem('/metrics', 'Client metrics')}
diff --git a/packages/unleash-frontend-next/src/data/metrics-api.js b/packages/unleash-frontend-next/src/data/metrics-api.js
new file mode 100644
index 0000000000..cb06ad9443
--- /dev/null
+++ b/packages/unleash-frontend-next/src/data/metrics-api.js
@@ -0,0 +1,20 @@
+const URI = '/metrics';
+
+function throwIfNotSuccess (response) {
+ if (!response.ok) {
+ let error = new Error('API call failed');
+ error.status = response.status;
+ throw error;
+ }
+ return response;
+}
+
+function fetchAll () {
+ return fetch(URI)
+ .then(throwIfNotSuccess)
+ .then(response => response.json());
+}
+
+module.exports = {
+ fetchAll,
+};
diff --git a/packages/unleash-frontend-next/src/index.jsx b/packages/unleash-frontend-next/src/index.jsx
index a45ca4a3ce..8133a8a81d 100644
--- a/packages/unleash-frontend-next/src/index.jsx
+++ b/packages/unleash-frontend-next/src/index.jsx
@@ -15,6 +15,7 @@ import Strategies from './page/strategies';
import CreateStrategies from './page/strategies/create';
import HistoryPage from './page/history';
import Archive from './page/archive';
+import Metrics from './page/metrics';
const unleashStore = createStore(
store,
@@ -35,6 +36,7 @@ ReactDOM.render(
+
, document.getElementById('app'));
diff --git a/packages/unleash-frontend-next/src/page/metrics/index.js b/packages/unleash-frontend-next/src/page/metrics/index.js
new file mode 100644
index 0000000000..c18da93b5c
--- /dev/null
+++ b/packages/unleash-frontend-next/src/page/metrics/index.js
@@ -0,0 +1,6 @@
+import React from 'react';
+import Metrics from '../../component/metrics/metrics-container';
+
+const render = () => ;
+
+export default render;
diff --git a/packages/unleash-frontend-next/src/store/index.js b/packages/unleash-frontend-next/src/store/index.js
index c064657e98..ef80a0a242 100644
--- a/packages/unleash-frontend-next/src/store/index.js
+++ b/packages/unleash-frontend-next/src/store/index.js
@@ -5,6 +5,7 @@ import input from './input-store';
import history from './history-store'; // eslint-disable-line
import archive from './archive-store';
import error from './error-store';
+import metrics from './metrics-store';
const unleashStore = combineReducers({
features,
@@ -13,6 +14,7 @@ const unleashStore = combineReducers({
history,
archive,
error,
+ metrics,
});
export default unleashStore;
diff --git a/packages/unleash-frontend-next/src/store/metrics-actions.js b/packages/unleash-frontend-next/src/store/metrics-actions.js
new file mode 100644
index 0000000000..7010d3857d
--- /dev/null
+++ b/packages/unleash-frontend-next/src/store/metrics-actions.js
@@ -0,0 +1,20 @@
+import api from '../data/metrics-api';
+
+export const RECEIVE_METRICS = 'RECEIVE_METRICS';
+export const ERROR_RECEIVE_METRICS = 'ERROR_RECEIVE_METRICS';
+
+const receiveMetrics = (json) => ({
+ type: RECEIVE_METRICS,
+ value: json,
+});
+
+const errorReceiveMetrics = (statusCode) => ({
+ type: ERROR_RECEIVE_METRICS,
+ statusCode,
+});
+
+export function fetchMetrics () {
+ return dispatch => api.fetchAll()
+ .then(json => dispatch(receiveMetrics(json)))
+ .catch(error => dispatch(errorReceiveMetrics(error)));
+}
diff --git a/packages/unleash-frontend-next/src/store/metrics-store.js b/packages/unleash-frontend-next/src/store/metrics-store.js
new file mode 100644
index 0000000000..f0b7a4a650
--- /dev/null
+++ b/packages/unleash-frontend-next/src/store/metrics-store.js
@@ -0,0 +1,21 @@
+import { fromJS } from 'immutable';
+import { RECEIVE_METRICS } from './metrics-actions';
+
+function getInitState () {
+ return fromJS({
+ totalCount: 0,
+ apps: [],
+ clients: {},
+ });
+}
+
+const historyStore = (state = getInitState(), action) => {
+ switch (action.type) {
+ case RECEIVE_METRICS:
+ return fromJS(action.value);
+ default:
+ return state;
+ }
+};
+
+export default historyStore;
diff --git a/packages/unleash-frontend-next/webpack.config.js b/packages/unleash-frontend-next/webpack.config.js
index 208b4a1e3d..edfd5096b1 100644
--- a/packages/unleash-frontend-next/webpack.config.js
+++ b/packages/unleash-frontend-next/webpack.config.js
@@ -73,6 +73,10 @@ module.exports = {
target: 'http://localhost:4242',
secure: false,
},
+ '/metrics': {
+ target: 'http://localhost:4242',
+ secure: false,
+ },
},
},
};