[WIP] CORD-686 Added pagination to table

Change-Id: I26c57cdf9759363e2bce2fd5333f45d63694278b
diff --git a/conf/webpack-dist.conf.js b/conf/webpack-dist.conf.js
index f322882..d0300a6 100644
--- a/conf/webpack-dist.conf.js
+++ b/conf/webpack-dist.conf.js
@@ -24,7 +24,7 @@
         test: /\.(css|scss)$/,
         loaders: ExtractTextPlugin.extract({
           fallbackLoader: 'style',
-          loader: 'css?minimize!sass!postcss'
+          loader: 'css?minimize!resolve-url-loader!sass?sourceMap!postcss'
         })
       },
       {
diff --git a/conf/webpack.conf.js b/conf/webpack.conf.js
index 544b2c5..8bef911 100644
--- a/conf/webpack.conf.js
+++ b/conf/webpack.conf.js
@@ -30,7 +30,8 @@
         loaders: [
           'style',
           'css',
-          'sass',
+          'resolve-url-loader',
+          'sass?sourceMap',
           'postcss'
         ]
       },
diff --git a/package.json b/package.json
index e80a510..d9bc915 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
     "angular-ui-bootstrap": "^2.3.1",
     "angular-ui-router": "1.0.0-beta.1",
     "bootstrap": "^3.3.7",
+    "bootstrap-sass": "^3.3.7",
     "jquery": "^3.1.1",
     "lodash": "^4.17.2",
     "oclazyload": "^1.0.9",
@@ -70,6 +71,7 @@
     "node-sass": "^3.4.2",
     "phantomjs-prebuilt": "^2.1.6",
     "postcss-loader": "^0.8.0",
+    "resolve-url-loader": "^1.6.1",
     "sass-loader": "^3.1.2",
     "style-loader": "^0.13.0",
     "ts-loader": "^0.8.2",
diff --git a/src/app/core/index.ts b/src/app/core/index.ts
index a31aa96..318ffeb 100644
--- a/src/app/core/index.ts
+++ b/src/app/core/index.ts
@@ -21,6 +21,8 @@
 import {XosComponentInjector} from './services/helpers/component-injector.helpers';
 import {XosKeyboardShortcut} from './services/keyboard-shortcut';
 import {xosKeyBindingPanel} from './key-binding/key-binding-panel';
+import {xosPagination} from './pagination/pagination';
+import {PaginationFilter} from './pagination/pagination.filter';
 
 export const xosCore = 'xosCore';
 
@@ -45,10 +47,12 @@
   .component('xosFooter', xosFooter)
   .component('xosNav', xosNav)
   .component('xosLogin', xosLogin)
+  .component('xosPagination', xosPagination)
   .component('xosTable', xosTable)
   .component('xosForm', xosForm)
   .component('xosField', xosField)
   .component('xosAlert', xosAlert)
   .component('xosValidation', xosValidation)
   .component('xosSidePanel', xosSidePanel)
-  .component('xosKeyBindingPanel', xosKeyBindingPanel);
+  .component('xosKeyBindingPanel', xosKeyBindingPanel)
+  .filter('pagination', PaginationFilter);
diff --git a/src/app/core/pagination/pagination.filter.ts b/src/app/core/pagination/pagination.filter.ts
new file mode 100644
index 0000000..2a561c9
--- /dev/null
+++ b/src/app/core/pagination/pagination.filter.ts
@@ -0,0 +1,9 @@
+export function PaginationFilter() {
+  return function(input: any[], start: string) {
+    if (!input || !angular.isArray(input)) {
+      return input;
+    }
+    let position: number = parseInt(start, 10);
+    return input.slice(position);
+  };
+}
diff --git a/src/app/core/pagination/pagination.html b/src/app/core/pagination/pagination.html
new file mode 100644
index 0000000..3bcdb51
--- /dev/null
+++ b/src/app/core/pagination/pagination.html
@@ -0,0 +1,21 @@
+<div class="row" ng-if="vm.pageList.length > 1">
+    <div class="col-xs-12 text-center">
+        <ul class="pagination">
+            <li ng-click="vm.goToPage(vm.currentPage - 1)"
+                ng-class="{disabled: vm.currentPage == 0}">
+                <a href="" aria-label="Previous">
+                    <span aria-hidden="true">&laquo;</span>
+                </a>
+            </li>
+            <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">
+                <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>
+            </li>
+            <li ng-click="vm.goToPage(vm.currentPage + 1)"
+                ng-class="{disabled: vm.currentPage == vm.pages - 1}">
+                <a href="" aria-label="Next">
+                    <span aria-hidden="true">&raquo;</span>
+                </a>
+            </li>
+        </ul>
+    </div>
+</div>
diff --git a/src/app/core/pagination/pagination.ts b/src/app/core/pagination/pagination.ts
new file mode 100644
index 0000000..997123c
--- /dev/null
+++ b/src/app/core/pagination/pagination.ts
@@ -0,0 +1,54 @@
+class XosPaginationCtrl {
+  $inject = ['$onInit', '$scope'];
+
+  public pageSize: number;
+  public totalElements: number;
+  public change: any; // fn
+  public currentPage: number;
+  public pages: number;
+  public pageList: number[];
+
+  constructor (
+    private $scope: ng.IScope
+  ) {
+  }
+
+  $onInit() {
+    this.currentPage = 0;
+
+    // watch for data changes
+    this.$scope.$watch(() => this.totalElements, () => {
+      if (this.totalElements) {
+        this.pages = Math.ceil(this.totalElements / this.pageSize);
+        this.pageList = this.createPages(this.pages);
+      }
+    });
+  }
+
+  public goToPage = (n) => {
+    if (n < 0 || n === this.pages) {
+      return;
+    }
+    this.currentPage = n;
+    this.change(n);
+  };
+
+  private createPages = (pages) => {
+    let arr = [];
+    for (let i = 0; i < pages; i++) {
+      arr.push(i);
+    }
+    return arr;
+  };
+}
+
+export const xosPagination: angular.IComponentOptions = {
+  template: require('./pagination.html'),
+  controllerAs: 'vm',
+  controller: XosPaginationCtrl,
+  bindings: {
+    pageSize: '=',
+    totalElements: '=',
+    change: '='
+  }
+};
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index b8ff208..28a16a3 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -33,7 +33,7 @@
   stateWithParamsForJs(name: string, model: any): any;
 }
 
-export class ConfigHelpers {
+export class ConfigHelpers implements IXosConfigHelpersService {
   static $inject = ['$state', 'toastr', 'AuthService', 'ModelStore'];
 
   excluded_fields = [
@@ -96,6 +96,9 @@
       columns: this.modelFieldsToColumnsCfg(model.fields, model.name),
       filter: 'fulltext',
       order: {field: 'id', reverse: false},
+      pagination: {
+        pageSize: 10
+      },
       actions: [
         {
           label: 'delete',
@@ -135,24 +138,23 @@
 
       if (f.name === 'id' || f.name === 'name') {
         col.link = item => this.stateWithParams(modelName, item);
-        // NOTE can we find a better method to generalize the route?
-        // col.link = item => `#/core${baseUrl.replace(':id?', item.id)}`;
       }
 
       // if the field identify a relation, create a link
       if (f.relation && f.relation.type === 'many_to_one') {
-        // TODO read the related model name and replace the value, use the xosTable format method?
         col.type = 'custom';
         col.formatter = item => {
           this.populateRelated(item, item[f.name], f);
           return item[f.name];
         };
         col.link = item => this.stateWithParams(f.relation.model, item);
-        // col.link = item => `#${this.urlFromCoreModel(f.relation.model)}/${item[f.name]}`;
       }
 
       if (f.name === 'backend_status') {
         col.type = 'icon';
+        col.hover = (item) => {
+          return item[f.name];
+        };
         col.formatter = (item) => {
           if (item.backend_status.indexOf('1') > -1) {
             return 'check';
diff --git a/src/app/core/table/table.html b/src/app/core/table/table.html
index 788e84c..d7df1db 100644
--- a/src/app/core/table/table.html
+++ b/src/app/core/table/table.html
@@ -48,7 +48,7 @@
             </tr>
         </tbody>
         <tbody>
-        <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse track by $index">
+        <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">
             <td ng-repeat="col in vm.columns" xos-link-wrapper>
                 <span ng-if="!col.type || col.type === 'text'">{{item[col.prop]}}</span>
                 <span ng-if="col.type === 'boolean'">
@@ -77,7 +77,12 @@
                 <span ng-if="col.type === 'icon'">
                     <i class="fa fa-{{col.formatter(item)}}">
                     </i>
-                  </span>
+                </span>
+                <div class="xos-table-hover" ng-if="col.hover">
+                    <div class="alert alert-info">
+                        {{col.hover(item)}}
+                    </div>
+                </div>
             </td>
             <td ng-if="vm.config.actions">
                 <a href=""
@@ -92,6 +97,12 @@
         </tr>
         </tbody>
     </table>
+    <xos-pagination
+            ng-if="vm.config.pagination"
+            page-size="vm.config.pagination.pageSize"
+            total-elements="vm.data.length"
+            change="vm.goToPage">
+    </xos-pagination>
     </div>
 <!--</div>-->
 <!--<div ng-show="(vm.data.length == 0 || !vm.data) && vm.loader == false">-->
diff --git a/src/app/core/table/table.scss b/src/app/core/table/table.scss
index a2af05e..9922026 100644
--- a/src/app/core/table/table.scss
+++ b/src/app/core/table/table.scss
@@ -1,3 +1,6 @@
+@import './../../style/vars.scss';
+@import '../../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap/variables';
+
 xos-table {
   .row + .table-responsive {
     margin-top: 10px;
@@ -5,40 +8,29 @@
 }
 
 table {
-  border-collapse: collapse !important;
-  background: darken(grey, 20);
-  border: 1px solid darken(grey, 35);
+  border: 1px solid $background-dark-color;
 
   td, th {
+    position: relative;
   }
-  > tbody > tr > th,
-  > tfoot > tr > th,
-  > thead > tr > td,
-  > tbody > tr > td,
-  > tfoot > tr > td {
-    padding: 8px;
-    line-height: 1.42857143;
-    vertical-align: top;
-    border-top: 1px solid #ddd;
+
+  .xos-table-hover {
+    position: absolute;
+    top: -$padding-large-vertical;
+    left: $padding-large-horizontal;
+    opacity: 0;
+    transition: all .5s;
+    z-index: -1;
+    width: 250px;
+
+    &:hover {
+      cursor: pointer;
+    }
   }
-  > thead > tr > th {
-    vertical-align: bottom;
-    border-bottom: 2px solid #ddd;
-    text-align: left;
-    padding: 8px;
+
+  td:hover .xos-table-hover {
+    opacity: 1;
+    z-index: 2;
   }
-  > caption + thead > tr:first-child > th,
-  > colgroup + thead > tr:first-child > th,
-  > thead:first-child > tr:first-child > th,
-  > caption + thead > tr:first-child > td,
-  > colgroup + thead > tr:first-child > td,
-  > thead:first-child > tr:first-child > td {
-    border-top: 0;
-  }
-  > tbody + tbody {
-    border-top: 2px solid #ddd;
-  }
-  .table .table {
-    background-color: #fff;
-  }
+
 }
\ No newline at end of file
diff --git a/src/app/core/table/table.ts b/src/app/core/table/table.ts
index 1ed258d..2c0b272 100644
--- a/src/app/core/table/table.ts
+++ b/src/app/core/table/table.ts
@@ -19,6 +19,7 @@
   type?: string; // understand why enum does not work
   formatter?(item: any): string;
   link?(item: any): string;
+  hover?(item: any): string;
 }
 
 interface IXosTableCgfOrder {
@@ -28,6 +29,9 @@
 
 export interface IXosTableCfg {
   columns: any[];
+  pagination?: {
+    pageSize: number;
+  };
   order?: IXosTableCgfOrder;
   filter?: string;
   actions?: any[]; // TODO create interface
@@ -41,6 +45,7 @@
   public reverse: boolean;
   public classes: string;
   private config: IXosTableCfg;
+  private currentPage: number;
 
 
   $onInit() {
@@ -94,9 +99,28 @@
       });
     }
 
+    // if an hover property is passed,
+    // it should be a function
+    let hoverColumns = _.filter(this.config.columns, col => angular.isDefined(col.hover));
+    if (angular.isArray(hoverColumns) && hoverColumns.length > 0) {
+      _.forEach(hoverColumns, (col) => {
+        if (!angular.isFunction(col.hover)) {
+          throw new Error('[xosTable] The hover property should be a function.');
+        }
+      });
+    }
+
+    if (this.config.pagination) {
+      this.currentPage = 0;
+    }
+
     this.columns = this.config.columns;
 
   }
+
+  public goToPage = (n) => {
+    this.currentPage = n;
+  };
 }
 
 export const xosTable: angular.IComponentOptions = {
diff --git a/src/app/style/style.scss b/src/app/style/style.scss
index 2a96d7c..3151e6c 100644
--- a/src/app/style/style.scss
+++ b/src/app/style/style.scss
@@ -2004,45 +2004,45 @@
 border-color:#484c5a !important
 }
 
-.alert-success{
-color:#ffffff;
-border-color:#1bbf89;
-background-color:#1bbf89
-}
-
-.alert-success .alert-link{
-color:#1bbf89
-}
-
-.alert-warning{
-color:#ffffff;
-border-color:#f7af3e;
-background-color:#f7af3e
-}
-
-.alert-warning .alert-link{
-color:#f7af3e
-}
-
-.alert-info{
-color:#ffffff;
-border-color:#56C0E0;
-background-color:#56C0E0
-}
-
-.alert-info .alert-link{
-color:#56C0E0
-}
-
-.alert-danger{
-color:#ffffff;
-border-color:#DB524B;
-background-color:#DB524B
-}
-
-.alert-danger .alert-link{
-color:#DB524B
-}
+//.alert-success{
+//color:#ffffff;
+//border-color:#1bbf89;
+//background-color:#1bbf89
+//}
+//
+//.alert-success .alert-link{
+//color:#1bbf89
+//}
+//
+//.alert-warning{
+//color:#ffffff;
+//border-color:#f7af3e;
+//background-color:#f7af3e
+//}
+//
+//.alert-warning .alert-link{
+//color:#f7af3e
+//}
+//
+//.alert-info{
+//color:#ffffff;
+//border-color:#56C0E0;
+//background-color:#56C0E0
+//}
+//
+//.alert-info .alert-link{
+//color:#56C0E0
+//}
+//
+//.alert-danger{
+//color:#ffffff;
+//border-color:#DB524B;
+//background-color:#DB524B
+//}
+//
+//.alert-danger .alert-link{
+//color:#DB524B
+//}
 
 .toast-success{
 color:#ffffff;
diff --git a/src/app/template/index.ts b/src/app/template/index.ts
index 605b840..11557f9 100644
--- a/src/app/template/index.ts
+++ b/src/app/template/index.ts
@@ -6,11 +6,10 @@
 import 'angular-ui-bootstrap';
 import 'angular-animate';
 import 'angular-toastr';
-import 'bootstrap/dist/css/bootstrap.css';
 import 'angular-toastr/dist/angular-toastr.min.css';
-import '../style/style.scss';
-import '../style/stroke-icons/style.css';
-import '../style/pe-icons/pe-icon-7-stroke.css';
+// import '../style/style.scss';
+// import '../style/stroke-icons/style.css';
+// import '../style/pe-icons/pe-icon-7-stroke.css';
 import {capitalize} from './filters/capitalize';
 
 
diff --git a/src/index.scss b/src/index.scss
index 4cc14b8..2d28146 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -1,3 +1,9 @@
+@import './app/style/vars';
+@import '../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss';
+@import './app/style/style.scss';
+@import './app/style/stroke-icons/style.css';
+@import './app/style/pe-icons/pe-icon-7-stroke.css';
+
 html, body.blank {
   height: 100%;
   min-height: 100%;
@@ -5,4 +11,27 @@
 
 .content > div {
   opacity: 1 !important;
-}
\ No newline at end of file
+}
+
+// BOOTSTRAP OVERRIDES
+// TODO Clean app/style/style.scss
+
+// Alternate styles
+//
+// Generate contextual modifier classes for colorizing the alert.
+
+.alert-success {
+  @include alert-variant($background-dark-color, $alert-success-text, $alert-success-text);
+}
+
+.alert-info {
+  @include alert-variant($background-dark-color, $alert-info-border, $alert-info-border);
+}
+
+.alert-warning {
+  @include alert-variant($background-dark-color, $alert-warning-border, $alert-warning-border);
+}
+
+.alert-danger {
+  @include alert-variant($background-dark-color, $alert-danger-text, $alert-danger-border);
+}