Created first CRUD view
Change-Id: I3e7f3f36896921cce671c6a53e0155de9165eeb3
diff --git a/src/app/config/app.config.ts b/src/app/config/app.config.ts
index 7b6fa12..2357f77 100644
--- a/src/app/config/app.config.ts
+++ b/src/app/config/app.config.ts
@@ -6,6 +6,6 @@
 }
 
 export const AppConfig: IAppConfig = {
-    apiEndpoint: 'http://xos.dev:3000/api',
-    websocketClient: 'http://xos.dev:3000'
+    apiEndpoint: 'http://xos-rest-gw:3000/api',
+    websocketClient: 'http://xos-rest-gw:3000'
 };
diff --git a/src/app/core/index.ts b/src/app/core/index.ts
index d0875b6..ee73f99 100644
--- a/src/app/core/index.ts
+++ b/src/app/core/index.ts
@@ -3,6 +3,7 @@
 import {xosNav} from './nav/nav';
 import routesConfig from './routes';
 import {xosLogin} from './login/login';
+import {xosTable} from './table/table';
 
 export const xosCore = 'xosCore';
 
@@ -12,4 +13,5 @@
   .component('xosHeader', xosHeader)
   .component('xosFooter', xosFooter)
   .component('xosNav', xosNav)
-  .component('xosLogin', xosLogin);
+  .component('xosLogin', xosLogin)
+  .component('xosTable', xosTable);
diff --git a/src/app/core/login/login.ts b/src/app/core/login/login.ts
index af46619..51dc7c9 100644
--- a/src/app/core/login/login.ts
+++ b/src/app/core/login/login.ts
@@ -16,8 +16,7 @@
       password: password
     })
       .then(res => {
-        console.log(res);
-        this.$state.go('app');
+        this.$state.go('xos.dashboard');
       })
       .catch(e => console.error);
   }
diff --git a/src/app/core/nav/nav.ts b/src/app/core/nav/nav.ts
index d90df10..82d9d64 100644
--- a/src/app/core/nav/nav.ts
+++ b/src/app/core/nav/nav.ts
@@ -1,6 +1,6 @@
 import './nav.scss';
 
-interface INavItem {
+export interface INavItem {
   label: string;
   state: string;
 }
diff --git a/src/app/core/table/table.html b/src/app/core/table/table.html
new file mode 100644
index 0000000..58a46e0
--- /dev/null
+++ b/src/app/core/table/table.html
@@ -0,0 +1,101 @@
+<!--<div ng-show="vm.data.length > 0 && vm.loader == false">-->
+    <div class="row" ng-if="vm.config.filter == 'fulltext'">
+        <div class="col-xs-12">
+            <input
+                    class="form-control"
+                    placeholder="Type to search.."
+                    type="text"
+                    ng-model="vm.query"/>
+        </div>
+    </div>
+    <table ng-class="vm.classes">
+        <thead>
+        <tr>
+            <th ng-repeat="col in vm.columns">
+                {{col.label}}
+                <span ng-if="vm.config.order">
+                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">
+                      <i class="glyphicon glyphicon-chevron-up"></i>
+                    </a>
+                    <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">
+                      <i class="glyphicon glyphicon-chevron-down"></i>
+                    </a>
+                  </span>
+            </th>
+            <th ng-if="vm.config.actions">Actions:</th>
+        </tr>
+        </thead>
+        <tbody ng-if="vm.config.filter == 'field'">
+        <tr>
+            <td ng-repeat="col in vm.columns">
+                <input
+                        ng-if="col.type !== 'boolean' && col.type !== 'array' && col.type !== 'object' && col.type !== 'custom'"
+                        class="form-control"
+                        placeholder="Type to search by {{col.label}}"
+                        type="text"
+                        ng-model="vm.query[col.prop]"/>
+                <select
+                        ng-if="col.type === 'boolean'"
+                        class="form-control"
+                        ng-model="vm.query[col.prop]">
+                    <option value="">-</option>
+                    <option value="true">True</option>
+                    <option value="false">False</option>
+                </select>
+            </td>
+            <td ng-if="vm.config.actions"></td>
+        </tr>
+        </tbody>
+        <tbody>
+        <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse track by $index">
+            <td ng-repeat="col in vm.columns">
+                <span ng-if="!col.type || col.type === 'text'">{{item[col.prop]}}</span>
+                <span ng-if="col.type === 'boolean'">
+                    <i class="glyphicon"
+                       ng-class="{'glyphicon-ok': item[col.prop], 'glyphicon-remove': !item[col.prop]}">
+                    </i>
+                  </span>
+                <span ng-if="col.type === 'date'">
+                    {{item[col.prop] | date:'H:mm MMM d, yyyy'}}
+                  </span>
+                <span ng-if="col.type === 'array'">
+                    {{item[col.prop] | arrayToList}}
+                  </span>
+                <span ng-if="col.type === 'object'">
+                    <dl class="dl-horizontal">
+                      <span ng-repeat="(k,v) in item[col.prop]">
+                        <dt>{{k}}</dt>
+                        <dd>{{v}}</dd>
+                      </span>
+                    </dl>
+                  </span>
+                <span ng-if="col.type === 'custom'">
+                    {{col.formatter(item)}}
+                  </span>
+                <span ng-if="col.type === 'icon'">
+                    <i class="glyphicon glyphicon-{{col.formatter(item)}}">
+                    </i>
+                  </span>
+            </td>
+            <td ng-if="vm.config.actions">
+                <a href=""
+                   ng-repeat="action in vm.config.actions"
+                   ng-click="action.cb(item)"
+                   title="{{action.label}}">
+                    <i
+                            class="glyphicon glyphicon-{{action.icon}}"
+                            style="color: {{action.color}};"></i>
+                </a>
+            </td>
+        </tr>
+        </tbody>
+    </table>
+<!--</div>-->
+<!--<div ng-show="(vm.data.length == 0 || !vm.data) && vm.loader == false">-->
+    <!--<xos-alert config="{type: 'info'}">-->
+        <!--No data to show.-->
+    <!--</xos-alert>-->
+<!--</div>-->
+<!--<div ng-show="vm.loader == true">-->
+    <!--<div class="loader"></div>-->
+<!--</div>-->
\ No newline at end of file
diff --git a/src/app/core/table/table.ts b/src/app/core/table/table.ts
new file mode 100644
index 0000000..43360c6
--- /dev/null
+++ b/src/app/core/table/table.ts
@@ -0,0 +1,88 @@
+// TODO fininsh to import all methods from https://github.com/opencord/ng-xos-lib/blob/master/src/ui_components/dumbComponents/table/table.component.js
+// TODO import tests
+
+import * as _ from 'lodash';
+
+interface IXosTableCgfOrder {
+  reverse: boolean;
+  field: string;
+}
+
+export interface IXosTableCfg {
+  columns: any[];
+  order: IXosTableCgfOrder; // | boolean;
+}
+
+class TableCtrl {
+  $inject = ['$onInit'];
+
+  public columns: any[];
+  public orderBy: string;
+  public reverse: boolean;
+
+  private data: any[];
+  private config: IXosTableCfg;
+
+
+  $onInit() {
+    if (!this.config) {
+      throw new Error('[xosTable] Please provide a configuration via the "config" attribute');
+    }
+
+    if (!this.config.columns) {
+      throw new Error('[xosTable] Please provide a columns list in the configuration');
+    }
+
+    // handle default ordering
+    if (this.config.order && angular.isObject(this.config.order)){
+      this.reverse = this.config.order.reverse || false;
+      this.orderBy = this.config.order.field || 'id';
+    }
+
+    // if columns with type 'custom' are provided
+    // check that a custom formatte3 is provided too
+    let customCols = _.filter(this.config.columns, {type: 'custom'});
+    if (angular.isArray(customCols) && customCols.length > 0) {
+      _.forEach(customCols, (col) => {
+        if (!col.formatter || !angular.isFunction(col.formatter)) {
+          throw new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.');
+        }
+      });
+    }
+
+    // if columns with type 'icon' are provided
+    // check that a custom formatte3 is provided too
+    let iconCols = _.filter(this.config.columns, {type: 'icon'});
+    if (angular.isArray(iconCols) && iconCols.length > 0) {
+      _.forEach(iconCols, (col) => {
+        if (!col.formatter || !angular.isFunction(col.formatter)) {
+          throw new Error('[xosTable] You have provided an icon field type, a formatter function should provided too.');
+        }
+      });
+    }
+
+    // if a link property is passed,
+    // it should be a function
+    let linkedColumns = _.filter(this.config.columns, col => angular.isDefined(col.link));
+    if (angular.isArray(linkedColumns) && linkedColumns.length > 0) {
+      _.forEach(linkedColumns, (col) => {
+        if (!angular.isFunction(col.link)) {
+          throw new Error('[xosTable] The link property should be a function.');
+        }
+      });
+    }
+
+    this.columns = this.config.columns;
+
+  }
+}
+
+export const xosTable: angular.IComponentOptions = {
+  template: require('./table.html'),
+  controllerAs: 'vm',
+  controller: TableCtrl,
+  bindings: {
+    data: '=',
+    config: '='
+  }
+};
diff --git a/src/app/views/crud/crud.html b/src/app/views/crud/crud.html
new file mode 100644
index 0000000..fbbec88
--- /dev/null
+++ b/src/app/views/crud/crud.html
@@ -0,0 +1,2 @@
+<h1>{{vm.title}}</h1>
+<xos-table config="vm.tableCfg" data="vm.tableData"></xos-table>
\ No newline at end of file
diff --git a/src/app/views/crud/crud.ts b/src/app/views/crud/crud.ts
new file mode 100644
index 0000000..b35c104
--- /dev/null
+++ b/src/app/views/crud/crud.ts
@@ -0,0 +1,42 @@
+import {IXosResourceService} from '../../rest/slices.rest';
+import {IXosTableCfg} from '../../core/table/table';
+export interface IXosCrudData {
+  title: string;
+  resource: string;
+  xosTableCfg: IXosTableCfg;
+}
+
+class CrudController {
+  static $inject = ['$state', '$injector'];
+
+  public data: IXosCrudData;
+  public tableCfg: IXosTableCfg;
+  public title: string;
+  public resourceName: string;
+  public resource: ng.resource.IResourceClass<ng.resource.IResource<any>>;
+  public tableData: any[];
+
+  constructor(
+    private $state: angular.ui.IStateService,
+    private $injector: angular.Injectable<any>
+  ) {
+    this.data = this.$state.current.data;
+    console.log('xosCrud', this.data);
+    this.tableCfg = this.data.xosTableCfg;
+    this.title = this.data.title;
+    this.resourceName = this.data.resource;
+    this.resource = this.$injector.get(this.resourceName).getResource();
+
+    this.resource
+      .query().$promise
+      .then(res => {
+        this.tableData = res;
+      });
+  }
+}
+
+export const xosCrud: angular.IComponentOptions = {
+  template: require('./crud.html'),
+  controllerAs: 'vm',
+  controller: CrudController
+};
diff --git a/src/app/views/index.ts b/src/app/views/index.ts
new file mode 100644
index 0000000..495e2a4
--- /dev/null
+++ b/src/app/views/index.ts
@@ -0,0 +1,8 @@
+import {xosCore} from '../core/index';
+import {xosCrud} from './crud/crud';
+
+export const xosViews = 'xosViews';
+
+angular
+  .module('xosViews', [xosCore])
+  .component('xosCrud', xosCrud);
diff --git a/src/interceptors.ts b/src/interceptors.ts
index bd4cba3..0a1cd08 100644
--- a/src/interceptors.ts
+++ b/src/interceptors.ts
@@ -1,5 +1,7 @@
 /// <reference path="../typings/index.d.ts" />
 
+// TODO handle backend failure
+
 export function interceptorConfig($httpProvider: angular.IHttpProvider, $resourceProvider: angular.resource.IResourceServiceProvider) {
   $httpProvider.interceptors.push('UserStatusInterceptor');
   $httpProvider.interceptors.push('CredentialsInterceptor');
diff --git a/src/routes.ts b/src/routes.ts
index 3c8ae3d..8290e9a 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -31,7 +31,16 @@
         title: 'Slices',
         resource: 'SlicesRest',
         xosTableCfg: {
-          columns: ['name', 'default_isolation']
+          columns: [
+            {
+              label: 'Name',
+              prop: 'name'
+            },
+            {
+              label: 'Default Isolation',
+              prop: 'default_isolation'
+            }
+          ]
         }
       }
     })