Fix issues with load spinner and stats 11/67011/2
authorRonan Kenny <ronan.kenny@ericsson.com>
Mon, 17 Sep 2018 14:11:43 +0000 (15:11 +0100)
committerRonan Kenny <ronan.kenny@ericsson.com>
Tue, 18 Sep 2018 09:19:10 +0000 (10:19 +0100)
Fixing issues where stats returned Nan and
implementation of loading spinner

Change-Id: I989efb739ed244de3760476ce2dfff8928f51c86
Issue-ID: SO-727
Signed-off-by: Ronan Kenny <ronan.kenny@ericsson.com>
so-monitoring/so-monitoring-ui/src/main/frontend/package-lock.json
so-monitoring/so-monitoring-ui/src/main/frontend/package.json
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/app.module.ts
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/details/details.component.html
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/details/details.component.ts
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/home/home.component.html
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/home/home.component.scss
so-monitoring/so-monitoring-ui/src/main/frontend/src/app/home/home.component.ts

index d9ec649..7864215 100644 (file)
       "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
       "dev": true
     },
+    "ngx-spinner": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-6.1.2.tgz",
+      "integrity": "sha512-j/R8T5vKvsLLib1pTxKLYK3GYAFXw5VoUJmaTlcocO6Yi4qIypfhmw9PX9triy7hWVGPu6cUzVs7g9cEG9OYBA==",
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "no-case": {
       "version": "2.3.2",
       "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
index c6f6f14..c793264 100644 (file)
@@ -26,6 +26,7 @@
     "bpmn-js": "^2.4.1",
     "core-js": "^2.5.4",
     "jquery": "^3.3.1",
+    "ngx-spinner": "^6.1.2",
     "rxjs": "^6.0.0",
     "toastr": "^2.1.4",
     "zone.js": "^0.8.26"
index c3a02b9..b9437cc 100644 (file)
@@ -40,6 +40,7 @@ import { MatFormFieldModule, MatInputModule } from '@angular/material';
 import { MatDatepickerModule } from '@angular/material/datepicker';\r
 import { MatNativeDateModule } from '@angular/material';\r
 import { MatCardModule } from '@angular/material/card';\r
+import { NgxSpinnerModule } from 'ngx-spinner';\r
 \r
 @NgModule({\r
   declarations: [\r
@@ -62,7 +63,8 @@ import { MatCardModule } from '@angular/material/card';
     MatInputModule,\r
     MatDatepickerModule,\r
     MatNativeDateModule,\r
-    MatCardModule\r
+    MatCardModule,\r
+    NgxSpinnerModule\r
   ],\r
   providers: [ToastrNotificationService],\r
   bootstrap: [AppComponent]\r
index fc682ac..45301c7 100644 (file)
@@ -97,3 +97,5 @@ SPDX-License-Identifier: Apache-2.0
     </mat-tab-group>\r
   </div>\r
 </div>\r
+\r
+<ngx-spinner bdColor="rgba(51, 51, 51, 0.8)" size="large" color="#00285f" type="ball-spin-clockwise-fade-rotating"></ngx-spinner>\r
index 9561e9a..4c19ba1 100644 (file)
@@ -33,6 +33,7 @@ import { ViewEncapsulation } from '@angular/core';
 import { MatTabsModule } from '@angular/material/tabs';\r
 import { VarInstance } from '../model/variableInstance.model';\r
 import { ToastrNotificationService } from '../toastr-notification-service.service';\r
+import { NgxSpinnerService } from 'ngx-spinner';\r
 \r
 @Component({\r
   selector: 'app-details',\r
@@ -63,7 +64,8 @@ export class DetailsComponent implements OnInit {
 \r
   displayedColumnsVariable = ['name', 'type', 'value'];\r
 \r
-  constructor(private route: ActivatedRoute, private data: DataService, private popup: ToastrNotificationService, private router: Router) { }\r
+  constructor(private route: ActivatedRoute, private data: DataService, private popup: ToastrNotificationService,\r
+    private router: Router, private spinner: NgxSpinnerService) { }\r
 \r
   getActInst(procInstId: string) {\r
     this.data.getActivityInstance(procInstId).subscribe(\r
@@ -104,12 +106,15 @@ export class DetailsComponent implements OnInit {
   }\r
 \r
   displayCamundaflow(bpmnXml, activities: ACTINST[], r: Router) {\r
+    this.spinner.show();\r
 \r
     this.bpmnViewer.importXML(bpmnXml, (error) => {\r
       if (error) {\r
         console.error('Unable to load BPMN flow ', error);\r
         this.popup.error('Unable to load BPMN flow ');\r
+        this.spinner.hide();\r
       } else {\r
+        this.spinner.hide();\r
         let canvas = this.bpmnViewer.get('canvas');\r
         var eventBus = this.bpmnViewer.get('eventBus');\r
         eventBus.on('element.click', function(e) {\r
@@ -118,6 +123,7 @@ export class DetailsComponent implements OnInit {
             if (a.activityId == e.element.id && a.calledProcessInstanceId !== null) {\r
               console.log("will drill down to : " + a.calledProcessInstanceId);\r
               r.navigate(['/details/' + a.calledProcessInstanceId]);\r
+              this.spinner.show();\r
             }\r
           });\r
         });\r
index 6adea3b..2b580e2 100644 (file)
@@ -50,26 +50,26 @@ SPDX-License-Identifier: Apache-2.0
         <input matInput #searchValueRI type="text" [(ngModel)]="searchData.requestId" placeholder="Request Id">\r
       </mat-form-field>\r
 \r
-      <!-- Angular Start Date Picker -->\r
-      <mat-form-field class="startDate">\r
-        <input matInput #startDate [matDatepicker]="picker" [(ngModel)]="searchData.startDate" placeholder="Choose a start date">\r
-        <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>\r
-        <mat-datepicker #picker></mat-datepicker>\r
-      </mat-form-field>\r
-\r
-      <!-- Dropdown box for Start Hour selection -->\r
-      <mat-form-field class="selectHour">\r
-        <mat-select class="formatBox" [(ngModel)]="searchData.selectedStartHour" name="hourFrom" placeholder="Select Hour">\r
-          <mat-option *ngFor="let option of hourOptions" [value]="option">{{option}}</mat-option>\r
-        </mat-select>\r
-      </mat-form-field>\r
-\r
-      <!-- Dropdown box for Start Minute selection -->\r
-      <mat-form-field class="selectMinute">\r
-        <mat-select class="formatBox" [(ngModel)]="searchData.selectedStartMinute" name="minuteFrom" placeholder="Select Minute">\r
-          <mat-option *ngFor="let option of minuteOptions" [value]="option">{{option}}</mat-option>\r
-        </mat-select>\r
-      </mat-form-field>\r
+        <!-- Angular Start Date Picker -->\r
+        <mat-form-field class="startDate">\r
+          <input matInput #startDate [matDatepicker]="picker" [(ngModel)]="searchData.startDate" placeholder="Choose a start date">\r
+          <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>\r
+          <mat-datepicker #picker></mat-datepicker>\r
+        </mat-form-field>\r
+\r
+        <!-- Dropdown box for Start Hour selection -->\r
+        <mat-form-field class="selectHour">\r
+          <mat-select class="formatBox" [(ngModel)]="searchData.selectedStartHour" name="hourFrom" placeholder="Select Hour">\r
+            <mat-option *ngFor="let option of hourOptions" [value]="option">{{option}}</mat-option>\r
+          </mat-select>\r
+        </mat-form-field>\r
+\r
+        <!-- Dropdown box for Start Minute selection -->\r
+        <mat-form-field class="selectMinute">\r
+          <mat-select class="formatBox" [(ngModel)]="searchData.selectedStartMinute" name="minuteFrom" placeholder="Select Minute">\r
+            <mat-option *ngFor="let option of minuteOptions" [value]="option">{{option}}</mat-option>\r
+          </mat-select>\r
+        </mat-form-field>\r
     </div>\r
 \r
     <!-- Dropdown Filter and TextBox for Service Name -->\r
@@ -83,26 +83,26 @@ SPDX-License-Identifier: Apache-2.0
         <input matInput #searchValueSN type="text" [(ngModel)]="searchData.serviceInstanceName" placeholder="Service Name">\r
       </mat-form-field>\r
 \r
-      <!-- Angular End Date Picker -->\r
-      <mat-form-field class="endDate">\r
-        <input matInput #endDate [matDatepicker]="endpicker" [(ngModel)]="searchData.endDate" placeholder="Choose an end date">\r
-        <mat-datepicker-toggle matSuffix [for]="endpicker"></mat-datepicker-toggle>\r
-        <mat-datepicker #endpicker></mat-datepicker>\r
-      </mat-form-field>\r
-\r
-      <!-- Dropdown box for End Hour selection -->\r
-      <mat-form-field class="selectHour">\r
-        <mat-select class="formatBox" [(ngModel)]="searchData.selectedEndHour" name="hourTo" placeholder="Select Hour">\r
-          <mat-option *ngFor="let option of hourOptions" [value]="option">{{option}}</mat-option>\r
-        </mat-select>\r
-      </mat-form-field>\r
-\r
-      <!-- Dropdown box for End Minute selection -->\r
-      <mat-form-field class="selectMinute">\r
-        <mat-select class="formatBox" [(ngModel)]="searchData.selectedEndMinute" name="minuteTo" placeholder="Select Minute">\r
-          <mat-option *ngFor="let option of minuteOptions" [value]="option">{{option}}</mat-option>\r
-        </mat-select>\r
-      </mat-form-field>\r
+        <!-- Angular End Date Picker -->\r
+        <mat-form-field class="endDate">\r
+          <input matInput #endDate [matDatepicker]="endpicker" [(ngModel)]="searchData.endDate" placeholder="Choose an end date">\r
+          <mat-datepicker-toggle matSuffix [for]="endpicker"></mat-datepicker-toggle>\r
+          <mat-datepicker #endpicker></mat-datepicker>\r
+        </mat-form-field>\r
+\r
+        <!-- Dropdown box for End Hour selection -->\r
+        <mat-form-field class="selectHour">\r
+          <mat-select class="formatBox" [(ngModel)]="searchData.selectedEndHour" name="hourTo" placeholder="Select Hour">\r
+            <mat-option *ngFor="let option of hourOptions" [value]="option">{{option}}</mat-option>\r
+          </mat-select>\r
+        </mat-form-field>\r
+\r
+        <!-- Dropdown box for End Minute selection -->\r
+        <mat-form-field class="selectMinute">\r
+          <mat-select class="formatBox" [(ngModel)]="searchData.selectedEndMinute" name="minuteTo" placeholder="Select Minute">\r
+            <mat-option *ngFor="let option of minuteOptions" [value]="option">{{option}}</mat-option>\r
+          </mat-select>\r
+        </mat-form-field>\r
     </div>\r
 \r
     <!-- Dropdown Filter for Status -->\r
@@ -165,23 +165,39 @@ SPDX-License-Identifier: Apache-2.0
 \r
       <mat-tab label="Service Statistics">\r
         <div id="servStats">\r
-          <p>Total: {{ totalVal }}</p>\r
-          <hr/>\r
-          <p>Complete: {{ completeVal }}</p>\r
-          <p><b> {{ percentageComplete }}%</b></p>\r
-          <hr/>\r
-          <p>Failed: {{ failedVal }}</p>\r
-          <p><b> {{ percentageFailed }}%</b></p>\r
-          <hr/>\r
-          <p>In Progress: {{ inProgressVal }}</p>\r
-          <hr/>\r
-          <p>Pending: {{ pendingVal }}</p>\r
-          <hr/>\r
-          <p>Unlocked: {{ unlockedVal }}</p>\r
+          <table class="statsTable">\r
+            <tbody>\r
+              <tr>\r
+                <td>Total: {{ totalVal }}</td>\r
+                <td></td>\r
+              </tr>\r
+              <tr>\r
+                <td>Complete: {{ completeVal }}</td>\r
+                <td> {{ percentageComplete }}%</td>\r
+              </tr>\r
+              <tr>\r
+                <td>Failed: {{ failedVal }}</td>\r
+                <td> {{ percentageFailed }}%</td>\r
+              </tr>\r
+              <tr>\r
+                <td>In Progress: {{ inProgressVal }}</td>\r
+                <td> {{ percentageInProg }}%</td>\r
+              </tr>\r
+              <tr>\r
+                <td>Pending: {{ pendingVal }}</td>\r
+                <td> {{ percentagePending }}%</td>\r
+              </tr>\r
+              <tr>\r
+                <td>Unlocked: {{ unlockedVal }}</td>\r
+                <td> {{ percentageUnlocked }}%</td>\r
+              </tr>\r
+            </tbody>\r
+          </table>\r
         </div>\r
       </mat-tab>\r
     </mat-tab-group>\r
   </div>\r
 </div>\r
 \r
+<ngx-spinner bdColor="rgba(51, 51, 51, 0.8)" size="large" color="#00285f" type="ball-spin-clockwise-fade-rotating"></ngx-spinner>\r
 <router-outlet></router-outlet>\r
index d475c52..923066f 100644 (file)
@@ -19,7 +19,6 @@ SPDX-License-Identifier: Apache-2.0
 \r
 @authors: ronan.kenny@ericsson.com, waqas.ikram@ericsson.com\r
 */\r
-\r
 @import "~@angular/material/prebuilt-themes/indigo-pink.css";\r
 \r
 .searchArea {\r
@@ -77,7 +76,7 @@ SPDX-License-Identifier: Apache-2.0
 }\r
 \r
 .fa {\r
- float: left;\r
 float: left;\r
   width: 120px;\r
   padding: 10px;\r
   background: #2196F3;\r
@@ -103,28 +102,29 @@ form.example::after {
   display: inline-flex;\r
 }\r
 \r
-.startDate, .endDate{\r
+.endDate,\r
+.startDate {\r
   margin-left: 90px;\r
   width: 140px;\r
 }\r
 \r
-.selectHour, .selectMinute{\r
+.selectHour,\r
+.selectMinute {\r
   margin-left: 30px;\r
-  width: 100px\r
+  width: 100px;\r
 }\r
 \r
-#servStats{\r
+#servStats {\r
   background-color: white;\r
   padding: 10px;\r
   font-size: 17px;\r
   font-family: 'Montserrat', sans-serif;\r
 }\r
 \r
-hr {\r
-    display: block;\r
-    height: 1px;\r
-    border: 0;\r
-    border-top: 1px solid #ccc;\r
-    margin: 1em 0;\r
-    padding: 0;\r
+.statsTable {\r
+  td {\r
+    padding: 12px 80px 12px 12px;\r
+    text-align: left;\r
+    border-bottom: 1px solid #ccc;\r
+  }\r
 }\r
index dd08bb4..b8fac61 100644 (file)
@@ -35,9 +35,9 @@ import { SearchData } from '../model/searchData.model';
 import { MatDatepickerModule } from '@angular/material/datepicker';
 import { FormControl } from '@angular/forms';
 import { SearchRequest } from '../model/SearchRequest.model';
-import { ViewChild } from '@angular/core';
 import { ElementRef } from '@angular/core';
 import { Input } from '@angular/core';
+import { NgxSpinnerService } from 'ngx-spinner';
 
 @Component({
   selector: 'app-home',
@@ -56,6 +56,9 @@ export class HomeComponent implements OnInit {
   unlockedVal = 0;
   percentageComplete = 0;
   percentageFailed = 0;
+  percentageInProg = 0;
+  percentagePending = 0;
+  percentageUnlocked = 0;
 
   options = [{ name: "EQUAL", value: "EQ" }, { name: "NOT EQUAL", value: "NEQ" }, { name: "LIKE", value: "LIKE" }];
   statusOptions = [{ name: "ALL", value: "ALL" }, { name: "COMPLETE", value: "COMPLETE" }, { name: "IN_PROGRESS", value: "IN_PROGRESS" },
@@ -77,17 +80,22 @@ export class HomeComponent implements OnInit {
   displayedColumns = ['requestId', 'serviceInstanceId', 'serviceIstanceName', 'networkId', 'requestStatus', 'serviceType', 'startTime', 'endTime'];
 
   constructor(private route: ActivatedRoute, private data: DataService,
-    private router: Router, private popup: ToastrNotificationService) {
+    private router: Router, private popup: ToastrNotificationService,
+    private spinner: NgxSpinnerService) {
     this.searchData = new SearchData();
   }
 
   makeCall() {
+    this.spinner.show();
+
     var search = this.searchData.getSearchRequest().subscribe((result: SearchRequest) => {
 
       this.data.retrieveInstance(result.getFilters(), result.getStartTimeInMilliseconds(), result.getEndTimeInMilliseconds())
         .subscribe((data: Process[]) => {
+          this.spinner.hide();
           this.processData = data;
-          this.popup.info("Number of records found: " + data.length);
+          this.popup.info("Number of records found: " + data.length)
+
           // Calculate Statistics for Service Statistics tab
           this.completeVal = this.processData.filter(i => i.requestStatus === "COMPLETE").length;
           this.inProgressVal = this.processData.filter(i => i.requestStatus === "IN_PROGRESS").length;
@@ -95,28 +103,40 @@ export class HomeComponent implements OnInit {
           this.pendingVal = this.processData.filter(i => i.requestStatus === "PENDING").length;
           this.unlockedVal = this.processData.filter(i => i.requestStatus === "UNLOCKED").length;
           this.totalVal = this.processData.length;
-          this.percentageComplete = Math.round(((this.completeVal / this.totalVal) * 100) * 100) / 100;
-          this.percentageFailed = Math.round(((this.failedVal / this.totalVal) * 100) * 100) / 100;
 
+          // Calculate percentages to 2 decimal places and compare to 0 to avoid NaN error
+          if (this.totalVal != 0) {
+            this.percentageComplete = Math.round(((this.completeVal / this.totalVal) * 100) * 100) / 100;
+            this.percentageFailed = Math.round(((this.failedVal / this.totalVal) * 100) * 100) / 100;
+            this.percentageInProg = Math.round(((this.inProgressVal / this.totalVal) * 100) * 100) / 100;
+            this.percentagePending = Math.round(((this.pendingVal / this.totalVal) * 100) * 100) / 100;
+            this.percentageUnlocked = Math.round(((this.unlockedVal / this.totalVal) * 100) * 100) / 100;
+          }
           console.log("COMPLETE: " + this.completeVal);
           console.log("FAILED: " + this.failedVal);
         }, error => {
           console.log(error);
           this.popup.error("Unable to perform search Error code:" + error.status);
+          this.spinner.hide();
         });
     }, error => {
       console.log("Data validation error " + error);
       this.popup.error(error);
+      this.spinner.hide();
     });
   }
 
   getProcessIsntanceId(requestId: string) {
+    this.spinner.show();
+
     var response = this.data.getProcessInstanceId(requestId).subscribe((data) => {
       if (data.status == 200) {
+        this.spinner.hide();
         var processInstanceId = (data.body as ProcessInstanceId).processInstanceId;
         this.router.navigate(['/details/' + processInstanceId]);
       } else {
         this.popup.error('No process instance id found: ' + requestId);
+        this.spinner.hide();
         console.log('No process instance id found: ' + requestId);
       }
     });