Add event log search and filter capabilities

Change-Id: I9dc891e0f1e30abe488d401c57e1cf4f5656c3af
Signed-off-by: Iftekharul Islam <iislam@us.ibm.com>
diff --git a/app/server-health/controllers/log-controller.html b/app/server-health/controllers/log-controller.html
new file mode 100644
index 0000000..713b879
--- /dev/null
+++ b/app/server-health/controllers/log-controller.html
@@ -0,0 +1,70 @@
+<div id="event-log">
+    <div class="row column">
+        <h1>Event Log</h1>
+    </div>
+    <section class="row column">
+        <div class="page-header">
+            <h2 class="inline h4">All events generated by the system</h2>
+            <div class="event-log__timezone inline float-right">
+                <button class="dropdown__button" ng-click="timezone = timezone == true ? false : true;" toggle-flag="timezone"
+                        >User timezone: <span ng-show="tmz== 'EDT'">EDT (UTC-4)</span><span ng-show="tmz=='UTC'">UTC - 0</span>
+                </button>
+                <ul class="dropdown__list inline" ng-show="timezone">
+                    <li>
+                        <button ng-click="tmz = 'EDT'; timezone=false;">User timezone: EDT (UTC-4)</button>
+                    </li>
+                    <li>
+                        <button ng-click="tmz = 'UTC'; timezone=false;">UTC Timezone : UTC - 0</button>
+                    </li>
+                </ul>
+            </div>
+        </div>
+    </section>
+    <!-- Filters -->
+    <section class="row column">
+        <!-- search -->
+        <log-search-control></log-search-control>
+        <!-- filters -->
+        <log-filter></log-filter>
+    </section> <!-- end filter -->
+    <section id="event-log__events" class="row column">
+        <div id="event__actions-bar" class="row header__actions-bar">
+            <div class="column small-1 large-1 event-log__col-check">
+                <label class="control-check">
+                    <input type="checkbox" name="events__check-all" ng-model="all" ng-checked="(logs|filter:{selected: true}).length == logs.length"/>
+                    <div class="control__indicator"></div>
+                </label>
+            </div>
+            <div class="column small-11 large-11 end col-logged-events">
+                <!-- top bar confirmation - ADD ACTIVE CLASS TO DISPLAY-->
+                <div class="inline__confirm event__confirm" ng-class="{active: confirm}">
+                    <div class="inline__confirm-message">
+                        <p class="h3"><i></i>Are you sure you want to <strong class="ng-binding">delete {{selectedEvents.length}} logs</strong>?
+                        </p>
+                    </div>
+                    <div class="inline__confirm-buttons">
+                        <button class="btn-primary" ng-click="accept()">Yes</button>
+                        <button class="btn-primary" ng-click="confirm = false">No</button>
+                    </div>
+                </div>
+                <p class="inline"><span class="event__select-count">{{filteredLogs.length}}</span> Events are logged</p>
+                <!-- when logs are selected, this text changes to show how many logs are checked -->
+                <div class="event__actions">
+                    <button class="inline btn-delete" ng-show="selectedEvents.length || all" ng-click="confirm= ! confirm">
+                        <img class="event__icon" src="assets/images/icon-trashcan-white.svg" alt="">Delete
+                    </button>
+                    <button class="inline btn-resolve" ng-show="selectedEvents.length || all">
+                        <img class="event__icon" src="assets/images/icon-checklist-white.svg" alt="">Mark as resolved
+                    </button>
+                    <a ng-href="data:text/json;charset=utf-8,{{export_data}}" class="inline btn-export"  download="{{export_name}}" ng-show="selectedEvents.length || all">Export</a>
+                </div>
+            </div>
+        </div>
+        <log-event 
+        dir-paginate="event in (filteredLogs = (logs|filter:filterBySeverity|filter:filterByStatus|filter:filterByDate|filter:filterBySearchTerms))| itemsPerPage: itemsPerPage" 
+        event="event" 
+        tmz="tmz">
+        </log-event>
+    </section>
+    <dir-pagination-controls template-url="common/directives/dirPagination.tpl.html"></dir-pagination-controls> 
+</div> <!-- end event log -->
\ No newline at end of file
diff --git a/app/server-health/controllers/log-controller.js b/app/server-health/controllers/log-controller.js
new file mode 100644
index 0000000..59ebd01
--- /dev/null
+++ b/app/server-health/controllers/log-controller.js
@@ -0,0 +1,139 @@
+/**
+ * Controller for log
+ *
+ * @module app/serverHealth
+ * @exports logController
+ * @name logController
+ * @version 0.1.0
+ */
+
+window.angular && (function (angular) {
+    'use strict';
+    var logData = [], originalData = {};
+    angular
+        .module('app.serverHealth')
+        .controller('logController', [
+            '$scope', 
+            '$window', 
+            'APIUtils', 
+            'dataService',
+            'Constants',
+            function($scope, $window, APIUtils, dataService, Constants){
+                $scope.dataService = dataService;
+                $scope.logs = [];
+                $scope.tmz = 'EDT';
+                $scope.itemsPerPage = Constants.PAGINATION.LOG_ITEMS_PER_PAGE;
+                // priority buttons
+                $scope.selectedSeverity = {
+                    all: true,
+                    low: false,
+                    medium: false,
+                    high: false
+                };
+                $scope.selectedStatus = {
+                    all: true,
+                    resolved: false
+                };
+
+                $scope.customSearch = "";
+                $scope.searchItems = [];
+                $scope.selectedEvents = [];
+
+                $scope.loadLogs = function(){
+                    APIUtils.getLogs(function(data, originalData){
+                        logData = data;
+                        originalData = originalData;
+                        $scope.logs = data;
+                        $scope.originalData = originalData;
+                    });
+                };
+                $scope.jsonData = function(data){
+                    return JSON.stringify(data);
+                };
+
+                $scope.filterBySeverity = function(log){
+                    if($scope.selectedSeverity.all) return true;
+
+                    return( (log.severity_flags.low && $scope.selectedSeverity.low) ||
+                            (log.severity_flags.medium && $scope.selectedSeverity.medium) ||
+                            (log.severity_flags.high && $scope.selectedSeverity.high)
+                    );
+                }
+
+
+                $scope.filterByStatus = function(log){
+                    if ($scope.selectedStatus.all) return true;
+                    return( (log.Resolved && $scope.selectedStatus.resolved)||
+                            (!log.Resolved && !$scope.selectedStatus.resolved)
+                    );
+                }
+
+                $scope.filterByDate = function(log){
+                    if($scope.start_date && $scope.end_date){
+                        var date = new Date(log.Timestamp);
+                        return (date >= $scope.start_date &&
+                               date <= $scope.end_date );
+                    }else{
+                        return true;
+                    }
+                }
+
+                $scope.filterBySearchTerms = function(log){
+                    if(!$scope.searchItems.length) return true; 
+
+                    var flag = false;
+                    for(var i = 0, length = $scope.searchItems.length; i < length; i++){
+                        if(log.search_text.indexOf($scope.searchItems[i].toLowerCase()) == -1) return false;
+                    }
+                    return true;
+                }
+
+                $scope.addSearchItem = function(searchTerms){
+                    var terms = searchTerms.split(" ");
+                    terms.forEach(function(searchTerm){
+                        if($scope.searchItems.indexOf(searchTerm) == -1){
+                            $scope.searchItems.push(searchTerm);
+                        }  
+                    });
+                }
+
+                $scope.clearSearchItem = function(searchTerm){
+                    $scope.searchItems = [];
+                }
+
+                $scope.removeSearchItem = function(searchTerm){
+                    var termIndex = $scope.searchItems.indexOf(searchTerm);
+
+                    if(termIndex > -1){
+                        $scope.searchItems.splice(termIndex,1);
+                    }
+                }
+
+                $scope.$watch('all', function(){
+                    $scope.logs.forEach(function(item){
+                        item.selected = $scope.all;
+                    });
+                });
+
+                function updateExportData(){
+                    $scope.export_name = ($scope.selectedEvents.length == 1) ? $scope.selectedEvents[0].Id + ".json" : "export.json";
+                    var data = {};
+                    $scope.selectedEvents.forEach(function(item){
+                        data[item.data.key] = item.data.value;
+                    });
+                    $scope.export_data = JSON.stringify(data);
+                }
+
+                $scope.$watch('logs', function(){
+                    $scope.selectedEvents = $scope.logs.filter(function(item){
+                        return item.selected;
+                    });
+                    updateExportData();
+                }, true);
+
+                $scope.loadLogs();
+            }
+        ]
+    );
+
+})(angular);
diff --git a/app/server-health/index.js b/app/server-health/index.js
index 68a9a90..f64300b 100644
--- a/app/server-health/index.js
+++ b/app/server-health/index.js
@@ -58,6 +58,11 @@
                     'controller': 'diagnosticsController',
                     authenticated: true
                 })
+                .when('/server-health', {
+                    'templateUrl': 'server-health/controllers/log-controller.html',
+                    'controller': 'logController',
+                    authenticated: true
+                });
         }]);
 
 })(window.angular);
diff --git a/app/server-health/styles/index.scss b/app/server-health/styles/index.scss
index dd2e5b6..858d836 100644
--- a/app/server-health/styles/index.scss
+++ b/app/server-health/styles/index.scss
@@ -1,3 +1,4 @@
+@import "./log.scss";
 @import "./sensors.scss";
 @import "./inventory.scss";
 @import "./unit-id.scss";
diff --git a/app/server-health/styles/log.scss b/app/server-health/styles/log.scss
new file mode 100644
index 0000000..f1a5e8c
--- /dev/null
+++ b/app/server-health/styles/log.scss
@@ -0,0 +1,334 @@
+// Event Log SCSS
+#event-log {
+  .accord-trigger {
+    transform: rotate(90deg);
+    font-size: 1.5em;
+    color: lighten($darkgrey, 10%);
+    padding: .3em;
+    display: block;
+    margin: 0 auto;
+    transition: all .2s ease;
+    &:after {
+      content: '\276F'
+    }
+    &.active {
+      transform: rotate(-90deg);
+      color: $darkbg__accent;
+    }
+    &:focus {
+      outline: 0;
+      color: $darkbg__accent;
+    }
+  }
+
+  // Dropwdowns filter
+  .dropdown__date.row {
+    padding: .5em;
+  }
+
+  //Timezone select
+  .event-log__timezone,
+  .event-log__timezone button {
+    position: relative;
+    text-transform: uppercase;
+    color: $lightbg__primary;
+    font-size: .9em;
+    font-weight: 700;
+    border: 0;
+  }
+  .content__search {
+    float: none;
+    @include mediaQuery(x-large) {
+      max-width: 61%;
+      @include fastTransition-all;
+    }
+  }
+}
+
+#event-filter {
+  .filter-label {
+    color: darken($lightgrey, 10%);
+    text-transform: uppercase;
+    font-weight: 700;
+    font-size: .75em;
+    margin-bottom: 3px;
+  }
+  .event__severity-filter {
+    margin-right: 2em;
+    margin-bottom: 1em;
+    button {
+      margin: 3px -3px;
+      padding: .6em 2em;
+      min-height: 2.1em;
+      font-size: .9em;
+      font-weight: 700;
+      border-radius: 0;
+      &.first,
+      &.last {
+        border-radius: 3px;
+      }
+      &.last {
+        border-left: 0;
+      }
+    }
+    .btn-primary {
+      border: 2px solid $primebtn__bg;
+    }
+
+  }
+  .event__date-filter {
+    margin-right: 1.5em;
+    color: $darkgrey;
+    input {
+      width: 170px;
+      height: 2.1rem;
+    }
+    label {
+      height: 0;
+      text-indent: -999px;
+    }
+  }
+}
+
+.event-log__filters {
+  position: relative;
+  padding-bottom: .5em;
+  padding-top: .5em;
+
+  @media (min-width: 1333px) {
+    float: right;
+    display: inline-block;
+  }
+}
+
+// Event Log Events
+#event-log__events.row {
+  padding-left: 0;
+  padding-right: 0;
+}
+#event-log__events {
+  display: block;
+  margin-top: 1.6em;
+  position: relative;
+  overflow: hidden;
+  .header__actions-bar {
+    .btn-export,
+    .btn-delete,
+    .btn-resolve {
+      color: $white;
+    }
+  }
+  .inline__confirm {
+    height: auto;
+    margin-left: 0;
+    left: 0;
+    padding: 1em 2em 1em 2em
+  }
+  .inline__confirm-message {
+    margin-top: 5px;
+    margin-bottom: -10px;
+  }
+  .inline__confirm-buttons .btn-primary {
+    padding: .5em 2em;
+    min-height: 25px;
+    margin-top: .5em;
+    @include mediaQuery(medium) {
+      margin-top: 0;
+    }
+  }
+
+  .event-log__col-check {
+    max-width: 60px;
+    text-align: center;
+  }
+  .col-logged-events {
+    span {
+      display: inline-block;
+      font-weight: 700;
+      margin-right: .3em;
+    }
+  }
+
+  //Export log
+  .btn-export, .btn-meta-copy,
+  .btn-delete,
+  .btn-resolve {
+    text-transform: capitalize;
+    color: black;
+    font-size: .9em;
+    font-weight: 700;
+    position: relative;
+    padding: 0 0 1em 2em;
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+  .btn-export {
+    margin-top: 7px;
+    padding-bottom: 0;
+  }
+  .btn-export:before {
+    content: '\21E5';
+    position: absolute;
+    font-size: 1.7em;
+    vertical-align: middle;
+    transform: rotate(90deg);
+    display: inline-block;
+    left: 2px;
+    top: -5px;
+  }
+
+  .btn-meta-copy,
+  .btn-delete,
+  .btn-resolve {
+    margin-left: 5px;
+    padding: .5em .5em;
+  }
+
+  // Single event log card
+  .event-log__single-event {
+    border-top: 1px solid $lightgrey;
+    padding: 1em 1em 1em .7em;
+  }
+
+  .event-log__event-info {
+    &:hover {
+      cursor: pointer;
+    }
+  }
+
+  //Event priorities
+  .event__priority {
+    color: $white;
+    font-size: .8em;
+    text-transform: uppercase;
+    padding: 2px 2em;
+    font-weight: 700;
+    line-height: inherit;
+    min-width: 103px;
+    text-align: center;
+
+    &.high-priority {
+      background: $error-color;
+
+    }
+    &.med-priority {
+      background: $alert__warning;
+
+    }
+    &.low-priority {
+      background: $lightbg__primary;
+    }
+    &.event-resolved {
+      background: $purple;
+      padding: 2px 1em;
+    }
+  }
+
+  //Event Severity
+  .event__severity {
+    font-size: .7em;
+    text-transform: uppercase;
+    color: $darkgrey;
+    font-weight: 700;
+    margin: 0 1em;
+    min-width: 103px;
+  }
+
+  //Event description
+  .event__description {
+    font-weight: 400;
+  }
+
+  //Event ID
+  .event__id {
+    @include fontCourierBold;
+    font-size: .9em;
+    color: $darkgrey;
+  }
+
+  .event__timestamp {
+    @include fontCourierBold;
+    font-size: .9em;
+    color: #999999;
+    margin-left: 1.2em;
+  }
+
+  // Event metadata row
+  .event__metadata-row {
+    max-height: 0;
+    overflow: hidden;
+    -webkit-transition: 0.5s linear max-height;
+    transition: 0.5s linear max-height;
+    &.active {
+      max-height: 1000px; //max-height used to allow flexible height of content and still allow transition
+      @include mediaQuery(small) {
+        max-height: 1000px;
+      }
+      @include mediaQuery(medium) {
+        max-height: 1000px;
+      }
+    }
+  }
+
+  //Event metadata
+  .event__metadata {
+    height: 200px;
+    overflow-y: scroll;
+    border: 1px solid $lightgrey;
+    padding: .5em .5em 0;
+    background: $white;
+    display: block;
+    margin-bottom: 1.5em;
+    &::-webkit-scrollbar {
+      -webkit-appearance: none;
+      width: 7px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      border-radius: 4px;
+      background-color: rgba(0, 0, 0, .5);
+      -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5);
+    }
+  }
+
+  //Event Related Items
+  .event__related {
+    width: 100%;
+  }
+  .event__related-label {
+    font-weight: 700;
+    margin-right: 1.2em;
+    padding-top: .3em;
+  }
+
+  .event__related-item {
+    margin-right: 1em;
+    padding-top: .3em;
+    display: inline-block;
+    float: left;
+    clear: both;
+  }
+  .event__actions {
+    margin-left: -1em;
+    //margin-bottom: 1em;
+    @include mediaQuery(medium) {
+      float: right;
+    }
+  }
+
+  .event__icon {
+    width: 20px;
+    height: 20px;
+    font-weight: normal;
+    margin-right: .5em;
+    margin-top: -3px;
+    display: inline-block;
+  }
+
+}
+
+//end event-log__events
+
+
+