diff --git a/packages/unleash-frontend-next/.babelrc b/packages/unleash-frontend-next/.babelrc
new file mode 100644
index 0000000000..4d3b7d3676
--- /dev/null
+++ b/packages/unleash-frontend-next/.babelrc
@@ -0,0 +1,8 @@
+{
+ "presets": ["react", "es2015", "stage-2"],
+ "env": {
+ "development": {
+ "presets": ["react-hmre"]
+ }
+ }
+}
diff --git a/packages/unleash-frontend-next/.eslintrc b/packages/unleash-frontend-next/.eslintrc
new file mode 100644
index 0000000000..8f68bb4a87
--- /dev/null
+++ b/packages/unleash-frontend-next/.eslintrc
@@ -0,0 +1,6 @@
+{
+ "extends": [
+ "finn",
+ "finn/node"
+ ]
+}
diff --git a/packages/unleash-frontend-next/.gitignore b/packages/unleash-frontend-next/.gitignore
new file mode 100644
index 0000000000..1521c8b765
--- /dev/null
+++ b/packages/unleash-frontend-next/.gitignore
@@ -0,0 +1 @@
+dist
diff --git a/packages/unleash-frontend-next/index.html b/packages/unleash-frontend-next/index.html
new file mode 100644
index 0000000000..3ec0888efe
--- /dev/null
+++ b/packages/unleash-frontend-next/index.html
@@ -0,0 +1,13 @@
+
+
+
+ Unleash UI
+
+
+
+
+
+
+
+
+
diff --git a/packages/unleash-frontend-next/index.js b/packages/unleash-frontend-next/index.js
new file mode 100644
index 0000000000..a801f347b6
--- /dev/null
+++ b/packages/unleash-frontend-next/index.js
@@ -0,0 +1,6 @@
+'use strict';
+const path = require('path');
+
+module.exports = {
+ publicFolder: path.join(__dirname, 'dist'),
+};
diff --git a/packages/unleash-frontend-next/jest-preprocessor.js b/packages/unleash-frontend-next/jest-preprocessor.js
new file mode 100644
index 0000000000..66c8d10d00
--- /dev/null
+++ b/packages/unleash-frontend-next/jest-preprocessor.js
@@ -0,0 +1,9 @@
+// preprocessor.js
+'use strict';
+
+const ReactTools = require('react-tools');
+module.exports = {
+ process (src) {
+ return ReactTools.transform(src);
+ },
+};
diff --git a/packages/unleash-frontend-next/package.json b/packages/unleash-frontend-next/package.json
new file mode 100644
index 0000000000..a50e697aab
--- /dev/null
+++ b/packages/unleash-frontend-next/package.json
@@ -0,0 +1,78 @@
+{
+ "name": "unleash-frontend-next",
+ "description": "unleash your features",
+ "version": "1.0.0",
+ "keywords": [
+ "unleash",
+ "feature toggle",
+ "feature",
+ "toggle"
+ ],
+ "files": [
+ "public"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "ssh://git@github.com:finn-no/unleash.git"
+ },
+ "bugs": {
+ "url": "https://github.com/finn-no/unleash/issues"
+ },
+ "engines": {
+ "node": "6"
+ },
+ "scripts": {
+ "build": "webpack -p",
+ "start": "webpack-dev-server --config webpack.config.js --hot --progress --colors --port 3000",
+ "test": "echo 'no test'",
+ "test:ci": "npm run test",
+ "prepublish": "npm run build"
+ },
+ "main": "./index.js",
+ "dependencies": {
+ "immutability-helper": "^2.0.0",
+ "react": "^15.3.1",
+ "react-addons-css-transition-group": "^15.3.1",
+ "react-dom": "^15.3.1",
+ "react-redux": "^4.4.5",
+ "react-router": "^2.8.0",
+ "react-toolbox": "^1.2.1",
+ "redux": "^3.6.0"
+ },
+ "devDependencies": {
+ "babel-core": "^6.14.0",
+ "babel-loader": "^6.2.5",
+ "babel-preset-es2015": "^6.14.0",
+ "babel-preset-react": "^6.11.1",
+ "babel-preset-react-hmre": "^1.1.1",
+ "babel-preset-stage-0": "^6.5.0",
+ "babel-preset-stage-2": "^6.13.0",
+ "css-loader": "^0.25.0",
+ "extract-text-webpack-plugin": "^1.0.1",
+ "node-sass": "~3.7.0",
+ "postcss-loader": "^0.13.0",
+ "redux-devtools": "^3.3.1",
+ "sass-loader": "^4.0.2",
+ "style-loader": "^0.13.1",
+ "toolbox-loader": "0.0.3",
+ "webpack": "^1.13.2",
+ "webpack-dev-server": "^1.15.1"
+ },
+ "jest": {
+ "scriptPreprocessor": "/jest-preprocessor.js",
+ "modulePathIgnorePatterns": [
+ "/node_modules/npm"
+ ],
+ "unmockedModulePathPatterns": [
+ "/node_modules/react",
+ "/node_modules/reflux"
+ ],
+ "moduleFileExtensions": [
+ "jsx",
+ "js"
+ ]
+ },
+ "pre-commit": [
+ "lint"
+ ]
+}
diff --git a/packages/unleash-frontend-next/src/.eslintrc b/packages/unleash-frontend-next/src/.eslintrc
new file mode 100644
index 0000000000..b2d0257498
--- /dev/null
+++ b/packages/unleash-frontend-next/src/.eslintrc
@@ -0,0 +1,24 @@
+{
+ "extends": [
+ "finn",
+ "finn-react"
+ ],
+ "env": {
+ "browser": true,
+ "commonjs": true,
+ "es6": true
+ },
+ "rules": {
+ "react/sort-comp": "off"
+ },
+ "ecmaFeatures": {
+ "sourceType": "module"
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true,
+ "experimentalObjectRestSpread": true
+ }
+ }
+}
diff --git a/packages/unleash-frontend-next/src/App.jsx b/packages/unleash-frontend-next/src/App.jsx
new file mode 100644
index 0000000000..0457573a27
--- /dev/null
+++ b/packages/unleash-frontend-next/src/App.jsx
@@ -0,0 +1,37 @@
+import React, { Component } from 'react';
+import { Layout, Panel, NavDrawer, AppBar, IconButton } from 'react-toolbox';
+import style from './style';
+
+import Navigation from './Navigation';
+
+export default class App extends Component {
+ constructor (props) {
+ super(props);
+ this.state = { drawerActive: false };
+
+ this.toggleDrawerActive = () => {
+ this.setState({ drawerActive: !this.state.drawerActive });
+ };
+ }
+
+ render () {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {this.props.children}
+
+
+
+
+
+ );
+ }
+};
diff --git a/packages/unleash-frontend-next/src/Navigation.jsx b/packages/unleash-frontend-next/src/Navigation.jsx
new file mode 100644
index 0000000000..0d1dd4198b
--- /dev/null
+++ b/packages/unleash-frontend-next/src/Navigation.jsx
@@ -0,0 +1,25 @@
+import React, { Component } from 'react';
+import { Link } from 'react-router';
+import { ListSubHeader, List, ListItem, ListDivider } from 'react-toolbox';
+
+export default class UnleashNav extends Component {
+ render () {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ A product by FINN.no
+
+
+
+ );
+ }
+};
diff --git a/packages/unleash-frontend-next/src/action/index.js b/packages/unleash-frontend-next/src/action/index.js
new file mode 100644
index 0000000000..69e53f0e88
--- /dev/null
+++ b/packages/unleash-frontend-next/src/action/index.js
@@ -0,0 +1,11 @@
+let nextFeatureId = 0;
+export const addFeatureToggle = (featureName) => ({
+ type: 'ADD_FEATURE_TOGGLE',
+ id: nextFeatureId++,
+ featureName,
+});
+
+export const toggleFeature = (id) => ({
+ type: 'TOGGLE_FEATURE_TOGGLE',
+ id,
+});
diff --git a/packages/unleash-frontend-next/src/component/feature/AddFeatureToggle.jsx b/packages/unleash-frontend-next/src/component/feature/AddFeatureToggle.jsx
new file mode 100644
index 0000000000..1f569edf2c
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/feature/AddFeatureToggle.jsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { addFeatureToggle } from '../../action';
+
+
+let AddFeatureToggle = ({ dispatch }) => {
+ let input;
+
+ return (
+
+
+
+ );
+};
+AddFeatureToggle = connect()(AddFeatureToggle);
+
+export default AddFeatureToggle;
diff --git a/packages/unleash-frontend-next/src/component/feature/Feature.jsx b/packages/unleash-frontend-next/src/component/feature/Feature.jsx
new file mode 100644
index 0000000000..0b41e9c9f0
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/feature/Feature.jsx
@@ -0,0 +1,16 @@
+import React, { PropTypes } from 'react';
+
+const Feature = ({ onClick, featureName, enabled }) => (
+
+ {featureName} is {enabled ? 'enabled ' : 'disabled '}
+ toggle
+
+);
+
+Feature.propTypes = {
+ onClick: PropTypes.func.isRequired,
+ enabled: PropTypes.bool.isRequired,
+ featureName: PropTypes.string.isRequired,
+};
+
+export default Feature;
diff --git a/packages/unleash-frontend-next/src/component/feature/FeatureList.jsx b/packages/unleash-frontend-next/src/component/feature/FeatureList.jsx
new file mode 100644
index 0000000000..55347eaab3
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/feature/FeatureList.jsx
@@ -0,0 +1,21 @@
+import React, { PropTypes } from 'react';
+import Feature from './Feature';
+
+const FeatureList = ({ features, onFeatureClick }) => (
+
+ {features.map(featureToggle =>
+ onFeatureClick(featureToggle.id)}
+ />
+ )}
+
+);
+
+FeatureList.propTypes = {
+ onFeatureClick: PropTypes.func.isRequired,
+ features: PropTypes.array.isRequired,
+};
+
+export default FeatureList;
diff --git a/packages/unleash-frontend-next/src/component/feature/FeatureListContainer.jsx b/packages/unleash-frontend-next/src/component/feature/FeatureListContainer.jsx
new file mode 100644
index 0000000000..416fa3a5fa
--- /dev/null
+++ b/packages/unleash-frontend-next/src/component/feature/FeatureListContainer.jsx
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux';
+import { toggleFeature } from '../../action';
+import FeatureList from './FeatureList';
+
+const mapStateToProps = (state) => ({
+ features: state.features,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ onFeatureClick: (id) => {
+ dispatch(toggleFeature(id));
+ },
+});
+
+
+const FeatureListContainer = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(FeatureList);
+
+export default FeatureListContainer;
diff --git a/packages/unleash-frontend-next/src/index.jsx b/packages/unleash-frontend-next/src/index.jsx
new file mode 100644
index 0000000000..c2897ae9e6
--- /dev/null
+++ b/packages/unleash-frontend-next/src/index.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Router, Route, IndexRoute, hashHistory } from 'react-router';
+import { Provider } from 'react-redux';
+import { createStore } from 'redux';
+
+import store from './store';
+import App from './App';
+
+import Features from './page/features';
+import Strategies from './page/strategies';
+import Logs from './page/logs';
+import Archive from './page/archive';
+
+const unleashStore = createStore(store);
+
+ReactDOM.render(
+
+
+
+
+
+
+
+
+
+ , document.getElementById('app'));
diff --git a/packages/unleash-frontend-next/src/page/archive/index.js b/packages/unleash-frontend-next/src/page/archive/index.js
new file mode 100644
index 0000000000..7abb59def9
--- /dev/null
+++ b/packages/unleash-frontend-next/src/page/archive/index.js
@@ -0,0 +1,28 @@
+import React, { Component } from 'react';
+import { Card, CardTitle, CardText } from 'react-toolbox';
+
+
+export default class Archive extends Component {
+ render () {
+ return (
+
+ Archived Feture Toggles
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam pharetra finibus
+ ullamcorper. Proin laoreet faucibus venenatis. Aenean quis leo finibus, maximus
+ nisi finibus, fringilla ex. Nam mollis congue orci eu consectetur. Aliquam a
+ massa quis tortor vestibulum lacinia. Phasellus nisi velit, mattis vel nulla
+ a, rhoncus porta dui. Vestibulum viverra augue in pellentesque tincidunt.
+ Aliquam rhoncus nunc ipsum, sed vehicula leo dictum in. Phasellus accumsan
+ elis sem, in ullamcorper nisi accumsan vitae. Nullam vitae consectetur mi,
+ sed vulputate augue. In quis augue tellus. Duis convallis cursus elit, in
+ interdum nisl pulvinar viverra. Sed vel ornare sapien, eu consectetur metus.
+ Vivamus at porta nisl. Nullam in aliquam nisl.
+
+
+
+
+ );
+ }
+};
diff --git a/packages/unleash-frontend-next/src/page/features/index.js b/packages/unleash-frontend-next/src/page/features/index.js
new file mode 100644
index 0000000000..812e601d9b
--- /dev/null
+++ b/packages/unleash-frontend-next/src/page/features/index.js
@@ -0,0 +1,17 @@
+import React, { Component } from 'react';
+import FeatureListContainer from '../../component/feature/FeatureListContainer';
+import AddFeatureToggle from '../../component/feature/AddFeatureToggle';
+
+export default class Features extends Component {
+ render () {
+ return (
+
+
Feature Toggles
+
+
+
+
+
+ );
+ }
+};
diff --git a/packages/unleash-frontend-next/src/page/strategies/index.js b/packages/unleash-frontend-next/src/page/strategies/index.js
new file mode 100644
index 0000000000..7c493453fa
--- /dev/null
+++ b/packages/unleash-frontend-next/src/page/strategies/index.js
@@ -0,0 +1,11 @@
+import React, { Component } from 'react';
+
+export default class Strategies extends Component {
+ render () {
+ return (
+
+
Strategies
+
+ );
+ }
+};
diff --git a/packages/unleash-frontend-next/src/store/features.js b/packages/unleash-frontend-next/src/store/features.js
new file mode 100644
index 0000000000..e4a838ea43
--- /dev/null
+++ b/packages/unleash-frontend-next/src/store/features.js
@@ -0,0 +1,39 @@
+const feature = (state = {}, action) => {
+ switch (action.type) {
+ case 'ADD_FEATURE_TOGGLE':
+ return {
+ id: action.id,
+ featureName: action.featureName,
+ enabled: false,
+ };
+ case 'TOGGLE_FEATURE_TOGGLE':
+ if (state.id !== action.id) {
+ return state;
+ }
+
+ return Object.assign({}, state, {
+ enabled: !state.enabled,
+ });
+
+ default:
+ return state;
+ }
+};
+
+const features = (state = [{ id: 1, featureName: 'test', enabled: true }], action) => {
+ switch (action.type) {
+ case 'ADD_FEATURE_TOGGLE':
+ return [
+ ...state,
+ feature(undefined, action),
+ ];
+ case 'TOGGLE_FEATURE_TOGGLE':
+ return state.map(t =>
+ feature(t, action)
+ );
+ default:
+ return state;
+ }
+};
+
+export default features;
diff --git a/packages/unleash-frontend-next/src/store/index.js b/packages/unleash-frontend-next/src/store/index.js
new file mode 100644
index 0000000000..bebe5c7167
--- /dev/null
+++ b/packages/unleash-frontend-next/src/store/index.js
@@ -0,0 +1,8 @@
+import { combineReducers } from 'redux';
+import features from './features';
+
+const unleashStore = combineReducers({
+ features,
+});
+
+export default unleashStore;
diff --git a/packages/unleash-frontend-next/src/style.scss b/packages/unleash-frontend-next/src/style.scss
new file mode 100644
index 0000000000..a2a9af46ad
--- /dev/null
+++ b/packages/unleash-frontend-next/src/style.scss
@@ -0,0 +1,8 @@
+.container {
+ height: auto;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+}
diff --git a/packages/unleash-frontend-next/src/theme/_config.scss b/packages/unleash-frontend-next/src/theme/_config.scss
new file mode 100644
index 0000000000..2b301c05f3
--- /dev/null
+++ b/packages/unleash-frontend-next/src/theme/_config.scss
@@ -0,0 +1,22 @@
+@import "~react-toolbox/lib/colors";
+@import "~react-toolbox/lib/globals";
+@import "~react-toolbox/lib/mixins";
+@import "~react-toolbox/lib/commons";
+
+
+$color-primary:$palette-blue-400;
+$color-primary-dark: $palette-blue-700;
+
+$appbar-height-m-portrait: 5.6 * $unit !default;
+$appbar-height-m-landscape: 4.8 * $unit !default;
+
+$navigation-drawer-desktop-width: 3 * $standard-increment-desktop !default;
+$navigation-drawer-max-desktop-width: 40 * $unit !default;
+
+// Mobile:
+// Width = Screen width − 56 dp
+// Maximum width: 320dp
+$navigation-drawer-mobile-width: 5 * $standard-increment-mobile !default;
+
+// sass doesn't like use of variable here: calc(100% - $standard-increment-mobile);
+$navigation-drawer-max-mobile-width: calc(100% - 5.6rem) !default;
\ No newline at end of file
diff --git a/packages/unleash-frontend-next/webpack.config.js b/packages/unleash-frontend-next/webpack.config.js
new file mode 100644
index 0000000000..99165d864a
--- /dev/null
+++ b/packages/unleash-frontend-next/webpack.config.js
@@ -0,0 +1,60 @@
+// docs: http://webpack.github.io/docs/configuration.html
+
+'use strict';
+
+const path = require('path');
+const webpack = require('webpack');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+
+module.exports = {
+ entry: [
+ 'webpack-dev-server/client?http://localhost:3000',
+ 'webpack/hot/only-dev-server',
+ './src/index',
+ ],
+
+ resolve: {
+ root: [path.join(__dirname, 'src')],
+ extensions: ['', '.scss', '.css', '.js', '.jsx', '.json'],
+ modulesDirectories: ['web_modules', 'node_modules'],
+ },
+
+ output: {
+ path: path.join(__dirname, 'dist'),
+ filename: 'bundle.js',
+ publicPath: '/static/',
+ },
+
+ module: {
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ exclude: /node_modules/,
+ loaders: ['babel'],
+ include: path.join(__dirname, 'src'),
+ },
+ {
+ test: /(\.scss|\.css)$/,
+ loader: ExtractTextPlugin.extract('style',
+ 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass'),
+ },
+ ],
+ },
+
+ plugins: [
+ new ExtractTextPlugin('bundle.css', { allChunks: true }),
+ new webpack.HotModuleReplacementPlugin(),
+ ],
+
+ sassLoader: {
+ data: '@import "theme/_config.scss";',
+ includePaths: [path.resolve(__dirname, './src')],
+ },
+
+ devtool: 'source-map',
+
+ externals: {
+ // stuff not in node_modules can be resolved here.
+ },
+
+};