You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2016/09/21 15:19:10 UTC

[19/29] fauxton commit: updated refs/heads/new-replication to b0541e1

clean up react components and css


Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/ecea635a
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/ecea635a
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/ecea635a

Branch: refs/heads/new-replication
Commit: ecea635ab9a13f03e579e9a8673a053b0224d032
Parents: a76ef79
Author: Garren Smith <ga...@gmail.com>
Authored: Wed Aug 17 17:14:54 2016 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Wed Sep 14 17:22:30 2016 +0200

----------------------------------------------------------------------
 app/addons/replication/actions.js               |   1 +
 .../replication/assets/less/replication.less    | 132 ++++++-
 app/addons/replication/components.react.jsx     | 343 +-----------------
 app/addons/replication/components/options.js    |  92 +++++
 app/addons/replication/components/source.js     | 164 +++++++++
 app/addons/replication/components/submit.js     |  34 ++
 app/addons/replication/components/target.js     | 202 +++++++++++
 app/addons/replication/controller.js            | 349 +++++++++++++++++++
 app/addons/replication/helpers.js               |   7 -
 app/addons/replication/route.js                 |   7 +-
 10 files changed, 967 insertions(+), 364 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/actions.js b/app/addons/replication/actions.js
index 72e1909..5c2fde5 100644
--- a/app/addons/replication/actions.js
+++ b/app/addons/replication/actions.js
@@ -68,6 +68,7 @@ function replicate (params) {
 }
 
 function updateFormField (fieldName, value) {
+  console.log('ff', fieldName, value);
   FauxtonAPI.dispatch({
     type: ActionTypes.REPLICATION_UPDATE_FORM_FIELD,
     options: {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/assets/less/replication.less
----------------------------------------------------------------------
diff --git a/app/addons/replication/assets/less/replication.less b/app/addons/replication/assets/less/replication.less
index 4e97de0..a0ba9d0 100644
--- a/app/addons/replication/assets/less/replication.less
+++ b/app/addons/replication/assets/less/replication.less
@@ -13,10 +13,124 @@
 @import "../../../../../assets/less/variables.less";
 @import "../../../../../assets/less/mixins.less";
 
-.replication-page {
+div.replication-page {
+  padding-top: 25px !important;
+  display: flex;
+  flex-direction: column;
+}
+
+.replication-section {
+  display: flex;
+  flex-direction: row;
+}
+
+.replication-seperator {
+  margin: 6px 0 15px;
+}
+
+.replication-input-label {
+  padding-right: 15px;
+  width: 220px;
+  text-align: right;
+  margin-top: 12px;
   font-size: 14px;
+  margin-left: 10px;
+  margin-right: 10px;
+}
 
-  input, select {
+.replication-input-select {
+  width: 540px;
+  select {
+    font-size: 14px;
+    width: 246px;
+    margin-bottom: 10px;
+    background-color: white;
+    border: 1px solid #cccccc;
+  }
+  .styled-select {
+    width: 250px;
+  }
+}
+
+.replication-input-react-select {
+  font-size: 14px;
+
+  .Select div.Select-control {
+    padding: 6px;
+    border: 1px solid #cccccc;
+    width: 246px;
+
+    .Select-value, .Select-placeholder {
+      padding: 6px 10px;
+    }
+
+    input {
+      margin-left: -6px;
+    }
+
+    .Select-arrow-zone {
+      padding: 0;
+      width: 18px;
+      color: black;
+    }
+  }
+}
+
+.replication-remote-connection-url {
+  font-size: 14px;
+  width: 100%;
+}
+
+.replication-remote-connection-url-text {
+  font-size: 9pt;
+  color: #999999;
+  margin-bottom: 8px;
+}
+
+.replication-new-input {
+  width: 248px;
+  font-size: 14px;
+}
+
+.replication-doc-name {
+  position: relative;
+  width: 250px;
+
+}
+
+.replication-doc-name-icon {
+  cursor: pointer;
+  position: absolute;
+  right: 6px;
+  top: 8px;
+  font-size: 11px;
+  padding: 8px;
+  color: #999999;
+  .transition(all 0.25s linear);
+}
+
+.replication-doc-name-icon:hover {
+  color: #333333;
+}
+
+.replication-doc-name-input {
+  padding-right: 32px;
+  font-size: 14px;
+  width: 248px;
+}
+
+.replication-button-row {
+  margin-top: 10px;
+  width: 540px;
+  margin-left: 240px;
+}
+
+.replication-clear-link {
+  padding: 12px;
+}
+
+
+  /*input, select {
     font-size: 14px;
   }
   input {
@@ -59,13 +173,9 @@
     font-weight: bold;
     font-size: 14pt;
   }
-}
+}*/
 
-#dashboard-content .replication-page {
-  padding-top: 25px;
-}
-
-.connection-url-example {
+/*.connection-url-example {
   font-size: 9pt;
   color: #999999;
   margin-bottom: 8px;
@@ -91,10 +201,10 @@
       padding-right: 32px;
     }
   }
-}
+}*/
 
 
-body .Select div.Select-control {
+/*body .Select div.Select-control {
   padding: 6px;
   border: 1px solid #cccccc;
   width: 246px;
@@ -109,4 +219,4 @@ body .Select div.Select-control {
     width: 18px;
     color: black;
   }
-}
+}*/

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/replication/components.react.jsx b/app/addons/replication/components.react.jsx
index b721bc0..378fbde 100644
--- a/app/addons/replication/components.react.jsx
+++ b/app/addons/replication/components.react.jsx
@@ -12,324 +12,15 @@
 import app from '../../app';
 import FauxtonAPI from '../../core/api';
 import React from 'react';
-import Stores from './stores';
-import Actions from './actions';
 import Constants from './constants';
 import Helpers from './helpers';
 import Components from '../components/react-components.react';
-import base64 from 'base-64';
-import AuthActions from '../auth/actions';
 import AuthComponents from '../auth/components.react';
 import ReactSelect from 'react-select';
 
-const store = Stores.replicationStore;
-const LoadLines = Components.LoadLines;
-const StyledSelect = Components.StyledSelect;
-const ConfirmButton = Components.ConfirmButton;
+const {LoadLines, StyledSelect, ConfirmButton} = Components;
 const PasswordModal = AuthComponents.PasswordModal;
 
-
-class ReplicationController extends React.Component {
-  constructor (props) {
-    super(props);
-    this.state = this.getStoreState();
-    this.submit = this.submit.bind(this);
-    this.clear = this.clear.bind(this);
-    this.showPasswordModal = this.showPasswordModal.bind(this);
-  }
-
-  getStoreState () {
-    return {
-      loading: store.isLoading(),
-      databases: store.getDatabases(),
-      authenticated: store.isAuthenticated(),
-      password: store.getPassword(),
-
-      // source fields
-      replicationSource: store.getReplicationSource(),
-      sourceDatabase: store.getSourceDatabase(),
-      localSourceDatabaseKnown: store.isLocalSourceDatabaseKnown(),
-      remoteSource: store.getRemoteSource(),
-
-      // target fields
-      replicationTarget: store.getReplicationTarget(),
-      targetDatabase: store.getTargetDatabase(),
-      localTargetDatabaseKnown: store.isLocalTargetDatabaseKnown(),
-      remoteTarget: store.getRemoteTarget(),
-
-      // other
-      passwordModalVisible: store.isPasswordModalVisible(),
-      replicationType: store.getReplicationType(),
-      replicationDocName: store.getReplicationDocName()
-    };
-  }
-
-  componentDidMount () {
-    Actions.initReplicator(this.props.sourceDatabase);
-    store.on('change', this.onChange, this);
-  }
-
-  componentWillUnmount () {
-    store.off('change', this.onChange);
-    Actions.clearReplicationForm();
-  }
-
-  onChange () {
-    this.setState(this.getStoreState());
-  }
-
-  clear (e) {
-    e.preventDefault();
-    Actions.clearReplicationForm();
-  }
-
-  showPasswordModal () {
-    const { replicationSource, replicationTarget } = this.state;
-
-    const hasLocalSourceOrTarget = (replicationSource === Constants.REPLICATION_SOURCE.LOCAL ||
-      replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE ||
-      replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE);
-
-    // if the user is authenticated, or if NEITHER the source nor target are local, just submit. The password
-    // modal isn't necessary
-    if (!hasLocalSourceOrTarget || this.state.authenticated) {
-      this.submit();
-      return;
-    }
-
-    AuthActions.showPasswordModal();
-  }
-
-  getUsername () {
-    return app.session.get('userCtx').name;
-  }
-
-  getAuthHeaders () {
-    const username = this.getUsername();
-    return {
-      'Authorization': 'Basic ' + base64.encode(username + ':' + this.state.password)
-    };
-  }
-
-  submit () {
-    const { replicationTarget, replicationType, replicationDocName} = this.state;
-
-    if (!this.validate()) {
-      return;
-    }
-
-    const params = {
-      source: this.getSource(),
-      target: this.getTarget()
-    };
-
-    if (_.contains([Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE, Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE], replicationTarget)) {
-      params.create_target = true;
-    }
-    if (replicationType === Constants.REPLICATION_TYPE.CONTINUOUS) {
-      params.continuous = true;
-    }
-
-    if (replicationDocName) {
-      params._id = this.state.replicationDocName;
-    }
-
-    // POSTing to the _replicator DB requires auth
-    const user = FauxtonAPI.session.user();
-    const userName = _.isNull(user) ? '' : FauxtonAPI.session.user().name;
-    params.user_ctx = {
-      name: userName,
-      roles: ['_admin', '_reader', '_writer']
-    };
-
-    Actions.replicate(params);
-  }
-
-  getSource () {
-    const { replicationSource, sourceDatabase, remoteSource } = this.state;
-    if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
-      return {
-        headers: this.getAuthHeaders(),
-        url: window.location.origin + '/' + sourceDatabase
-      };
-    } else {
-      return remoteSource;
-    }
-  }
-
-  getTarget () {
-    const { replicationTarget, targetDatabase, remoteTarget, replicationSource, password } = this.state;
-
-    let target = remoteTarget;
-    if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE) {
-      target = {
-        headers: this.getAuthHeaders(),
-        url: window.location.origin + '/' + targetDatabase
-      };
-    } else if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
-
-      // check to see if we really need to send headers here or can just do the ELSE clause in all scenarioe
-      if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
-        target = {
-          headers: this.getAuthHeaders(),
-          url: window.location.origin + '/' + targetDatabase
-        };
-      } else {
-        const port = window.location.port === '' ? '' : ':' + window.location.port;
-        target = window.location.protocol + '//' + this.getUsername() + ':' + password + '@'
-          + window.location.hostname + port + '/' + targetDatabase;
-      }
-    }
-
-    return target;
-  }
-
-  validate () {
-    const { replicationTarget, targetDatabase, databases } = this.state;
-
-    if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE && _.contains(databases, targetDatabase)) {
-      FauxtonAPI.addNotification({
-        msg: 'The <code>' + targetDatabase + '</code> database already exists locally. Please enter another database name.',
-        type: 'error',
-        escape: false,
-        clear: true
-      });
-      return false;
-    }
-    if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE ||
-        replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE) {
-      let error = '';
-      if (/\s/.test(targetDatabase)) {
-        error = 'The target database may not contain any spaces.';
-      } else if (/^_/.test(targetDatabase)) {
-        error = 'The target database may not start with an underscore.';
-      }
-
-      if (error) {
-        FauxtonAPI.addNotification({
-          msg: error,
-          type: 'error',
-          escape: false,
-          clear: true
-        });
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  render () {
-    const { loading, replicationSource, replicationTarget, replicationType, replicationDocName, passwordModalVisible,
-      localSourceDatabaseKnown, databases, localTargetDatabaseKnown, sourceDatabase, remoteSource, remoteTarget,
-      targetDatabase } = this.state;
-
-    if (loading) {
-      return (
-        <LoadLines />
-      );
-    }
-
-    let confirmButtonEnabled = true;
-    if (!replicationSource || !replicationTarget) {
-      confirmButtonEnabled = false;
-    }
-    if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL && !localSourceDatabaseKnown) {
-      confirmButtonEnabled = false;
-    }
-    if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE && !localTargetDatabaseKnown) {
-      confirmButtonEnabled = false;
-    }
-
-    return (
-      <div className="replication-page">
-        <div className="row">
-          <div className="span3">
-            Replication Source:
-          </div>
-          <div className="span7">
-            <ReplicationSource
-              value={replicationSource}
-              onChange={(repSource) => Actions.updateFormField('replicationSource', repSource)}/>
-          </div>
-        </div>
-
-        {replicationSource ?
-          <ReplicationSourceRow
-            replicationSource={replicationSource}
-            databases={databases}
-            sourceDatabase={sourceDatabase}
-            remoteSource={remoteSource}
-            onChange={(val) => Actions.updateFormField('remoteSource', val)}
-          /> : null}
-
-        <hr size="1"/>
-
-        <div className="row">
-          <div className="span3">
-            Replication Target:
-          </div>
-          <div className="span7">
-            <ReplicationTarget
-              value={replicationTarget}
-              onChange={(repTarget) => Actions.updateFormField('replicationTarget', repTarget)}/>
-          </div>
-        </div>
-        {replicationTarget ?
-          <ReplicationTargetRow
-            remoteTarget={remoteTarget}
-            replicationTarget={replicationTarget}
-            databases={databases}
-            targetDatabase={targetDatabase}
-          /> : null}
-
-        <hr size="1"/>
-
-        <div className="row">
-          <div className="span3">
-            Replication Type:
-          </div>
-          <div className="span7">
-            <ReplicationType
-              value={replicationType}
-              onChange={(repType) => Actions.updateFormField('replicationType', repType)}/>
-          </div>
-        </div>
-
-        <div className="row">
-          <div className="span3">
-            Replication Document:
-          </div>
-          <div className="span7">
-            <div className="custom-id-field">
-              <span className="fonticon fonticon-cancel" title="Clear field"
-                onClick={(e) => Actions.updateFormField('replicationDocName', '')} />
-              <input type="text" placeholder="Custom, new ID (optional)" value={replicationDocName}
-                onChange={(e) => Actions.updateFormField('replicationDocName', e.target.value)}/>
-            </div>
-          </div>
-        </div>
-
-        <div className="row buttons-row">
-          <div className="span3">
-          </div>
-          <div className="span7">
-            <ConfirmButton id="replicate" text="Start Replication" onClick={this.showPasswordModal} disabled={!confirmButtonEnabled}/>
-            <a href="#" data-bypass="true" onClick={this.clear}>Clear</a>
-          </div>
-        </div>
-
-        <PasswordModal
-          visible={passwordModalVisible}
-          modalMessage={<p>Replication requires authentication.</p>}
-          submitBtnLabel="Continue Replication"
-          onSuccess={this.submit} />
-      </div>
-    );
-  }
-}
-
-
 class ReplicationSourceRow extends React.Component {
   render () {
     const { replicationSource, databases, sourceDatabase, remoteSource, onChange} = this.props;
@@ -376,36 +67,6 @@ ReplicationSourceRow.propTypes = {
 };
 
 
-class ReplicationSource extends React.Component {
-  getOptions () {
-    const options = [
-      { value: '', label: 'Select source' },
-      { value: Constants.REPLICATION_SOURCE.LOCAL, label: 'Local database' },
-      { value: Constants.REPLICATION_SOURCE.REMOTE, label: 'Remote database' }
-    ];
-    return options.map((option) => {
-      return (
-        <option value={option.value} key={option.value}>{option.label}</option>
-      );
-    });
-  }
-
-  render () {
-    return (
-      <StyledSelect
-        selectContent={this.getOptions()}
-        selectChange={(e) => this.props.onChange(e.target.value)}
-        selectId="replication-source"
-        selectValue={this.props.value} />
-    );
-  }
-}
-ReplicationSource.propTypes = {
-  value: React.PropTypes.string.isRequired,
-  onChange: React.PropTypes.func.isRequired
-};
-
-
 class ReplicationTarget extends React.Component {
   getOptions () {
     const options = [
@@ -537,8 +198,6 @@ ReplicationTargetRow.propTypes = {
 
 
 export default {
-  ReplicationController,
-  ReplicationSource,
   ReplicationTarget,
   ReplicationType,
   ReplicationTargetRow

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/components/options.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/options.js b/app/addons/replication/components/options.js
new file mode 100644
index 0000000..02c5b98
--- /dev/null
+++ b/app/addons/replication/components/options.js
@@ -0,0 +1,92 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+import ReactSelect from 'react-select';
+
+const { StyledSelect } = Components;
+
+const getReplicationTypeOptions = () => {
+  return [
+    { value: Constants.REPLICATION_TYPE.ONE_TIME, label: 'One time' },
+    { value: Constants.REPLICATION_TYPE.CONTINUOUS, label: 'Continuous' }
+  ].map(option => <option value={option.value} key={option.value}>{option.label}</option>);
+};
+
+const ReplicationType = ({value, onChange}) => {
+  return (
+    <div className="replication-section">
+      <div className="replication-input-label">
+        Replication Type:
+      </div>
+      <div className="replication-input-select">
+        <StyledSelect
+          selectContent={getReplicationTypeOptions()}
+          selectChange={(e) => onChange(e.target.value)}
+          selectId="replication-target"
+          selectValue={value} />
+      </div>
+    </div>
+  );
+};
+
+ReplicationType.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ReplicationDoc = ({value, onChange}) =>
+<div className="replication-section">
+  <div className="replication-input-label">
+    Replication Document:
+  </div>
+  <div className="replication-doc-name">
+    <span className="fonticon fonticon-cancel replication-doc-name-icon" title="Clear field"
+      onClick={(e) => onChange('')} />
+    <input type="text" className="replication-doc-name-input" placeholder="Custom, new ID (optional)" value={value}
+      onChange={(e) => onChange(e.target.value)}/>
+  </div>
+</div>;
+
+ReplicationDoc.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+export class ReplicationOptions extends React.Component {
+
+  render () {
+    const {replicationType, replicationDocName, onDocChange, onTypeChange} = this.props;
+
+    return (
+      <div>
+        <ReplicationType
+          onChange={onTypeChange}
+          value={replicationType}
+        />
+        <ReplicationDoc
+          onChange={onDocChange}
+          value={replicationDocName}
+        />
+      </div>
+    );
+  }
+
+}
+
+ReplicationOptions.propTypes = {
+  replicationDocName: React.PropTypes.string.isRequired,
+  replicationType: React.PropTypes.string.isRequired,
+  onDocChange: React.PropTypes.func.isRequired,
+  onTypeChange: React.PropTypes.func.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/components/source.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/source.js b/app/addons/replication/components/source.js
new file mode 100644
index 0000000..7336804
--- /dev/null
+++ b/app/addons/replication/components/source.js
@@ -0,0 +1,164 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+import ReactSelect from 'react-select';
+
+const { StyledSelect } = Components;
+
+const RemoteSourceInput = ({onChange, value}) =>
+  <div className="replication-section">
+    <div className="replication-input-label">Database URL:</div>
+    <div className="">
+      <input type="text" className="replication-remote-connection-url" placeholder="https://" value={value}
+        onChange={(e) => onChange(e.target.value)} />
+      <div className="replication-remote-connection-url-text">e.g. https://$REMOTE_USERNAME:$REMOTE_PASSWORD@$REMOTE_SERVER/$DATABASE</div>
+    </div>
+  </div>;
+
+RemoteSourceInput.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const LocalSourceInput = ({value, onChange, databases}) => {
+  const options = databases.map(db => ({value: db, label: db}));
+  return (
+    <div className="replication-section">
+      <div className="replication-input-label">
+        Source Name:
+      </div>
+      <div className="replication-input-react-select">
+        <ReactSelect
+          name="source-name"
+          value={value}
+          placeholder="Database name"
+          options={options}
+          clearable={false}
+          onChange={onChange} />
+      </div>
+    </div>
+  );
+};
+
+LocalSourceInput.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ReplicationSourceRow = ({replicationSource, databases, sourceDatabase, remoteSource, onChangeRemote, onChangeLocal}) => {
+  if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
+    return <LocalSourceInput
+      value={sourceDatabase}
+      databases={databases}
+      onChange={onChangeLocal}
+           />;
+  }
+
+  return <RemoteSourceInput value={remoteSource} onChange={onChangeRemote} />;
+};
+
+ReplicationSourceRow.propTypes = {
+  replicationSource: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  sourceDatabase: React.PropTypes.string.isRequired,
+  remoteSource: React.PropTypes.string.isRequired,
+  onChangeRemote: React.PropTypes.func.isRequired,
+  onChangeLocal: React.PropTypes.func.isRequired
+};
+
+const replicationSourceSelectOptions = () => {
+  return [
+    { value: '', label: 'Select source' },
+    { value: Constants.REPLICATION_SOURCE.LOCAL, label: 'Local database' },
+    { value: Constants.REPLICATION_SOURCE.REMOTE, label: 'Remote database' }
+  ].map((option) => {
+    return (
+      <option value={option.value} key={option.value}>{option.label}</option>
+    );
+  });
+};
+
+export const ReplicationSourceSelect = ({onChange, value}) => {
+
+  return (
+    <div className="replication-section">
+      <div className="replication-input-label">
+        Replication Source:
+      </div>
+      <div className="replication-input-select">
+        <StyledSelect
+          selectContent={replicationSourceSelectOptions()}
+          selectChange={(e) => onChange(e.target.value)}
+          selectId="replication-source"
+          selectValue={value} />
+      </div>
+    </div>
+  );
+};
+
+ReplicationSourceSelect.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+export class ReplicationSource extends React.Component {
+
+  getReplicationSourceRow () {
+    const {
+      replicationSource,
+      sourceDatabase,
+      onLocalSourceChange,
+      onRemoteSourceChange,
+      remoteSource,
+      databases
+    } = this.props;
+
+    if (!replicationSource) {
+      return null;
+    }
+
+    return <ReplicationSourceRow
+      replicationSource={replicationSource}
+      databases={databases}
+      sourceDatabase={sourceDatabase}
+      remoteSource={remoteSource}
+      onChangeLocal={onLocalSourceChange}
+      onChangeRemote={onRemoteSourceChange}
+           />;
+  }
+
+  render () {
+    const {replicationSource, onSourceSelect, sourceDatabase, remoteSource, databases} = this.props;
+    const Actions = {};
+    return (
+      <div>
+        <ReplicationSourceSelect
+          onChange={onSourceSelect}
+          value={replicationSource}
+        />
+        {this.getReplicationSourceRow()}
+      </div>
+    );
+  }
+}
+
+ReplicationSource.propTypes = {
+  replicationSource: React.PropTypes.string.isRequired,
+  sourceDatabase: React.PropTypes.string.isRequired,
+  remoteSource: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onLocalSourceChange: React.PropTypes.func.isRequired,
+  onRemoteSourceChange: React.PropTypes.func.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/components/submit.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/submit.js b/app/addons/replication/components/submit.js
new file mode 100644
index 0000000..521f8d7
--- /dev/null
+++ b/app/addons/replication/components/submit.js
@@ -0,0 +1,34 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+
+const {ConfirmButton} = Components;
+
+export const ReplicationSubmit = ({onClear, disabled, onClick}) =>
+<div className="replication-button-row">
+  <ConfirmButton
+    id="replicate"
+    text="Start Replication"
+    onClick={onClick}
+    disabled={disabled}
+  />
+  <a className="replication-clear-link" href="#" data-bypass="true" onClick={onClear}>Clear</a>
+</div>;
+
+
+ReplicationSubmit.propTypes = {
+  disabled: React.PropTypes.bool.isRequired,
+  onClick: React.PropTypes.func.isRequired,
+  onClear: React.PropTypes.func.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/components/target.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/components/target.js b/app/addons/replication/components/target.js
new file mode 100644
index 0000000..1472118
--- /dev/null
+++ b/app/addons/replication/components/target.js
@@ -0,0 +1,202 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import React from 'react';
+import Constants from '../constants';
+import Components from '../../components/react-components.react';
+import ReactSelect from 'react-select';
+
+const { StyledSelect } = Components;
+
+const replicationTargetSourceOptions = () => {
+  return [
+    { value: '', label: 'Select target' },
+    { value: Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE, label: 'Existing local database' },
+    { value: Constants.REPLICATION_TARGET.EXISTING_REMOTE_DATABASE, label: 'Existing remote database' },
+    { value: Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE, label: 'New local database' },
+    { value: Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE, label: 'New remote database' }
+  ].map((option) => {
+    return (
+      <option value={option.value} key={option.value}>{option.label}</option>
+    );
+  });
+};
+
+const ReplicationTargetSelect = ({value, onChange}) => {
+  return (
+    <div className="replication-section">
+      <div className="replication-input-label">
+        Replication Target:
+      </div>
+      <div className="replication-input-select">
+        <StyledSelect
+          selectContent={replicationTargetSourceOptions()}
+          selectChange={(e) => onChange(e.target.value)}
+          selectId="replication-target"
+          selectValue={value} />
+      </div>
+    </div>
+  );
+};
+
+ReplicationTargetSelect.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const RemoteTargetReplicationRow = ({onChange, value}) => {
+  return (
+    <div>
+      <input type="text" className="replication-remote-connection-url" placeholder="https://" value={value}
+        onChange={(e) => onChange(e.target.value)} />
+      <div className="replication-remote-connection-url-text">e.g. https://$REMOTE_USERNAME:$REMOTE_PASSWORD@$REMOTE_SERVER/$DATABASE</div>
+    </div>
+  );
+};
+
+RemoteTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ExistingLocalTargetReplicationRow = ({onChange, value, databases}) => {
+  const options = databases.map(db => ({value: db, label: db}));
+  return (
+    <div className="replication-input-react-select">
+      <ReactSelect
+        value={value}
+        options={options}
+        placeholder="Database name"
+        clearable={false}
+        onChange={(selected) => onChange(selected.value)}
+      />
+    </div>
+  );
+};
+
+ExistingLocalTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  databases: React.PropTypes.array.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const NewLocalTargetReplicationRow = ({onChange, value}) =>
+  <input
+    type="text"
+    className="replication-new-input"
+    placeholder="Database name"
+    value={value}
+    onChange={(e) => onChange(e.target.value)}
+  />;
+
+NewLocalTargetReplicationRow.propTypes = {
+  value: React.PropTypes.string.isRequired,
+  onChange: React.PropTypes.func.isRequired
+};
+
+const ReplicationTargetRow = ({
+  replicationTarget,
+  onLocalTargetChange,
+  onRemoteTargetChange,
+  localTarget,
+  remoteTarget,
+  databases
+}) => {
+  if (!replicationTarget) {
+    return null;
+  }
+  let input;
+
+  if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+    targetLabel = 'New Database:';
+    input = <NewLocalTargetReplicationRow
+      value={localTarget}
+      onChange={onLocalTargetChange}
+            />;
+  } else if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE) {
+    input = <ExistingLocalTargetReplicationRow
+      onChange={onLocalTargetChange}
+      databases={databases}
+      value={localTarget}
+            />;
+  } else {
+    input = <RemoteTargetReplicationRow
+      onChange={onRemoteTargetChange}
+      value={remoteTarget}
+            />;
+  }
+
+  let targetLabel = 'Target Name:';
+
+  if (replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE ||
+      replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+    targetLabel = 'New Database:';
+  }
+
+  return (
+    <div className="replication-section">
+      <div className="replication-input-label">{targetLabel}</div>
+      <div>
+        {input}
+      </div>
+    </div>
+  );
+};
+
+ReplicationTargetRow.propTypes = {
+  databases: React.PropTypes.array.isRequired,
+  onLocalTargetChange: React.PropTypes.func.isRequired,
+  onRemoteTargetChange: React.PropTypes.func.isRequired,
+  remoteTarget: React.PropTypes.string.isRequired,
+  localTarget: React.PropTypes.string.isRequired,
+  replicationTarget: React.PropTypes.string.isRequired
+};
+
+export class ReplicationTarget extends React.Component {
+
+  render () {
+    const {
+      replicationTarget,
+      onLocalTargetChange,
+      onTargetChange,
+      databases,
+      localTarget,
+      onRemoteTargetChange,
+      remoteTarget
+    } = this.props;
+    return (
+      <div>
+        <ReplicationTargetSelect
+          value={replicationTarget}
+          onChange={onTargetChange}
+        />
+        <ReplicationTargetRow
+          remoteTarget={remoteTarget}
+          replicationTarget={replicationTarget}
+          databases={databases}
+          localTarget={localTarget}
+          onRemoteTargetChange={onRemoteTargetChange}
+          onLocalTargetChange={onLocalTargetChange}
+        />
+      </div>
+    );
+  }
+}
+
+ReplicationTarget.propTypes = {
+  databases: React.PropTypes.array.isRequired,
+  onTargetChange: React.PropTypes.func.isRequired,
+  onLocalTargetChange: React.PropTypes.func.isRequired,
+  onRemoteTargetChange: React.PropTypes.func.isRequired,
+  remoteTarget: React.PropTypes.string.isRequired,
+  localTarget: React.PropTypes.string.isRequired,
+  replicationTarget: React.PropTypes.string.isRequired
+};

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/controller.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/controller.js b/app/addons/replication/controller.js
new file mode 100644
index 0000000..92d3150
--- /dev/null
+++ b/app/addons/replication/controller.js
@@ -0,0 +1,349 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import React from 'react';
+import app from '../../app';
+import FauxtonAPI from '../../core/api';
+import Stores from './stores';
+import Actions from './actions';
+import AuthActions from '../auth/actions';
+import Constants from './constants';
+import base64 from 'base-64';
+import {ReplicationSource} from './components/source';
+import {ReplicationTarget} from './components/target';
+import {ReplicationOptions} from './components/options';
+import {ReplicationSubmit} from './components/submit';
+import Components from '../components/react-components.react';
+
+const {LoadLines, ConfirmButton} = Components;
+
+const store = Stores.replicationStore;
+
+export default class ReplicationController extends React.Component {
+  constructor (props) {
+    super(props);
+    this.state = this.getStoreState();
+    this.submit = this.submit.bind(this);
+    this.clear = this.clear.bind(this);
+    this.showPasswordModal = this.showPasswordModal.bind(this);
+  }
+
+  getStoreState () {
+    return {
+      loading: store.isLoading(),
+      databases: store.getDatabases(),
+      authenticated: store.isAuthenticated(),
+      password: store.getPassword(),
+
+      // source fields
+      replicationSource: store.getReplicationSource(),
+      sourceDatabase: store.getSourceDatabase(),
+      localSourceDatabaseKnown: store.isLocalSourceDatabaseKnown(),
+      remoteSource: store.getRemoteSource(),
+
+      // target fields
+      replicationTarget: store.getReplicationTarget(),
+      targetDatabase: store.getTargetDatabase(),
+      localTargetDatabaseKnown: store.isLocalTargetDatabaseKnown(),
+      remoteTarget: store.getRemoteTarget(),
+
+      // other
+      passwordModalVisible: store.isPasswordModalVisible(),
+      replicationType: store.getReplicationType(),
+      replicationDocName: store.getReplicationDocName()
+    };
+  }
+
+  componentDidMount () {
+    Actions.initReplicator(this.props.sourceDatabase);
+    store.on('change', this.onChange, this);
+  }
+
+  componentWillUnmount () {
+    store.off('change', this.onChange);
+    Actions.clearReplicationForm();
+  }
+
+  onChange () {
+    this.setState(this.getStoreState());
+  }
+
+  clear (e) {
+    e.preventDefault();
+    Actions.clearReplicationForm();
+  }
+
+  showPasswordModal () {
+    const { replicationSource, replicationTarget } = this.state;
+
+    const hasLocalSourceOrTarget = (replicationSource === Constants.REPLICATION_SOURCE.LOCAL ||
+      replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE ||
+      replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE);
+
+    // if the user is authenticated, or if NEITHER the source nor target are local, just submit. The password
+    // modal isn't necessary
+    if (!hasLocalSourceOrTarget || this.state.authenticated) {
+      this.submit();
+      return;
+    }
+
+    AuthActions.showPasswordModal();
+  }
+
+  getUsername () {
+    return app.session.get('userCtx').name;
+  }
+
+  getAuthHeaders () {
+    const username = this.getUsername();
+    return {
+      'Authorization': 'Basic ' + base64.encode(username + ':' + this.state.password)
+    };
+  }
+
+  submit () {
+    const { replicationTarget, replicationType, replicationDocName} = this.state;
+
+    if (!this.validate()) {
+      return;
+    }
+
+    const params = {
+      source: this.getSource(),
+      target: this.getTarget()
+    };
+
+    if (_.contains([Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE, Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE], replicationTarget)) {
+      params.create_target = true;
+    }
+    if (replicationType === Constants.REPLICATION_TYPE.CONTINUOUS) {
+      params.continuous = true;
+    }
+
+    if (replicationDocName) {
+      params._id = this.state.replicationDocName;
+    }
+
+    // POSTing to the _replicator DB requires auth
+    const user = FauxtonAPI.session.user();
+    const userName = _.isNull(user) ? '' : FauxtonAPI.session.user().name;
+    params.user_ctx = {
+      name: userName,
+      roles: ['_admin', '_reader', '_writer']
+    };
+
+    Actions.replicate(params);
+  }
+
+  getSource () {
+    const { replicationSource, sourceDatabase, remoteSource } = this.state;
+    if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
+      return {
+        headers: this.getAuthHeaders(),
+        url: window.location.origin + '/' + sourceDatabase
+      };
+    } else {
+      return remoteSource;
+    }
+  }
+
+  getTarget () {
+    const { replicationTarget, targetDatabase, remoteTarget, replicationSource, password } = this.state;
+
+    let target = remoteTarget;
+    if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE) {
+      target = {
+        headers: this.getAuthHeaders(),
+        url: window.location.origin + '/' + targetDatabase
+      };
+    } else if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE) {
+
+      // check to see if we really need to send headers here or can just do the ELSE clause in all scenarioe
+      if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL) {
+        target = {
+          headers: this.getAuthHeaders(),
+          url: window.location.origin + '/' + targetDatabase
+        };
+      } else {
+        const port = window.location.port === '' ? '' : ':' + window.location.port;
+        target = window.location.protocol + '//' + this.getUsername() + ':' + password + '@'
+          + window.location.hostname + port + '/' + targetDatabase;
+      }
+    }
+
+    return target;
+  }
+
+  validate () {
+    const { replicationTarget, targetDatabase, databases } = this.state;
+
+    if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE && _.contains(databases, targetDatabase)) {
+      FauxtonAPI.addNotification({
+        msg: 'The <code>' + targetDatabase + '</code> database already exists locally. Please enter another database name.',
+        type: 'error',
+        escape: false,
+        clear: true
+      });
+      return false;
+    }
+    if (replicationTarget === Constants.REPLICATION_TARGET.NEW_LOCAL_DATABASE ||
+        replicationTarget === Constants.REPLICATION_TARGET.NEW_REMOTE_DATABASE) {
+      let error = '';
+      if (/\s/.test(targetDatabase)) {
+        error = 'The target database may not contain any spaces.';
+      } else if (/^_/.test(targetDatabase)) {
+        error = 'The target database may not start with an underscore.';
+      }
+
+      if (error) {
+        FauxtonAPI.addNotification({
+          msg: error,
+          type: 'error',
+          escape: false,
+          clear: true
+        });
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  confirmButtonEnabled () {
+    const {localSourceDatabaseKnown, replicationSource, replicationTarget, localTargetDatabaseKnown} = this.state;
+
+    if (!replicationSource || !replicationTarget) {
+      return false;
+    }
+    if (replicationSource === Constants.REPLICATION_SOURCE.LOCAL && !localSourceDatabaseKnown) {
+      return false;
+    }
+    if (replicationTarget === Constants.REPLICATION_TARGET.EXISTING_LOCAL_DATABASE && !localTargetDatabaseKnown) {
+      return false;
+    }
+
+    return true;
+  }
+
+  render () {
+    const {
+      loading, replicationSource, replicationTarget, replicationType, replicationDocName,
+      passwordModalVisible, databases, sourceDatabase, remoteSource, remoteTarget,
+      targetDatabase
+    } = this.state;
+
+    if (loading) {
+      return (
+        <LoadLines />
+      );
+    }
+
+    return (
+      <div className="replication-page">
+        <ReplicationSource
+          replicationSource={replicationSource}
+          sourceDatabase={sourceDatabase}
+          databases={databases}
+          remoteSource={remoteSource}
+          onSourceSelect={Actions.updateFormField.bind(Actions, 'replicationSource')}
+          onRemoteSourceChange={Actions.updateFormField.bind(Actions, 'remoteSource')}
+          onLocalSourceChange={Actions.updateFormField.bind(Actions, 'sourceDatabase')}
+        />
+        <hr className="replication-seperator" size="1"/>
+        <ReplicationTarget
+          replicationTarget={replicationTarget}
+          onTargetChange={Actions.updateFormField.bind(Actions, 'replicationTarget')}
+          databases={databases}
+          localTarget={targetDatabase}
+          remoteTarget={remoteTarget}
+          onRemoteTargetChange={Actions.updateFormField.bind(Actions, 'remoteTarget')}
+          onLocalTargetChange={Actions.updateFormField.bind(Actions, 'targetDatabase')}
+        />
+        <hr className="replication-seperator" size="1"/>
+        <ReplicationOptions
+          replicationType={replicationType}
+          replicationDocName={replicationDocName}
+          onDocChange={Actions.updateFormField.bind(Actions, 'replicationDocName')}
+          onTypeChange={Actions.updateFormField.bind(Actions, 'replicationType')}
+        />
+        <ReplicationSubmit
+          disabled={!this.confirmButtonEnabled()}
+          onClick={() => {}}
+          onClear={Actions.clearReplicationForm}
+        />
+      </div>
+    );
+  }
+}
+
+
+
+/*
+<div className="row">
+  <div className="span3">
+    Replication Target:
+  </div>
+  <div className="span7">
+    <ReplicationTarget
+      value={replicationTarget}
+      onChange={(repTarget) => Actions.updateFormField('replicationTarget', repTarget)}/>
+  </div>
+</div>
+{replicationTarget ?
+  <ReplicationTargetRow
+    remoteTarget={remoteTarget}
+    replicationTarget={replicationTarget}
+    databases={databases}
+    targetDatabase={targetDatabase}
+  /> : null}
+
+<hr className="replication-seperator" className="replication-seperator" size="1"/>
+
+<div className="row">
+  <div className="span3">
+    Replication Type:
+  </div>
+  <div className="span7">
+    <ReplicationType
+      value={replicationType}
+      onChange={(repType) => Actions.updateFormField('replicationType', repType)}/>
+  </div>
+</div>
+
+<div className="row">
+  <div className="span3">
+    Replication Document:
+  </div>
+  <div className="span7">
+    <div className="custom-id-field">
+      <span className="fonticon fonticon-cancel" title="Clear field"
+        onClick={(e) => Actions.updateFormField('replicationDocName', '')} />
+      <input type="text" placeholder="Custom, new ID (optional)" value={replicationDocName}
+        onChange={(e) => Actions.updateFormField('replicationDocName', e.target.value)}/>
+    </div>
+  </div>
+</div>
+
+<div className="row buttons-row">
+  <div className="span3">
+  </div>
+  <div className="span7">
+    <ConfirmButton id="replicate" text="Start Replication" onClick={this.showPasswordModal} disabled={!this.confirmButtonEnabled()}/>
+    <a href="#" data-bypass="true" onClick={this.clear}>Clear</a>
+  </div>
+</div>
+
+<PasswordModal
+  visible={passwordModalVisible}
+  modalMessage={<p>Replication requires authentication.</p>}
+  submitBtnLabel="Continue Replication"
+  onSuccess={this.submit} />
+*/

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/helpers.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/helpers.js b/app/addons/replication/helpers.js
index 1ba40bc..4584faf 100644
--- a/app/addons/replication/helpers.js
+++ b/app/addons/replication/helpers.js
@@ -5,13 +5,6 @@ const getDatabaseLabel = db => {
   return matches[0];
 };
 
-const getReactSelectOptions = list => {
-  return list.map(item => {
-    return { value: item, label: item };
-  });
-};
-
 export default {
   getDatabaseLabel,
-  getReactSelectOptions
 };

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/ecea635a/app/addons/replication/route.js
----------------------------------------------------------------------
diff --git a/app/addons/replication/route.js b/app/addons/replication/route.js
index a3d7d46..b25782f 100644
--- a/app/addons/replication/route.js
+++ b/app/addons/replication/route.js
@@ -10,10 +10,8 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-import app from '../../app';
 import FauxtonAPI from '../../core/api';
-import Actions from './actions';
-import Components from './components.react';
+import ReplicationController from './controller';
 
 const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({
   layout: 'one_pane',
@@ -29,9 +27,10 @@ const ReplicationRouteObject = FauxtonAPI.RouteObject.extend({
     { name: 'Replication', link: 'replication' }
   ],
   roles: ['fx_loggedIn'],
+
   defaultView: function (databaseName) {
     const sourceDatabase = databaseName || '';
-    this.setComponent('#dashboard-content', Components.ReplicationController, {sourceDatabase: sourceDatabase});
+    this.setComponent('#dashboard-content', ReplicationController, {sourceDatabase: sourceDatabase});
   }
 });