[SECARSP-113][SECARSP-114][SECARSP-126] Communication service, registered devices...
authorArtem Motchanyi <a.motchanyi@samsung.com>
Thu, 1 Mar 2018 13:02:20 +0000 (15:02 +0200)
committerDmytro Lomtiev <d.lomtev@samsung.com>
Fri, 16 Mar 2018 14:17:19 +0000 (16:17 +0200)
Change-Id: I4c2c3f258bacdd1572437fd09f9ce19601d4ed85

39 files changed:
dashboard/src/app/@core/auth/auth.service.ts
dashboard/src/app/@theme/components/footer/footer.component.ts
dashboard/src/app/@theme/components/header/header.component.ts
dashboard/src/app/@theme/components/search-input/search-input.component.ts
dashboard/src/app/@theme/layout/main.layout.html
dashboard/src/app/@theme/layout/main.layout.ts
dashboard/src/app/@theme/pipes/chunks.pipe.ts [new file with mode: 0644]
dashboard/src/app/@theme/pipes/index.ts
dashboard/src/app/@theme/theme.module.ts
dashboard/src/app/app.component.ts
dashboard/src/app/models/device.ts [new file with mode: 0644]
dashboard/src/app/models/index.ts [new file with mode: 0644]
dashboard/src/app/models/update-device.ts [new file with mode: 0644]
dashboard/src/app/models/update-devices.ts [new file with mode: 0644]
dashboard/src/app/pages/dashboard/dashboard.component.ts
dashboard/src/app/pages/device/device.component.html [new file with mode: 0644]
dashboard/src/app/pages/device/device.component.scss [new file with mode: 0644]
dashboard/src/app/pages/device/device.component.ts [new file with mode: 0644]
dashboard/src/app/pages/device/device.module.ts [new file with mode: 0644]
dashboard/src/app/pages/devices/devices.component.html
dashboard/src/app/pages/devices/devices.component.ts
dashboard/src/app/pages/devices/devices.module.ts
dashboard/src/app/pages/pages.component.ts
dashboard/src/app/pages/pages.module.ts
dashboard/src/app/services/device.service.ts [new file with mode: 0644]
dashboard/src/assets/ui/devices/aircon.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/default.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/lamp.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/plug.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/refrigerator.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/robotcleaner.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/smartphone.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/tv.png [new file with mode: 0644]
dashboard/src/assets/ui/devices/washer.png [new file with mode: 0644]
dashboard/src/favicon.ico
dashboard/src/favicon.png
dashboard/src/index.html
dashboard/src/main.ts
dashboard/tslint.json

index 48548d6..7c8a374 100644 (file)
@@ -23,9 +23,11 @@ export class AuthService {
   }
 
   logout(): Observable<any> {
+    this.nbTokenService.clear().pipe();
+
     return this.http.delete<any>(this.logoutUrl).pipe(
       tap(
-        ok => this.nbTokenService.clear().pipe(),
+        ok => console.info(`logged out`),
         error => console.error(`logout error`),
       ),
     );
index f488977..86830c7 100644 (file)
@@ -1,7 +1,7 @@
 import { Component } from '@angular/core';
 
 @Component({
-  selector: 'ngx-footer',
+  selector: 'sam-footer',
   styleUrls: ['./footer.component.scss'],
   template: `
     <span class="created-by">Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.</span>
index 7d0dabe..d2bb173 100644 (file)
@@ -1,7 +1,6 @@
 import { Component, Input, OnInit } from '@angular/core';
 
 import { NbMenuService, NbSidebarService } from '@nebular/theme';
-import { NbUserMenuItem } from '@nebular/theme/components/user/user.component';
 import { UserService } from '../../../@core/data/users.service';
 import { AnalyticsService } from '../../../@core/utils/analytics.service';
 import { AuthService } from '../../../@core/auth';
@@ -9,7 +8,7 @@ import { Router } from '@angular/router';
 import { tap } from 'rxjs/operators';
 
 @Component({
-  selector: 'ngx-header',
+  selector: 'sam-header',
   styleUrls: ['./header.component.scss'],
   templateUrl: './header.component.html',
 })
@@ -47,7 +46,8 @@ export class HeaderComponent implements OnInit {
 
   menuItemClick(event) {
     if (event.target === 'logout') {
-      this.authService.logout().subscribe(() => this.router.navigate(['/auth/login']));
+      this.authService.logout().subscribe();
+      this.router.navigate(['/auth/login']);
     }
   }
 }
index d9f0f10..18306cd 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
 
 @Component({
-  selector: 'ngx-search-input',
+  selector: 'sam-search-input',
   styleUrls: ['./search-input.component.scss'],
   template: `
     <i class="control-icon ion ion-ios-search"
index 5b4c1b1..9d7052b 100644 (file)
@@ -1,6 +1,6 @@
 <nb-layout [center]="layout.id === 'center-column'" windowMode>
   <nb-layout-header fixed>
-    <ngx-header [position]="sidebar.id === 'left' ? 'normal': 'inverse'"></ngx-header>
+    <sam-header [position]="sidebar.id === 'left' ? 'normal': 'inverse'"></sam-header>
   </nb-layout-header>
 
   <nb-sidebar class="menu-sidebar"
@@ -20,6 +20,6 @@
   </nb-layout-column>
 
   <nb-layout-footer fixed>
-    <ngx-footer></ngx-footer>
+    <sam-footer></sam-footer>
   </nb-layout-footer>
 </nb-layout>
index 5bab719..c964999 100644 (file)
@@ -16,7 +16,7 @@ import 'rxjs/add/operator/delay';
 
 // TODO: move layouts into the framework
 @Component({
-  selector: 'ngx-main-layout',
+  selector: 'sam-main-layout',
   styleUrls: ['./main.layout.scss'],
   templateUrl: './main.layout.html',
 })
diff --git a/dashboard/src/app/@theme/pipes/chunks.pipe.ts b/dashboard/src/app/@theme/pipes/chunks.pipe.ts
new file mode 100644 (file)
index 0000000..0402096
--- /dev/null
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'chunks' })
+export class ChunksPipe implements PipeTransform {
+  transform(arr: any, chunkSize: number) {
+    return arr.reduce((prev, cur, index) =>
+      (index % chunkSize) ?
+        prev :
+        prev.concat([arr.slice(index, index + chunkSize)]), []);
+  }
+}
index 541ebeb..de91d38 100644 (file)
@@ -2,3 +2,4 @@ export * from './capitalize.pipe';
 export * from './plural.pipe';
 export * from './round.pipe';
 export * from './timing.pipe';
+export * from './chunks.pipe';
index fc02b6d..0bd8a54 100644 (file)
@@ -23,7 +23,7 @@ import {
   SearchInputComponent,
 } from './components';
 
-import { CapitalizePipe, PluralPipe, RoundPipe, TimingPipe } from './pipes';
+import { CapitalizePipe, PluralPipe, RoundPipe, TimingPipe, ChunksPipe } from './pipes';
 import {
   MainLayoutComponent,
 } from './layout';
@@ -58,6 +58,7 @@ const PIPES = [
   PluralPipe,
   RoundPipe,
   TimingPipe,
+  ChunksPipe,
 ];
 
 const NB_THEME_PROVIDERS = [
index f46b652..fd40f3f 100644 (file)
@@ -1,13 +1,8 @@
-/**
- * @license
- * Copyright Akveo. All Rights Reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- */
 import { Component, OnInit } from '@angular/core';
 import { AnalyticsService } from './@core/utils/analytics.service';
 
 @Component({
-  selector: 'ngx-app',
+  selector: 'sam-app',
   template: '<router-outlet></router-outlet>',
 })
 export class AppComponent implements OnInit {
diff --git a/dashboard/src/app/models/device.ts b/dashboard/src/app/models/device.ts
new file mode 100644 (file)
index 0000000..9570af4
--- /dev/null
@@ -0,0 +1,32 @@
+export class Device {
+  duid: string;
+  id: number;
+  ctime: string;
+  descr: string;
+  locked: number;
+  sn: string;
+  model: string;
+  os: OS;
+  type: Type;
+  geo: Geo;
+}
+
+class OS {
+  version: string;
+  name: string;
+  sw: string;
+}
+
+class Type {
+  name: string;
+  descr: string;
+}
+
+class Geo {
+  ip: string;
+  country_code: string;
+  country_name: string;
+  city: string;
+  latitude: string;
+  longitude: string;
+}
diff --git a/dashboard/src/app/models/index.ts b/dashboard/src/app/models/index.ts
new file mode 100644 (file)
index 0000000..b3c5786
--- /dev/null
@@ -0,0 +1,3 @@
+export * from './device';
+export * from './update-device';
+export * from './update-devices';
diff --git a/dashboard/src/app/models/update-device.ts b/dashboard/src/app/models/update-device.ts
new file mode 100644 (file)
index 0000000..010cb0a
--- /dev/null
@@ -0,0 +1,9 @@
+export class UpdateDevice {
+  constructor(id: number, locked: number) {
+    this.id = id;
+    this.locked = locked;
+  }
+
+  id: number;
+  locked: number;
+}
diff --git a/dashboard/src/app/models/update-devices.ts b/dashboard/src/app/models/update-devices.ts
new file mode 100644 (file)
index 0000000..5569ca1
--- /dev/null
@@ -0,0 +1,9 @@
+import { UpdateDevice } from './update-device';
+
+export class UpdateDevices {
+  constructor(updates: UpdateDevice[]) {
+    this.updates = updates;
+  }
+
+  updates: UpdateDevice[];
+}
index 9767e8f..6723642 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, ChangeDetectionStrategy } from '@angular/core';
 
 @Component({
-  selector: 'ngx-dashboard',
+  selector: 'sam-dashboard',
   templateUrl: './dashboard.component.html',
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
diff --git a/dashboard/src/app/pages/device/device.component.html b/dashboard/src/app/pages/device/device.component.html
new file mode 100644 (file)
index 0000000..9f9bc16
--- /dev/null
@@ -0,0 +1,27 @@
+<nb-card class="device-card {{device.type.name}}">
+  <nb-card-body>
+    <div cass="row">
+      <button class="btn btn-icon" [ngClass]="(device.locked===0) ? 'btn-hero-info' : 'btn-hero-danger'" type="button" (click)=toggleLock(device.id,device.locked)>
+            <div class="icon"></div>
+          </button>
+      <div class="info">
+        <div class="title font-w-bold">{{device.type.name | uppercase}}</div>
+        <div class="description font-w-light">{{device.model}}</div>
+      </div>
+      <div *ngIf="device.locked===1" class="locked"><i class="nb-locked"></i></div>
+    </div>
+  </nb-card-body>
+  <nb-card-footer>
+    <nb-actions fullwidth="" size="medium" class="medium full-width">
+      <nb-action>
+        <i class="nb-arrow-retweet"></i><span>Details</span>
+      </nb-action>
+      <nb-action>
+        <i class="nb-list"></i><span>Logs</span>
+      </nb-action>
+      <nb-action>
+        <i class="nb-gear"></i><span>Policies</span>
+      </nb-action>
+    </nb-actions>
+  </nb-card-footer>
+</nb-card>
diff --git a/dashboard/src/app/pages/device/device.component.scss b/dashboard/src/app/pages/device/device.component.scss
new file mode 100644 (file)
index 0000000..a70835a
--- /dev/null
@@ -0,0 +1,94 @@
+@import '../../@theme/styles/themes';
+@import '~@nebular/theme/styles/global/bootstrap/breakpoints';
+@import '~bootstrap/scss/mixins/breakpoints';
+
+.nomargin {
+    margin: 0 !important;
+}
+
+.device-card {
+    .btn-icon {
+        float: left;
+
+        &:hover {
+            cursor: pointer;
+        }
+    }
+    .icon {
+        height: 3em;
+        width: 3em;
+        float:left;
+        background-position: center center;
+        background-repeat: no-repeat;
+        background-size: 3em;
+        background-image: url("/assets/ui/devices/default.png");
+    }
+
+    &.tv .icon {
+        background-image: url("/assets/ui/devices/tv.png");
+    }
+
+    &.smartphone .icon {
+        background-image: url("/assets/ui/devices/smartphone.png");
+    }
+
+    &.lamp .icon {
+        background-image: url("/assets/ui/devices/lamp.png");
+    }
+
+    &.robotcleaner .icon {
+        background-image: url("/assets/ui/devices/robotcleaner.png");
+    }
+
+    &.airconditioner .icon {
+        background-image: url("/assets/ui/devices/aircon.png");
+    }
+
+    &.refrigerator .icon {
+        background-image: url("/assets/ui/devices/refrigerator.png");
+    }
+
+    &.smartplug .icon {
+        background-image: url("/assets/ui/devices/plug.png");
+    }
+
+    &.washer .icon {
+        background-image: url("/assets/ui/devices/washer.png");
+    }
+
+    .info {
+        padding-left: 1em;
+        width: 54%;
+        float:left;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+        .title {
+            width: 100%;
+            float:left;
+            font-size: 1.4rem;
+        }
+
+        .description {
+            width: 100%;
+            float:left;
+            opacity: 0.5;
+            font-size: 11pt;
+        }
+    }
+
+    .locked {
+        float: right;
+        font-size: 2rem;
+    }
+
+    //tmp solution, remove after migration
+    nb-actions {
+        &:hover {
+            cursor: pointer;
+        }
+        i {
+            font-size: 1.5em;
+        }
+    }
+}
diff --git a/dashboard/src/app/pages/device/device.component.ts b/dashboard/src/app/pages/device/device.component.ts
new file mode 100644 (file)
index 0000000..648390d
--- /dev/null
@@ -0,0 +1,25 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { DeviceService } from '../../services/device.service';
+import { Device, UpdateDevice } from '../../models';
+
+
+@Component({
+  selector: 'sam-device',
+  templateUrl: './device.component.html',
+  styleUrls: ['./device.component.scss'],
+})
+export class DeviceComponent implements OnInit {
+  @Input() device: Device;
+
+  constructor(private deviceService: DeviceService) {
+  }
+
+  ngOnInit() {
+  }
+
+  toggleLock(): void {
+    const newState = (this.device.locked === 0) ? 1 : 0;
+    const update = new UpdateDevice(this.device.id, newState);
+    this.deviceService.updateDevice(update).subscribe((devices) => this.device = devices[0]);
+  }
+}
diff --git a/dashboard/src/app/pages/device/device.module.ts b/dashboard/src/app/pages/device/device.module.ts
new file mode 100644 (file)
index 0000000..74bf97a
--- /dev/null
@@ -0,0 +1,22 @@
+import { NgModule } from '@angular/core';
+
+import { ThemeModule } from '../../@theme/theme.module';
+import { DeviceComponent } from './device.component';
+import { DeviceService } from '../../services/device.service';
+
+
+@NgModule({
+  imports: [
+    ThemeModule,
+  ],
+  declarations: [
+    DeviceComponent,
+  ],
+  providers: [
+    DeviceService,
+  ],
+  exports: [
+    DeviceComponent,
+  ],
+})
+export class DeviceModule { }
index 8b814ae..dec04a9 100644 (file)
@@ -1,5 +1,3 @@
-<div class="row">
-  <nb-card>
-    <nb-card-body>Hello Devices page</nb-card-body>
-  </nb-card>
+<div *ngFor="let chunk of devices | chunks: 4" class="row">
+    <sam-device *ngFor="let device of chunk" [device]="device" class="col-xxxl-3 col-md-6"></sam-device>
 </div>
index e1d1bcd..d0ff07f 100644 (file)
@@ -1,8 +1,23 @@
-import { Component, ChangeDetectionStrategy } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+import { DeviceService } from '../../services/device.service';
+import { Device } from '../../models';
+
 
 @Component({
-  selector: 'ngx-devices',
+  selector: 'sam-devices',
   templateUrl: './devices.component.html',
 })
-export class DevicesComponent {
+export class DevicesComponent implements OnInit {
+  devices: Device[];
+
+  constructor(private deviceService: DeviceService) {
+  }
+
+  ngOnInit() {
+    this.getAllDevices();
+  }
+
+  getAllDevices(): void {
+    this.deviceService.getAllDevices().subscribe(devices => this.devices = devices);
+  }
 }
index 1996cbc..20ce573 100644 (file)
@@ -2,14 +2,21 @@ import { NgModule } from '@angular/core';
 
 import { ThemeModule } from '../../@theme/theme.module';
 import { DevicesComponent } from './devices.component';
+import { DeviceModule } from '../device/device.module';
+import { DeviceComponent } from '../device/device.component';
+import { DeviceService } from '../../services/device.service';
 
 
 @NgModule({
   imports: [
     ThemeModule,
+    DeviceModule,
   ],
   declarations: [
     DevicesComponent,
   ],
+  providers: [
+    DeviceService,
+  ],
 })
 export class DevicesModule { }
index 8153b38..322d4ce 100644 (file)
@@ -3,12 +3,12 @@ import { Component } from '@angular/core';
 import { MENU_ITEMS } from './pages-menu';
 
 @Component({
-  selector: 'ngx-pages',
+  selector: 'sam-pages',
   template: `
-    <ngx-main-layout>
+    <sam-main-layout>
       <nb-menu [items]="menu"></nb-menu>
       <router-outlet></router-outlet>
-    </ngx-main-layout>
+    </sam-main-layout>
   `,
 })
 export class PagesComponent {
index 3d57ec0..80d6de7 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 import { PagesComponent } from './pages.component';
 import { DashboardModule } from './dashboard/dashboard.module';
 import { DevicesModule } from './devices/devices.module';
+import { DeviceModule } from './device/device.module';
 import { PagesRoutingModule } from './pages-routing.module';
 import { ThemeModule } from '../@theme/theme.module';
 
@@ -15,6 +16,7 @@ const PAGES_COMPONENTS = [
     PagesRoutingModule,
     ThemeModule,
     DashboardModule,
+    DeviceModule,
     DevicesModule,
   ],
   declarations: [
diff --git a/dashboard/src/app/services/device.service.ts b/dashboard/src/app/services/device.service.ts
new file mode 100644 (file)
index 0000000..0b65a37
--- /dev/null
@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs/Observable';
+import { tap } from 'rxjs/operators';
+import { Device, UpdateDevice, UpdateDevices } from '../models';
+import { SERVER_URL } from '../../../config';
+
+@Injectable()
+export class DeviceService {
+  private getAllDevicesUrl = SERVER_URL + '/devices';
+  private updateDevicesUrl = SERVER_URL + '/devices/update';
+
+  constructor(private http: HttpClient) { }
+
+  getAllDevices(): Observable<Device[]> {
+    return this.http.get<Device[]>(this.getAllDevicesUrl).pipe(
+      tap(
+        ok => console.info(`got devices`),
+        error => console.error(`get all devices error`),
+      ),
+    );
+  }
+
+  updateDevices(updateDevices: UpdateDevice[]): Observable<Device[]> {
+    const updates = new UpdateDevices(updateDevices);
+
+    return this.http.put<Device[]>(this.updateDevicesUrl, updates).pipe(
+      tap(
+        ok => console.info(`successfully updated`),
+        error => console.error(`error during update`),
+      ),
+    );
+  }
+
+  updateDevice(updateDevices: UpdateDevice): Observable<Device[]> {
+    return this.updateDevices(new Array(updateDevices));
+  }
+}
diff --git a/dashboard/src/assets/ui/devices/aircon.png b/dashboard/src/assets/ui/devices/aircon.png
new file mode 100644 (file)
index 0000000..a221d00
Binary files /dev/null and b/dashboard/src/assets/ui/devices/aircon.png differ
diff --git a/dashboard/src/assets/ui/devices/default.png b/dashboard/src/assets/ui/devices/default.png
new file mode 100644 (file)
index 0000000..3e310bc
Binary files /dev/null and b/dashboard/src/assets/ui/devices/default.png differ
diff --git a/dashboard/src/assets/ui/devices/lamp.png b/dashboard/src/assets/ui/devices/lamp.png
new file mode 100644 (file)
index 0000000..1499b2c
Binary files /dev/null and b/dashboard/src/assets/ui/devices/lamp.png differ
diff --git a/dashboard/src/assets/ui/devices/plug.png b/dashboard/src/assets/ui/devices/plug.png
new file mode 100644 (file)
index 0000000..b32d83e
Binary files /dev/null and b/dashboard/src/assets/ui/devices/plug.png differ
diff --git a/dashboard/src/assets/ui/devices/refrigerator.png b/dashboard/src/assets/ui/devices/refrigerator.png
new file mode 100644 (file)
index 0000000..ed0e5ba
Binary files /dev/null and b/dashboard/src/assets/ui/devices/refrigerator.png differ
diff --git a/dashboard/src/assets/ui/devices/robotcleaner.png b/dashboard/src/assets/ui/devices/robotcleaner.png
new file mode 100644 (file)
index 0000000..a7f9462
Binary files /dev/null and b/dashboard/src/assets/ui/devices/robotcleaner.png differ
diff --git a/dashboard/src/assets/ui/devices/smartphone.png b/dashboard/src/assets/ui/devices/smartphone.png
new file mode 100644 (file)
index 0000000..00d67b2
Binary files /dev/null and b/dashboard/src/assets/ui/devices/smartphone.png differ
diff --git a/dashboard/src/assets/ui/devices/tv.png b/dashboard/src/assets/ui/devices/tv.png
new file mode 100644 (file)
index 0000000..2eb7790
Binary files /dev/null and b/dashboard/src/assets/ui/devices/tv.png differ
diff --git a/dashboard/src/assets/ui/devices/washer.png b/dashboard/src/assets/ui/devices/washer.png
new file mode 100644 (file)
index 0000000..f4d0520
Binary files /dev/null and b/dashboard/src/assets/ui/devices/washer.png differ
index d4340cf..be4b8a0 100644 (file)
Binary files a/dashboard/src/favicon.ico and b/dashboard/src/favicon.ico differ
index 9d11611..c6658c0 100644 (file)
Binary files a/dashboard/src/favicon.png and b/dashboard/src/favicon.png differ
index 2d256a3..d3bcf74 100644 (file)
@@ -11,7 +11,7 @@
   </head>
 
   <body>
-    <ngx-app>Loading...</ngx-app>
+    <sam-app>Loading...</sam-app>
 
     <style>
       @-webkit-keyframes spin {
@@ -48,8 +48,8 @@
         width: 100%;
         height: 100%;
         z-index: 1003;
-        background: #000000;
-        overflow: hidden
+        background: #ffffff;
+        overflow: hidden;
       }
 
       .spinner div:first-child {
index 8fd0e9d..c7b673c 100644 (file)
@@ -1,8 +1,3 @@
-/**
- * @license
- * Copyright Akveo. All Rights Reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- */
 import { enableProdMode } from '@angular/core';
 import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
 
index 4bf7359..3835df0 100644 (file)
     "directive-selector": [
       true,
       "attribute",
-      "ngx",
+      "sam",
       "camelCase"
     ],
     "component-selector": [
       true,
       "element",
-      "ngx",
+      "sam",
       "kebab-case"
     ],
     "ban": [