From 1d543c9212e5e030c116fde4c1006aeba8cacaa9 Mon Sep 17 00:00:00 2001
From: Steven Zou <szou@vmware.com>
Date: Tue, 13 Jun 2017 20:38:21 +0800
Subject: [PATCH 1/3] Improve components in UI library

---
 .../src/endpoint/endpoint.component.css.ts    |  9 +++++--
 .../src/endpoint/endpoint.component.html.ts   | 12 +++++----
 src/ui_ng/lib/src/filter/filter.component.ts  | 15 +++++++++++
 src/ui_ng/lib/src/filter/filter.template.ts   | 26 +++++++++++++++++--
 .../list-replication-rule.component.html.ts   |  9 ++++---
 src/ui_ng/lib/src/log/recent-log.component.ts |  8 +++---
 src/ui_ng/lib/src/log/recent-log.template.ts  |  7 ++---
 .../replication/replication.component.css.ts  | 11 ++++++--
 .../replication/replication.component.html.ts | 18 ++++++-------
 .../repository-stackview.component.css.ts     |  9 ++++---
 .../repository-stackview.component.html.ts    | 10 ++++---
 .../lib/src/tag/tag-detail.component.spec.ts  |  4 ++-
 src/ui_ng/lib/src/utils.ts                    |  7 ++++-
 .../result-grid.component.spec.ts             |  3 ++-
 .../result-grid.component.ts                  |  8 ++++++
 .../vulnerability-scanning/scanning.css.ts    | 24 +++++++----------
 .../vulnerability-scanning/scanning.html.ts   | 12 ++++++++-
 17 files changed, 137 insertions(+), 55 deletions(-)

diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts
index 8eab04166..6452b5d9a 100644
--- a/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts
+++ b/src/ui_ng/lib/src/endpoint/endpoint.component.css.ts
@@ -1,10 +1,15 @@
 export const ENDPOINT_STYLE: string = `
   .option-left {
     padding-left: 16px;
-    margin-top: 24px;
+    margin-top: -6px;
   }
   .option-right {
     padding-right: 16px;
-    margin-top: 36px;
+  }
+  .refresh-btn {
+    cursor: pointer;
+  }
+  .refresh-btn:hover {
+    color: #007CBB;
   }
 `;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
index 926d79083..ebd4ce84d 100644
--- a/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
+++ b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
@@ -1,17 +1,17 @@
 export const ENDPOINT_TEMPLATE: string = `
-    <confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
+<div>
     <div class="row">
         <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
-            <div class="row flex-items-xs-between">
+            <div class="row flex-items-xs-between" style="height: 24px;">
                 <div class="flex-items-xs-middle option-left">
                     <button class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'DESTINATION.ENDPOINT' | translate}}</button>
                     <create-edit-endpoint (reload)="reload($event)"></create-edit-endpoint>
                 </div>
                 <div class="flex-items-xs-middle option-right">
-                    <hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_TARGETS_PLACEHOLDER" | translate}}' (filter)="doSearchTargets($event)" [currentValue]="targetName"></hbr-filter>
-                    <a href="javascript:void(0)" (click)="refreshTargets()">
+                    <hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_TARGETS_PLACEHOLDER" | translate}}' (filter)="doSearchTargets($event)" [currentValue]="targetName"></hbr-filter>
+                    <span class="refresh-btn" (click)="refreshTargets()">
                         <clr-icon shape="refresh"></clr-icon>
-                    </a>
+                    </span>
                 </div>
             </div>
         </div>
@@ -37,4 +37,6 @@ export const ENDPOINT_TEMPLATE: string = `
             </clr-datagrid>
         </div>
     </div>
+    <confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
+</div>
 `;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/filter/filter.component.ts b/src/ui_ng/lib/src/filter/filter.component.ts
index 7e6f90a3c..5f7c80ada 100644
--- a/src/ui_ng/lib/src/filter/filter.component.ts
+++ b/src/ui_ng/lib/src/filter/filter.component.ts
@@ -31,6 +31,7 @@ export class FilterComponent implements OnInit {
 
     placeHolder: string = "";
     filterTerms = new Subject<string>();
+    isExpanded: boolean = false;
 
     @Output("filter") private filterEvt = new EventEmitter<string>();
 
@@ -39,6 +40,8 @@ export class FilterComponent implements OnInit {
     public set flPlaceholder(placeHolder: string) {
         this.placeHolder = placeHolder;
     }
+    @Input() expandMode: boolean = false;
+    @Input() withDivider: boolean = false;
 
     ngOnInit(): void {
         this.filterTerms
@@ -54,4 +57,16 @@ export class FilterComponent implements OnInit {
         //Send out filter terms
         this.filterTerms.next(this.currentValue.trim());
     }
+
+    onClick(): void {
+        //Only enabled when expandMode is set to false
+        if(this.expandMode){
+            return;
+        }
+        this.isExpanded = !this.isExpanded;
+    }
+
+    public get isShowSearchBox(): boolean {
+        return this.expandMode || (!this.expandMode && this.isExpanded);
+    }
 }
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/filter/filter.template.ts b/src/ui_ng/lib/src/filter/filter.template.ts
index 1f55ad227..9bed73db5 100644
--- a/src/ui_ng/lib/src/filter/filter.template.ts
+++ b/src/ui_ng/lib/src/filter/filter.template.ts
@@ -4,8 +4,9 @@
 
 export const FILTER_TEMPLATE: string = `
 <span>
-    <clr-icon shape="filter" size="12" class="is-solid filter-icon"></clr-icon>
-    <input type="text" style="padding-left: 15px;" (keyup)="valueChange()" placeholder="{{placeHolder}}" [(ngModel)]="currentValue"/>
+    <clr-icon shape="search" size="20" class="search-btn" [class.filter-icon]="isShowSearchBox" (click)="onClick()"></clr-icon>
+    <input [hidden]="!isShowSearchBox" type="text" style="padding-left: 15px;" (keyup)="valueChange()" placeholder="{{placeHolder}}" [(ngModel)]="currentValue"/>
+    <span class="filter-divider" *ngIf="withDivider"></span>
 </span>
 `;
 
@@ -14,4 +15,25 @@ export const FILTER_STYLES: string = `
     position: relative;
     right: -12px;
 }
+
+.filter-divider {
+    display: inline-block;
+    height: 16px;
+    width: 2px;
+    background-color: #cccccc;
+    padding-top: 12px;
+    padding-bottom: 12px;
+    position: relative;
+    top: 9px;
+    margin-right: 6px;
+    margin-left: 6px;
+}
+
+.search-btn {
+    cursor: pointer;
+}
+
+.search-btn:hover {
+    color: #007CBB;
+}
 `;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
index 83e5df4eb..3292a7f42 100644
--- a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
+++ b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
@@ -1,6 +1,5 @@
 export const LIST_REPLICATION_RULE_TEMPLATE: string = `
-<confirmation-dialog #toggleConfirmDialog (confirmAction)="toggleConfirm($event)"></confirmation-dialog>
-<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
+<div>
 <clr-datagrid [clrDgLoading]="loading">
     <clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
     <clr-dg-column [clrDgField]="'project_name'" *ngIf="projectScope">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
@@ -37,4 +36,8 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
       {{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} {{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
       <clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
     </clr-dg-footer>
-</clr-datagrid>`;
\ No newline at end of file
+</clr-datagrid>
+<confirmation-dialog #toggleConfirmDialog (confirmAction)="toggleConfirm($event)"></confirmation-dialog>
+<confirmation-dialog #deletionConfirmDialog (confirmAction)="deletionConfirm($event)"></confirmation-dialog>
+</div>
+`;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/log/recent-log.component.ts b/src/ui_ng/lib/src/log/recent-log.component.ts
index 601f4345f..3e6b411c2 100644
--- a/src/ui_ng/lib/src/log/recent-log.component.ts
+++ b/src/ui_ng/lib/src/log/recent-log.component.ts
@@ -11,7 +11,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Input } from '@angular/core';
 import { Router } from '@angular/router';
 import {
     AccessLogService,
@@ -23,6 +23,7 @@ import { ErrorHandler } from '../error-handler/index';
 import { Observable } from 'rxjs/Observable';
 import { toPromise, CustomComparator } from '../utils';
 import { LOG_TEMPLATE, LOG_STYLES } from './recent-log.template';
+import { DEFAULT_PAGE_SIZE } from '../utils';
 
 import { Comparator, State } from 'clarity-angular';
 
@@ -37,11 +38,12 @@ export class RecentLogComponent implements OnInit {
     logsCache: AccessLog;
     loading: boolean = true;
     currentTerm: string;
+    @Input() withTitle: boolean = false;
 
-    pageSize: number = 15;
+    pageSize: number = DEFAULT_PAGE_SIZE;
     currentPage: number = 0;
 
-    opTimeComparator: Comparator<AccessLog> = new CustomComparator<AccessLog>('op_time', 'date');
+    opTimeComparator: Comparator<AccessLogItem> = new CustomComparator<AccessLogItem>('op_time', 'date');
 
     constructor(
         private logService: AccessLogService,
diff --git a/src/ui_ng/lib/src/log/recent-log.template.ts b/src/ui_ng/lib/src/log/recent-log.template.ts
index 95fcf5d1d..9abf1081c 100644
--- a/src/ui_ng/lib/src/log/recent-log.template.ts
+++ b/src/ui_ng/lib/src/log/recent-log.template.ts
@@ -4,11 +4,11 @@
 
 export const LOG_TEMPLATE: string = `
 <div>
-    <h2 class="h2-log-override">{{'SIDE_NAV.LOGS' | translate}}</h2>
+    <h2 class="h2-log-override" *ngIf="withTitle">{{'SIDE_NAV.LOGS' | translate}}</h2>
     <div class="row flex-items-xs-between flex-items-xs-bottom">
         <div></div>
         <div class="action-head-pos">
-            <hbr-filter filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)" [currentValue]="currentTerm"></hbr-filter>
+            <hbr-filter [withDivider]="true" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)" [currentValue]="currentTerm"></hbr-filter>
             <span (click)="refresh()" class="refresh-btn">
             <clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress"></clr-icon>
             <span class="spinner spinner-inline" [hidden]="!inProgress"></span>
@@ -47,6 +47,7 @@ export const LOG_STYLES: string = `
 
 .action-head-pos {
     padding-right: 18px;
+    height: 24px;
 }
 
 .refresh-btn {
@@ -54,7 +55,7 @@ export const LOG_STYLES: string = `
 }
 
 .refresh-btn:hover {
-    color: #00bfff;
+    color: #007CBB;
 }
 
 .custom-lines-button {
diff --git a/src/ui_ng/lib/src/replication/replication.component.css.ts b/src/ui_ng/lib/src/replication/replication.component.css.ts
index 7e5276d15..850dfc259 100644
--- a/src/ui_ng/lib/src/replication/replication.component.css.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.css.ts
@@ -1,11 +1,18 @@
 export const REPLICATION_STYLE: string = `
+.refresh-btn {
+    cursor: pointer;
+}
+
+.refresh-btn:hover {
+    color: #007CBB;
+}
+
 .option-left {
   padding-left: 16px;
-  margin-top: 24px;
+  margin-top: 12px;
 }
 .option-right {
   padding-right: 16px;
-  margin-top: 18px;
 }
 
 .option-left-down {
diff --git a/src/ui_ng/lib/src/replication/replication.component.html.ts b/src/ui_ng/lib/src/replication/replication.component.html.ts
index 9c8f4984c..283a45e3c 100644
--- a/src/ui_ng/lib/src/replication/replication.component.html.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.html.ts
@@ -1,21 +1,21 @@
 export const REPLICATION_TEMPLATE: string = `
 <div class="row">
   <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
-    <div class="row flex-items-xs-between">
+    <div class="row flex-items-xs-between" style="height:24px;">
       <div class="flex-xs-middle option-left">
         <button *ngIf="projectId" class="btn btn-link" (click)="openModal()"><clr-icon shape="add"></clr-icon> {{'REPLICATION.REPLICATION_RULE' | translate}}</button>
         <create-edit-rule [projectId]="projectId" (reload)="reloadRules($event)"></create-edit-rule>
       </div>
       <div class="flex-xs-middle option-right">
-        <div class="select" style="float: left;">
+        <div class="select" style="float: left; top: 9px;">
           <select (change)="doFilterRuleStatus($event)">
             <option *ngFor="let r of ruleStatus" value="{{r.key}}">{{r.description | translate}}</option>
           </select>
         </div> 
-        <hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchRules($event)" [currentValue]="search.ruleName"></hbr-filter>
-        <a href="javascript:void(0)" (click)="refreshRules()">
+        <hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_POLICIES_PLACEHOLDER" | translate}}' (filter)="doSearchRules($event)" [currentValue]="search.ruleName"></hbr-filter>
+        <span class="refresh-btn" (click)="refreshRules()">
           <clr-icon shape="refresh"></clr-icon>
-        </a>
+        </span>
       </div>
     </div>
     </div>
@@ -23,14 +23,14 @@ export const REPLICATION_TEMPLATE: string = `
       <hbr-list-replication-rule #listReplicationRule [projectId]="projectId" (selectOne)="selectOneRule($event)" (editOne)="openEditRule($event)" (reload)="reloadRules($event)" [loading]="loading" [withReplicationJob]="withReplicationJob" (redirect)="customRedirect($event)"></hbr-list-replication-rule>
     </div>
     <div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
-      <div class="row flex-items-xs-between">
+      <div class="row flex-items-xs-between" style="height:60px;">
         <h5 class="flex-items-xs-bottom option-left-down" style="margin-left: 14px;">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
         <div class="flex-items-xs-bottom option-right-down">
           <button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
-          <hbr-filter filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></hbr-filter>
-          <a href="javascript:void(0)" (click)="refreshJobs()">
+          <hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filter)="doSearchJobs($event)" [currentValue]="search.repoName" ></hbr-filter>
+          <span class="refresh-btn" (click)="refreshJobs()">
             <clr-icon shape="refresh"></clr-icon>
-          </a>
+          </span>
         </div>
       </div>
       <div class="row flex-items-xs-right option-right" [hidden]="currentJobSearchOption === 0">
diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.css.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.css.ts
index fdbaab321..3a0a4588e 100644
--- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.css.ts
+++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.css.ts
@@ -1,14 +1,17 @@
 export const REPOSITORY_STACKVIEW_STYLES: string = `
 .option-right {
   padding-right: 16px;
-  margin-bottom: 12px;
 }
-
 .sub-grid-custom {
   position: relative;
   left: 40px;
 }
-
+.refresh-btn {
+    cursor: pointer;
+}
+.refresh-btn:hover {
+    color: #007CBB;
+}
 :host >>> .datagrid .datagrid-body .datagrid-row {
   overflow-x: hidden;
   overflow-y: hidden;
diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
index fe0f6a31a..b9d935914 100644
--- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
+++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
@@ -1,11 +1,11 @@
 export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
-<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
+<div>
 <div class="row">
-  <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">  
+  <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="height: 24px;">  
     <div class="row flex-items-xs-right option-right">
       <div class="flex-xs-middle">
-        <hbr-filter filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>  
-        <a href="javascript:void(0)" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></a>
+        <hbr-filter [withDivider]="true" filterPlaceholder="{{'REPOSITORY.FILTER_FOR_REPOSITORIES' | translate}}" (filter)="doSearchRepoNames($event)"></hbr-filter>  
+        <span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
       </div>
     </div>
   </div>
@@ -31,4 +31,6 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
     </clr-datagrid>
   </div>
 </div>
+<confirmation-dialog #confirmationDialog (confirmAction)="confirmDeletion($event)"></confirmation-dialog>
+</div>
 `;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts b/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts
index 039415b55..ecdbab517 100644
--- a/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts
+++ b/src/ui_ng/lib/src/tag/tag-detail.component.spec.ts
@@ -8,6 +8,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
 import { Tag, VulnerabilitySummary } from '../service/interface';
 import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
 import { TagService, TagDefaultService, ScanningResultService, ScanningResultDefaultService } from '../service/index';
+import { FilterComponent } from '../filter/index';
 
 describe('TagDetailComponent (inline template)', () => {
 
@@ -47,7 +48,8 @@ describe('TagDetailComponent (inline template)', () => {
       ],
       declarations: [
         TagDetailComponent,
-        ResultGridComponent
+        ResultGridComponent,
+        FilterComponent
       ],
       providers: [
         ErrorHandler,
diff --git a/src/ui_ng/lib/src/utils.ts b/src/ui_ng/lib/src/utils.ts
index 3c5fb9add..cd7c8eb77 100644
--- a/src/ui_ng/lib/src/utils.ts
+++ b/src/ui_ng/lib/src/utils.ts
@@ -118,4 +118,9 @@ export class CustomComparator<T> implements Comparator<T> {
     }
     return comp;
   }
-}
\ No newline at end of file
+}
+
+/**
+ * The default page size
+ */
+export const DEFAULT_PAGE_SIZE: number = 15;
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.spec.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.spec.ts
index 1213c62ad..1ef98afa2 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.spec.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.spec.ts
@@ -10,6 +10,7 @@ import { ScanningResultService, ScanningResultDefaultService } from '../service/
 import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
 import { ErrorHandler } from '../error-handler/index';
 import { SharedModule } from '../shared/shared.module';
+import { FilterComponent } from '../filter/index';
 
 describe('ResultGridComponent (inline template)', () => {
   let component: ResultGridComponent;
@@ -26,7 +27,7 @@ describe('ResultGridComponent (inline template)', () => {
       imports: [
         SharedModule
       ],
-      declarations: [ResultGridComponent],
+      declarations: [ResultGridComponent, FilterComponent],
       providers: [
         ErrorHandler,
         { provide: SERVICE_CONFIG, useValue: testConfig },
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.ts
index a3d340b62..99e4750f5 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/result-grid.component.ts
@@ -38,4 +38,12 @@ export class ResultGridComponent implements OnInit {
             })
             .catch(error => { this.errorHandler.error(error) })
     }
+
+    filterVulnerabilities(terms: string): void {
+        console.log(terms);
+    }
+
+    refresh(): void {
+        this.loadResults(this.tagId);
+    }
 }
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts b/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
index c10a11f7a..5affed773 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/scanning.css.ts
@@ -4,11 +4,9 @@ export const SCANNING_STYLES: string = `
     height: 24px;
     display: inline-block;
 }
-
 .bar-state {
     text-align: center !important;
 }
-
 .scanning-button {
     height: 24px;
     margin-top: 0px;
@@ -17,62 +15,58 @@ export const SCANNING_STYLES: string = `
     top: -6px;
     position: relative;
 }
-
 .tip-wrapper {
     display: inline-block;
     height: 16px;
     max-height: 16px;
     max-width: 150px;
 }
-
 .tip-position {
     margin-left: -4px;
 }
-
 .tip-block {
     margin-left: -4px;
 }
-
 .bar-block-high {
     background-color: red;
 }
-
 .bar-block-medium {
     background-color: orange;
 }
-
 .bar-block-low {
     background-color: yellow;
 }
-
 .bar-block-none {
     background-color: green;
 }
-
 .bar-block-unknown {
     background-color: grey;
 }
-
 .bar-tooltip-font {
     font-size: 13px;
     color: #ffffff;
 }
-
 .bar-tooltip-font-title {
     font-weight: 600;
 }
-
 .bar-summary {
     margin-top: 12px;
     text-align: left;
 }
-
 .bar-scanning-time {
     margin-top: 12px;
 }
-
 .bar-summary-item {
     margin-top: 3px;
     margin-bottom: 3px;
 }
+.option-right {
+  padding-right: 16px;
+}
+.refresh-btn {
+    cursor: pointer;
+}
+.refresh-btn:hover {
+    color: #007CBB;
+}
 `; 
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
index a7ee640d6..f65de9497 100644
--- a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
+++ b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts
@@ -42,7 +42,16 @@ export const TIP_COMPONENT_HTML: string = `
 `;
 
 export const GRID_COMPONENT_HTML: string = `
-<div>
+<div class="row">
+  <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12" style="height: 24px;">  
+    <div class="row flex-items-xs-right option-right">
+      <div class="flex-xs-middle">
+        <hbr-filter [withDivider]="true" filterPlaceholder="{{'VULNERABILITY.PLACEHOLDER' | translate}}" (filter)="filterVulnerabilities($event)"></hbr-filter>  
+        <span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
+      </div>
+    </div>
+  </div>
+  <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
     <clr-datagrid>
         <clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
         <clr-dg-column [clrDgField]="'severity'">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>
@@ -71,6 +80,7 @@ export const GRID_COMPONENT_HTML: string = `
             <clr-dg-pagination #pagination [clrDgPageSize]="25" [clrDgTotalItems]="scanningResults.length"></clr-dg-pagination>
         </clr-dg-footer>
     </clr-datagrid>
+  </div>
 </div>
 `;
 

From f981415b5b64ee7c51444151c5cd5d57ed5b7b58 Mon Sep 17 00:00:00 2001
From: Steven Zou <szou@vmware.com>
Date: Tue, 13 Jun 2017 22:24:38 +0800
Subject: [PATCH 2/3] make tag name clickable in repo-tag-stack view

---
 src/ui_ng/lib/src/tag/tag.component.html.ts |  2 +-
 src/ui_ng/lib/src/tag/tag.component.ts      | 10 +++++++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/ui_ng/lib/src/tag/tag.component.html.ts b/src/ui_ng/lib/src/tag/tag.component.html.ts
index e6dd182e5..dcf1a4f7d 100644
--- a/src/ui_ng/lib/src/tag/tag.component.html.ts
+++ b/src/ui_ng/lib/src/tag/tag.component.html.ts
@@ -27,7 +27,7 @@ export const TAG_TEMPLATE = `
         <button class="action-item" (click)="showDigestId(t)">{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
         <button class="action-item" [hidden]="!hasProjectAdminRole" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</button>
       </clr-dg-action-overflow>
-      <clr-dg-cell>{{t.name}}</clr-dg-cell>
+      <clr-dg-cell><a href="javascript:void(0)" (click)="onTagClick(t)">{{t.name}}</a></clr-dg-cell>
       <clr-dg-cell>docker pull {{registryUrl}}/{{repoName}}:{{t.name}}</clr-dg-cell>
       <clr-dg-cell *ngIf="withNotary"  [ngSwitch]="t.signature !== null">
         <clr-icon shape="check" *ngSwitchCase="true" style="color: #1D5100;"></clr-icon>
diff --git a/src/ui_ng/lib/src/tag/tag.component.ts b/src/ui_ng/lib/src/tag/tag.component.ts
index 361fcfef4..a32e57db8 100644
--- a/src/ui_ng/lib/src/tag/tag.component.ts
+++ b/src/ui_ng/lib/src/tag/tag.component.ts
@@ -51,6 +51,7 @@ export class TagComponent implements OnInit {
   @Input() withNotary: boolean;
 
   @Output() refreshRepo = new EventEmitter<boolean>();
+  @Output() tagClickEvent = new EventEmitter<Tag>();
 
   tags: Tag[];
 
@@ -105,7 +106,7 @@ export class TagComponent implements OnInit {
       this.errorHandler.error('Repo name cannot be unset.');
       return;
     }
-    
+
     this.retrieve();
   }
 
@@ -161,7 +162,14 @@ export class TagComponent implements OnInit {
       this.showTagManifestOpened = true;
     }
   }
+
   selectAndCopy($event: any) {
     $event.target.select();
   }
+
+  onTagClick(tag: Tag): void {
+    if (tag) {
+      this.tagClickEvent.emit(tag);
+    }
+  }
 }
\ No newline at end of file

From 23635b69665d44af34fce1899fd136f4e09ff570 Mon Sep 17 00:00:00 2001
From: Steven Zou <szou@vmware.com>
Date: Wed, 14 Jun 2017 00:00:22 +0800
Subject: [PATCH 3/3] add placeholders to the datagird

---
 src/ui_ng/lib/README.md                       | 70 +++++++++++++++----
 .../src/endpoint/endpoint.component.html.ts   |  1 +
 .../list-replication-rule.component.html.ts   |  1 +
 .../list-repository.component.html.ts         |  1 +
 .../replication/replication.component.html.ts |  1 +
 .../repository-stackview.component.html.ts    |  2 +-
 .../repository-stackview.component.ts         | 17 ++++-
 src/ui_ng/lib/src/tag/tag.component.html.ts   |  1 +
 8 files changed, 79 insertions(+), 15 deletions(-)

diff --git a/src/ui_ng/lib/README.md b/src/ui_ng/lib/README.md
index 6fa8c0ec0..b42a7e74c 100644
--- a/src/ui_ng/lib/README.md
+++ b/src/ui_ng/lib/README.md
@@ -52,10 +52,9 @@ If no parameters are passed to **'forRoot'**, the module will be initialized wit
 
 * **Registry log view**
 
+Use **withTitle** to set whether self-contained a header with title or not. Default is **false**, that means no header is existing.
 ```
-//No @Input properties
-
-<hbr-log></hbr-log>
+<hbr-log [withTitle]="..."></hbr-log>
 ```
 
 * **Replication Management View**
@@ -85,8 +84,18 @@ If **projectId** is set to the id of specified project, then only show the repli
 
 **hasProjectAdminRole** is a user session related property to determined whether the current user has project administrator role. Some action menus might be disabled based on this property.
 
+**tagClickEvent** is an @output event emitter for you to catch the tag click events.
+
 ```
-<hbr-repository-stackview [projectId]="..." [hasSignedIn]="..." [hasProjectAdminRole]="..."></hbr-repository-stackview>
+<hbr-repository-stackview [projectId]="..." [hasSignedIn]="..." [hasProjectAdminRole]="..." (tagClickEvent)="watchTagClickEvent($event)"></hbr-repository-stackview>
+
+...
+
+watchTagClickEvent(tag: Tag): void {
+    //Process tag
+    ...
+}
+
 ```
 
 ## Configurations
@@ -96,7 +105,7 @@ All the related configurations are defined in the **HarborModuleConfig** interfa
 The base configuration for the module. Mainly used to define the relevant endpoints of services which are in charge of retrieving data from backend APIs. It's a 'OpaqueToken' and defined by 'IServiceConfig' interface. If **config** is not set, the default value will be used.
 ```
 export const DefaultServiceConfig: IServiceConfig = {
-  systemInfoEndpoint: "/api/system",
+  systemInfoEndpoint: "/api/systeminfo",
   repositoryBaseEndpoint: "/api/repositories",
   logBaseEndpoint: "/api/logs",
   targetBaseEndpoint: "/api/targets",
@@ -126,6 +135,8 @@ HarborLibraryModule.forRoot({
 
 ```
 It supports partially overriding. For the items not overridden, default values will be adopted. The items contained in **config** are:
+* **systemInfoEndpoint:** The base endpoint of the service used to get the related system configurations. Default value is "/api/systeminfo".
+
 * **repositoryBaseEndpoint:** The base endpoint of the service used to handle the repositories of registry and/or tags of repository. Default value is "/api/repositories".
 
 * **logBaseEndpoint:** The base endpoint of the service used to handle the recent access logs. Default is "/api/logs".
@@ -578,32 +589,39 @@ HarborLibraryModule.forRoot({
 * **ScanningResultService:** Get the vulnerabilities scanning results for the specified tag.
 ```
 @Injectable()
+/**
+ * Get the vulnerabilities scanning results for the specified tag.
+ * 
+ * @export
+ * @abstract
+ * @class ScanningResultService
+ */
 export class MyScanningResultService extends ScanningResultService {
     /**
      * Get the summary of vulnerability scanning result.
      * 
      * @abstract
      * @param {string} tagId
-     * @returns {(Observable<ScanningResultSummary> | Promise<ScanningResultSummary> | ScanningResultSummary)}
+     * @returns {(Observable<VulnerabilitySummary> | Promise<VulnerabilitySummary> | VulnerabilitySummary)}
      * 
      * @memberOf ScanningResultService
      */
-    getScanningResultSummary(tagId: string): Observable<ScanningResultSummary> | Promise<ScanningResultSummary> | ScanningResultSummary {
-        ...
-    }
+     getVulnerabilityScanningSummary(tagId: string): Observable<VulnerabilitySummary> | Promise<VulnerabilitySummary> | VulnerabilitySummary{
+         ...
+     }
 
     /**
      * Get the detailed vulnerabilities scanning results.
      * 
      * @abstract
      * @param {string} tagId
-     * @returns {(Observable<ScanningDetailResult[]> | Promise<ScanningDetailResult[]> | ScanningDetailResult[])}
+     * @returns {(Observable<VulnerabilityItem[]> | Promise<VulnerabilityItem[]> | VulnerabilityItem[])}
      * 
      * @memberOf ScanningResultService
      */
-    getScanningResults(tagId: string): Observable<ScanningDetailResult[]> | Promise<ScanningDetailResult[]> | ScanningDetailResult[] {
-        ...
-    }
+     getVulnerabilityScanningResults(tagId: string): Observable<VulnerabilityItem[]> | Promise<VulnerabilityItem[]> | VulnerabilityItem[]{
+         ...
+     }
 }
 
 ...
@@ -612,4 +630,30 @@ HarborLibraryModule.forRoot({
 })
 ...
 
+```
+
+* **SystemInfoService:** Get related system configurations.
+```
+/**
+ * Get System information about current backend server.
+ * @abstract
+ * @class
+ */
+export class MySystemInfoService extends SystemInfoService {
+  /**
+   *  Get global system information.
+   *  @abstract
+   *  @returns 
+   */
+   getSystemInfo(): Observable<SystemInfo> | Promise<SystemInfo> | SystemInfo {
+       ...
+   }
+}
+
+...
+HarborLibraryModule.forRoot({
+    systemInfoService: { provide: SystemInfoService, useClass: MySystemInfoService }
+})
+...
+
 ```
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
index ebd4ce84d..1a15723a1 100644
--- a/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
+++ b/src/ui_ng/lib/src/endpoint/endpoint.component.html.ts
@@ -20,6 +20,7 @@ export const ENDPOINT_TEMPLATE: string = `
                 <clr-dg-column [clrDgField]="'name'">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
                 <clr-dg-column [clrDgField]="'endpoint'">{{'DESTINATION.URL' | translate}}</clr-dg-column>
                 <clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
+                <clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
                 <clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
                     <clr-dg-action-overflow>
                         <button class="action-item" (click)="editTarget(t)">{{'DESTINATION.TITLE_EDIT' | translate}}</button>
diff --git a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
index 3292a7f42..f19268eee 100644
--- a/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
+++ b/src/ui_ng/lib/src/list-replication-rule/list-replication-rule.component.html.ts
@@ -7,6 +7,7 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
     <clr-dg-column [clrDgField]="'target_name'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
     <clr-dg-column [clrDgSortBy]="startTimeComparator">{{'REPLICATION.LAST_START_TIME' | translate}}</clr-dg-column>
     <clr-dg-column [clrDgSortBy]="enabledComparator">{{'REPLICATION.ACTIVATION' | translate}}</clr-dg-column>
+    <clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
     <clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" (click)="selectRule(p)" [style.backgroundColor]="(!projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
         <clr-dg-action-overflow>
             <button class="action-item" (click)="editRule(p)">{{'REPLICATION.EDIT_POLICY' | translate}}</button>
diff --git a/src/ui_ng/lib/src/list-repository/list-repository.component.html.ts b/src/ui_ng/lib/src/list-repository/list-repository.component.html.ts
index 1a5e65037..158fac220 100644
--- a/src/ui_ng/lib/src/list-repository/list-repository.component.html.ts
+++ b/src/ui_ng/lib/src/list-repository/list-repository.component.html.ts
@@ -3,6 +3,7 @@ export const LIST_REPOSITORY_TEMPLATE = `
   <clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
   <clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
   <clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
+  <clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
   <clr-dg-row *clrDgItems="let r of repositories" [clrDgItem]='r'>
      <clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
        <button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
diff --git a/src/ui_ng/lib/src/replication/replication.component.html.ts b/src/ui_ng/lib/src/replication/replication.component.html.ts
index 283a45e3c..5e89a2843 100644
--- a/src/ui_ng/lib/src/replication/replication.component.html.ts
+++ b/src/ui_ng/lib/src/replication/replication.component.html.ts
@@ -53,6 +53,7 @@ export const REPLICATION_TEMPLATE: string = `
         <clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
         <clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.END_TIME' | translate}}</clr-dg-column>
         <clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
+        <clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder>
         <clr-dg-row *clrDgItems="let j of jobs" [clrDgItem]='j'>
             <clr-dg-cell>{{j.repository}}</clr-dg-cell>
             <clr-dg-cell>{{j.status}}</clr-dg-cell>
diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
index b9d935914..8ab942517 100644
--- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
+++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.html.ts
@@ -21,7 +21,7 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
         <clr-dg-cell>{{r.name}}</clr-dg-cell>
         <clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
         <clr-dg-cell>{{r.pull_count}}</clr-dg-cell>        
-        <hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
+        <hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" (tagClickEvent)="watchTagClickEvt($event)" class="sub-grid-custom" [repoName]="r.name" [registryUrl]="registryUrl" [withNotary]="withNotary" [hasSignedIn]="hasSignedIn" [hasProjectAdminRole]="hasProjectAdminRole" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
       </clr-dg-row>
       <clr-dg-footer> 
         {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
diff --git a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts
index c6d1a5ce3..7321a1134 100644
--- a/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts
+++ b/src/ui_ng/lib/src/repository-stackview/repository-stackview.component.ts
@@ -1,4 +1,13 @@
-import { Component, Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
+import {
+  Component,
+  Input,
+  Output,
+  OnInit,
+  ViewChild,
+  ChangeDetectionStrategy,
+  ChangeDetectorRef,
+  EventEmitter
+} from '@angular/core';
 import { TranslateService } from '@ngx-translate/core';
 import { Comparator } from 'clarity-angular';
 
@@ -21,6 +30,7 @@ import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation
 import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message';
 import { ConfirmationAcknowledgement } from '../confirmation-dialog/confirmation-state-message';
 import { Subscription } from 'rxjs/Subscription';
+import { Tag } from '../service/interface';
 
 @Component({
   selector: 'hbr-repository-stackview',
@@ -34,6 +44,7 @@ export class RepositoryStackviewComponent implements OnInit {
 
   @Input() hasSignedIn: boolean;
   @Input() hasProjectAdminRole: boolean;
+  @Output() tagClickEvent = new EventEmitter<Tag>();
 
   lastFilteredRepoName: string;
   repositories: Repository[];
@@ -120,4 +131,8 @@ export class RepositoryStackviewComponent implements OnInit {
   refresh() {
     this.retrieve();
   }
+
+  watchTagClickEvt(tag: Tag): void {
+    this.tagClickEvent.emit(tag);
+  }
 }
\ No newline at end of file
diff --git a/src/ui_ng/lib/src/tag/tag.component.html.ts b/src/ui_ng/lib/src/tag/tag.component.html.ts
index dcf1a4f7d..1fb6ba5f0 100644
--- a/src/ui_ng/lib/src/tag/tag.component.html.ts
+++ b/src/ui_ng/lib/src/tag/tag.component.html.ts
@@ -22,6 +22,7 @@ export const TAG_TEMPLATE = `
     <clr-dg-column [clrDgField]="'docker_version'">{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
     <clr-dg-column [clrDgField]="'architecture'">{{'REPOSITORY.ARCHITECTURE' | translate}}</clr-dg-column>
     <clr-dg-column [clrDgField]="'os'">{{'REPOSITORY.OS' | translate}}</clr-dg-column>
+    <clr-dg-placeholder>{{'TGA.PLACEHOLDER' | translate }}</clr-dg-placeholder>
     <clr-dg-row *clrDgItems="let t of tags" [clrDgItem]='t'>
       <clr-dg-action-overflow>
         <button class="action-item" (click)="showDigestId(t)">{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>