diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..86c445f
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+  "presets": ["es2015", "react"]
+}
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..64d2227
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,257 @@
+{
+  "ecmaFeatures": {
+    "jsx": true
+  },
+  "env": {
+    "browser": true,
+    "mocha": true,
+    "node": true
+  },
+  "parser": "babel-eslint",
+  "plugins": [
+    "react"
+  ],
+  "globals": {
+    "window": true,
+    "$": true,
+    "ga": true,
+    "jQuery": true,
+    "router": true
+  },
+  "rules": {
+    "comma-dangle": 2,
+    "no-cond-assign": 2,
+    "no-console": 0,
+    "no-constant-condition": 2,
+    "no-control-regex": 2,
+    "no-debugger": 2,
+    "no-dupe-keys": 2,
+    "no-empty": 2,
+    "no-empty-character-class": 2,
+    "no-ex-assign": 2,
+    "no-extra-boolean-cast": 2,
+    "no-extra-parens": 0,
+    "no-extra-semi": 2,
+    "no-func-assign": 2,
+    "no-inner-declarations": 2,
+    "no-invalid-regexp": 2,
+    "no-irregular-whitespace": 2,
+    "no-negated-in-lhs": 2,
+    "no-obj-calls": 2,
+    "no-regex-spaces": 2,
+    "no-reserved-keys": 0,
+    "no-sparse-arrays": 2,
+    "no-unreachable": 2,
+    "use-isnan": 2,
+    "valid-jsdoc": 2,
+    "valid-typeof": 2,
+    "block-scoped-var": 0,
+    "complexity": 0,
+    "consistent-return": 2,
+    "curly": 2,
+    "default-case": 1,
+    "dot-notation": 0,
+    "eqeqeq": 1,
+    "guard-for-in": 1,
+    "no-alert": 1,
+    "no-caller": 2,
+    "no-div-regex": 2,
+    "no-else-return": 0,
+    "no-empty-label": 2,
+    "no-eq-null": 1,
+    "no-eval": 2,
+    "no-extend-native": 2,
+    "no-extra-bind": 2,
+    "no-fallthrough": 2,
+    "no-floating-decimal": 2,
+    "no-implied-eval": 2,
+    "no-iterator": 2,
+    "no-labels": 2,
+    "no-lone-blocks": 2,
+    "no-loop-func": 1,
+    "no-multi-spaces": 1,
+    "no-multi-str": 2,
+    "no-native-reassign": 2,
+    "no-new": 2,
+    "no-new-func": 2,
+    "no-new-wrappers": 2,
+    "no-octal": 2,
+    "no-octal-escape": 2,
+    "no-process-env": 0,
+    "no-proto": 2,
+    "no-redeclare": 1,
+    "no-return-assign": 2,
+    "no-script-url": 2,
+    "no-self-compare": 2,
+    "no-sequences": 2,
+    "no-unused-expressions": 2,
+    "no-void": 1,
+    "no-warning-comments": [
+      1,
+      {
+        "terms": [
+          "fixme"
+        ],
+        "location": "start"
+      }
+    ],
+    "no-with": 2,
+    "radix": 2,
+    "vars-on-top": 0,
+    "wrap-iife": [
+      2,
+      "any"
+    ],
+    "yoda": 0,
+    "strict": 0,
+    "no-catch-shadow": 2,
+    "no-delete-var": 2,
+    "no-label-var": 2,
+    "no-shadow": 0,
+    "no-shadow-restricted-names": 2,
+    "no-undef": 2,
+    "no-undef-init": 2,
+    "no-undefined": 1,
+    "no-unused-vars": 2,
+    "no-use-before-define": 0,
+    "handle-callback-err": 2,
+    "no-mixed-requires": 0,
+    "no-new-require": 2,
+    "no-path-concat": 2,
+    "no-process-exit": 2,
+    "no-restricted-modules": 0,
+    "no-sync": 0,
+    "brace-style": [
+      2,
+      "1tbs",
+      {
+        "allowSingleLine": true
+      }
+    ],
+    "camelcase": 1,
+    "comma-spacing": [
+      2,
+      {
+        "before": false,
+        "after": true
+      }
+    ],
+    "comma-style": [
+      2,
+      "last"
+    ],
+    "consistent-this": 0,
+    "eol-last": 2,
+    "func-names": 0,
+    "func-style": 0,
+    "key-spacing": [
+      2,
+      {
+        "beforeColon": false,
+        "afterColon": true
+      }
+    ],
+    "max-nested-callbacks": 0,
+    "new-cap": 0,
+    "new-parens": 2,
+    "no-array-constructor": 2,
+    "no-inline-comments": 1,
+    "no-lonely-if": 1,
+    "no-mixed-spaces-and-tabs": 2,
+    "no-multiple-empty-lines": [
+      1,
+      {
+        "max": 2
+      }
+    ],
+    "no-nested-ternary": 2,
+    "no-new-object": 2,
+    "semi-spacing": [
+      2,
+      {
+        "before": false,
+        "after": true
+      }
+    ],
+    "no-spaced-func": 2,
+    "no-ternary": 0,
+    "no-trailing-spaces": 1,
+    "no-underscore-dangle": 0,
+    "one-var": 0,
+    "operator-assignment": 0,
+    "padded-blocks": 0,
+    "quote-props": 0,
+    "quotes": [
+      2,
+      "single",
+      "avoid-escape"
+    ],
+    "semi": [
+      2,
+      "always"
+    ],
+    "sort-vars": 0,
+    "space-after-keywords": [
+      2,
+      "always"
+    ],
+    "space-before-function-paren": [
+      2,
+      "never"
+    ],
+    "space-before-blocks": [
+      2,
+      "always"
+    ],
+    "space-in-brackets": 0,
+    "space-in-parens": 0,
+    "space-infix-ops": 2,
+    "space-return-throw-case": 2,
+    "space-unary-ops": [
+      1,
+      {
+        "words": true,
+        "nonwords": false
+      }
+    ],
+    "spaced-comment": [
+      2,
+      "always",
+      {
+        "exceptions": [
+          "-"
+        ]
+      }
+    ],
+    "wrap-regex": 1,
+    "max-depth": 0,
+    "max-len": [
+      1,
+      80,
+      2
+    ],
+    "max-params": 0,
+    "max-statements": 0,
+    "no-bitwise": 1,
+    "no-plusplus": 0,
+    "react/jsx-boolean-value": [
+      1,
+      "always"
+    ],
+    "jsx-quotes": [
+      1,
+      "prefer-single"
+    ],
+    "react/jsx-no-undef": 1,
+    "react/jsx-sort-props": 1,
+    "react/jsx-uses-react": 1,
+    "react/jsx-uses-vars": 1,
+    "react/no-did-mount-set-state": 2,
+    "react/no-did-update-set-state": 2,
+    "react/no-multi-comp": 2,
+    "react/prop-types": 2,
+    "react/react-in-jsx-scope": 1,
+    "react/self-closing-comp": 1,
+    "react/wrap-multilines": 1
+  }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3b02d40
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+npm-debug.log
+.DS_Store
+dist
+.idea
+.env
\ No newline at end of file
diff --git a/Challenge_Editor_Mockup.png b/Challenge_Editor_Mockup.png
new file mode 100644
index 0000000..ee26bf7
Binary files /dev/null and b/Challenge_Editor_Mockup.png differ
diff --git a/Layout.png b/Layout.png
new file mode 100644
index 0000000..47e74d0
Binary files /dev/null and b/Layout.png differ
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b022d5b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+# Challenge-O-Matic 1003
+Free Code Camp's Challenge Editor (Very Alpha)
+
+## Note
+This **will** alter the challenge files in your Free Code Camp `/seed/challenges` directory!
+
+
+## Initial Setup
+* Clone this repo to the same directory where your Free Code Camp directory is (ex> `/Users/terakilobyte/Developer)`
+* `cd ChallengeOMatic1003 && npm install`
+* `<yourFCC_FOLDER_NAME>` is whatever you named your Free Code Camp repo locally(mine is freecodecamp).
+* `echo 'FCC_FOLDER_NAME = "<yourFCC_FOLDER_NAME>"' >> .env`
+* `npm run build`
+
+## To Run
+* `npm start`
+ 
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..98f025b
--- /dev/null
+++ b/app.js
@@ -0,0 +1,75 @@
+var express = require('express');
+var path = require('path');
+var logger = require('morgan');
+var bodyParser = require('body-parser');
+var fs = require('fs');
+var config = require('./config');
+var ObjectID = require('mongodb').ObjectID;
+
+var app = express();
+
+// view engine setup
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'jade');
+
+// uncomment after placing your favicon in /public
+// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
+app.use(logger('dev'));
+app.use(bodyParser.urlencoded({parameterLimit: 10000000, limit: '50mb', extended: true}));
+app.use(bodyParser.json({limit: '50mb'}));
+app.use(express.static(path.join(__dirname, 'public')));
+
+app.post('/export', function(req, res, next) {
+  Object.keys(req.body.data).forEach(function(file) {
+    var fileData = req.body.data[file];
+    console.log(JSON.stringify(fileData, null, 2));
+    fs.writeFile(config.fccPath + file,
+                 JSON.stringify(fileData, null, 2),
+                 function(err) {
+      console.error(err);
+    });
+  });
+});
+
+app.get('/mongoid', function(req, res, next) {
+  var objectId = new ObjectID();
+  res.json({objectId: objectId});
+});
+
+app.get('/*', function(req, res, next) {
+  res.render('index');
+});
+
+// catch 404 and forward to error handler
+app.use(function(req, res, next) {
+  var err = new Error('Not Found');
+  err.status = 404;
+  next(err);
+});
+
+// error handlers
+
+// development error handler
+// will print stacktrace
+if (app.get('env') === 'development') {
+  app.use(function(err, req, res, next) {
+    res.status(err.status || 500);
+    res.render('error', {
+      message: err.message,
+      error: err
+    });
+  });
+}
+
+// production error handler
+// no stacktraces leaked to user
+app.use(function(err, req, res, next) {
+  res.status(err.status || 500);
+  res.render('error', {
+    message: err.message,
+    error: {}
+  });
+});
+
+
+module.exports = app;
diff --git a/bin/www b/bin/www
new file mode 100644
index 0000000..73a12f2
--- /dev/null
+++ b/bin/www
@@ -0,0 +1,94 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var app = require('../app');
+var debug = require('debug')('ChallengeOMatic1003:server');
+var http = require('http');
+var dotenv = require('dotenv');
+
+/**
+ * Get port from environment and store in Express.
+ */
+
+dotenv.load();
+
+var port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+/**
+ * Create HTTP server.
+ */
+
+var server = http.createServer(app);
+
+/**
+ * Listen on provided port, on all network interfaces.
+ */
+
+server.listen(port);
+server.on('error', onError);
+server.on('listening', onListening);
+
+/**
+ * Normalize a port into a number, string, or false.
+ */
+
+function normalizePort(val) {
+  var port = parseInt(val, 10);
+
+  if (isNaN(port)) {
+    // named pipe
+    return val;
+  }
+
+  if (port >= 0) {
+    // port number
+    return port;
+  }
+
+  return false;
+}
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+
+function onError(error) {
+  if (error.syscall !== 'listen') {
+    throw error;
+  }
+
+  var bind = typeof port === 'string'
+    ? 'Pipe ' + port
+    : 'Port ' + port;
+
+  // handle specific listen errors with friendly messages
+  switch (error.code) {
+    case 'EACCES':
+      console.error(bind + ' requires elevated privileges');
+      process.exit(1);
+      break;
+    case 'EADDRINUSE':
+      console.error(bind + ' is already in use');
+      process.exit(1);
+      break;
+    default:
+      throw error;
+  }
+}
+
+/**
+ * Event listener for HTTP server "listening" event.
+ */
+
+function onListening() {
+  var addr = server.address();
+  var bind = typeof addr === 'string'
+    ? 'pipe ' + addr
+    : 'port ' + addr.port;
+  debug('Listening on ' + bind);
+  console.log('listening on port: ' + addr.port);
+}
diff --git a/config.js b/config.js
new file mode 100644
index 0000000..b4167aa
--- /dev/null
+++ b/config.js
@@ -0,0 +1,9 @@
+var dotenv = require('dotenv');
+dotenv.load();
+
+var PATH_MARKER = process.platform === 'win32' ? '\\' : '/';
+module.exports = {
+  'fccPath': process.cwd().substr(0, process.cwd().lastIndexOf(PATH_MARKER))
+    + '/' + process.env.FCC_FOLDER_NAME + '/seed/challenges/'
+};
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..c4d9c49
--- /dev/null
+++ b/index.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>Sample App</title>
+  </head>
+  <body>
+    <div id='root'>
+    </div>
+    <script src="/static/bundle.js"></script>
+  </body>
+</html>
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0bfd4d6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "ChallengeOMatic1003",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "build": "webpack",
+    "start": "node ./bin/www"
+  },
+  "dependencies": {
+    "babel-core": "^6.2.1",
+    "body-parser": "^1.14.1",
+    "codemirror": "^5.8.0",
+    "cookie-parser": "~1.3.5",
+    "debug": "~2.2.0",
+    "dotenv": "^1.2.0",
+    "express": "^4.13.3",
+    "jade": "~1.11.0",
+    "jquery": "^2.1.4",
+    "material-ui": "^0.13.3",
+    "mongodb": "^2.0.49",
+    "morgan": "~1.6.1",
+    "react": "^0.14.3",
+    "react-dom": "^0.14.3",
+    "react-redux": "^4.0.0",
+    "react-tap-event-plugin": "^0.2.1",
+    "redux": "^3.0.4",
+    "style": "0.0.3"
+  },
+  "devDependencies": {
+    "babel-loader": "^6.2.0",
+    "babel-preset-es2015": "^6.1.18",
+    "babel-preset-react": "^6.1.18",
+    "css-loader": "^0.23.0",
+    "style-loader": "^0.13.0",
+    "webpack": "^1.12.8"
+  }
+}
diff --git a/src/App.js b/src/App.js
new file mode 100644
index 0000000..0092a81
--- /dev/null
+++ b/src/App.js
@@ -0,0 +1,10 @@
+import React, { Component } from 'react';
+import GrandCentralStation from './Components/GrandCentralStation';
+
+export default class App extends Component {
+  render() {
+    return (
+      <GrandCentralStation />
+    );
+  }
+}
diff --git a/src/Components/Editor.js b/src/Components/Editor.js
new file mode 100644
index 0000000..36c673e
--- /dev/null
+++ b/src/Components/Editor.js
@@ -0,0 +1,156 @@
+import './../../node_modules/codemirror/lib/codemirror.css';
+import './../../node_modules/codemirror/theme/monokai.css';
+import './../../node_modules/codemirror/addon/scroll/simplescrollbars.css';
+
+import React, {Component} from 'react';
+
+import {updateChallenge} from './../actions/editorActions';
+
+import {connect} from 'react-redux';
+
+import CodeMirror from './../../node_modules/codemirror/lib/codemirror';
+import './../../node_modules/codemirror/mode/javascript/javascript';
+import './../../node_modules/codemirror/mode/xml/xml';
+import './../../node_modules/codemirror/mode/css/css';
+import './../../node_modules/codemirror/mode/htmlmixed/htmlmixed';
+import './../../node_modules/codemirror/addon/edit/closebrackets';
+import './../../node_modules/codemirror/addon/edit/matchbrackets';
+import './../../node_modules/codemirror/addon/scroll/simplescrollbars';
+import './../../node_modules/codemirror/addon/scroll/annotatescrollbar';
+import './../../node_modules/codemirror/addon/scroll/scrollpastend';
+import './../../node_modules/codemirror/addon/lint/lint';
+import './../../node_modules/codemirror/addon/lint/javascript-lint';
+
+const connector = connect(function(state, props) {
+  // State from redux
+  return (
+    {
+      challenge: state.challenges.reduce(function(prevC, challenge) {
+        if (challenge.id === props.id) {
+          return (challenge);
+        } else {
+          return (prevC);
+        }
+      }, {}),
+      activeFile: state.activeFile
+    }
+  );
+});
+
+class Editor extends Component {
+
+  constructor(props) {
+    super(props);
+
+    var codeMirrorData = [];
+
+    var unrenderedCodeMirrors = [];
+
+    for (let i in this.props.challenge ) {
+      var challengeDataField = this.props.challenge[i];
+      codeMirrorData.push([i, challengeDataField]);
+    }
+
+    codeMirrorData = codeMirrorData.filter(function(field) {
+      return (field[0] !== 'id');
+    });
+
+    unrenderedCodeMirrors = codeMirrorData.map(function(data) {
+      if (Array.isArray(data[1])) {
+        if (data[0] === 'tests') {
+          data[1] = data[1].join('EOL\n');
+        } else {
+          data[1] = data[1].join('\n');
+        }
+      }
+      return (
+          <div key = {data[0]}>
+          <h3>{data[0]}</h3>
+          <textarea id = {data[0]} defaultValue = {data[1]}></textarea>
+          </div>
+      );
+    });
+
+    this.state = {
+      codeMirrorData: codeMirrorData,
+      unrenderedCodeMirrors: unrenderedCodeMirrors
+    };
+  }
+
+  componentDidMount() {
+    let codeMirrors = [];
+    const dispatch = this.props.dispatch;
+    const challengeId = this.props.challenge.id;
+    const activeFile = this.props.activeFile;
+    const challengeType = this.props.challenge.challengeType;
+
+    this.state.codeMirrorData.map(function(codeMirror) {
+      // Determine mode
+      let mode = 'htmlmixed';
+      /* eslint-disable no-fallthrough */
+      switch (codeMirror[0]) {
+        case 'challengeSeed':
+        case 'solutions':
+          if (challengeType !== 5 && challengeType !== 1) {
+            mode = 'htmlmixed';
+            break;
+          }
+        case 'head':
+        case 'tail':
+        case 'tests':
+          mode = 'javascript';
+          break;
+        default:
+          mode = 'htmlmixed';
+          break;
+      }
+      /* eslint-enable no-fallthrough */
+
+      let editor = CodeMirror.fromTextArea(
+        document.getElementById(codeMirror[0]),
+        {
+          lineNumbers: true,
+          mode: mode,
+          theme: 'monokai',
+          runnable: true,
+          matchBrackets: true,
+          autoCloseBrackets: true,
+          scrollbarStyle: 'simple',
+          lineWrapping: true,
+          gutters: ['CodeMirror-lint-markers']
+        }
+      );
+
+      editor.on('change', function(instance) {
+        updateChallenge(dispatch,
+                        {
+                          id: challengeId,
+                          props: {
+                            [codeMirror[0]]: instance.getValue()
+                          },
+                          activeFile: activeFile
+
+                        }
+                       );
+      });
+
+      codeMirrors.push(editor);
+    });
+  }
+
+  render() {
+    return (
+      <div>
+        {this.state.unrenderedCodeMirrors}
+      </div>
+    );
+  }
+}
+
+export default connector(Editor);
+
+Editor.propTypes = {
+  challenge: React.PropTypes.object.isRequired,
+  activeFile: React.PropTypes.string.isRequired,
+  dispatch: React.PropTypes.func.isRequired
+};
diff --git a/src/Components/GrandCentralStation.js b/src/Components/GrandCentralStation.js
new file mode 100644
index 0000000..8cb27fb
--- /dev/null
+++ b/src/Components/GrandCentralStation.js
@@ -0,0 +1,228 @@
+import React, { Component } from 'react';
+import {connect} from 'react-redux';
+import {
+  backAction,
+  loadFile,
+  createChallenge,
+  loadChallenge,
+  fileSelect
+} from './../actions/editorActions';
+
+import $ from 'jquery';
+
+import Menu from './Menu';
+import TabBar from './Tabs';
+import SelectChallenge from './SelectChallenge';
+import Editor from './Editor';
+
+import './../style.css';
+
+const connector = connect(function(state) {
+  return (
+    state
+  );
+}, null, null, {pure: false});
+
+class GrandCentralStation extends Component {
+
+  constructor(props) {
+    super(props);
+    this.backView = this.backView.bind(this);
+    this.exportFiles = this.exportFiles.bind(this);
+    this.handleFileSelect = this.handleFileSelect.bind(this);
+    this.handleFileIsSelected = this.handleFileIsSelected.bind(this);
+    this.handleChallengeClick = this.handleChallengeClick.bind(this);
+  }
+
+  backView() {
+    let dispatch = this.props.dispatch;
+    backAction(dispatch, {
+      view: 'challengeSelect'
+    });
+  }
+
+  exportFiles() {
+    $.post('/export', {
+      data: this.props.fileStore,
+      success: function(data) {
+        console.log(data);
+      }
+    });
+  }
+
+  handleFileSelect(to) {
+    let dispatch = this.props.dispatch;
+    let fileStore = this.props.fileStore;
+    fileSelect(dispatch, {
+      activeFile: to,
+      challenges: fileStore[to].challenges
+    });
+  }
+
+  handleFileIsSelected(event) {
+
+    let files = event.target.files;
+    for (let i in files) {
+      if (files.hasOwnProperty(i)) {
+        let file = files[i];
+        let reader = new FileReader();
+        let dispatch = this.props.dispatch;
+
+        reader.onload = function(upload) {
+          let newFileStoreObject = this.props.fileStore;
+          newFileStoreObject[file.name] =
+            JSON.parse(upload.target.result);
+
+          loadFile(dispatch, {
+            fileStore: newFileStoreObject,
+            activeFile: file.name,
+            challenges: newFileStoreObject[file.name].challenges,
+            activeChallenge: {}
+          });
+        }.bind(this);
+        reader.readAsText(file);
+      }
+    }
+  }
+
+  handleChallengeClick(id) {
+    let dispatch = this.props.dispatch;
+
+    let oldFileStore = this.props.fileStore;
+    let currentFile = this.props.activeFile;
+
+    if (id === 'new') {
+      $.getJSON('/mongoid', function(mongoid) {
+        mongoid = mongoid.objectId;
+        oldFileStore[currentFile].challenges.push({
+          'id': mongoid,
+          'title': mongoid,
+          'description': [
+            ''
+          ],
+          'tests': [
+            ''
+          ],
+          'challengeSeed': [
+            ''
+          ],
+          'MDNlinks': [
+            ''
+          ],
+          'solutions': [
+            ''
+          ],
+          'type': '',
+          'challengeType': 0,
+          'nameCn': '',
+          'descriptionCn': [],
+          'nameFr': '',
+          'descriptionFr': [],
+          'nameRu': '',
+          'descriptionRu': [],
+          'nameEs': '',
+          'descriptionEs': [],
+          'namePt': '',
+          'descriptionPt': []
+        });
+
+        let AddedChallenge = {fileStore: oldFileStore};
+
+        createChallenge(dispatch,
+                        AddedChallenge
+                       );
+      });
+    } else {
+      loadChallenge(dispatch, {
+        'activeChallenge':
+        this.props.fileStore[this.props.activeFile]
+          .challenges.filter((challenge) => {
+            return challenge.id === id;
+          }).pop(), 'view': 'ChallengeEdit'
+      });
+    }
+  }
+
+  render() {
+    let elements = [];
+    let selectChallenges;
+    if (this.props !== null && this.props.view === 'ChallengeSelect') {
+      elements = [
+        {
+          name: 'Choose File',
+          handleChange: this.handleFileIsSelected
+        },
+        {
+          name: 'Export',
+          action: this.exportFiles
+        }
+      ];
+    } else {
+      elements = [
+        {
+          name: 'Choose File',
+          handleChange: this.handleFileIsSelected
+        },
+        {
+          name: 'Export',
+          action: this.exportFiles
+        },
+        {
+          name: 'Back',
+          action: this.backView
+        }
+      ];
+    }
+
+    if (this.props !== null
+        && this.props.fileStore
+        && Object.keys(this.props.fileStore).length) {
+      selectChallenges = (
+        <SelectChallenge
+          challengeClick = {this.handleChallengeClick}
+          data = {this.props.fileStore[this.props.activeFile]}
+        />
+      );
+    }
+
+    let menu =
+          <Menu elements = {elements} />;
+
+    let tabs;
+
+    tabs = Object.keys(this.props.fileStore).length === 0 ? ''
+      : <TabBar action = {this.handleFileSelect}
+           files = {this.props.fileStore} />;
+
+    if (Object.keys(this.props.view === 'ChallengeEdit' &&
+                    this.props.activeChallenge).length) {
+      return (
+          <div className = 'app'>
+          {menu}
+        {tabs}
+          <Editor id={this.props.activeChallenge.id} />
+          </div>
+      );
+    } else {
+
+      return (
+          <div className = 'app'>
+          {menu}
+        {tabs}
+        {selectChallenges}
+        </div>
+      );
+    }
+  }
+}
+
+export default connector(GrandCentralStation);
+
+GrandCentralStation.propTypes = {
+  dispatch: React.PropTypes.func.isRequired,
+  fileStore: React.PropTypes.object,
+  activeFile: React.PropTypes.string,
+  view: React.PropTypes.string.isRequired,
+  activeChallenge: React.PropTypes.object
+};
+
diff --git a/src/Components/Menu.js b/src/Components/Menu.js
new file mode 100644
index 0000000..c865f94
--- /dev/null
+++ b/src/Components/Menu.js
@@ -0,0 +1,74 @@
+import React, { Component } from 'react';
+import {connect} from 'react-redux';
+
+import RaisedButton from 'material-ui/lib/raised-button';
+
+const styles = {
+  fileInput: {
+    cursor: 'pointer',
+    position: 'absolute',
+    top: '0',
+    bottom: '0',
+    right: '0',
+    left: '0',
+    width: '100%',
+    opacity: '0'
+  },
+  buttonStyle: {
+    paddingRight: '5px'
+  }
+};
+
+const connector = connect(function(state) {
+  return (
+    state
+  );
+}, null, null, {pure: false});
+
+class Menu extends Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    let MenuElements = this.props.elements.map((elem, ix) => {
+      let potentialInput;
+      if (elem.name === 'Choose File') {
+        potentialInput = (
+          <input
+            style = {styles.fileInput}
+            type = 'file' multiple>
+          </input>
+        );
+      }
+      return (
+        <span key = { ix }
+              style = {styles.buttonStyle}>
+        <RaisedButton key = {elem.name}
+                      label = { elem.name }
+                      onChange = {elem.handleChange}
+                      onClick = {elem.action}
+        >
+          {potentialInput}
+        </RaisedButton>
+        </span>
+      );
+    });
+
+    return (
+      <div>
+        <ul>
+          {MenuElements}
+        </ul>
+      </div>
+    );
+  }
+}
+
+export default connector(Menu);
+
+Menu.propTypes = {
+  elements: React.PropTypes.array.isRequired
+};
+
diff --git a/src/Components/SelectChallenge.js b/src/Components/SelectChallenge.js
new file mode 100644
index 0000000..ec398af
--- /dev/null
+++ b/src/Components/SelectChallenge.js
@@ -0,0 +1,44 @@
+import React, {Component} from 'react';
+import List from 'material-ui/lib/lists/list';
+import ListItem from 'material-ui/lib/lists/list-item';
+
+export default class SelectChallenge extends Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  handleClick(id) {
+    this.props.challengeClick(id);
+  }
+
+  render() {
+    let data = this.props.data.challenges.map((challenge) => {
+      return (
+        <ListItem
+          data-challengid = {challenge.id}
+          key = {challenge.id}
+          primaryText = {challenge.title}
+          onClick = {this.handleClick.bind(this, challenge.id)}
+        />
+      );
+    });
+
+    return (
+      <List>
+        {data}
+        <ListItem
+          data-challengid = 'new'
+          key = 'new'
+          primaryText = 'Create new'
+          onClick = {this.handleClick.bind(this, 'new')}
+        />
+      </List>
+    );
+  }
+}
+
+SelectChallenge.propTypes = {
+  challengeClick: React.PropTypes.func,
+  data: React.PropTypes.object
+};
diff --git a/src/Components/Tabs.js b/src/Components/Tabs.js
new file mode 100644
index 0000000..94a9b3d
--- /dev/null
+++ b/src/Components/Tabs.js
@@ -0,0 +1,49 @@
+import React, {Component} from 'react';
+import {Tab, Tabs} from 'material-ui/lib/tabs';
+
+export default class TabBar extends Component {
+
+  constructor(props) {
+    super(props);
+    this.state = {'activeTab': '', 'tabChange': function() {
+      this.props.action((arguments[1].split(/\=1\$/gi)[1])
+        .replace(/\=01/gi, '.'));
+      this.setState({'activeTab': (arguments[1]
+        .split(/\=1\$/gi)[1]).replace(/\=01/gi, '.')});
+    }};
+  }
+
+  render() {
+    let fileNames = [];
+    let FileElements = [];
+    for (let i in this.props.files) {
+      let file = this.props.files[i];
+      if (file !== undefined) {
+        fileNames.push(i);
+        FileElements.push(
+          <Tab key= { i }
+               label = { i }
+               onClick = {this.state.tabChange.bind(this)}
+               value = { i }
+          >
+            {file.name}
+          </Tab>
+        );
+      }
+    }
+    return (
+      <Tabs onActive = { this.props.action }
+            valueLink={{value: this.state.activeTab,
+                    requestChange: function() {console.log('tab changes');}}
+                  }>
+        {FileElements}
+      </Tabs>
+    );
+  }
+}
+
+TabBar.propTypes = {
+  action: React.PropTypes.func.isRequired,
+  files: React.PropTypes.object.isRequired
+};
+
diff --git a/src/actions/editorActions.js b/src/actions/editorActions.js
new file mode 100644
index 0000000..f8a4ce1
--- /dev/null
+++ b/src/actions/editorActions.js
@@ -0,0 +1,48 @@
+export function backAction(dispatch, payload) {
+  dispatch({
+    type: 'backAction',
+    payload
+  });
+}
+
+export function loadFile(dispatch, payload) {
+  dispatch({
+    type: 'loadFile',
+    payload
+  });
+}
+
+export function createChallenge(dispatch, payload) {
+  dispatch({
+    type: 'createChallenge',
+    payload
+  });
+}
+
+export function loadChallenge(dispatch, payload) {
+  dispatch({
+    type: 'loadChallenge',
+    payload
+  });
+}
+
+export function fileSelect(dispatch, payload) {
+  dispatch({
+    type: 'fileSelect',
+    payload
+  });
+}
+
+export function updateChallenge(dispatch, payload) {
+  dispatch({
+    type: 'updateChallenge',
+    payload
+  });
+}
+
+export function exportChallenge(payload) {
+  return {
+    type: 'exportChallenge',
+    payload
+  };
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..6a9fa0f
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import App from './App';
+import {Provider} from 'react-redux';
+import store from './store/store';
+
+ReactDOM.render(
+    <Provider store = {store} ><App /></Provider>,
+  document.getElementById('root')
+
+);
diff --git a/src/reducers/reducer.js b/src/reducers/reducer.js
new file mode 100644
index 0000000..7c45bdc
--- /dev/null
+++ b/src/reducers/reducer.js
@@ -0,0 +1,73 @@
+const initialState = {
+  'fileStore': {},
+  'activeFile': '',
+  'activeChallenge': {},
+  'view': 'ChallengeSelect'
+};
+
+function parser(key) {
+  switch (key) {
+    case 'description':
+    case 'descriptionEs':
+    case 'descriptionRu':
+    case 'descriptionCn':
+    case 'descriptionFr':
+    case 'head':
+    case 'tail':
+    case 'challengeSeed':
+    case 'MDNlinks':
+    case 'solutions':  // NOTE:  This only works for one solution
+      return function(val) { return val.split('\n'); };
+    case 'tests':
+      return function(val) { return val.split('EOL\n'); };
+    default:
+      return function(val) { return val; };
+  }
+}
+
+export default function(prevState = initialState, action) {
+  switch (action.type) {
+
+    case 'updateChallenge':
+      let challenges = prevState.challenges.slice();
+      challenges = challenges.map(function(challenge) {
+        if (challenge.id === action.payload.id) {
+          Object.keys(action.payload.props).forEach(function(key) {
+            action.payload.props[key] = parser(key)(action.payload.props[key]);
+          });
+          return Object.assign({}, challenge, action.payload.props);
+        }
+        return (challenge);
+      });
+
+      let fileStore = prevState.fileStore;
+      let newFileStore = fileStore[prevState.activeFile];
+
+      newFileStore.challenges = challenges;
+
+      fileStore[prevState.activeFile] = newFileStore;
+      let newState = prevState;
+      newState.challenges = challenges;
+      Object.assign({}, newState, newFileStore);
+
+      return (Object.assign({}, prevState, newState));
+
+    case 'createChallenge':
+      return (Object.assign({}, prevState, action.payload));
+
+    case 'loadChallenge':
+      return (Object.assign({}, prevState, action.payload));
+
+    case 'loadFile':
+      return (Object.assign({}, prevState, action.payload));
+
+    case 'fileSelect':
+      return (Object.assign({}, prevState, action.payload));
+
+    case 'backAction':
+      return (Object.assign({}, prevState, action.payload));
+
+    default:
+      return (prevState);
+  }
+}
diff --git a/src/store/store.js b/src/store/store.js
new file mode 100644
index 0000000..9ccaa57
--- /dev/null
+++ b/src/store/store.js
@@ -0,0 +1,6 @@
+import reducer from './../reducers/reducer';
+import {createStore} from 'redux';
+
+let store = createStore(reducer);
+
+export default store;
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 0000000..e69de29
diff --git a/views/error.jade b/views/error.jade
new file mode 100644
index 0000000..eb1d7fc
--- /dev/null
+++ b/views/error.jade
@@ -0,0 +1,3 @@
+h1= message
+h2= error.status
+pre #{error.stack}
diff --git a/views/index.jade b/views/index.jade
new file mode 100644
index 0000000..9307d69
--- /dev/null
+++ b/views/index.jade
@@ -0,0 +1 @@
+extends layout
\ No newline at end of file
diff --git a/views/layout.jade b/views/layout.jade
new file mode 100644
index 0000000..1df13fa
--- /dev/null
+++ b/views/layout.jade
@@ -0,0 +1,7 @@
+doctype html
+html
+  head
+    title= title
+  body
+    #root
+    script(src = "/dist/bundle.js")
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..69a7455
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,25 @@
+var path = require('path');
+
+module.exports = {
+  devtool: 'eval',
+  entry: [
+    './src/index.js'
+  ],
+  output: {
+    path: path.join(__dirname, 'public/dist'),
+    filename: 'bundle.js'
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        loaders: ['babel-loader'],
+        include: path.join(__dirname, 'src')
+      },
+      {
+        test: /\.css$/,
+        loaders: ['style', 'css']
+      }
+    ]
+  }
+};
