<commit_before>'use strict';

angular.module('myApp.view1', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/', {
            templateUrl: 'view1/view1.html',
            controller:  'View1Ctrl'
        });
    }])

    .controller('View1Ctrl', ['$scope', '$timeout', '$interval', function ($scope, $timeout, $interval) {
        function stackTrace(msg) {
            var err = new Error(msg);
            throw err.stack;
        }

        var simon = {
            states:          {
                on:        function () {
                    /* state: on */
                    this.view.gameOn    = true;
                    this.view.counterOn = true;
                    // next state
                    this._addEvent(0, this, this._setState, this.states.ready);
                    console.log('state on ran');
                },
                off:       function () {
                    /* state: off */
                    this.view.gameOn    = false;
                    this.view.strict    = false;
                    this.view.counter   = '- -';
                    this.view.counterOn = false;

                    // clean up
                    this._cleanup();

                    // set state to undefined - this is our initial state
                    this._addEvent(0, this, this._setState, undefined);
                    // stop heartbeat 100ms latter - this gives update time to process any clean up events
                    this._addEvent(100, this, this._stopHeartBeat, undefined);
                },
                ready:     function () {
                    /* state: ready */
                },
                run:       function () {
                    /* state: run */
                    this._setCounter(this.seqCount + 1);
                },
                waiting:   function () {
                    /* state: waiting */
                },
                animating: function () {
                    /* state: animating */
                    // empty marker state
                },
                playing:   function () {
                    /* state: playing */
                },
                start:     function () {
                    /* state: start */

                    // clean up
                    this._cleanup();

                    // generate a sequence
                    this._generateSequence();

                    // set start to restart - this was done to avoid code duplication as
                    // restart already dose what needed to be done to start
                    var parent = this;
                    this.failTimerID = this._addEvent(0, this, this._setState, parent.states.restart);
                },
                restart:   function () {
                    /* state: restart */
                    this.seqNum = 0;

                    // play sequence
                    var delay = this._playSequence();

                    // set state to run
                    var parent = this;
                    this._addEvent(delay, this, this._setState, parent.states.run);

                    // add event to time out if they take too long to input buttons
                    // keep event id so we can cancel it
                    this.failTimerID = this._addEvent(delay + this.inputTimeOut, this, this._setState, parent.states.failed);
                },
                failed:    function () {
                    /* state: failed */

                    // cancel any fail timer - this way it doesnt get called twice
                    if (this.failTimerID !== undefined) {
                        this._removeEvent(this.failTimerID);
                        this.failTimerID = undefined;
                    }

                    console.log('failed');
                    // player failed for some reason, we don't care why

                    var nextState = this.states.restart;
                    // if strict that's it game over / restart
                    if (this.strict) {
                        nextState = this.states.start;
                    }

                    // play animation.error
                    this.animation.play(this, this.animation.error, nextState);
                }
            }, /* end of states */
            animation:       {
                play:  function (scope, animation, nextState) {
                    scope._addEvent(0, scope, scope._setState, scope.states.animating);
                    var animationTime = animation(scope);
                    scope._addEvent(animationTime, scope, scope._setState, nextState);
                },
                error: function (scope) {
                    var flashTime = 300;
                    var time = 0;

                    scope.view.counter    = '! !';
                    var flashFunc = function () {
                        scope.view.counterOn = !scope.view.counterOn;
                    };

                    // tell view to flash ! !
                    for (var i = 1; i < 6; i++) {
                        scope._addEvent(time, scope, flashFunc);
                        time += flashTime;
                    }
                    return time;
                }

            }, /* end of animation */
            _startHeartBeat: function () {
                // heart beat
                var parent     = this;
                var lastTick   = Date.now();
                var updateTick = function () {
                    var tick = Date.now();
                    parent._onUpdate(tick - lastTick);
                    lastTick = tick;
                };
                this.heartBeat = $interval(updateTick, 33, 0, true);
            },
            _stopHeartBeat:  function () {
                $interval.cancel(this.heartBeat);
                this.heartBeat = undefined;
            },
            _setCallback:    function (callback) {
                if (callback === undefined) {
                    stackTrace('callback undefined!');
                }
                this.callback = callback;
            },
            on:              function (callback) {
                if (callback === undefined) {
                    stackTrace('callback cannot be undefined!');
                }
                this._setCallback(callback);
                this._startHeartBeat();
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.on);
            },
            off:             function () {
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.off);
            },
            toggleStrict:    function () {
                this.view.strictOn = !this.view.strictOn;
            },
            _onUpdate:       function (dt) {
                this._processEvents(dt);

                // testing
                //console.log(this.state);
                if (this.state !== undefined) {
                    this.state();
                }
                /*if (this.state === this.states.off) {
                    //this.state();
                }*/

                // tell view to update
                this.callback();
                //$scope.$apply();
            },
            _addEvent:       function (delay, scope, func, prams) {
                var id = this.events.length;
                this.events.push({id: id, scope: scope, delay: delay, func: func, prams: prams});
                return id;
            },
            _removeEvent:    function (id) {
                this.events.some(function (element, index) {
                    if (element.id === id) {
                        this.events[index] = undefined;
                        return true;
                    }
                }, this);
            },
            _removeAllEvents: function () {
                this.events = [];
            },
            _processEvents:  function (dt) {
                var marker = 'marker';
                this.events.push(marker);

                var shifted;
                do {
                    shifted = this.events.shift();
                    if (shifted !== marker && shifted !== undefined) {
                        shifted.delay -= dt;
                        if (shifted.delay <= 0) {
                            shifted.func.bind(shifted.scope)(shifted.prams);
                        } else {
                            this.events.push(shifted);
                        }
                    }
                } while (shifted !== marker);
            },
            _cleanup:        function () {
                // clear all events
                this._removeAllEvents();

                // reset view and other data
                this.view.counter   = '- -';
                this.seqCount       = 0;
                this.seqNum         = 0;
                this.view.btnGreen  = false;
                this.view.btnRed    = false;
                this.view.btnBlue   = false;
                this.view.btnYellow = false;
                this.view.sndToPlay = undefined;
            },

            start:             function () {
                if (!this.view.gameOn) {
                    return;
                }
                console.log('starting');

                // set state to start
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.start);
            },
            _setState:         function (newState) {
                this.state = newState;
            },
            btnInput:          function (color) {
                if (this.state !== this.states.run) {
                    return;
                }
                var btn;
                var btnNum;
                if (color === 'green') {
                    btn    = 'btnGreen';
                    btnNum = 1;
                } else if (color === 'red') {
                    btn    = 'btnRed';
                    btnNum = 2;
                } else if (color === 'blue') {
                    btn    = 'btnBlue';
                    btnNum = 3;
                } else if (color === 'yellow') {
                    btn    = 'btnYellow';
                    btnNum = 4;
                }

                // play sound and light button
                this._addEvent(0, this, this._btnOn, btn);
                this._addEvent(this.btnFlashTime, this, this._btnOff, btn);

                // process input
                // good input
                var parent = this;
                if (this.sequence[this.seqNum] === btnNum) {
                    this.seqNum++;

                    if (this.seqNum > this.seqCount) {
                        console.log('this.seqNum > this.seqCount');
                        this.seqNum = 0;
                        this.seqCount++;

                        // cancel fail timer
                        this._removeEvent(this.failTimerID);

                        // play sequence
                        var delay = this._playSequence();

                        // set state to run
                        this._addEvent(delay, this, this._setState, parent.states.run);

                        // add event to time out if they take too long to input buttons
                        // keep event id so we can cancel it
                        this.failTimerID = this._addEvent(delay + this.inputTimeOut, this, this._setState, parent.states.failed);
                    }
                } else {
                    // bad input - set state to failed
                    this._addEvent(0, this, this._setState, parent.states.failed);
                }
            },
            _generateSequence: function () {
                this.sequence = [];
                for (var i = 0; i < 20; i++) {
                    var num = Math.floor(Math.random() * (5 - 1)) + 1;
                    this.sequence.push(num);
                }
            },
            // todo turn playSequence into an animation
            _playSequence:     function () {
                console.log('_playSequence');

                var delay = this.timeDelay;
                // set state to playing to prevent user actions other than on/off
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.animating);

                for (var i = 0; i < this.seqCount + 1; i++) {
                    // button on
                    this._addEvent(delay, this, this._btnOn, this._getBtn(this.sequence[i]));
                    // button off
                    this._addEvent(delay + this.btnFlashTime, this, this._btnOff, this._getBtn(this.sequence[i]));
                    delay += this.timeDelay;
                }
                return delay;
            },
            _btnOn:            function (btn) {
                this.view[btn]      = true;
                this.view.sndToPlay = this.sounds[btn + 'Snd'];
            },
            _btnOff:           function (btn) {
                this.view[btn]      = false;
                this.view.sndToPlay = undefined;
            },
            _getBtn:           function (nbr) {
                switch (nbr) {
                    case 1:
                        return 'btnGreen';
                    case 2:
                        return 'btnRed';
                    case 3:
                        return 'btnBlue';
                    case 4:
                        return 'btnYellow';
                }
            },
            getSoundToPlay:  function () {
                var result          = this.view.sndToPlay;
                this.view.sndToPlay = undefined;
                return result;
            },
            _setCounter: function (value){
                value = value.toString().split('');
                if (value.length === 1){
                    value.unshift(' ');
                    value.unshift('0');
                } else {
                    var tmp = value[0];
                    value[0] = ' ';
                    value.unshift(tmp);
                }
                this.view.counter = value.toString();
            },
            view:            {
                btnGreen:  false,
                btnRed:    false,
                btnBlue:   false,
                btnYellow: false,
                sndToPlay: undefined,
                counter:   '- -',
                counterOn: false,
                strictOn:  false,
                gameOn:    false
            },
            sounds:          {
                btnGreenSnd:  'assets/sounds/simonSound1.mp3',
                btnRedSnd:    'assets/sounds/simonSound2.mp3',
                btnBlueSnd:   'assets/sounds/simonSound3.mp3',
                btnYellowSnd: 'assets/sounds/simonSound4.mp3',
            },
            events:          [],
            state:           undefined,
            sequence:        [],
            seqCount:        0,                 // end of the current rnd
            seqNum:          0,                 // how many correct buttons player has pushed
            promises:        [],
            timeDelay:       1250,              // millisecond time delay between buttons
            btnFlashTime:    750,               // millisecond how long to leave a button on.
            inputTimeOut:    8000,              // time player has to input the correct sequence
            autoRestartTime: 2000,              // millisecond how long to wait before restart
            flashTime:       300,
            failTimerID:     undefined,
            heartBeat:       undefined
        };
        /* end of simon */

        var onOffSwitch    = false;
        var updateView     = function () {
            $scope.onOff       = simon.view.gameOn;
            $scope.counterOn   = simon.view.counterOn;
            $scope.strictLed   = simon.view.strictOn;
            $scope.count       = simon.view.counter;
            $scope.greenBtnOn  = simon.view.btnGreen;
            $scope.redBtnOn    = simon.view.btnRed;
            $scope.blueBtnOn   = simon.view.btnBlue;
            $scope.yellowBtnOn = simon.view.btnYellow;

            var sndToPlay = simon.getSoundToPlay();
            if (sndToPlay !== undefined) {
                var audio = new Audio(sndToPlay);
                audio.play();
            }
            //$scope.$apply();
        };
        $scope.switchClick = function () {
            onOffSwitch = !onOffSwitch;
            if (onOffSwitch) {
                simon.on(updateView);
            } else {
                simon.off();
            }
        };
        $scope.strictClick = function () {
            simon.toggleStrict();
        };
        $scope.btnPress    = function (color) {
            simon.btnInput(color);
        };
        $scope.startBtn    = function () {
            simon.start();
        };
        updateView();

        /*
         simon.toggleOnOff();
         simon._generateSequence();
         simon.seqCount = 19;
         simon._playSequence('testing');
         */

    }])
;<commit_msg>_setCounter fixed<commit_after>'use strict';

angular.module('myApp.view1', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/', {
            templateUrl: 'view1/view1.html',
            controller:  'View1Ctrl'
        });
    }])

    .controller('View1Ctrl', ['$scope', '$timeout', '$interval', function ($scope, $timeout, $interval) {
        function stackTrace(msg) {
            var err = new Error(msg);
            throw err.stack;
        }

        var simon = {
            states:          {
                on:        function () {
                    /* state: on */
                    this.view.gameOn    = true;
                    this.view.counterOn = true;
                    // next state
                    this._addEvent(0, this, this._setState, this.states.ready);
                    console.log('state on ran');
                },
                off:       function () {
                    /* state: off */
                    this.view.gameOn    = false;
                    this.view.strict    = false;
                    this.view.counter   = '- -';
                    this.view.counterOn = false;

                    // clean up
                    this._cleanup();

                    // set state to undefined - this is our initial state
                    this._addEvent(0, this, this._setState, undefined);
                    // stop heartbeat 100ms latter - this gives update time to process any clean up events
                    this._addEvent(100, this, this._stopHeartBeat, undefined);
                },
                ready:     function () {
                    /* state: ready */
                },
                run:       function () {
                    /* state: run */
                    this._setCounter(this.seqCount + 1);
                },
                waiting:   function () {
                    /* state: waiting */
                },
                animating: function () {
                    /* state: animating */
                    // empty marker state
                },
                playing:   function () {
                    /* state: playing */
                },
                start:     function () {
                    /* state: start */

                    // clean up
                    this._cleanup();

                    // generate a sequence
                    this._generateSequence();

                    // set start to restart - this was done to avoid code duplication as
                    // restart already dose what needed to be done to start
                    var parent = this;
                    this.failTimerID = this._addEvent(0, this, this._setState, parent.states.restart);
                },
                restart:   function () {
                    /* state: restart */
                    this.seqNum = 0;

                    // play sequence
                    var delay = this._playSequence();

                    // set state to run
                    var parent = this;
                    this._addEvent(delay, this, this._setState, parent.states.run);

                    // add event to time out if they take too long to input buttons
                    // keep event id so we can cancel it
                    this.failTimerID = this._addEvent(delay + this.inputTimeOut, this, this._setState, parent.states.failed);
                },
                failed:    function () {
                    /* state: failed */

                    // cancel any fail timer - this way it doesnt get called twice
                    if (this.failTimerID !== undefined) {
                        this._removeEvent(this.failTimerID);
                        this.failTimerID = undefined;
                    }

                    console.log('failed');
                    // player failed for some reason, we don't care why

                    var nextState = this.states.restart;
                    // if strict that's it game over / restart
                    if (this.strict) {
                        nextState = this.states.start;
                    }

                    // play animation.error
                    this.animation.play(this, this.animation.error, nextState);
                }
            }, /* end of states */
            animation:       {
                play:  function (scope, animation, nextState) {
                    scope._addEvent(0, scope, scope._setState, scope.states.animating);
                    var animationTime = animation(scope);
                    scope._addEvent(animationTime, scope, scope._setState, nextState);
                },
                error: function (scope) {
                    var flashTime = 300;
                    var time = 0;

                    scope.view.counter    = '! !';
                    var flashFunc = function () {
                        scope.view.counterOn = !scope.view.counterOn;
                    };

                    // tell view to flash ! !
                    for (var i = 1; i < 6; i++) {
                        scope._addEvent(time, scope, flashFunc);
                        time += flashTime;
                    }
                    return time;
                }

            }, /* end of animation */
            _startHeartBeat: function () {
                // heart beat
                var parent     = this;
                var lastTick   = Date.now();
                var updateTick = function () {
                    var tick = Date.now();
                    parent._onUpdate(tick - lastTick);
                    lastTick = tick;
                };
                this.heartBeat = $interval(updateTick, 33, 0, true);
            },
            _stopHeartBeat:  function () {
                $interval.cancel(this.heartBeat);
                this.heartBeat = undefined;
            },
            _setCallback:    function (callback) {
                if (callback === undefined) {
                    stackTrace('callback undefined!');
                }
                this.callback = callback;
            },
            on:              function (callback) {
                if (callback === undefined) {
                    stackTrace('callback cannot be undefined!');
                }
                this._setCallback(callback);
                this._startHeartBeat();
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.on);
            },
            off:             function () {
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.off);
            },
            toggleStrict:    function () {
                this.view.strictOn = !this.view.strictOn;
            },
            _onUpdate:       function (dt) {
                this._processEvents(dt);

                // testing
                //console.log(this.state);
                if (this.state !== undefined) {
                    this.state();
                }
                /*if (this.state === this.states.off) {
                    //this.state();
                }*/

                // tell view to update
                this.callback();
                //$scope.$apply();
            },
            _addEvent:       function (delay, scope, func, prams) {
                var id = this.events.length;
                this.events.push({id: id, scope: scope, delay: delay, func: func, prams: prams});
                return id;
            },
            _removeEvent:    function (id) {
                this.events.some(function (element, index) {
                    if (element.id === id) {
                        this.events[index] = undefined;
                        return true;
                    }
                }, this);
            },
            _removeAllEvents: function () {
                this.events = [];
            },
            _processEvents:  function (dt) {
                var marker = 'marker';
                this.events.push(marker);

                var shifted;
                do {
                    shifted = this.events.shift();
                    if (shifted !== marker && shifted !== undefined) {
                        shifted.delay -= dt;
                        if (shifted.delay <= 0) {
                            shifted.func.bind(shifted.scope)(shifted.prams);
                        } else {
                            this.events.push(shifted);
                        }
                    }
                } while (shifted !== marker);
            },
            _cleanup:        function () {
                // clear all events
                this._removeAllEvents();

                // reset view and other data
                this.view.counter   = '- -';
                this.seqCount       = 0;
                this.seqNum         = 0;
                this.view.btnGreen  = false;
                this.view.btnRed    = false;
                this.view.btnBlue   = false;
                this.view.btnYellow = false;
                this.view.sndToPlay = undefined;
            },

            start:             function () {
                if (!this.view.gameOn) {
                    return;
                }
                console.log('starting');

                // set state to start
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.start);
            },
            _setState:         function (newState) {
                this.state = newState;
            },
            btnInput:          function (color) {
                if (this.state !== this.states.run) {
                    return;
                }
                var btn;
                var btnNum;
                if (color === 'green') {
                    btn    = 'btnGreen';
                    btnNum = 1;
                } else if (color === 'red') {
                    btn    = 'btnRed';
                    btnNum = 2;
                } else if (color === 'blue') {
                    btn    = 'btnBlue';
                    btnNum = 3;
                } else if (color === 'yellow') {
                    btn    = 'btnYellow';
                    btnNum = 4;
                }

                // play sound and light button
                this._addEvent(0, this, this._btnOn, btn);
                this._addEvent(this.btnFlashTime, this, this._btnOff, btn);

                // process input
                // good input
                var parent = this;
                if (this.sequence[this.seqNum] === btnNum) {
                    this.seqNum++;

                    if (this.seqNum > this.seqCount) {
                        console.log('this.seqNum > this.seqCount');
                        this.seqNum = 0;
                        this.seqCount++;

                        // cancel fail timer
                        this._removeEvent(this.failTimerID);

                        // play sequence
                        var delay = this._playSequence();

                        // set state to run
                        this._addEvent(delay, this, this._setState, parent.states.run);

                        // add event to time out if they take too long to input buttons
                        // keep event id so we can cancel it
                        this.failTimerID = this._addEvent(delay + this.inputTimeOut, this, this._setState, parent.states.failed);
                    }
                } else {
                    // bad input - set state to failed
                    this._addEvent(0, this, this._setState, parent.states.failed);
                }
            },
            _generateSequence: function () {
                this.sequence = [];
                for (var i = 0; i < 20; i++) {
                    var num = Math.floor(Math.random() * (5 - 1)) + 1;
                    this.sequence.push(num);
                }
            },
            // todo turn playSequence into an animation
            _playSequence:     function () {
                console.log('_playSequence');

                var delay = this.timeDelay;
                // set state to playing to prevent user actions other than on/off
                var parent = this;
                this._addEvent(0, this, this._setState, parent.states.animating);

                for (var i = 0; i < this.seqCount + 1; i++) {
                    // button on
                    this._addEvent(delay, this, this._btnOn, this._getBtn(this.sequence[i]));
                    // button off
                    this._addEvent(delay + this.btnFlashTime, this, this._btnOff, this._getBtn(this.sequence[i]));
                    delay += this.timeDelay;
                }
                return delay;
            },
            _btnOn:            function (btn) {
                this.view[btn]      = true;
                this.view.sndToPlay = this.sounds[btn + 'Snd'];
            },
            _btnOff:           function (btn) {
                this.view[btn]      = false;
                this.view.sndToPlay = undefined;
            },
            _getBtn:           function (nbr) {
                switch (nbr) {
                    case 1:
                        return 'btnGreen';
                    case 2:
                        return 'btnRed';
                    case 3:
                        return 'btnBlue';
                    case 4:
                        return 'btnYellow';
                }
            },
            getSoundToPlay:  function () {
                var result          = this.view.sndToPlay;
                this.view.sndToPlay = undefined;
                return result;
            },
            _setCounter: function (value){
                value = value.toString().split('');
                if (value.length === 1){
                    value.unshift('0');
                }
                this.view.counter = value.join(' ');
            },
            view:            {
                btnGreen:  false,
                btnRed:    false,
                btnBlue:   false,
                btnYellow: false,
                sndToPlay: undefined,
                counter:   '- -',
                counterOn: false,
                strictOn:  false,
                gameOn:    false
            },
            sounds:          {
                btnGreenSnd:  'assets/sounds/simonSound1.mp3',
                btnRedSnd:    'assets/sounds/simonSound2.mp3',
                btnBlueSnd:   'assets/sounds/simonSound3.mp3',
                btnYellowSnd: 'assets/sounds/simonSound4.mp3',
            },
            events:          [],
            state:           undefined,
            sequence:        [],
            seqCount:        0,                 // end of the current rnd
            seqNum:          0,                 // how many correct buttons player has pushed
            promises:        [],
            timeDelay:       1250,              // millisecond time delay between buttons
            btnFlashTime:    750,               // millisecond how long to leave a button on.
            inputTimeOut:    8000,              // time player has to input the correct sequence
            autoRestartTime: 2000,              // millisecond how long to wait before restart
            flashTime:       300,
            failTimerID:     undefined,
            heartBeat:       undefined
        };
        /* end of simon */

        var onOffSwitch    = false;
        var updateView     = function () {
            $scope.onOff       = simon.view.gameOn;
            $scope.counterOn   = simon.view.counterOn;
            $scope.strictLed   = simon.view.strictOn;
            $scope.count       = simon.view.counter;
            $scope.greenBtnOn  = simon.view.btnGreen;
            $scope.redBtnOn    = simon.view.btnRed;
            $scope.blueBtnOn   = simon.view.btnBlue;
            $scope.yellowBtnOn = simon.view.btnYellow;

            var sndToPlay = simon.getSoundToPlay();
            if (sndToPlay !== undefined) {
                var audio = new Audio(sndToPlay);
                audio.play();
            }
            //$scope.$apply();
        };
        $scope.switchClick = function () {
            onOffSwitch = !onOffSwitch;
            if (onOffSwitch) {
                simon.on(updateView);
            } else {
                simon.off();
            }
        };
        $scope.strictClick = function () {
            simon.toggleStrict();
        };
        $scope.btnPress    = function (color) {
            simon.btnInput(color);
        };
        $scope.startBtn    = function () {
            simon.start();
        };
        updateView();

        /*
         simon.toggleOnOff();
         simon._generateSequence();
         simon.seqCount = 19;
         simon._playSequence('testing');
         */

    }])
;<|endoftext|>


<commit_before>import React from 'react';
import globalSystemCss from '!style-loader!css-loader!./global.css'; // eslint-disable-line
import { withRouter } from 'react-router';

import css from './MainContainer.css';

class MainContainer extends React.Component {
  static propTypes = {
    children: React.PropTypes.node.isRequired,
    history: React.PropTypes.shape({
      listen: React.PropTypes.func.isRequired,
    }).isRequired,
  };

  constructor(props) {
    super(props);
    props.history.listen((hist, op) => {
      // eslint-disable-next-line no-console
      console.log('MainContainer history changed: hist =', hist, 'op =', op);
    });
  }

  render() {
    return (

{this.props.children}

    );
  }
}

export default withRouter(MainContainer);
<commit_msg>Revert "<MainContainer> listens to all route changes. Towards STRIPES-362"<commit_after>import React from 'react';
// import globalCssReset from '!style!css!normalize.css';
import globalSystemCss from '!style-loader!css-loader!./global.css'; // eslint-disable-line

import css from './MainContainer.css';

const propTypes = {
  children: React.PropTypes.node.isRequired,
};

function MainContainer(props) {
  return (

{props.children}

  );
}

MainContainer.propTypes = propTypes;

export default MainContainer;
<|endoftext|>


<commit_before>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseRadius = 150;

var nLasers = 36;

var robotDir = 0;
var dirOffset = 0;

var sensorNoise = 0.1;

//Results of laser scan
var z = new Array(nLasers);

var sensorModel;

function update()
{
    //Only scan if location changed
    scan(robotX, robotY, senseRadius, map, z);
}

function drawLaserLines(ctx)
{
    //Angle between each laser, in radians
    var rad = Math.PI*2/nLasers;

    //index of corresponding entry in z
    var i = (dirOffset - nLasers/4);
    i = (i+nLasers+nLasers);

    for(var index = 0; index <= nLasers/2; index ++, i++)
    {
        //dirOffset is the direction the user's mouse
        //is point at, -nLasers/4 to offset it by 90 degrees,
        //so that laser scan is centered at where the user
        //points to

        //get i stay in bound
        i %= nLasers;

        var dir = i * rad;
        var laserLen = senseRadius;

        //Grey color for a miss
        if(z[i] > senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            //distance reported by the laser sensor
            var dist = z[i] + gaussian()*z[i]*sensorNoise;
            laserLen = min(laserLen, dist);

            //red for a hit
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.circle(robotX + cos(dir)*dist, robotY + sin(dir)*dist, 5);
            ctx.fill();
        }
        ctx.strokeLine(robotX, robotY, robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen)
    }
}

function drawRobot(ctx)
{
    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, robotDir, 20);
}

function parameterChanged(event)
{
    var target = event.target || event.srcElement;

    if(target.id === 'sensorRadius')
    {
        senseRadius = Number(target.value) / 0.02;
    }
    else if(target.id === 'sensorNoise')
    {
        sensorNoise = Number(target.value) / 100.0;
    }
    else if(target.id === 'nLasers')
    {
        nLasers = Number(target.value) * 2;
        z = new Array(nLasers);
    }
}

function trackRobotDir(event)
{
    var coor = getClickLoc(event);
    var x = coor.x;
    var y = coor.y;

    robotDir = atan2(y-robotY, x-robotX);
    dirOffset = round(robotDir/Math.PI/2 * nLasers);
    clearCanvas(canvas);
    canvas.getContext('2d').drawRobot(robotX, robotY, robotDir, 20);
}

function mouseDown(event)
{
	var coor = getClickLoc(event);
	var x = coor.x;
	var y = coor.y;

	//Do nothing if it is not a left button event
	if(event.button !== 0)
    {

		return;
	}

    robotX = x;
    robotY = y;
    clearCanvas(canvas);
    drawRobot(canvas.getContext('2d'));
    bgCanvas.onmousemove = trackRobotDir;
    bgCanvas.onmouseout = mouseUp;
	bgCanvas.onmouseup = mouseUp;
}

function mouseUp()
{
    bgCanvas.onmousemove = undefined;
    bgCanvas.onmouseout = undefined;
    bgCanvas.onmouseup = undefined;
    update();
    drawLaserLines(canvas.getContext('2d'));
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    bgCanvas.getContext('2d').drawMap(map);
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);
    robotDir = random()*TWO_PI;
    dirOffset = round(robotDir/Math.PI/2 * nLasers);

    update();
    drawRobot(canvas.getContext('2d'));
    drawLaserLines(canvas.getContext('2d'));

	sensorModel = new BeamModel(getSensorNoise(), getSensorRadius(), map,
		canvas.width, canvas.height);

    Particle.size = 5;

    //Listen to mouse click events
    bgCanvas.onmousedown = mouseDown;
    senseRadius = getValue('sensorRadius')/0.02;
}

function getSensorNoise()
{
	return getValue('sensorNoise')/100.0;
}

function getSensorRadius()
{
	return getValue('sensorRadius')/0.02;
}<commit_msg>display probability of particle when user clicks<commit_after>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseRadius = 150;

var nLasers = 36;

var robotDir = 0;
var dirOffset = 0;

var sensorNoise = 0.1;

//Results of laser scan
var z = new Array(nLasers);

var sensorModel;

function update()
{
    //Only scan if location changed
    scan(robotX, robotY, senseRadius, map, z);
}

function drawLaserLines(ctx)
{
    //Angle between each laser, in radians
    var rad = Math.PI*2/nLasers;

    //index of corresponding entry in z
    var i = (dirOffset - nLasers/4);
    i = (i+nLasers+nLasers);

    for(var index = 0; index <= nLasers/2; index ++, i++)
    {
        //dirOffset is the direction the user's mouse
        //is point at, -nLasers/4 to offset it by 90 degrees,
        //so that laser scan is centered at where the user
        //points to

        //get i stay in bound
        i %= nLasers;

        var dir = i * rad;
        var laserLen = senseRadius;

        //Grey color for a miss
        if(z[i] > senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            //distance reported by the laser sensor
            var dist = z[i] + gaussian()*z[i]*sensorNoise;
            laserLen = min(laserLen, dist);

            //red for a hit
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.circle(robotX + cos(dir)*dist, robotY + sin(dir)*dist, 5);
            ctx.fill();
        }
        ctx.strokeLine(robotX, robotY, robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen)
    }
}

function drawRobot(ctx)
{
    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, robotDir, 20);
}

function parameterChanged(event)
{
    var target = event.target || event.srcElement;

    if(target.id === 'sensorRadius')
    {
        senseRadius = Number(target.value) / 0.02;
    }
    else if(target.id === 'sensorNoise')
    {
        sensorNoise = Number(target.value) / 100.0;
    }
    else if(target.id === 'nLasers')
    {
        nLasers = Number(target.value) * 2;
        z = new Array(nLasers);
    }
}

function trackRobotDir(event)
{
    var coor = getClickLoc(event);
    var x = coor.x;
    var y = coor.y;

    robotDir = atan2(y-robotY, x-robotX);
    dirOffset = round(robotDir/Math.PI/2 * nLasers);
    clearCanvas(canvas);
    canvas.getContext('2d').drawRobot(robotX, robotY, robotDir, 20);
}

function mouseDown(event)
{
	var coor = getClickLoc(event);
	var x = coor.x;
	var y = coor.y;

	//Do nothing if it is not a left button event
	if(event.altKey)
    {
    	clearCanvas(canvas);
		var ctx = canvas.getContext('2d');
		drawRobot(ctx);
		ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
		ctx.drawRobot(x, y, robotDir, 5);
		var probability = sensorModel.probability(z, new RobotState(x, y, robotDir));
		document.getElementById('probability').innerHTML = "Probability: " + probability;
		return;
	}

    robotX = x;
    robotY = y;
    clearCanvas(canvas);
    drawRobot(canvas.getContext('2d'));
    bgCanvas.onmousemove = trackRobotDir;
    bgCanvas.onmouseout = mouseUp;
	bgCanvas.onmouseup = mouseUp;
}

function mouseUp()
{
    bgCanvas.onmousemove = undefined;
    bgCanvas.onmouseout = undefined;
    bgCanvas.onmouseup = undefined;
    update();
    drawLaserLines(canvas.getContext('2d'));
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    bgCanvas.getContext('2d').drawMap(map);
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);
    robotDir = random()*TWO_PI;
    dirOffset = round(robotDir/Math.PI/2 * nLasers);

    update();
    drawRobot(canvas.getContext('2d'));
    drawLaserLines(canvas.getContext('2d'));

	sensorModel = new BeamModel(getSensorNoise(), getSensorRadius(), map,
		canvas.width, canvas.height);

    Particle.size = 5;

    //Listen to mouse click events
    bgCanvas.onmousedown = mouseDown;
    senseRadius = getValue('sensorRadius')/0.02;
}

function getSensorNoise()
{
	return getValue('sensorNoise')/100.0;
}

function getSensorRadius()
{
	return getValue('sensorRadius')/0.02;
}<|endoftext|>


<commit_before>import React from 'react';
import { Table, Pagination, Popconfirm } from 'antd';
import immutable from 'immutable';
import { config, fun, modular } from '../../common';
import styles from './InfoTable.less';
import EditableInputCell from './EditableInputCell';
import EditableRadioCell from './EditableRadioCell';
import FollowUpCell from './FollowUpCell';

const { is, Map } = immutable;
const moduleName = '信息表控件(infoTable)';
const { serviceId, status, rownum, name, sex, birthday, tel, cardDate, disease, diseaseCase, drugs, present,
  remark, operation, visit, followUp } = config.ritField;
const CALL_BACK_STATUS = { save: 'save', submit: 'submit' };
const ORDER_STATUS = config.ORDER_STATUS;
/**
 * 用户信息表控件
 *
 * @class InfoTable
 * @extends {React.Component}
 */
class InfoTable extends React.Component {
  untreated = 0;
  treated = 0;
  allColumns = {
    [ rownum.key ]: {
      title: rownum.cn,
      width: 50,
      dataIndex: rownum.key,
      key: rownum.key,
    },
    [ name.key ]: {
      title: name.cn,
      width: 60,
      dataIndex: name.key,
      key: name.key,
    },
    [ sex.key ]: {
      title: sex.cn,
      width: 60,
      dataIndex: sex.key,
      key: sex.key,
    },
    [ birthday.key ]: {
      title: birthday.cn,
      width: 120,
      dataIndex: birthday.key,
      key: birthday.key,
    },
    [ tel.key ]: {
      title: tel.cn,
      width: 120,
      dataIndex: tel.key,
      key: tel.key,
    },
    [ cardDate.key ]: {
      title: cardDate.cn,
      width: 200,
      dataIndex: cardDate.key,
      key: cardDate.key,
    },
    [ disease.key ]: {
      title: disease.cn,
      width: 280,
      dataIndex: disease.key,
      key: disease.key,
    },
    [ diseaseCase.key ]: {
      title: diseaseCase.cn,
      width: 280,
      dataIndex: diseaseCase.key,
      key: diseaseCase.key,
    },
    [ drugs.key ]: {
      title: drugs.cn,
      width: 280,
      dataIndex: drugs.key,
      key: drugs.key,
    },
    [ present.key ]: {
      title: present.cn,
      width: 150,
      dataIndex: present.key,
      key: present.key,
      render: ( text, record, index ) => this.renderRadioCell( text, index, present.key )
    },
    [ visit.key ]: {
      title: visit.cn,
      width: 150,
      dataIndex: visit.key,
      key: visit.key,
      render: ( text, record, index ) => this.renderRadioCell( text, index, visit.key )
    },
    [ remark.key ]: {
      title: remark.cn,
      width: 280,
      dataIndex: remark.key,
      key: remark.key,
      render: ( text, record, index ) => this.renderInputCell( text, index, remark.key )
    },
    [ followUp.key ]: {
      title: followUp.cn,
      width: 120,
      dataIndex: followUp.key,
      key: followUp.key,
      render: ( text, record, index ) => this.renderFollowUpCellCell( text, index, followUp.key )
    },
    [ operation.key ]: {
      title: operation.cn,
      width: 160,
      dataIndex: operation.key,
      key: operation.key,
      render: ( text, record, index ) => {
        const myStatus = record.myStatus;
        return (

            {
              myStatus === config.ritStatus.editing ?

                   this.save( index )}>保存
                   |
                  <Popconfirm title="取销将不保存" onConfirm={() => this.cancel( index )}>
                    取销
                  </Popconfirm>

                :

                   this.edit( index )}>编辑

            }

               |
               this.submit( index )}
                style={this.state.operationStatus ? { color: '#b1b8bd' } : { color: '#C00' }}>提交

             this.revoke( index )}>撤回

        );
      }
    }
  };
  constructor( props ) {
    super( props );
    fun.printLoader( moduleName );
    this.monitor = props.monitor;

    let formatData = this.getFormatData( props.data );
    let filterData = this.getFilterData( formatData, props.orderStatus );

    this.state = {
      data: props.data, // 未处理过的数据
      formatData: formatData, // 用 getFormatData 函数处理过的 immutable 类型的数据
      filterData: filterData, // 用 getFilterData 函数处理过的 immutable 类型的数据
      pagination: {
        showQuickJumper: true,
        showTotal: total => `共 ${total} 条`,
        defaultCurrent: 1,
        total: 0,
        pageSize: 5,
      },
      saveCallback: this.props.onSave,
      submitCallback: this.props.onSubmit,
      /** 用户操作的类型：保存或者提交 */
      callBackStatus: '',
      operationStatus: false,
      columns: this.getColums(),
      parentName: props.name,
      orderStatus: props.orderStatus,
    }
  }
  // public function
  /**
   * 获取用户信息表数据
   */
  getData = () => {
    let data = this.state.formatData.toJS();
    return data;
  }
  // private function
  /**
   * 初始化数据，将可编辑的字段改为 object 类型，并将数据转换为 immutable 类型
   */
  getFormatData = ( data ) => {
    if ( typeof data !== 'undefined' && data.length > 0 ) {
      data = immutable.fromJS( data );
      data = data.map(( v, i ) => {
        v = v.set( 'myStatus', config.ritStatus.general );
        // 监听数量，代表每一列有多少个可编辑单元格，
        // 只有当所有单元格都触发了回调函数才会执行最终的保存或提交动作
        v = v.set( 'monitor', 0 );
        return v;
      } );
    }
    else {
      data = immutable.fromJS( [] );
    }
    return data;
  };
  /**
   * 筛选出等于当前的订单状态值的数据
   */
  getFilterData = ( data, orderStatus ) => {
    let filterData = immutable.fromJS( [] );
    if ( data.size > 0 )
      filterData = data.filter( item => is( item.get( 'status' ), orderStatus ) );
    this.treated = orderStatus === ORDER_STATUS.treated ? filterData.size : data.size - filterData.size;
    this.untreated = data.size - this.treated;

    return filterData;
  }
  /**
   * 获取表头数据
   */
  getColums = () => {
    let columnConfig = this.props.userType === config.userType.doctor ?
      modular[ this.props.name ][ 'ritDoctor' ] : modular[ this.props.name ][ 'ritWorker' ];
    return columnConfig.map(( item, index, arr ) => {
      if ( this.allColumns.hasOwnProperty( item ) )
        return this.allColumns[ item ];
    } );
  }
  /**
   * 创建可编辑的 Input 单元格
   */
  renderInputCell = ( value, index, key ) => {
    const myStatus = this.state.filterData.getIn( [ index, 'myStatus' ] );

    return ( <EditableInputCell
      name={key + '_input_' + index}
      value={value}
      onChange={( name, value ) => this.handleChange( name, key, index, value )}
      myStatus={myStatus}
    /> );
  }
  /**
   * 创建随访情况单元格（未完成）
   */
  renderFollowUpCellCell = ( value, index, key ) => {
    return ( <FollowUpCell
      name={key + '_appointment_' + index}
    /> );
  }
  /**
   * 创建可编辑的 Radio 单元格
   */
  renderRadioCell = ( value, index, key ) => {
    const myStatus = this.state.filterData.getIn( [ index, 'myStatus' ] );

    return ( <EditableRadioCell
      name={key + '_radio_' + index}
      value={value}
      onChange={( name, value ) => this.handleChange( name, key, index, value )}
      myStatus={myStatus}
    /> );
  }
  /**
   * 可编辑的子组件的数据发生更改时
   */
  handleChange = ( name, key, index, value ) => {
    const { filterData, callBackStatus, saveCallback, submitCallback } = this.state;
    let monitor = filterData.getIn( [ index, 'monitor' ] );
    monitor--;
    let data = filterData.setIn( [ index, 'monitor' ], monitor ).setIn( [ index, key ], value );
    if ( !is( filterData.getIn( [ index, key ] ), value ) ) {
      this.setState( { filterData: data } );
    }
    fun.print( value, 'handleChange', name );

    if ( monitor <= 0 ) {
      if ( callBackStatus === CALL_BACK_STATUS.save && typeof saveCallback === 'function' ) {
        let row = this.rebuildData( data.get( index ) );
        saveCallback( index, row.toJS() );
      }
      if ( callBackStatus === CALL_BACK_STATUS.submit && typeof submitCallback === 'function' ) {
        let row = this.rebuildData(
          data.setIn( [ index, status.key ], ORDER_STATUS.treated ).get( index ) );
        submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
      }
    }
  }
  /**
   * 重构数据，剔除掉提交时不需要的字段
   */
  rebuildData = ( item ) => {
    let row = Map( {} );
    let ritField = config.ritField;
    item.map(( v, k ) => {
      for ( let field in ritField ) {
        if ( ritField[ field ].key === k && ritField[ field ].need ) {
          row = row.set( k, v );
          break;
        }
      }
    } );
    return row;
  }
  /**
   * 点击编辑按钮
   */
  edit = ( index ) => {
    this.setState( {
      filterData: this.state.filterData
        .setIn( [ index, 'myStatus' ], config.ritStatus.editing )
        .setIn( [ index, 'monitor' ], this.monitor )
    } );
  }
  /**
   * 确定保存：更新数据，并回到非编辑状态
   */
  save = ( index ) => {
    this.setState( {
      filterData: this.state.filterData.setIn( [ index, 'myStatus' ], config.ritStatus.general ),
      callBackStatus: CALL_BACK_STATUS.save
    } );
  }
  /**
   * 提交数据
   */
  submit = ( index ) => {
    const { filterData, submitCallback } = this.state;
    // 当前状态是显示，既未在编辑状态下，而是直接点击提交按钮
    if ( filterData.getIn( [ index, 'myStatus' ] ) === config.ritStatus.general ) {
      let row = this.rebuildData(
        filterData.setIn( [ index, status.key ], ORDER_STATUS.treated ).get( index ) );
      if ( typeof submitCallback === 'function' )
        submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
    }
    else {
      this.setState( {
        filterData: filterData.setIn( [ index, 'myStatus' ], config.ritStatus.general ),
        callBackStatus: CALL_BACK_STATUS.submit
      } );
    }
  }
  /**
   * 成功后调用
   */
  successCallback = ( id ) => {
    let index = this.getDataByServiceId( id );
    if ( index >= 0 ) {
      let formatData = this.state.formatData.updateIn( [ index, status.key ],
        v => v === ORDER_STATUS.treated ? ORDER_STATUS.untreated : ORDER_STATUS.treated );
      this.setState( {
        formatData: formatData,
        filterData: this.getFilterData( formatData, this.state.orderStatus )
      } );
      this.updateTreated();
    }
  }
  /**
   * 更新父组件的 已处理 和 未处理 的数值
   */
  updateTreated = () => {
    this.props.setTreated( this.untreated, this.treated );
  }
  /**
   * 撤销已处理的数据
   */
  revoke = ( index ) => {
    const { filterData, submitCallback } = this.state;
    // let i = this.getDataByServiceId( filterData.getIn( [ index, serviceId.key ] ) );
    // let ftd = formatData.setIn( [ i, status.key ], ORDER_STATUS.untreated );
    let row = this.rebuildData( filterData.setIn( [ index, status.key ], ORDER_STATUS.untreated )
      .get( index ) );
    // this.setState( { formatData: ftd } );

    if ( typeof submitCallback === 'function' )
      submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
  }
  /**
   * 确认撤销：取消刚刚录入的数据，并回到非编辑状态
   */
  cancel = ( index ) => {
    this.setState( {
      formatData: this.state.formatData.setIn( [ index, 'myStatus' ], config.ritStatus.cancel )
    } );
  }
  /**
   * 根据 serviceId 查找改数据在 formatData 中的下标
   */
  getDataByServiceId = ( id ) => {
    let index = -1;
    this.state.formatData.forEach(
      ( v, i ) => {
        if ( v.get( serviceId.key ) === id ) {
          index = i;
          return false;
        }
      }
    )
    return index;
  }

  render() {
    return (

 0 ? this.state.filterData.toJS() : []}
        size="middle" pagination={this.state.pagination} />
    )
  }

  componentWillReceiveProps( nextProps ) {
    let operationStatus = nextProps.orderStatus === ORDER_STATUS.treated ? true : false;
    let columns = [];
    let filterData = [];
    if ( nextProps.name != this.state.parentName ) {
      columns = this.getColums()
    }
    // 有新数据进来
    if ( !is( nextProps.data, this.state.data ) ) {
      let formatData = this.getFormatData( nextProps.data );
      if ( nextProps.userType === config.userType.worker ) {
        filterData = this.getFilterData( formatData, nextProps.orderStatus );
      } else {
        filterData = formatData;
      }

      if ( columns.length > 0 ) {
        this.setState( {
          data: nextProps.data,
          formatData: formatData,
          filterData: filterData,
          operationStatus: operationStatus,
          columns: this.getColums()
        } );
      }
      else {
        this.setState( {
          data: nextProps.data,
          formatData: formatData,
          filterData: filterData,
          operationStatus: operationStatus,
        } );
      }
    }
    else {
      // 切换了订单状态
      if ( nextProps.orderStatus !== this.state.orderStatus ) {
        this.setState( {
          filterData: this.getFilterData( this.state.formatData, nextProps.orderStatus ),
          orderStatus: nextProps.orderStatus,
          operationStatus: operationStatus,
        } );
      }
    }
  }

  componentWillMount() {
    this.updateTreated();
  }
}

export default InfoTable;<commit_msg>bug修复第二回<commit_after>import React from 'react';
import { Table, Pagination, Popconfirm } from 'antd';
import immutable from 'immutable';
import { config, fun, modular } from '../../common';
import styles from './InfoTable.less';
import EditableInputCell from './EditableInputCell';
import EditableRadioCell from './EditableRadioCell';
import FollowUpCell from './FollowUpCell';

const { is, Map } = immutable;
const moduleName = '信息表控件(infoTable)';
const { serviceId, status, rownum, name, sex, birthday, tel, cardDate, disease, diseaseCase, drugs, present,
  remark, operation, visit, followUp } = config.ritField;
const CALL_BACK_STATUS = { save: 'save', submit: 'submit' };
const ORDER_STATUS = config.ORDER_STATUS;
/**
 * 用户信息表控件
 *
 * @class InfoTable
 * @extends {React.Component}
 */
class InfoTable extends React.Component {
  untreated = 0;
  treated = 0;
  allColumns = {
    [ rownum.key ]: {
      title: rownum.cn,
      width: 50,
      dataIndex: rownum.key,
      key: rownum.key,
    },
    [ name.key ]: {
      title: name.cn,
      width: 60,
      dataIndex: name.key,
      key: name.key,
    },
    [ sex.key ]: {
      title: sex.cn,
      width: 60,
      dataIndex: sex.key,
      key: sex.key,
    },
    [ birthday.key ]: {
      title: birthday.cn,
      width: 120,
      dataIndex: birthday.key,
      key: birthday.key,
    },
    [ tel.key ]: {
      title: tel.cn,
      width: 120,
      dataIndex: tel.key,
      key: tel.key,
    },
    [ cardDate.key ]: {
      title: cardDate.cn,
      width: 200,
      dataIndex: cardDate.key,
      key: cardDate.key,
    },
    [ disease.key ]: {
      title: disease.cn,
      width: 280,
      dataIndex: disease.key,
      key: disease.key,
    },
    [ diseaseCase.key ]: {
      title: diseaseCase.cn,
      width: 280,
      dataIndex: diseaseCase.key,
      key: diseaseCase.key,
    },
    [ drugs.key ]: {
      title: drugs.cn,
      width: 280,
      dataIndex: drugs.key,
      key: drugs.key,
    },
    [ present.key ]: {
      title: present.cn,
      width: 150,
      dataIndex: present.key,
      key: present.key,
      render: ( text, record, index ) => this.renderRadioCell( text, index, present.key )
    },
    [ visit.key ]: {
      title: visit.cn,
      width: 150,
      dataIndex: visit.key,
      key: visit.key,
      render: ( text, record, index ) => this.renderRadioCell( text, index, visit.key )
    },
    [ remark.key ]: {
      title: remark.cn,
      width: 280,
      dataIndex: remark.key,
      key: remark.key,
      render: ( text, record, index ) => this.renderInputCell( text, index, remark.key )
    },
    [ followUp.key ]: {
      title: followUp.cn,
      width: 120,
      dataIndex: followUp.key,
      key: followUp.key,
      render: ( text, record, index ) => this.renderFollowUpCellCell( text, index, followUp.key )
    },
    [ operation.key ]: {
      title: operation.cn,
      width: 160,
      dataIndex: operation.key,
      key: operation.key,
      render: ( text, record, index ) => {
        const myStatus = record.myStatus;
        return (

            {
              myStatus === config.ritStatus.editing ?

                   this.save( index )}>保存
                   |
                  <Popconfirm title="取销将不保存" onConfirm={() => this.cancel( index )}>
                    取销
                  </Popconfirm>

                :

                   this.edit( index )}>编辑

            }

               |
               this.submit( index )}
                style={this.state.operationStatus ? { color: '#b1b8bd' } : { color: '#C00' }}>提交

             this.revoke( index )}>撤回

        );
      }
    }
  };
  constructor( props ) {
    super( props );
    fun.printLoader( moduleName );
    this.monitor = props.monitor;

    let formatData = this.getFormatData( props.data );
    let filterData = this.getFilterData( formatData, props.orderStatus );

    this.state = {
      data: props.data, // 未处理过的数据
      formatData: formatData, // 用 getFormatData 函数处理过的 immutable 类型的数据
      filterData: filterData, // 用 getFilterData 函数处理过的 immutable 类型的数据
      pagination: {
        showQuickJumper: true,
        showTotal: total => `共 ${total} 条`,
        defaultCurrent: 1,
        total: 0,
        pageSize: 5,
      },
      saveCallback: this.props.onSave,
      submitCallback: this.props.onSubmit,
      /** 用户操作的类型：保存或者提交 */
      callBackStatus: '',
      operationStatus: false,
      columns: this.getColums(),
      parentName: props.name,
      orderStatus: props.orderStatus,
    }
  }
  // public function
  /**
   * 获取用户信息表数据
   */
  getData = () => {
    let data = this.state.formatData.toJS();
    return data;
  }
  // private function
  /**
   * 初始化数据，将可编辑的字段改为 object 类型，并将数据转换为 immutable 类型
   */
  getFormatData = ( data ) => {
    if ( typeof data !== 'undefined' && data.length > 0 ) {
      data = immutable.fromJS( data );
      data = data.map(( v, i ) => {
        v = v.set( 'myStatus', config.ritStatus.general );
        // 监听数量，代表每一列有多少个可编辑单元格，
        // 只有当所有单元格都触发了回调函数才会执行最终的保存或提交动作
        v = v.set( 'monitor', 0 );
        return v;
      } );
    }
    else {
      data = immutable.fromJS( [] );
    }
    return data;
  };
  /**
   * 筛选出等于当前的订单状态值的数据
   */
  getFilterData = ( data, orderStatus ) => {
    let filterData = immutable.fromJS( [] );
    if ( data.size > 0 )
      filterData = data.filter( item => is( item.get( 'status' ), orderStatus ) );
    this.treated = orderStatus === ORDER_STATUS.treated ? filterData.size : data.size - filterData.size;
    this.untreated = data.size - this.treated;

    return filterData;
  }
  /**
   * 获取表头数据
   */
  getColums = () => {
    let columnConfig = this.props.userType === config.userType.doctor ?
      modular[ this.props.name ][ 'ritDoctor' ] : modular[ this.props.name ][ 'ritWorker' ];
    return columnConfig.map(( item, index, arr ) => {
      if ( this.allColumns.hasOwnProperty( item ) )
        return this.allColumns[ item ];
    } );
  }
  /**
   * 创建可编辑的 Input 单元格
   */
  renderInputCell = ( value, index, key ) => {
    const myStatus = this.state.filterData.getIn( [ index, 'myStatus' ] );

    return ( <EditableInputCell
      name={key + '_input_' + index}
      value={value}
      onChange={( name, value ) => this.handleChange( name, key, index, value )}
      myStatus={myStatus}
    /> );
  }
  /**
   * 创建随访情况单元格（未完成）
   */
  renderFollowUpCellCell = ( value, index, key ) => {
    return ( <FollowUpCell
      name={key + '_appointment_' + index}
    /> );
  }
  /**
   * 创建可编辑的 Radio 单元格
   */
  renderRadioCell = ( value, index, key ) => {
    const myStatus = this.state.filterData.getIn( [ index, 'myStatus' ] );

    return ( <EditableRadioCell
      name={key + '_radio_' + index}
      value={value}
      onChange={( name, value ) => this.handleChange( name, key, index, value )}
      myStatus={myStatus}
    /> );
  }
  /**
   * 可编辑的子组件的数据发生更改时
   */
  handleChange = ( name, key, index, value ) => {
    const { filterData, callBackStatus, saveCallback, submitCallback } = this.state;
    let monitor = filterData.getIn( [ index, 'monitor' ] );
    monitor--;
    let data = filterData.setIn( [ index, 'monitor' ], monitor ).setIn( [ index, key ], value );
    // 其实应该在服务器端成功后再更新 filterData
    if ( !is( filterData.getIn( [ index, key ] ), value ) ) {
      this.setState( { filterData: data } );
    }
    fun.print( value, 'handleChange', name );

    if ( monitor <= 0 ) {
      // 保存
      if ( callBackStatus === CALL_BACK_STATUS.save && typeof saveCallback === 'function' ) {
        let row = this.rebuildData( data.get( index ) );
        saveCallback( index, row.toJS() );
      }
      // 提交
      if ( callBackStatus === CALL_BACK_STATUS.submit && typeof submitCallback === 'function' ) {
        let row = this.rebuildData(
          data.setIn( [ index, status.key ], ORDER_STATUS.treated ).get( index ) );
        submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
      }
      // 更新 formatData
      let i = this.getDataByServiceId( data.getIn( [ index, serviceId.key ] ) );
      if ( i >= 0 ) {
        let formatData = this.state.formatData.set( i, data.get( index ) );
        this.setState( {
          formatData: formatData,
          // filterData: this.getFilterData( formatData, this.state.orderStatus )
        } )
      }
    }
  }
  /**
   * 重构数据，剔除掉提交时不需要的字段
   */
  rebuildData = ( item ) => {
    let row = Map( {} );
    let ritField = config.ritField;
    item.map(( v, k ) => {
      for ( let field in ritField ) {
        if ( ritField[ field ].key === k && ritField[ field ].need ) {
          row = row.set( k, v );
          break;
        }
      }
    } );
    return row;
  }
  /**
   * 点击编辑按钮
   */
  edit = ( index ) => {
    this.setState( {
      filterData: this.state.filterData
        .setIn( [ index, 'myStatus' ], config.ritStatus.editing )
        .setIn( [ index, 'monitor' ], this.monitor )
    } );
  }
  /**
   * 确定保存：更新数据，并回到非编辑状态
   */
  save = ( index ) => {
    this.setState( {
      filterData: this.state.filterData.setIn( [ index, 'myStatus' ], config.ritStatus.general ),
      callBackStatus: CALL_BACK_STATUS.save
    } );
  }
  /**
   * 提交数据
   */
  submit = ( index ) => {
    const { filterData, submitCallback } = this.state;
    // 当前状态是显示，既未在编辑状态下，而是直接点击提交按钮
    if ( filterData.getIn( [ index, 'myStatus' ] ) === config.ritStatus.general ) {
      let row = this.rebuildData(
        filterData.setIn( [ index, status.key ], ORDER_STATUS.treated ).get( index ) );
      if ( typeof submitCallback === 'function' )
        submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
    }
    else {
      this.setState( {
        filterData: filterData.setIn( [ index, 'myStatus' ], config.ritStatus.general ),
        callBackStatus: CALL_BACK_STATUS.submit
      } );
    }
  }
  /**
   * 成功后调用
   */
  successCallback = ( id ) => {
    let index = this.getDataByServiceId( id );
    if ( index >= 0 ) {
      let formatData = this.state.formatData.updateIn( [ index, status.key ],
        v => v === ORDER_STATUS.treated ? ORDER_STATUS.untreated : ORDER_STATUS.treated );
      this.setState( {
        formatData: formatData,
        filterData: this.getFilterData( formatData, this.state.orderStatus )
      } );
      this.updateTreated();
    }
  }
  /**
   * 更新父组件的 已处理 和 未处理 的数值
   */
  updateTreated = () => {
    this.props.setTreated( this.untreated, this.treated );
  }
  /**
   * 撤销已处理的数据
   */
  revoke = ( index ) => {
    const { filterData, submitCallback } = this.state;
    // let i = this.getDataByServiceId( filterData.getIn( [ index, serviceId.key ] ) );
    // let ftd = formatData.setIn( [ i, status.key ], ORDER_STATUS.untreated );
    let row = this.rebuildData( filterData.setIn( [ index, status.key ], ORDER_STATUS.untreated )
      .get( index ) );
    // this.setState( { formatData: ftd } );

    if ( typeof submitCallback === 'function' )
      submitCallback( row.toJS(), ( id ) => { this.successCallback( id ) } );
  }
  /**
   * 确认撤销：取消刚刚录入的数据，并回到非编辑状态
   */
  cancel = ( index ) => {
    this.setState( {
      filterData: this.state.filterData.setIn( [ index, 'myStatus' ], config.ritStatus.cancel )
    } );
  }
  /**
   * 根据 serviceId 查找改数据在 formatData 中的下标
   */
  getDataByServiceId = ( id ) => {
    let index = -1;
    this.state.formatData.forEach(
      ( v, i ) => {
        if ( v.get( serviceId.key ) === id ) {
          index = i;
          return false;
        }
      }
    )
    return index;
  }

  render() {
    return (

 0 ? this.state.filterData.toJS() : []}
        size="middle" pagination={this.state.pagination} />
    )
  }

  componentWillReceiveProps( nextProps ) {
    let operationStatus = nextProps.orderStatus === ORDER_STATUS.treated ? true : false;
    let columns = [];
    let filterData = [];
    if ( nextProps.name != this.state.parentName ) {
      columns = this.getColums()
    }
    // 有新数据进来
    if ( !is( nextProps.data, this.state.data ) ) {
      let formatData = this.getFormatData( nextProps.data );
      if ( nextProps.userType === config.userType.worker ) {
        filterData = this.getFilterData( formatData, nextProps.orderStatus );
      } else {
        filterData = formatData;
      }

      if ( columns.length > 0 ) {
        this.setState( {
          data: nextProps.data,
          formatData: formatData,
          filterData: filterData,
          operationStatus: operationStatus,
          columns: this.getColums()
        } );
      }
      else {
        this.setState( {
          data: nextProps.data,
          formatData: formatData,
          filterData: filterData,
          operationStatus: operationStatus,
        } );
      }
    }
    else {
      // 切换了订单状态
      if ( nextProps.orderStatus !== this.state.orderStatus ) {
        this.setState( {
          filterData: this.getFilterData( this.state.formatData, nextProps.orderStatus ),
          orderStatus: nextProps.orderStatus,
          operationStatus: operationStatus,
        } );
      }
    }
  }

  componentWillMount() {
    this.updateTreated();
  }
}

export default InfoTable;<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'

import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { saveGlobalOptions } from '_/actions'
import { FormControl } from 'patternfly-react'
import { Switch, Nav, NavItem, NavList, Split, SplitItem } from '@patternfly/react-core'
import { withMsg, localeWithFullName, DEFAULT_LOCALE } from '_/intl'
import style from './style.css'

import { Settings, SettingsBase } from '../Settings'
import SelectBox from '../SelectBox'
import moment from 'moment'
import AppConfiguration from '_/config'

const GENERAL_SECTION = 'general'

class GlobalSettings extends Component {
  dontDisturbList (msg) {
    return [
      {
        id: 10,
        value: moment.duration(10, 'minutes').humanize(),
      },
      {
        id: 60,
        value: moment.duration(1, 'hours').humanize(),
      },
      {
        id: 60 * 24,
        value: moment.duration(1, 'days').humanize(),
      },
      {
        id: Number.MAX_SAFE_INTEGER,
        value: msg.untilNextPageReload(),
      },
    ]
  }

  refreshIntervalList (msg) {
    return [
      {
        id: 30,
        value: msg.every30Seconds(),
      },
      {
        id: 60,
        value: msg.everyMinute(),
      },
      {
        id: 120,
        value: msg.every2Minute(),
      },
      {
        id: 300,
        value: msg.every5Minute(),
      },
    ]
  }

  constructor (props) {
    super(props)
    /**
     * Typical flow (happy path):
     * 1. at the begining:
     *    baseValues == draftValues == currentValues
     * 2. after user edit:
     *    baseValues == currentValues
     *    BUT
     *    baseValues != draftValues
     * 3. after 'save' but before action finished:
     *    baseValues == currentValues
     *    AND
     *    baseValue + sentValues == draftValues
     * 4. successful 'save' triggers re-basing (back to step 1.)
     */
    this.state = {
      // editable by the user (used by the widgets)
      // represent the current state of user work
      draftValues: {
        ...props.currentValues,
      },
      // state before editing
      // allows to detect changes by comparing values (baseValues - draftValues == changes)
      // note that it's perfectly legal to have: baseValues != currentValues
      // store can change i.e. after fetching data from the server
      // or after some action i.e. 'do not disturb' expired
      baseValues: {
        ...props.currentValues,
      },
      // values submitted using 'save' action
      // inlcude both remote(server and store) or local(store only)
      sentValues: {},
      activeSectionKey: GENERAL_SECTION,
      defaultValues: {
        language: DEFAULT_LOCALE,
        showNotifications: AppConfiguration.showNotificationsDefault,
        refreshInterval: AppConfiguration.schedulerFixedDelayInSeconds,
        notificationSnoozeDuration: AppConfiguration.notificationSnoozeDurationInMinutes,
        persistLocale: AppConfiguration.persistLocale,
      },
    }
    this.handleCancel = this.handleCancel.bind(this)
    this.buildSections = this.buildSections.bind(this)
    this.saveOptions = this.saveOptions.bind(this)
    this.resetBaseValues = this.resetBaseValues.bind(this)
    this.onChange = this.onChange.bind(this)
    this.onReset = this.onReset.bind(this)
  }

  resetBaseValues () {
    const { currentValues } = this.props
    this.setState({
      sentValues: {},
      baseValues: { ...currentValues },
    })
  }

  saveOptions (values, transactionId) {
    this.props.saveOptions(values, transactionId)
    this.setState({
      sentValues: { ...values },
    })
  }

  handleCancel () {
    this.props.goToMainPage()
  }

  onChange (field) {
    return (value) => {
      this.setState((state) => ({
        draftValues: {
          ...state.draftValues,
          [field]: value,
        },
      }))
    }
  }
  onReset (saveFields, id) {
    this.setState(state => ({
      draftValues: {
        ...this.props.currentValues,
        ...state.defaultValues,
      },
    }))
    this.saveOptions(saveFields, id)
  }

  buildSections (onChange, translatedLabels) {
    const { draftValues } = this.state
    const { config, msg } = this.props
    const idPrefix = 'global-user-settings'
    return {
      [GENERAL_SECTION]: {
        title: msg.general(),
        fields: [
          {
            title: msg.username(),
            name: 'username',
            body: {config.userName},
          },
          {
            title: msg.email(),
            name: 'email',
            body: {config.email},
          },
          {
            title: translatedLabels.language,
            name: 'language',
            tooltip: draftValues.persistLocale ? undefined : msg.optionIsNotSavedOnTheServer({ persistenceReEnableHowTo: msg.persistenceReEnableHowTo({ advancedOptions: msg.advancedOptions() }) }),
            body: (

                <SelectBox
                  id={`${idPrefix}-language`}
                  items={Object.entries(localeWithFullName).map(([id, value]) => ({ id, value, isDefault: id === DEFAULT_LOCALE }))}
                  selected={draftValues.language}
                  onChange={onChange('language')}
                />

            ),
          },
          {
            title: translatedLabels.sshKey,
            tooltip: msg.sshKeyTooltip(),
            name: 'sshKey',
            body: (

                <FormControl
                  id={`${idPrefix}-ssh-key`}
                  componentClass='textarea'
                  onChange={e => onChange('sshKey')(e.target.value)}
                  value={draftValues.sshKey || ''}
                  rows={8}
                />

            ),
          },
        ],
      },
      refreshInterval: {
        title: msg.refreshInterval(),
        tooltip: msg.refreshIntervalTooltip(),
        fields: [
          {
            title: translatedLabels.refreshInterval,
            name: 'refreshInterval',
            body: (

                <SelectBox
                  id={`${idPrefix}-update-rate`}
                  items={this.refreshIntervalList(msg)
                    .map(({ id, value }) => ({ id, value, isDefault: id === AppConfiguration.schedulerFixedDelayInSeconds }))}
                  selected={draftValues.refreshInterval}
                  onChange={onChange('refreshInterval')}
                />

            ),
          },
        ],
      },
      notifications: {
        title: msg.notifications(),
        tooltip: msg.notificationSettingsAffectAllNotifications(),
        fields: [
          {
            title: translatedLabels.showNotifications,
            name: 'showNotificatons',
            body: (
              <Switch
                id={`${idPrefix}-dont-disturb`}
                isChecked={!draftValues.showNotifications}
                onChange={(dontDisturb) => {
                  onChange('showNotifications')(!dontDisturb)
                }}
              />
            ),
          },
          {
            title: translatedLabels.notificationSnoozeDuration,
            name: 'notificationSnoozeDuration',
            body: (

                <SelectBox
                  id={`${idPrefix}-dont-disturb-for`}
                  items={this.dontDisturbList(msg)}
                  selected={draftValues.notificationSnoozeDuration}
                  onChange={onChange('notificationSnoozeDuration')}
                  disabled={draftValues.showNotifications}
                />

            ),
          },
        ],
      },
      advancedOptions: {
        title: msg.advancedOptions(),
        fields: [
          {
            title: msg.persistLanguage(),
            name: 'persistLocale',
            tooltip: msg.persistLanguageTooltip(),
            body: (<Switch
              id={`${idPrefix}-persist-locale`}
              isChecked={draftValues.persistLocale}
              onChange={(persist) => onChange('persistLocale')(persist)}
            />),
          },
        ],
      },
    }
  }

  render () {
    const { lastTransactionId, currentValues, msg } = this.props
    const { draftValues, baseValues, sentValues, defaultValues, activeSectionKey } = this.state
    // required also in Settings for error handling: the case of partial success(only some fields saved)
    // the alert shows the names of the fields that were NOT saved
    const translatedLabels = {
      sshKey: msg.sshKey(),
      language: msg.language(),
      showNotifications: msg.dontDisturb(),
      notificationSnoozeDuration: msg.dontDisturbFor(),
      refreshInterval: msg.uiRefresh(),
      persistLocale: msg.persistLanguage(),
    }

    const sections = this.buildSections(this.onChange, translatedLabels)
    const { [activeSectionKey]: activeSection } = sections

    const onSelect = result => {
      this.setState({
        activeSectionKey: result.itemId,
      })
    }

    return (

        <Split hasGutter>
          <SplitItem>

              <NavList className={`card-pf global-settings-nav-list`}>
                { Object.entries(sections).map(([key, section]) =>
                  <NavItem className='border' itemId={key} key={key} isActive={activeSectionKey === key}>
                    {section.title}
                  </NavItem>)
                }
              </NavList>

          </SplitItem>
          <SplitItem isFilled>
            <Settings
              draftValues={draftValues}
              baseValues={baseValues}
              currentValues={currentValues}
              sentValues={sentValues}
              translatedLabels={translatedLabels}
              lastTransactionId={lastTransactionId}
              resetBaseValues={this.resetBaseValues}
              onSave={this.saveOptions}
              onCancel={this.handleCancel}
              onReset={this.onReset}
              defaultValues={defaultValues}
            >
              <SettingsBase section={activeSection} name={activeSectionKey} />
            </Settings>
          </SplitItem>
        </Split>

    )
  }
}

GlobalSettings.propTypes = {
  currentValues: PropTypes.object.isRequired,
  config: PropTypes.object.isRequired,
  lastTransactionId: PropTypes.string,
  saveOptions: PropTypes.func.isRequired,
  goToMainPage: PropTypes.func.isRequired,
  msg: PropTypes.object.isRequired,
}

export default connect(
  ({ options, config }) => ({
    config: {
      userName: config.getIn(['user', 'name']),
      email: config.getIn(['user', 'email']),
    },
    currentValues: {
      sshKey: options.getIn(['ssh', 'key']),
      language: options.getIn(['remoteOptions', 'locale', 'content']),
      showNotifications: options.getIn(['localOptions', 'showNotifications']),
      notificationSnoozeDuration: options.getIn(['localOptions', 'notificationSnoozeDuration']),
      refreshInterval: options.getIn(['remoteOptions', 'refreshInterval', 'content']),
      persistLocale: options.getIn(['remoteOptions', 'persistLocale', 'content']),
    },
    lastTransactionId: options.getIn(['lastTransactions', 'global', 'transactionId'], ''),
  }),

  (dispatch) => ({
    saveOptions: (values, transactionId) => dispatch(saveGlobalOptions({ values }, { transactionId })),
    goToMainPage: () => dispatch(push('/')),
  })
)(withMsg(GlobalSettings))
<commit_msg>Mark a default option for the Notifications' SelectBox in Account Settings<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'

import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import { saveGlobalOptions } from '_/actions'
import { FormControl } from 'patternfly-react'
import { Switch, Nav, NavItem, NavList, Split, SplitItem } from '@patternfly/react-core'
import { withMsg, localeWithFullName, DEFAULT_LOCALE } from '_/intl'
import style from './style.css'

import { Settings, SettingsBase } from '../Settings'
import SelectBox from '../SelectBox'
import moment from 'moment'
import AppConfiguration from '_/config'

const GENERAL_SECTION = 'general'

class GlobalSettings extends Component {
  dontDisturbList (msg) {
    return [
      {
        id: 10,
        value: moment.duration(10, 'minutes').humanize(),
      },
      {
        id: 60,
        value: moment.duration(1, 'hours').humanize(),
      },
      {
        id: 60 * 24,
        value: moment.duration(1, 'days').humanize(),
      },
      {
        id: Number.MAX_SAFE_INTEGER,
        value: msg.untilNextPageReload(),
      },
    ]
  }

  refreshIntervalList (msg) {
    return [
      {
        id: 30,
        value: msg.every30Seconds(),
      },
      {
        id: 60,
        value: msg.everyMinute(),
      },
      {
        id: 120,
        value: msg.every2Minute(),
      },
      {
        id: 300,
        value: msg.every5Minute(),
      },
    ]
  }

  constructor (props) {
    super(props)
    /**
     * Typical flow (happy path):
     * 1. at the begining:
     *    baseValues == draftValues == currentValues
     * 2. after user edit:
     *    baseValues == currentValues
     *    BUT
     *    baseValues != draftValues
     * 3. after 'save' but before action finished:
     *    baseValues == currentValues
     *    AND
     *    baseValue + sentValues == draftValues
     * 4. successful 'save' triggers re-basing (back to step 1.)
     */
    this.state = {
      // editable by the user (used by the widgets)
      // represent the current state of user work
      draftValues: {
        ...props.currentValues,
      },
      // state before editing
      // allows to detect changes by comparing values (baseValues - draftValues == changes)
      // note that it's perfectly legal to have: baseValues != currentValues
      // store can change i.e. after fetching data from the server
      // or after some action i.e. 'do not disturb' expired
      baseValues: {
        ...props.currentValues,
      },
      // values submitted using 'save' action
      // inlcude both remote(server and store) or local(store only)
      sentValues: {},
      activeSectionKey: GENERAL_SECTION,
      defaultValues: {
        language: DEFAULT_LOCALE,
        showNotifications: AppConfiguration.showNotificationsDefault,
        refreshInterval: AppConfiguration.schedulerFixedDelayInSeconds,
        notificationSnoozeDuration: AppConfiguration.notificationSnoozeDurationInMinutes,
        persistLocale: AppConfiguration.persistLocale,
      },
    }
    this.handleCancel = this.handleCancel.bind(this)
    this.buildSections = this.buildSections.bind(this)
    this.saveOptions = this.saveOptions.bind(this)
    this.resetBaseValues = this.resetBaseValues.bind(this)
    this.onChange = this.onChange.bind(this)
    this.onReset = this.onReset.bind(this)
  }

  resetBaseValues () {
    const { currentValues } = this.props
    this.setState({
      sentValues: {},
      baseValues: { ...currentValues },
    })
  }

  saveOptions (values, transactionId) {
    this.props.saveOptions(values, transactionId)
    this.setState({
      sentValues: { ...values },
    })
  }

  handleCancel () {
    this.props.goToMainPage()
  }

  onChange (field) {
    return (value) => {
      this.setState((state) => ({
        draftValues: {
          ...state.draftValues,
          [field]: value,
        },
      }))
    }
  }
  onReset (saveFields, id) {
    this.setState(state => ({
      draftValues: {
        ...this.props.currentValues,
        ...state.defaultValues,
      },
    }))
    this.saveOptions(saveFields, id)
  }

  buildSections (onChange, translatedLabels) {
    const { draftValues } = this.state
    const { config, msg } = this.props
    const idPrefix = 'global-user-settings'
    return {
      [GENERAL_SECTION]: {
        title: msg.general(),
        fields: [
          {
            title: msg.username(),
            name: 'username',
            body: {config.userName},
          },
          {
            title: msg.email(),
            name: 'email',
            body: {config.email},
          },
          {
            title: translatedLabels.language,
            name: 'language',
            tooltip: draftValues.persistLocale ? undefined : msg.optionIsNotSavedOnTheServer({ persistenceReEnableHowTo: msg.persistenceReEnableHowTo({ advancedOptions: msg.advancedOptions() }) }),
            body: (

                <SelectBox
                  id={`${idPrefix}-language`}
                  items={Object.entries(localeWithFullName).map(([id, value]) => ({ id, value, isDefault: id === DEFAULT_LOCALE }))}
                  selected={draftValues.language}
                  onChange={onChange('language')}
                />

            ),
          },
          {
            title: translatedLabels.sshKey,
            tooltip: msg.sshKeyTooltip(),
            name: 'sshKey',
            body: (

                <FormControl
                  id={`${idPrefix}-ssh-key`}
                  componentClass='textarea'
                  onChange={e => onChange('sshKey')(e.target.value)}
                  value={draftValues.sshKey || ''}
                  rows={8}
                />

            ),
          },
        ],
      },
      refreshInterval: {
        title: msg.refreshInterval(),
        tooltip: msg.refreshIntervalTooltip(),
        fields: [
          {
            title: translatedLabels.refreshInterval,
            name: 'refreshInterval',
            body: (

                <SelectBox
                  id={`${idPrefix}-update-rate`}
                  items={this.refreshIntervalList(msg)
                    .map(({ id, value }) => ({ id, value, isDefault: id === AppConfiguration.schedulerFixedDelayInSeconds }))}
                  selected={draftValues.refreshInterval}
                  onChange={onChange('refreshInterval')}
                />

            ),
          },
        ],
      },
      notifications: {
        title: msg.notifications(),
        tooltip: msg.notificationSettingsAffectAllNotifications(),
        fields: [
          {
            title: translatedLabels.showNotifications,
            name: 'showNotificatons',
            body: (
              <Switch
                id={`${idPrefix}-dont-disturb`}
                isChecked={!draftValues.showNotifications}
                onChange={(dontDisturb) => {
                  onChange('showNotifications')(!dontDisturb)
                }}
              />
            ),
          },
          {
            title: translatedLabels.notificationSnoozeDuration,
            name: 'notificationSnoozeDuration',
            body: (

                <SelectBox
                  id={`${idPrefix}-dont-disturb-for`}
                  items={this.dontDisturbList(msg)
                    .map(({ id, value }) => ({ id, value, isDefault: id === AppConfiguration.notificationSnoozeDurationInMinutes }))}
                  selected={draftValues.notificationSnoozeDuration}
                  onChange={onChange('notificationSnoozeDuration')}
                  disabled={draftValues.showNotifications}
                />

            ),
          },
        ],
      },
      advancedOptions: {
        title: msg.advancedOptions(),
        fields: [
          {
            title: msg.persistLanguage(),
            name: 'persistLocale',
            tooltip: msg.persistLanguageTooltip(),
            body: (<Switch
              id={`${idPrefix}-persist-locale`}
              isChecked={draftValues.persistLocale}
              onChange={(persist) => onChange('persistLocale')(persist)}
            />),
          },
        ],
      },
    }
  }

  render () {
    const { lastTransactionId, currentValues, msg } = this.props
    const { draftValues, baseValues, sentValues, defaultValues, activeSectionKey } = this.state
    // required also in Settings for error handling: the case of partial success(only some fields saved)
    // the alert shows the names of the fields that were NOT saved
    const translatedLabels = {
      sshKey: msg.sshKey(),
      language: msg.language(),
      showNotifications: msg.dontDisturb(),
      notificationSnoozeDuration: msg.dontDisturbFor(),
      refreshInterval: msg.uiRefresh(),
      persistLocale: msg.persistLanguage(),
    }

    const sections = this.buildSections(this.onChange, translatedLabels)
    const { [activeSectionKey]: activeSection } = sections

    const onSelect = result => {
      this.setState({
        activeSectionKey: result.itemId,
      })
    }

    return (

        <Split hasGutter>
          <SplitItem>

              <NavList className={`card-pf global-settings-nav-list`}>
                { Object.entries(sections).map(([key, section]) =>
                  <NavItem className='border' itemId={key} key={key} isActive={activeSectionKey === key}>
                    {section.title}
                  </NavItem>)
                }
              </NavList>

          </SplitItem>
          <SplitItem isFilled>
            <Settings
              draftValues={draftValues}
              baseValues={baseValues}
              currentValues={currentValues}
              sentValues={sentValues}
              translatedLabels={translatedLabels}
              lastTransactionId={lastTransactionId}
              resetBaseValues={this.resetBaseValues}
              onSave={this.saveOptions}
              onCancel={this.handleCancel}
              onReset={this.onReset}
              defaultValues={defaultValues}
            >
              <SettingsBase section={activeSection} name={activeSectionKey} />
            </Settings>
          </SplitItem>
        </Split>

    )
  }
}

GlobalSettings.propTypes = {
  currentValues: PropTypes.object.isRequired,
  config: PropTypes.object.isRequired,
  lastTransactionId: PropTypes.string,
  saveOptions: PropTypes.func.isRequired,
  goToMainPage: PropTypes.func.isRequired,
  msg: PropTypes.object.isRequired,
}

export default connect(
  ({ options, config }) => ({
    config: {
      userName: config.getIn(['user', 'name']),
      email: config.getIn(['user', 'email']),
    },
    currentValues: {
      sshKey: options.getIn(['ssh', 'key']),
      language: options.getIn(['remoteOptions', 'locale', 'content']),
      showNotifications: options.getIn(['localOptions', 'showNotifications']),
      notificationSnoozeDuration: options.getIn(['localOptions', 'notificationSnoozeDuration']),
      refreshInterval: options.getIn(['remoteOptions', 'refreshInterval', 'content']),
      persistLocale: options.getIn(['remoteOptions', 'persistLocale', 'content']),
    },
    lastTransactionId: options.getIn(['lastTransactions', 'global', 'transactionId'], ''),
  }),

  (dispatch) => ({
    saveOptions: (values, transactionId) => dispatch(saveGlobalOptions({ values }, { transactionId })),
    goToMainPage: () => dispatch(push('/')),
  })
)(withMsg(GlobalSettings))
<|endoftext|>