AMBARI-21740 Log Search UI: implement multiple options select for filters. 
(ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/b00da9b5
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/b00da9b5
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/b00da9b5

Branch: refs/heads/branch-feature-logsearch-ui
Commit: b00da9b5e9e5a178b1c91af1c6a6855b42cbbf3a
Parents: 01bcae9
Author: ababiichuk <ababiic...@hortonworks.com>
Authored: Thu Aug 17 13:11:00 2017 +0300
Committer: ababiichuk <ababiic...@hortonworks.com>
Committed: Thu Aug 17 13:11:00 2017 +0300

----------------------------------------------------------------------
 .../queries/service-logs-query-params.class.ts  |  2 +-
 .../accordion-panel.component.html              |  2 +-
 .../dropdown-button.component.html              |  2 +-
 .../dropdown-button.component.ts                | 23 ++++++---
 .../dropdown-list/dropdown-list.component.html  | 10 +++-
 .../dropdown-list/dropdown-list.component.less  | 15 ++++++
 .../dropdown-list/dropdown-list.component.ts    |  3 ++
 .../filter-button/filter-button.component.ts    | 10 ++--
 .../filter-dropdown.component.ts                |  4 ++
 .../filters-panel/filters-panel.component.html  |  6 +--
 .../logs-container.component.spec.ts            |  3 ++
 .../logs-container/logs-container.component.ts  | 20 +++++---
 .../logs-list/logs-list.component.html          |  2 +-
 .../menu-button/menu-button.component.html      |  2 +-
 .../menu-button/menu-button.component.ts        |  3 ++
 .../src/app/components/variables.less           | 20 +++++++-
 .../src/app/models/app-state.model.ts           |  6 ++-
 .../src/app/models/store.model.ts               | 20 +++-----
 .../app/services/component-actions.service.ts   |  6 ++-
 .../src/app/services/filtering.service.ts       | 29 ++++-------
 .../src/app/services/mock-api-data.service.ts   | 22 +++++---
 .../src/app/services/utils.service.spec.ts      | 53 ++++++++++++++++++++
 .../src/app/services/utils.service.ts           | 11 ++++
 23 files changed, 203 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/classes/queries/service-logs-query-params.class.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/classes/queries/service-logs-query-params.class.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/classes/queries/service-logs-query-params.class.ts
index 0914864..125b237 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/classes/queries/service-logs-query-params.class.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/classes/queries/service-logs-query-params.class.ts
@@ -21,7 +21,7 @@ import {AuditLogsQueryParams} from 
'@app/classes/queries/audit-logs-query-params
 export class ServiceLogsQueryParams extends AuditLogsQueryParams {
   level?: string;
   host_name?: string;
-  component_name?: string;
+  mustBe?: string;
   file_name?: string;
   bundle_id?: string;
   hostList?: string;

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/accordion-panel/accordion-panel.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/accordion-panel/accordion-panel.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/accordion-panel/accordion-panel.component.html
index b2ce3a0..1b134c3 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/accordion-panel/accordion-panel.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/accordion-panel/accordion-panel.component.html
@@ -19,7 +19,7 @@
   <div class="panel-body">
     <ng-template [ngTemplateOutlet]="template"></ng-template>
     <div class="accordion-toggle">
-      <span class="fa collapsed toggle-icon" data-toggle="collapse" 
[attr.data-target]="'#' + toggleId" aria-expanded="false">&nbsp;</span>
+      <span class="fa collapsed toggle-icon" data-toggle="collapse" 
attr.data-target="#{{toggleId}}" aria-expanded="false">&nbsp;</span>
     </div>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
index b777b52..a16b205 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.html
@@ -21,5 +21,5 @@
     <span *ngIf="!isMultipleChoice">{{selectedLabel | translate}}</span> <span 
class="caret"></span>
   </button>
   <ul data-component="dropdown-list" [ngClass]="{'dropdown-menu': true, 
'dropdown-menu-right': isRightAlign}"
-      [items]="options" (selectedItemChange)="updateValue($event)"></ul>
+      [items]="options" [isMultipleChoice]="isMultipleChoice" 
(selectedItemChange)="updateValue($event)"></ul>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
index 029d87b..63103ee 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-button/dropdown-button.component.ts
@@ -73,13 +73,22 @@ export class DropdownButtonComponent implements OnInit {
     this.selectedValue = value;
   }
 
-  updateValue(options: any) {
-    const value = options && options.value;
-    if (this.utils.valueHasChanged(this.value, value)) {
-      this.value = value;
-      this.selectedLabel = options.label;
-      if (this.action) {
-        this.actions[this.action](value, ...this.additionalArgs);
+  updateValue(eventOptions: any): void {
+    const value = eventOptions && eventOptions.value,
+      action = this.action && this.actions[this.action];
+    if (this.isMultipleChoice) {
+      this.value = this.utils.updateMultiSelectValue(this.value, value, 
eventOptions.isChecked);
+      this.options.find(item => item.value === value).isChecked = 
eventOptions.isChecked;
+      if (action) {
+        action(this.options.filter(item => item.isChecked).map(item => 
item.value), ...this.additionalArgs);
+      }
+    } else {
+      if (this.utils.valueHasChanged(this.value, value)) {
+        this.value = value;
+        this.selectedLabel = eventOptions.label;
+        if (action) {
+          action(this.value, ...this.additionalArgs);
+        }
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.html
index db4ee79..d9d1de0 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.html
@@ -16,5 +16,13 @@
 -->
 
 <li *ngFor="let item of items">
-  <a href="#" (click)="changeSelectedItem({value: item.value, label: 
item.label})">{{item.label | translate}}</a>
+  <label class="list-item-label" *ngIf="isMultipleChoice">
+    <input type="checkbox" [attr.id]="item.id || item.value" 
[attr.checked]="item.isChecked ? 'checked' : null"
+           (change)="changeSelectedItem({value: item.value, isChecked: 
$event.currentTarget.checked})">
+    <label [attr.for]="item.id || item.value">{{item.label | 
translate}}</label>
+  </label>
+  <span class="list-item-label" *ngIf="!isMultipleChoice"
+        (click)="changeSelectedItem({value: item.value, label: item.label})">
+    {{item.label | translate}}
+  </span>
 </li>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.less
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.less
index 0853883..804bf48 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.less
@@ -16,7 +16,22 @@
  * limitations under the License.
  */
 
+@import '../variables';
+
 :host {
   max-height: 500px; // TODO get rid of magic number, base on actual design
   overflow-y: auto;
+
+  .list-item-label {
+    .dropdown-item-default;
+
+    label {
+      margin-bottom: 0;
+      cursor: pointer;
+    }
+
+    input[type=checkbox]:checked + label:after {
+      top: @checkbox-top;
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.ts
index 3e9a445..555e4a8 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/dropdown-list/dropdown-list.component.ts
@@ -31,6 +31,9 @@ export class DropdownListComponent {
   @Input()
   defaultAction: Function;
 
+  @Input()
+  isMultipleChoice?: boolean = false;
+
   @Output()
   selectedItemChange: EventEmitter<any> = new EventEmitter();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-button/filter-button.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-button/filter-button.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-button/filter-button.component.ts
index 2f8c969..9940d73 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-button/filter-button.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-button/filter-button.component.ts
@@ -56,10 +56,14 @@ export class FilterButtonComponent extends 
MenuButtonComponent implements Contro
     this.onChange(newValue);
   }
 
-  updateValue(options: any) {
+  updateValue(options: any): void {
     const value = options && options.value;
-    if (this.utils.valueHasChanged(this.selectedValue, value)) {
-      this.value = value;
+    if (this.isMultipleChoice) {
+      this.value = this.utils.updateMultiSelectValue(this.value, value, 
options.isChecked);
+    } else {
+      if (this.utils.valueHasChanged(this.selectedValue, value)) {
+        this.value = value;
+      }
     }
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
index c414782..9e5a6f1 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filter-dropdown/filter-dropdown.component.ts
@@ -41,6 +41,10 @@ export class FilterDropdownComponent extends 
DropdownButtonComponent implements
 
   private onChange: (fn: any) => void;
 
+  get value(): any {
+    return this.selectedValue;
+  }
+
   set value(newValue: any) {
     this.selectedValue = newValue;
     this.onChange(newValue);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filters-panel/filters-panel.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filters-panel/filters-panel.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filters-panel/filters-panel.component.html
index d2de5e2..4f751fd 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filters-panel/filters-panel.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/filters-panel/filters-panel.component.html
@@ -18,7 +18,7 @@
 <form class="col-md-12" [formGroup]="filtersForm">
   <div class="form-inline filter-input-container col-md-8">
     <filter-dropdown [label]="filters.clusters.label" 
formControlName="clusters" [options]="filters.clusters.options"
-                     
[defaultLabel]="filters.clusters.defaultLabel"></filter-dropdown>
+                     [defaultLabel]="filters.clusters.defaultLabel" 
[isMultipleChoice]="true"></filter-dropdown>
     <filter-text-field [label]="filters.text.label"
                        formControlName="text"></filter-text-field>
     <filter-dropdown formControlName="timeRange" 
[options]="filters.timeRange.options"
@@ -35,9 +35,9 @@
     </a>
     <filter-button formControlName="components" 
[label]="filters.components.label"
                    [iconClass]="filters.components.iconClass" 
[subItems]="filters.components.options"
-                   isRightAlign="true"></filter-button>
+                   [isMultipleChoice]="true" 
isRightAlign="true"></filter-button>
     <filter-button formControlName="levels" [label]="filters.levels.label" 
[iconClass]="filters.levels.iconClass"
-                   [subItems]="filters.levels.options" 
isRightAlign="true"></filter-button>
+                   [subItems]="filters.levels.options" 
[isMultipleChoice]="true" isRightAlign="true"></filter-button>
     <menu-button label="filter.capture" iconClass="fa 
fa-caret-right"></menu-button>
   </div>
 </form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
index 4d6509b..7b5169b 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.spec.ts
@@ -20,6 +20,7 @@ import {async, ComponentFixture, TestBed} from 
'@angular/core/testing';
 import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
 import {StoreModule} from '@ngrx/store';
 import {AppSettingsService, appSettings} from 
'@app/services/storage/app-settings.service';
+import {AppStateService, appState} from 
'@app/services/storage/app-state.service';
 import {ClustersService, clusters} from 
'@app/services/storage/clusters.service';
 import {ComponentsService, components} from 
'@app/services/storage/components.service';
 import {AuditLogsService, auditLogs} from 
'@app/services/storage/audit-logs.service';
@@ -51,6 +52,7 @@ describe('LogsContainerComponent', () => {
       imports: [
         StoreModule.provideStore({
           appSettings,
+          appState,
           clusters,
           components,
           auditLogs,
@@ -66,6 +68,7 @@ describe('LogsContainerComponent', () => {
           useValue: httpClient
         },
         AppSettingsService,
+        AppStateService,
         ClustersService,
         ComponentsService,
         AuditLogsService,

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
index 40e7d89..57a147c 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-container/logs-container.component.ts
@@ -27,6 +27,7 @@ import {AuditLogsFieldsService} from 
'@app/services/storage/audit-logs-fields.se
 import {ServiceLogsService} from '@app/services/storage/service-logs.service';
 import {ServiceLogsFieldsService} from 
'@app/services/storage/service-logs-fields.service';
 import {ServiceLogsHistogramDataService} from 
'@app/services/storage/service-logs-histogram-data.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
 import {AuditLog} from '@app/models/audit-log.model';
 import {ServiceLog} from '@app/models/service-log.model';
 import {LogField} from '@app/models/log-field.model';
@@ -38,7 +39,7 @@ import {LogField} from '@app/models/log-field.model';
 })
 export class LogsContainerComponent implements OnInit {
 
-  constructor(private httpClient: HttpClientService, private auditLogsStorage: 
AuditLogsService, private auditLogsFieldsStorage: AuditLogsFieldsService, 
private serviceLogsStorage: ServiceLogsService, private 
serviceLogsFieldsStorage: ServiceLogsFieldsService, private 
serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private 
filtering: FilteringService) {
+  constructor(private httpClient: HttpClientService, private auditLogsStorage: 
AuditLogsService, private auditLogsFieldsStorage: AuditLogsFieldsService, 
private serviceLogsStorage: ServiceLogsService, private 
serviceLogsFieldsStorage: ServiceLogsFieldsService, private 
serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: 
AppStateService, private filtering: FilteringService) {
     this.serviceLogsHistogramStorage.getAll().subscribe(data => {
       let histogramData = {};
       data.forEach(type => {
@@ -59,10 +60,11 @@ export class LogsContainerComponent implements OnInit {
 
   ngOnInit() {
     this.logsTypeMapObject = this.logsTypeMap[this.logsType];
+    
this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe(value => 
this.isLogsSet = value);
     this.availableColumns = 
this[this.logsTypeMapObject.fieldsModel].getAll().map(fields => {
       const availableFields = fields.filter(field => field.isAvailable),
         availableNames = availableFields.map(field => field.name);
-      if (availableNames.length) {
+      if (availableNames.length && !this.isLogsSet) {
         this.logs = this[this.logsTypeMapObject.logsModel].getAll().map(logs 
=> logs.map(log => {
           let logObject = availableNames.reduce((obj, key) => 
Object.assign(obj, {
             [key]: log[key]
@@ -72,11 +74,13 @@ export class LogsContainerComponent implements OnInit {
           }
           return logObject;
         }));
+        this.appState.setParameter(this.logsTypeMapObject.isSetFlag, true);
       }
       return availableFields.map(field => {
         return {
           value: field.name,
-          label: field.displayName || field.name
+          label: field.displayName || field.name,
+          isChecked: field.isDisplayed
         };
       });
     });
@@ -92,6 +96,8 @@ export class LogsContainerComponent implements OnInit {
   @Input()
   private logsType: string;
 
+  private isLogsSet: boolean = false;
+
   logsTypeMapObject: any;
 
   totalCount: number = 0;
@@ -100,7 +106,7 @@ export class LogsContainerComponent implements OnInit {
     clusters: ['clusters'],
     text: ['iMessage'],
     timeRange: ['end_time', 'start_time'],
-    components: ['component_name'],
+    components: ['mustBe'],
     levels: ['level'],
     sorting: ['sortType', 'sortBy'],
     pageSize: ['pageSize'],
@@ -114,11 +120,13 @@ export class LogsContainerComponent implements OnInit {
   private readonly logsTypeMap = {
     auditLogs: {
       logsModel: 'auditLogsStorage',
-      fieldsModel: 'auditLogsFieldsStorage'
+      fieldsModel: 'auditLogsFieldsStorage',
+      isSetFlag: 'isAuditLogsSet'
     },
     serviceLogs: {
       logsModel: 'serviceLogsStorage',
-      fieldsModel: 'serviceLogsFieldsStorage'
+      fieldsModel: 'serviceLogsFieldsStorage',
+      isSetFlag: 'isServiceLogsSet'
     }
   };
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
index caae24e..23a62a4 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/logs-list/logs-list.component.html
@@ -36,7 +36,7 @@
       </time>
     </div>
     <div class="col-md-6 log-content-wrapper">
-      <div class="collapse log-actions" [attr.id]="'details-' + i">
+      <div class="collapse log-actions" attr.id="details-{{i}}">
         <span class="action-icon fa fa-search"></span>
         <span class="action-icon fa fa-external-link"></span>
         <span class="action-icon fa fa-bullseye"></span>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.html
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.html
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.html
index f18285f..a0f25ff 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.html
@@ -23,5 +23,5 @@
   <a *ngIf="label" (mousedown)="onMouseDown($event)" [ngClass]="labelClass" 
(mouseup)="onMouseUp($event)"
      (click)="$event.stopPropagation()">{{label | translate}}</a>
   <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" 
(selectedItemChange)="updateValue($event)"
-      [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': 
isRightAlign}"></ul>
+      [isMultipleChoice]="isMultipleChoice" [ngClass]="{'dropdown-menu': true, 
'dropdown-menu-right': isRightAlign}"></ul>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.ts
index d1baedc..0b58ce1 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/menu-button/menu-button.component.ts
@@ -49,6 +49,9 @@ export class MenuButtonComponent {
   subItems?: any[];
 
   @Input()
+  isMultipleChoice?: boolean = false;
+
+  @Input()
   hideCaret?: boolean = false;
 
   @Input()

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/components/variables.less
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/variables.less 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/variables.less
index 7715876..0d739a1 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/components/variables.less
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/components/variables.less
@@ -22,7 +22,7 @@
 @button-border-radius: 4px;
 @input-border: 1px solid #CFD3D7;
 @button-border-radius: 4px;
-@input-group-addon-padding: 6px 12px;
+@input-group-addon-padding: 6px 0 6px 12px;
 @block-margin-top: 20px;
 @link-color: #1491C1;
 @link-hover-color: #23527C;
@@ -32,6 +32,7 @@
 @filters-panel-background-color: #FFF;
 @filters-panel-padding: 10px 0;
 @list-header-background-color: #F2F2F2;
+@checkbox-top: 4px;
 
 @fatal-color: #830A0A;
 @error-color: #E81D1D;
@@ -96,3 +97,20 @@
   bottom: 0;
   left: 0;
 }
+
+.dropdown-item-default {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: 400;
+  line-height: 1.42857143;
+  color: #333;
+  white-space: nowrap;
+  cursor: pointer;
+
+  &:hover {
+    color: #262626;
+    text-decoration: none;
+    background-color: #f5f5f5;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/models/app-state.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/app-state.model.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/app-state.model.ts
index 028c10d..2995002 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/app-state.model.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/app-state.model.ts
@@ -20,10 +20,14 @@ export interface AppState {
   isAuthorized: boolean;
   isInitialLoading: boolean;
   isLoginInProgress: boolean;
+  isAuditLogsSet: boolean;
+  isServiceLogsSet: boolean;
 }
 
 export const initialState: AppState = {
   isAuthorized: false,
   isInitialLoading: false,
-  isLoginInProgress: false
+  isLoginInProgress: false,
+  isAuditLogsSet: false,
+  isServiceLogsSet: false
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
index 13a1e68..8d71174 100644
--- a/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
+++ b/ambari-logsearch/ambari-logsearch-web-new/src/app/models/store.model.ts
@@ -35,7 +35,7 @@ export const storeActions = {
   'ARRAY.DELETE.PRIMITIVE': 'DELETE_PRIMITIVE',
   'ARRAY.DELETE.OBJECT': 'DELETE_OBJECT',
   'ARRAY.CLEAR': 'CLEAR',
-  'ARRAY.UPDATE.OBJECT': 'UPDATE_OBJECT',
+  'ARRAY.MAP': 'MAP',
 
   'OBJECT.SET': 'SET'
 };
@@ -106,13 +106,11 @@ export class CollectionModelService extends ModelService {
     });
   }
 
-  updateObjectInstance(key: string, value: any, keyToModify: string, modifier: 
(value: any) => {}): void {
+  mapCollection(modifier: (item: any) => {}): void {
     this.store.dispatch({
-      type: `${storeActions['ARRAY.UPDATE.OBJECT']}_${this.modelName}`,
+      type: `${storeActions['ARRAY.MAP']}_${this.modelName}`,
       payload: {
-        selector: item => item[key] === value,
-        key: keyToModify,
-        modifier: (currentValue) => modifier(currentValue)
+        modifier: modifier
       }
     });
   }
@@ -151,14 +149,8 @@ export function getCollectionReducer(modelName: string, 
defaultState: any = []):
         return state.filter(item => item !== action.payload);
       case `${storeActions['ARRAY.CLEAR']}_${modelName}`:
         return [];
-      case `${storeActions['ARRAY.UPDATE.OBJECT']}_${modelName}`:
-        const payload = action.payload;
-        let newState = state.slice(),
-          item = newState.find(payload.selector);
-        if (item) {
-          item[payload.key] = payload.modifier(item[payload.key]);
-        }
-        return newState;
+      case `${storeActions['ARRAY.MAP']}_${modelName}`:
+        return state.map(action.payload.modifier);
       default:
         return state;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
index f79ba45..a8235fa 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/component-actions.service.ts
@@ -44,8 +44,10 @@ export class ComponentActionsService {
     this.appSettings.setParameter('timeZone', timeZone);
   }
 
-  updateSelectedColumns(columnName: string, model: CollectionModelService): 
void {
-    model.updateObjectInstance('name', columnName, 'isDisplayed', currentValue 
=> !currentValue);
+  updateSelectedColumns(columnNames: string[], model: CollectionModelService): 
void {
+    model.mapCollection(item => Object.assign({}, item, {
+      isDisplayed: columnNames.indexOf(item.name) > -1
+    }));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/services/filtering.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/filtering.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/filtering.service.ts
index 009b263..9a589cc 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/filtering.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/filtering.service.ts
@@ -29,8 +29,12 @@ export class FilteringService {
 
   constructor(private appSettings: AppSettingsService, private 
clustersStorage: ClustersService, private componentsStorage: ComponentsService, 
private utils: UtilsService) {
     this.appSettings.getParameter('timeZone').subscribe(value => this.timeZone 
= value || this.defaultTimeZone);
-    this.clustersStorage.getAll().subscribe(clusters => 
this.filters.clusters.options = [...this.filters.clusters.options, 
...clusters.map(this.getListItem)]);
-    this.componentsStorage.getAll().subscribe(components => 
this.filters.components.options = [...this.filters.components.options, 
...components.map(this.getListItem)]);
+    this.clustersStorage.getAll().subscribe(clusters => {
+      this.filters.clusters.options = [...this.filters.clusters.options, 
...clusters.map(this.getListItem)];
+    });
+    this.componentsStorage.getAll().subscribe(components => {
+      this.filters.components.options = [...this.filters.components.options, 
...components.map(this.getListItem)];
+    });
   }
 
   private getListItem(name: string): any {
@@ -49,14 +53,8 @@ export class FilteringService {
   filters = {
     clusters: {
       label: 'filter.clusters',
-      options: [
-        {
-          label: 'filter.all',
-          value: ''
-        }
-      ],
-      defaultValue: '',
-      defaultLabel: 'filter.all'
+      options: [],
+      defaultValue: ''
     },
     text: {
       label: 'filter.message',
@@ -141,12 +139,7 @@ export class FilteringService {
     components: {
       label: 'filter.components',
       iconClass: 'fa fa-cubes',
-      options: [
-        {
-          label: 'filter.all',
-          value: ''
-        }
-      ],
+      options: [],
       defaultValue: ''
     },
     levels: {
@@ -154,10 +147,6 @@ export class FilteringService {
       iconClass: 'fa fa-sort-amount-asc',
       options: [
         {
-          label: 'filter.all',
-          value: ''
-        },
-        {
           label: 'levels.fatal',
           value: 'FATAL'
         },

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/services/mock-api-data.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/mock-api-data.service.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/mock-api-data.service.ts
index 985a0bf..2289f09 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/mock-api-data.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/mock-api-data.service.ts
@@ -37,13 +37,16 @@ export class mockApiDataService implements 
InMemoryDbService {
       totalCountKey: 'totalCount',
       filters: {
         clusters: {
-          key: 'cluster'
+          key: 'cluster',
+          isValuesList: true
         },
-        component_name: {
-          key: 'type'
+        mustBe: {
+          key: 'type',
+          isValuesList: true
         },
         level: {
-          key: 'level'
+          key: 'level',
+          isValuesList: true
         },
         iMessage: {
           key: 'log_message',
@@ -110,11 +113,14 @@ export class mockApiDataService implements 
InMemoryDbService {
           let filteredCollection = collection.filter(item => {
             let result = true;
               query.paramsMap.forEach((value, key) => {
-              const paramValue = decodeURIComponent(value[0]), // TODO 
implement multiple conditions
-                paramFilter = filterMapItem.filters[key];
+              const paramValue = decodeURIComponent(value[0]),
+                paramFilter = filterMapItem.filters[key],
+                paramValuesList = paramFilter && paramFilter.isValuesList && 
paramValue ? paramValue.split(',') : [],
+                currentValue = paramFilter && item[paramFilter.key];
               if (paramFilter &&
-                ((paramFilter.filterFunction && 
!paramFilter.filterFunction(item[paramFilter.key], paramValue)) ||
-                (!paramFilter.filterFunction && item[paramFilter.key] !== 
paramValue))) {
+                ((paramFilter.filterFunction && 
!paramFilter.filterFunction(currentValue, paramValue)) ||
+                (!paramFilter.filterFunction && !paramFilter.isValuesList && 
currentValue !== paramValue) ||
+                (!paramFilter.filterFunction && paramFilter.isValuesList && 
paramValuesList.indexOf(currentValue) === -1))) {
                 result = false;
               }
             });

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.spec.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.spec.ts
 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.spec.ts
index a93eee5..a4a0cf8 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.spec.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.spec.ts
@@ -30,4 +30,57 @@ describe('UtilsService', () => {
   it('should create service', inject([UtilsService], (service: UtilsService) 
=> {
     expect(service).toBeTruthy();
   }));
+
+  describe('#updateMultiSelectValue()', () => {
+    const cases = [
+      {
+        currentValue: '',
+        value: 'v0',
+        isChecked: true,
+        result: 'v0',
+        title: 'check; no checked items before'
+      },
+      {
+        currentValue: 'v1,v2',
+        value: 'v3',
+        isChecked: true,
+        result: 'v1,v2,v3',
+        title: 'check'
+      },
+      {
+        currentValue: 'v4,v5',
+        value: 'v4',
+        isChecked: false,
+        result: 'v5',
+        title: 'uncheck'
+      },
+      {
+        currentValue: 'v6,v7',
+        value: 'v6',
+        isChecked: true,
+        result: 'v6,v7',
+        title: 'avoid repeating check action'
+      },
+      {
+        currentValue: 'v8,v9',
+        value: 'v10',
+        isChecked: false,
+        result: 'v8,v9',
+        title: 'avoid repeating uncheck action'
+      },
+      {
+        currentValue: 'v11',
+        value: 'v11',
+        isChecked: false,
+        result: '',
+        title: 'uncheck last item'
+      }
+    ];
+
+    cases.forEach(test => {
+      it(test.title, inject([UtilsService], (service: UtilsService) => {
+        expect(service.updateMultiSelectValue(test.currentValue, test.value, 
test.isChecked)).toEqual(test.result);
+      }));
+    });
+  });
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/b00da9b5/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.ts
----------------------------------------------------------------------
diff --git 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.ts 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.ts
index 7180ebb..2deff9e 100644
--- 
a/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web-new/src/app/services/utils.service.ts
@@ -33,6 +33,17 @@ export class UtilsService {
     }
   }
 
+  updateMultiSelectValue(currentValue: string, value: string, isChecked: 
boolean): string {
+    let valuesArray = currentValue ? currentValue.split(',') : [],
+      valuePosition = valuesArray.indexOf(value);
+    if (isChecked && valuePosition === -1) {
+      valuesArray.push(value);
+    } else if (!isChecked && valuePosition > -1) {
+      valuesArray.splice(valuePosition, 1);
+    }
+    return valuesArray.join(',');
+  }
+
   getTimeZoneLabel(timeZone) {
     return `${timeZone} (${moment.tz(timeZone).format('Z')})`;
   }

Reply via email to