livingsocial/rearview-engine

View on GitHub
public/rearview-src/js/view/dashboard.js

Summary

Maintainability
D
1 day
Test Coverage
define([
    'view/base',
    'model/monitor',
    'collection/monitor',
    'view/smallmonitor',
    'view/expandedmonitor',
    'view/addmonitor',
    'view/resetmonitor',
    'view/alerttimeline',
    'codemirror',
    'codemirror-ruby',
    'jquery-validate'
], function(
    BaseView,
    MonitorModel,
    MonitorCollection,
    SmallMonitorView,
    ExpandedMonitorView,
    AddMonitorView,
    ResetMonitorView,
    AlertTimelineView,
    CodeMirror
){  
    var DashboardView = BaseView.extend({
        rowMonitorLimit : 3,
        events : {
            'slid .carousel'  : 'advanceCarousel'
        },

        subscriptions : {
            'view:expandedmonitor:open'       : 'hideDash',
            'view:expandedmonitor:exit'       : 'updateDash',
            'view:addmonitor:close'           : 'showDash',
            'view:addmonitor:save'            : 'updateDash',
            'view:addmonitor:show'            : 'hideDash',
            'view:smallmonitor:edit'          : 'editMonitor',
            'view:alerttimeline:troubleshoot' : 'editMonitor',
            // 'view:dashboard:complete'         : 'advanceCarousel'
        },

        hidePrevCaption : function() {
            this.$el.find('.carousel-caption').parent().children('.carousel-caption p').hide();
        },

        initialize : function(options) {
            _.bindAll(this);

            this.templar            = options.templar;
            this.dashboardId        = ( options.dashboardId ) ? options.dashboardId : null;
            this.categoryId         = ( options.categoryId ) ? options.categoryId : null;
            this.user               = ( options.user ) ? options.user : null;
            this.router             = ( options.router ) ? options.router : null;
            this.categories         = [];
            this.monitors           = [];
            this.monitorCollections = [];
            this.currentOrder       = [];
            this.carouselIndex      = 0;

            this.expandedMonitorView = new ExpandedMonitorView({
                'el'      : $('.edit-monitor-wrap'),
                'user'    : this.user,
                'templar' : this.templar,
                'router'  : this.router
            });

            this.addMonitorView = new AddMonitorView({
                'model'       : new MonitorModel({
                    'dashboardId' : this.dashboardId
                }),
                'user'        : this.user,
                'dashboardId' : this.dashboardId,
                'templar'     : this.templar
            });

            this.alertTimelineView = new AlertTimelineView({
                'el'          : $('.timeline-wrap'),
                'collection'  : this.collection,
                'dashboardId' : this.dashboardId,
                'user'        : this.user,
                'status'      : this.checkDashboardAlertState(),
                'templar'     : this.templar
            });

            this.resetMonitorView = new ResetMonitorView({
                'el'      : $('.reset-monitor-wrap'),
                'templar' : this.templar
            });

            this.addHelpers();

            Backbone.Mediator.pub('view:dashboard:init');
        },

        render : function() {
            this.initMonitors();
            return this;
        },
        /**
         * DashboardView#initMonitors()
         *                  
         * 
         **/
        initMonitors : function() {
            this.getCategories();
            if(this.categories.length && this.categoryId==null) {
              Backbone.Mediator.pub('view:dashboard:category', this.setCategoryId(this.categories[this.carouselIndex].id));
            }
            // publish dashboard information for the header view
            this.getDashboardInfo(this.dashboardId, function(title) {
                Backbone.Mediator.pub('view:dashboard:render', {
                    'title'    : title,
                    'subtitle' : this.getDashboardSubtitle(this.getCategoryIndex()),
                    'nav'      : {
                        'ecosystem' : false,
                        'dashboard' : true
                    },
                    'dashboardId'    : this.dashboardId
                });
            }.bind(this));

            this.updateMonitorList();
            this.setupCarousel();
            this.setupDrop();
            this.goToCategory();

            Backbone.Mediator.pub('view:dashboard:complete');
        },

        setCategories : function(categories) {
            // we need to remove the parent if categories exist
            _.each(categories, function(category, index) {
                if ( category.children.length ) {
                    this.categories.splice(index,1);
                }
            }, this);
            
            this.categories = categories;
        },

        getCategories : function() {
            $.ajax({
                url : '/dashboards/' + this.dashboardId + '/children',
                async : false,
                success : function(result) {
                    var categories = result;
                    // NOTE : a decision was made once a category exists
                    //        to move all current monitors to that category
                    //        and never show the parent
                    this.setCategories(categories);
                }.bind(this)
            });
        },

        setupDrop : function() {
            this.$monitorDragWraps = $('.small-monitor-drag');
            this.$monitorWraps     = $('.small-monitor-wrap');

            this.$monitorWraps.droppable({ 
                accept : '.small-monitor',
                
                over : function(e, ui) {
                    $(e.target).addClass('active-drop');
                    ui.draggable.draggable( 'option', 'revert', false );
                },
                out : function(e, ui) {
                    $(e.target).removeClass('active-drop');
                    ui.draggable.draggable( 'option', 'revert', true );
                }, 
                drop : function(e, ui) {
                    $(e.target).removeClass('active-drop');
                    $(window).trigger('resize');
                }
            });
        },

        setupCarousel : function() {
            this.$carousel = $('#dashboardCarousel').carousel({
                interval : false
            });

            this.$el.find('.icon-chevron-sign-right').click(function() {
                this.$carousel.carousel('next');
            }.bind(this));

            this.$el.find('.icon-chevron-sign-left').click(function() {
                this.$carousel.carousel('prev');
            }.bind(this));
        },

        getDashboardSubtitle : function(carouselIndex) {
            this.getCategories();
            var subtitle = '';
            if ( ( this.categories.length >= 1 ) &&
                 ( this.parentDashboardInfo.id !== this.getCategoryId() ) ) {

                subtitle = _.str.capitalize( this.categories[carouselIndex].name ) + ' Dashboard';
            } else {
                subtitle = 'Monitor Dashboard';
            }

            return subtitle;
        },

        publishDashboardSubtitle : function(carouselIndex) {
            Backbone.Mediator.pub('view:dashboard:render', {
                'subtitle' : this.getDashboardSubtitle(carouselIndex)
            });
        },

        advanceCarousel : function(e) {
            if(e) e.stopPropagation();

            this.carouselIndex = this.$el.find('.item.active').index('.item');
            this.setCategoryId( ( this.categories.length ) 
                                ? this.categories[this.carouselIndex].id 
                                : this.getCategoryId() );

            
            this.router.navigate('dash/' + this.dashboardId + '/category/' + this.getCategoryId() );
            this.$el.find('.dashboard-' + this.getCategoryId()).css({
                'min-height' : this.$el.find('.dashboard-' + this.getCategoryId() + ' .monitor-grid').height() + 40
            });

            this.publishDashboardSubtitle(this.carouselIndex);
            Backbone.Mediator.pub('view:dashboard:category', this.getCategoryId());
            $(window).trigger('resize');
        },

        getCategoryIndex : function() {
            var currentIndex = 0;
            _.each(this.categories, function(category, index) {
                if ( this.getCategoryId() === category.id ) {
                    currentIndex = index;
                }
            }, this);

            return currentIndex;
        },

        goToCategory : function() {
            if ( this.getCategoryIndex() ) {
                // When we advance directly to a slide
                // animation is unnecessary
                this.$carousel.toggleClass('slide');
                this.$carousel.carousel(this.getCategoryIndex());
                this.$carousel.toggleClass('slide');
            }
            
            if ( this.categories.length <= 1 ) {
                this.$el.find('.carousel-indicators, .carousel-control').hide();
            }
        },

        // This should return an id no matter what
        // whether it's a category ( which is just a nested dashboard )
        // or dashboard id
        getCategoryId : function() {
            return this.categoryId;
        },

        setCategoryId : function(id) {
            this.categoryId = ( !_.isNull(id) ) ? id : this.categoryId;
            return this.getCategoryId();
        },

        getMonitorOrder : function() {
            var userPreferences = this.user.get('preferences');

            if ( this.categories.length ) {
                _.each(this.categories, function(category) {
                    var categoryMonitorOrder = ( userPreferences.dashboards 
                                                 && !_.isEmpty(userPreferences.dashboards[category.id]) )
                                             ? userPreferences.dashboards[category.id].order
                                             : [];
                    this.currentOrder[category.id] = categoryMonitorOrder;
                }, this);
            } else {
                var dashboardMonitorOrder = ( userPreferences.dashboards 
                                             && !_.isEmpty(userPreferences.dashboards[this.parentDashboardInfo.id]) )
                                         ? userPreferences.dashboards[this.parentDashboardInfo.id].order
                                         : [];
                this.currentOrder[this.parentDashboardInfo.id] = dashboardMonitorOrder;
            }

            return this.currentOrder;
        },

        getDashboardInfo : function(dashboardId, cb) {
            $.ajax({
                url : '/dashboards/' + dashboardId,
                async : false,
                success : function(dashboard) {
                    this.parentDashboardInfo = dashboard;
                    
                    if (typeof cb === 'function') {
                        cb(this.parentDashboardInfo.name);
                    }
                }.bind(this),
                error : function(result) {
                }.bind(this),
                complete : function(jxhr, status) {
                }.bind(this)
            });
        },

        editMonitor : function(id) {
            this.expandedMonitorView.render(id, this.categories, this.getCategoryId(), this.dashboardId);
        },

        updateDash : function(data) {
            if (data && data.status && data.status != 'error') {
                this.collection = new MonitorCollection(null, {
                    dashboardId : this.dashboardId
                });

                this.reinitializeDash(data);
            }
            this.showDash();
        },

        reinitializeDash : function(data) {
            var $parentSibling = this.$el.parent();

            this.router.navigate();
            if ( this.getCategoryId() ) {
                this.router.navigate("dash/" + this.dashboardId + '/category/' + this.getCategoryId(), {trigger: true});
            } else {
                this.router.navigate("dash/" + this.dashboardId, {trigger: true});
            }
        },

        hideDash : function() {
            this.$el.hide();
        },

        showDash : function() {
            this.$el.show();
            // highcharts gets stuck sometimes, firing a 
            // resize event keeps it from sticking
            $(window).trigger('resize');
        },

        updateSavedMonitorStatus : function(data) {
            var model = ( data ) ? data.model : null;

            if ( model ) {
                _.each(this.monitors, function(view) {
                    if(view.model.get('id') === model.get('id')) {
                        view.nextRun();
                    }
                });
            }
        },

        checkDashboardAlertState : function() {
            this.collection.each(function(model) {
                if ( model.get('status') !== 'success' && typeof model.get('status') != 'undefined' && model.get('active') ) {
                    this.dashboardAlert = true;
                }
            });

            if ( this.dashboardAlert ) {
                Backbone.Mediator.pub('view:dashboard:alert');
            }

            return this.dashboardAlert;
        },
        /**
         * SmallMonitorView#updateMonitorList()
         *  
         * This method is simply to place small monitors in bootstrap
         * rows and add them correctly for template render. 
         **/
        updateMonitorList : function() {
            // to store current list in current order
            var monitorCollection = [],
                renderCategories  = [];

            // get current monitor order from user prefs
            this.getMonitorOrder();

            // NOTE: utilize handlebars to determine how many monitors
            // per row are needed which is configurable by this view
            Handlebars.registerHelper('mod', function(indexCount, block) {
                if ( parseInt(indexCount, 10) % (this.rowMonitorLimit) === 0 ) {
                    return block.fn(this);
                }
            }.bind(this));
            // build json payload for template render in handlebars
            if ( this.categories.length ) {
                _.each(this.categories, function(category) {
                    var monitorCollection = new MonitorCollection(null, {
                        dashboardId : category.id
                    });

                    // sometimes there is a preexisting order that may lack
                    // newly added monitors, etc. here we are making sure they 
                    // get rendered at the end of the current user's ordering
                    // preferences array
                    var categoryMonitorOrder = this.currentOrder[category.id];
                    if (categoryMonitorOrder.length !== 0) {
                        // instantiate views that don't have order yet...
                        var unorderedMonitors = _.difference(monitorCollection.pluck('id'), categoryMonitorOrder);
                        // add them to the order array
                        categoryMonitorOrder = _.union(categoryMonitorOrder, unorderedMonitors); 
                        monitorCollection.filterById( categoryMonitorOrder );
                    }

                    this.monitorCollections.push(monitorCollection);
                    category.monitors = monitorCollection.toJSON();
                }, this);
                renderCategories = this.categories;
            } else {
                var monitorCollection = new MonitorCollection(null, {
                    dashboardId : this.parentDashboardInfo.id
                });
                
                var dashboardMonitorOrder = ( this.currentOrder.length ) 
                                          ? this.currentOrder[this.parentDashboardInfo.id]
                                          : [];
                if (dashboardMonitorOrder.length !== 0) {
                    // instantiate views that don't have order yet...
                    var unorderedMonitors = _.difference(monitorCollection.pluck('id'), dashboardMonitorOrder);
                    // add them to the order array
                    dashboardMonitorOrder = _.union(dashboardMonitorOrder, unorderedMonitors); 
                    monitorCollection.filterById( dashboardMonitorOrder );
                }

                this.monitorCollections.push(monitorCollection);
                this.parentDashboardInfo.monitors = monitorCollection.toJSON();

                renderCategories.push(this.parentDashboardInfo);
            }

            // render the dashboard template before we render our
            // small monitors
            this.templar.render({
                path : 'dashboard',
                el   : this.$el,
                data : {
                    'categories' : renderCategories
                }
            });

            _.each(this.monitorCollections, function(monitorCollection) {
                monitorCollection.each(function(monitor) {
                    this.monitors.push(new SmallMonitorView({
                        'el'          : this.$el.find('.small-monitor-' + monitor.get('id'))[0],
                        'model'       : monitor,
                        'dashboardId' : this.dashboardId,
                        'templar'     : this.templar,
                        'user'        : this.user
                    }).render()); 
                }.bind(this));
            }, this);
        },

        destroyMonitors : function() {
            for (viewName in this.monitors) {
                var view = this.monitors[viewName];
                view.destructor();
                delete this.monitors[viewName];
            }
        },

        destructor : function() {
            var $parentSibling = this.$el.parent();

            // start cleaning up monitor views
            this.destroyMonitors();
            // unbind collection
            this.collection.off('remove', this.reinitializeDash, this);
            // unsubscribe from mediator channels
            this.destroySubscriptions();
            // clean up edit/add monitor view
            this.expandedMonitorView.destructor();
            this.addMonitorView.destructor();
            this.alertTimelineView.destructor();
            this.resetMonitorView.destructor();
            
            //
            this.monitors = [];
            this.categories = [];

            this.$el.empty();
            this.$el.remove();
            this.off();

            // place the containing element back in the page for later
            $parentSibling.prepend("<div class='monitor-panel container clearfix'>");
        }
    });

    return DashboardView;
});