diff --git a/public/js/app.jsx b/public/js/app.jsx index 22a8ea2ee3..276e6b701c 100644 --- a/public/js/app.jsx +++ b/public/js/app.jsx @@ -2,6 +2,7 @@ var React = require('react'); var TabView = require('./components/TabView'); var Menu = require('./components/Menu'); var UserStore = require('./stores/UserStore'); +var ErrorMessages = require('./components/ErrorMessages'); var LogEntriesComponent = React.createFactory(require('./components/log/LogEntriesComponent')); var FeatureTogglesComponent = React.createFactory(require('./components/feature/FeatureTogglesComponent')); var StrategiesComponent = React.createFactory(require('./components/strategy/StrategiesComponent')); @@ -37,10 +38,10 @@ React.render(
+
- - , + , document.getElementById('content') ); diff --git a/public/js/components/ErrorMessages.jsx b/public/js/components/ErrorMessages.jsx index cf3b0ff58b..3594583876 100644 --- a/public/js/components/ErrorMessages.jsx +++ b/public/js/components/ErrorMessages.jsx @@ -1,37 +1,38 @@ -var React = require('react'); +var React = require('react'); +var Ui = require('./ErrorMessages.ui'); +var ErrorStore = require('../stores/ErrorStore'); +var ErrorActions = require('../stores/ErrorActions'); var ErrorMessages = React.createClass({ + getInitialState: function() { + return { + errors: ErrorStore.getErrors() + }; + }, + + onStoreChange: function() { + this.setState({ + errors: ErrorStore.getErrors() + }); + }, + + componentDidMount: function() { + this.unsubscribe = ErrorStore.listen(this.onStoreChange); + }, + + componentWillUnmount: function() { + this.unsubscribe(); + }, + + onClearErrors: function() { + ErrorActions.clear(); + }, + render: function() { - if (!this.props.errors.length) { - return
; - } - - var errorNodes = this.props.errors.map(function(e, i) { - return (
  • {e}
  • ); - }); - return ( -
    -
    -
    -
    -
    -
    - - -
    -
    -
      {errorNodes}
    -
    -
    -
    -
    -
    -
    + ); } }); module.exports = ErrorMessages; - diff --git a/public/js/components/ErrorMessages.ui.jsx b/public/js/components/ErrorMessages.ui.jsx new file mode 100644 index 0000000000..7a93bcb899 --- /dev/null +++ b/public/js/components/ErrorMessages.ui.jsx @@ -0,0 +1,36 @@ +var React = require('react'); + +var ErrorMessages = React.createClass({ + render: function() { + if (!this.props.errors.length) { + return
    ; + } + + var errorNodes = this.props.errors.map(function(e, i) { + return (
  • {e}
  • ); + }); + + return ( +
    +
    +
    +
    +
    +
    + + +
    +
    +
      {errorNodes}
    +
    +
    +
    +
    +
    +
    + ); + } +}); + +module.exports = ErrorMessages; diff --git a/public/js/components/feature/ArchiveFeatureComponent.jsx b/public/js/components/feature/ArchiveFeatureComponent.jsx index 1f2e669129..3730d0d0cb 100644 --- a/public/js/components/feature/ArchiveFeatureComponent.jsx +++ b/public/js/components/feature/ArchiveFeatureComponent.jsx @@ -3,63 +3,63 @@ var FeatureActions = require('../../stores/FeatureToggleActions'); var FeatureToggleStore = require('../../stores/ArchivedToggleStore'); var ArchiveFeatureComponent = React.createClass({ - getInitialState: function() { - return { - archivedFeatures: FeatureToggleStore.getArchivedToggles() - }; - }, + getInitialState: function() { + return { + archivedFeatures: FeatureToggleStore.getArchivedToggles() + }; + }, - onStoreChange: function() { - this.setState({ - archivedFeatures: FeatureToggleStore.getArchivedToggles() - }); - }, + onStoreChange: function() { + this.setState({ + archivedFeatures: FeatureToggleStore.getArchivedToggles() + }); + }, - componentDidMount: function() { - this.unsubscribe = FeatureToggleStore.listen(this.onStoreChange); - }, + componentDidMount: function() { + this.unsubscribe = FeatureToggleStore.listen(this.onStoreChange); + }, - componentWillUnmount: function() { - this.unsubscribe(); - }, + componentWillUnmount: function() { + this.unsubscribe(); + }, - onRevive: function(item) { - FeatureActions.revive(item); - }, + onRevive: function(item) { + FeatureActions.revive.triggerPromise(item); + }, - render: function () { - return ( -
    -

    Archived feature toggles

    - - - - - - - - - {this.state.archivedFeatures.map(this.renderArchivedItem)} - -
    Name
    -
    - ); - }, + render: function () { + return ( +
    +

    Archived feature toggles

    + + + + + + + + + {this.state.archivedFeatures.map(this.renderArchivedItem)} + +
    Name
    +
    + ); + }, - renderArchivedItem: function(f) { - return ( - - - {f.name}
    - {f.description} - - - - - ); - } + renderArchivedItem: function(f) { + return ( + + + {f.name}
    + {f.description} + + + + + ); + } }); module.exports = ArchiveFeatureComponent; diff --git a/public/js/components/feature/FeatureTogglesComponent.jsx b/public/js/components/feature/FeatureTogglesComponent.jsx index c8d2853646..b1023debac 100644 --- a/public/js/components/feature/FeatureTogglesComponent.jsx +++ b/public/js/components/feature/FeatureTogglesComponent.jsx @@ -1,15 +1,14 @@ var React = require('react'); -var ErrorMessages = require('../ErrorMessages'); var FeatureList = require('./FeatureList'); var FeatureForm = require('./FeatureForm'); var FeatureActions = require('../../stores/FeatureToggleActions'); +var ErrorActions = require('../../stores/ErrorActions'); var FeatureToggleStore = require('../../stores/FeatureToggleStore'); var FeatureTogglesComponent = React.createClass({ getInitialState: function() { return { features: FeatureToggleStore.getFeatureToggles(), - errors: [], createView: false }; }, @@ -26,76 +25,31 @@ var FeatureTogglesComponent = React.createClass({ this.unsubscribe(); }, - handleError: function (error) { - console.log(error); - if (this.isClientError(error)) { - var errors = JSON.parse(error.responseText) - errors.forEach(function(e) { this.addError(e.msg); }.bind(this)) - } else if (error.status === 0) { - this.addError("server unreachable"); - } else { - this.addError(error); - } - }, - updateFeature: function (feature) { - FeatureActions.update.triggerPromise(feature) - .catch(this.handleError); + FeatureActions.update.triggerPromise(feature); }, archiveFeature: function (feature) { - FeatureActions.archive.triggerPromise(feature) - .catch(this.handleError); + FeatureActions.archive.triggerPromise(feature); }, createFeature: function (feature) { FeatureActions.create.triggerPromise(feature) - .then(this.cancelNewFeature) - .catch(this.handleError); + .then(this.cancelNewFeature); }, newFeature: function() { this.setState({createView: true}); }, - cancelNewFeature: function (feature) { + cancelNewFeature: function () { this.setState({createView: false}); - }, - - clearErrors: function() { - this.setState({errors: []}); - }, - - addError: function(msg) { - var errors = this.state.errors; - if (errors[errors.length - 1] !== msg) { - errors.push(msg); - this.setState(errors); - } - }, - - isClientError: function(error) { - try { - return error.status >= 400 && - error.status < 500 && - JSON.parse(error.responseText); - } catch (e) { - if (e instanceof SyntaxError) { - // fall through; - } else { - throw e; - } - } - - return false; + ErrorActions.clear(); }, render: function() { return (
    - {this.state.createView ? this.renderCreateView() : this.renderCreateButton()} @@ -111,11 +65,11 @@ var FeatureTogglesComponent = React.createClass({ }, renderCreateView: function() { - return + return ; }, renderCreateButton: function() { - return + return ; } }); diff --git a/public/js/components/log/LogEntriesComponent.jsx b/public/js/components/log/LogEntriesComponent.jsx index 45a943d7f6..71d5992e53 100644 --- a/public/js/components/log/LogEntriesComponent.jsx +++ b/public/js/components/log/LogEntriesComponent.jsx @@ -1,14 +1,13 @@ -var React = require('react'), - LogEntryList = require('./LogEntryList'), - eventStore = require('../../stores/EventStore'), - ErrorMessages = require('../ErrorMessages'); +var React = require('react'); +var LogEntryList = require('./LogEntryList'); +var eventStore = require('../../stores/EventStore'); +var ErrorActions = require('../../stores/ErrorActions'); var LogEntriesComponent = React.createClass({ getInitialState: function() { return { createView: false, - events: [], - errors: [] + events: [] }; }, @@ -19,29 +18,17 @@ var LogEntriesComponent = React.createClass({ }, initError: function() { - this.onError("Could not load events from server"); - }, - - clearErrors: function() { - this.setState({errors: []}); - }, - - onError: function(error) { - var errors = this.state.errors.concat([error]); - this.setState({errors: errors}); + ErrorActions.error("Could not load events from server"); }, render: function() { return (
    - -
    -
    ); }, }); -module.exports = LogEntriesComponent; \ No newline at end of file +module.exports = LogEntriesComponent; diff --git a/public/js/components/strategy/StrategiesComponent.jsx b/public/js/components/strategy/StrategiesComponent.jsx index 0df00d17de..1a625d61d5 100644 --- a/public/js/components/strategy/StrategiesComponent.jsx +++ b/public/js/components/strategy/StrategiesComponent.jsx @@ -1,15 +1,14 @@ -var React = require('react'), - StrategyList = require('./StrategyList'), - StrategyForm = require('./StrategyForm'), - strategyStore = require('../../stores/StrategyStore'), - ErrorMessages = require('../ErrorMessages'); +var React = require('react'); +var StrategyList = require('./StrategyList'); +var StrategyForm = require('./StrategyForm'); +var strategyStore = require('../../stores/StrategyStore'); +var ErrorActions = require('../../stores/ErrorActions'); var StrategiesComponent = React.createClass({ getInitialState: function() { return { createView: false, - strategies: [], - errors: [] + strategies: [] }; }, @@ -17,12 +16,12 @@ var StrategiesComponent = React.createClass({ this.fetchStrategies(); }, - fetchStrategies: function(res) { + fetchStrategies: function() { strategyStore.getStrategies() - .then(function(res) { - this.setState({strategies: res.strategies}) - }.bind(this)) - .catch(this.initError); + .then(function(res) { + this.setState({strategies: res.strategies}); + }.bind(this)) + .catch(this.initError); }, @@ -30,13 +29,8 @@ var StrategiesComponent = React.createClass({ this.onError("Could not load inital strategies from server"); }, - clearErrors: function() { - this.setState({errors: []}); - }, - onError: function(error) { - var errors = this.state.errors.concat([error]); - this.setState({errors: errors}); + ErrorActions.error(error); }, onNewStrategy: function() { @@ -66,33 +60,37 @@ var StrategiesComponent = React.createClass({ onRemove: function(strategy) { strategyStore.removeStrategy(strategy) - .then(this.fetchStrategies) - .catch(this.onError); + .then(this.fetchStrategies) + .catch(this.onError); }, render: function() { return (
    - - - {this.state.createView ? this.renderCreateView() : this.renderCreateButton()} - + {this.state.createView ? this.renderCreateView() : this.renderCreateButton()}
    - - +
    - ); + ); }, renderCreateView: function() { - return () + return ( + ); }, renderCreateButton: function() { return ( - + ); } }); -module.exports = StrategiesComponent; \ No newline at end of file +module.exports = StrategiesComponent; diff --git a/public/js/stores/ErrorActions.js b/public/js/stores/ErrorActions.js new file mode 100644 index 0000000000..f6e4aebc4b --- /dev/null +++ b/public/js/stores/ErrorActions.js @@ -0,0 +1,8 @@ +var Reflux = require('reflux'); + +var ErrorActions = Reflux.createActions([ + "clear", + "error" +]); + +module.exports = ErrorActions; diff --git a/public/js/stores/ErrorStore.js b/public/js/stores/ErrorStore.js new file mode 100644 index 0000000000..f038b12a47 --- /dev/null +++ b/public/js/stores/ErrorStore.js @@ -0,0 +1,65 @@ +var Reflux = require('reflux'); +var FeatureActions = require('./FeatureToggleActions'); +var ErrorActions = require('./ErrorActions'); + +// Creates a DataStore +var FeatureStore = Reflux.createStore({ + // Initial setup + init: function() { + this.listenTo(FeatureActions.create.failed, this.onError); + this.listenTo(FeatureActions.update.failed, this.onError); + this.listenTo(FeatureActions.archive.failed, this.onError); + this.listenTo(FeatureActions.revive.failed, this.onError); + this.listenTo(ErrorActions.error, this.onError); + this.listenTo(ErrorActions.clear, this.onClear); + this.errors = []; + }, + + onError: function (error) { + if (this.isClientError(error)) { + var errors = JSON.parse(error.responseText); + errors.forEach(function(e) { this.addError(e.msg); }.bind(this)); + } else if (error.status === 0) { + this.addError("server unreachable"); + } else { + this.addError(error); + } + }, + + onClear: function() { + this.errors = []; + this.trigger([]); + }, + + addError: function(msg) { + var errors = this.errors; + if (errors[errors.length - 1] !== msg) { + errors.push(msg); + this.errors = errors; + this.trigger(errors); + } + }, + + isClientError: function(error) { + try { + return error.status >= 400 && + error.status < 500 && + JSON.parse(error.responseText); + } catch (e) { + if (e instanceof SyntaxError) { + // fall through; + console.log("Syntax error!"); + } else { + throw e; + } + } + + return false; + }, + + getErrors: function() { + return this.errors; + } +}); + +module.exports = FeatureStore; diff --git a/public/js/stores/FeatureToggleStore.js b/public/js/stores/FeatureToggleStore.js index bd09302346..2554b81d14 100644 --- a/public/js/stores/FeatureToggleStore.js +++ b/public/js/stores/FeatureToggleStore.js @@ -20,14 +20,18 @@ var FeatureStore = Reflux.createStore({ this.listenTo(FeatureActions.revive.completed, this.onRevive); //TODO: this should not be part of the store! - this.timer = new Timer(this.loadDataFromServer, 30*1000); + this.timer = new Timer(this.loadDataFromServer, 3*1000); this.timer.start(); }, loadDataFromServer: function() { //TODO: this should not be part of the store! Server.getFeatures(function(err, featureToggles) { - this.setToggles(featureToggles); + if(err) { + return; + } else { + this.setToggles(featureToggles); + } }.bind(this)); },