You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mr...@apache.org on 2017/09/11 04:39:07 UTC

[37/94] [abbrv] [partial] ambari git commit: AMBARI-21870. Integrate LogSearch new UI with the server and get rid of the old one (oleewere)

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
new file mode 100644
index 0000000..3e2a6c7
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+import {FilteringService} from '@app/services/filtering.service';
+import {LogsContainerService} from '@app/services/logs-container.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';
+
+@Component({
+  selector: 'logs-container',
+  templateUrl: './logs-container.component.html',
+  styleUrls: ['./logs-container.component.less']
+})
+export class LogsContainerComponent implements OnInit {
+
+  constructor(private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
+    serviceLogsHistogramStorage.getAll().subscribe(data => this.histogramData = this.logsContainer.getHistogramData(data));
+  }
+
+  ngOnInit() {
+    const fieldsModel = this.logsTypeMapObject.fieldsModel,
+      logsModel = this.logsTypeMapObject.logsModel;
+    this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe(value => this.isLogsSet = value);
+    this.availableColumns = fieldsModel.getAll().map(fields => {
+      return fields.filter(field => field.isAvailable).map(field => {
+        return {
+          value: field.name,
+          label: field.displayName || field.name,
+          isChecked: field.isDisplayed
+        };
+      });
+    });
+    fieldsModel.getAll().subscribe(columns => {
+      const availableFields = columns.filter(field => field.isAvailable),
+        availableNames = availableFields.map(field => field.name);
+      if (availableNames.length && !this.isLogsSet) {
+        this.logs = logsModel.getAll().map(logs => logs.map(log => {
+          let logObject = availableNames.reduce((obj, key) => Object.assign(obj, {
+            [key]: log[key]
+          }), {});
+          if (logObject.level) {
+            logObject.className = logObject.level.toLowerCase();
+          }
+          return logObject;
+        }));
+        this.appState.setParameter(this.logsTypeMapObject.isSetFlag, true);
+      }
+      this.displayedColumns = columns.filter(column => column.isAvailable && column.isDisplayed);
+    });
+    this.logsContainer.loadLogs(this.logsType);
+    this.filtersForm.valueChanges.subscribe(() => this.logsContainer.loadLogs(this.logsType));
+  }
+
+  @Input()
+  logsType: string;
+
+  private isLogsSet: boolean = false;
+
+  get logsTypeMapObject(): any {
+    return this.logsContainer.logsTypeMap[this.logsType];
+  }
+
+  get totalCount(): number {
+    return this.logsContainer.totalCount;
+  }
+
+  logs: Observable<AuditLog[] | ServiceLog[]>;
+
+  availableColumns: Observable<LogField[]>;
+
+  displayedColumns: any[] = [];
+
+  histogramData: any;
+
+  readonly histogramOptions = {
+    keysWithColors: this.logsContainer.colors
+  };
+
+  private get filtersForm(): FormGroup {
+    return this.filtering.filtersForm;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
new file mode 100644
index 0000000..888c524
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
@@ -0,0 +1,54 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="pull-right">
+  <filter-dropdown [label]="filters.sorting.label" formControlName="sorting" [options]="filters.sorting.options"
+                   [defaultLabel]="filters.sorting.defaultLabel" [isRightAlign]="true"></filter-dropdown>
+</form>
+<div class="col-md-12 text-center" *ngIf="logs && logs.length">
+  <div class="logs-header">
+    <div class="col-md-1">{{'logs.status' | translate}}</div>
+    <div class="col-md-11">{{'logs.details' | translate}}</div>
+  </div>
+</div>
+<accordion-panel *ngFor="let log of logs; let i = index" [toggleId]="'details-' + i" class="col-md-12">
+  <ng-template>
+    <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + log.className"></div>
+    <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' + log.className">{{log.level}}</div>
+    <div *ngIf="isColumnDisplayed('type') || isColumnDisplayed('logtime')" class="col-md-3">
+      <div *ngIf="isColumnDisplayed('type')" class="log-type">{{log.type}}</div>
+      <time *ngIf="isColumnDisplayed('logtime')" class="log-time">
+        {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}}
+      </time>
+    </div>
+    <div class="col-md-6 log-content-wrapper">
+      <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>
+      </div>
+      <div class="log-content-inner-wrapper">
+        <div class="log-content" *ngIf="isColumnDisplayed('log_message')">{{log.log_message}}</div>
+      </div>
+    </div>
+    <div *ngFor="let column of displayedColumns">
+      <div *ngIf="customStyledColumns.indexOf(column.name) === -1" [innerHTML]="log[column.name]" class="col-md-1"></div>
+    </div>
+  </ng-template>
+</accordion-panel>
+<pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount" [filtersForm]="filtersForm"
+            [filterInstance]="filters.pageSize" [currentCount]="logs.length"></pagination>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
new file mode 100644
index 0000000..91d796f
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 '../variables';
+
+.logs-header {
+  // TODO get rid of magic numbers, base on actual design
+  margin: 10px 0;
+  padding: 5px 0;
+  background-color: @list-header-background-color; // TODO implement actual color
+  overflow: hidden;
+  text-transform: uppercase;
+}
+
+/deep/ filter-dropdown {
+  justify-content: flex-end;
+}
+
+.hexagon {
+  // TODO get rid of magic numbers, base on actual design
+  left: -7.5px;
+
+  &.fatal {
+    .common-hexagon(15px, @fatal-color);
+  }
+
+  &.error {
+    .common-hexagon(15px, @error-color);
+  }
+
+  &.warn {
+    .common-hexagon(15px, @warning-color);
+  }
+
+  &.info {
+    .common-hexagon(15px, @info-color);
+  }
+
+  &.debug {
+    .common-hexagon(15px, @debug-color);
+  }
+
+  &.trace {
+    .common-hexagon(15px, @trace-color);
+  }
+
+  &.unknown {
+    .common-hexagon(15px, @unknown-color);
+  }
+}
+
+.log-status {
+  text-transform: uppercase;
+
+  &.fatal {
+    color: @fatal-color;
+  }
+
+  &.error {
+    color: @error-color;
+  }
+
+  &.warn {
+    color: @warning-color;
+  }
+
+  &.info {
+    color: @info-color;
+  }
+
+  &.debug {
+    color: @debug-color;
+  }
+
+  &.trace {
+    color: @trace-color;
+  }
+
+  &.unknown {
+    color: @unknown-color;
+  }
+}
+
+.log-type {
+  color: @link-color;
+}
+
+.log-time {
+  color: @grey-color;
+}
+
+.log-content-wrapper {
+  position: relative;
+
+  // TODO get rid of magic numbers, base on actual design
+  .log-content-inner-wrapper {
+    overflow: hidden;
+    max-height: @default-line-height * 2em;
+    padding-right: 65px;
+
+    .log-content {
+      white-space: pre-wrap;
+    }
+  }
+
+  .log-actions {
+    position: absolute;
+    right: 40px;
+    top: 0;
+    border: @input-border;
+
+    &.collapsing + .log-content-inner-wrapper, &.collapse.in + .log-content-inner-wrapper {
+      min-height: 6em;
+      max-height: none;
+      overflow-x: auto;
+    }
+
+    .action-icon {
+      .clickable-item;
+      display: block;
+      padding: 5px;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
new file mode 100644
index 0000000..02c3b23
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {StoreModule} from '@ngrx/store';
+import {MomentModule} from 'angular2-moment';
+import {MomentTimezoneModule} from 'angular-moment-timezone';
+import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
+import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {ClustersService, clusters} from '@app/services/storage/clusters.service';
+import {ComponentsService, components} from '@app/services/storage/components.service';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {HttpClientService} from '@app/services/http-client.service';
+import {FilteringService} from '@app/services/filtering.service';
+import {UtilsService} from '@app/services/utils.service';
+
+import {LogsListComponent} from './logs-list.component';
+
+export function HttpLoaderFactory(http: Http) {
+  return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('LogsListComponent', () => {
+  let component: LogsListComponent;
+  let fixture: ComponentFixture<LogsListComponent>;
+  const httpClient = {
+    get: () => {
+      return {
+        subscribe: () => {
+        }
+      };
+    }
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [LogsListComponent],
+      imports: [
+        StoreModule.provideStore({
+          auditLogs,
+          serviceLogs,
+          appSettings,
+          clusters,
+          components,
+          hosts
+        }),
+        MomentModule,
+        MomentTimezoneModule,
+        TranslateModule.forRoot({
+          provide: TranslateLoader,
+          useFactory: HttpLoaderFactory,
+          deps: [Http]
+        })
+      ],
+      providers: [
+        {
+          provide: HttpClientService,
+          useValue: httpClient
+        },
+        AuditLogsService,
+        ServiceLogsService,
+        AppSettingsService,
+        ClustersService,
+        ComponentsService,
+        HostsService,
+        FilteringService,
+        UtilsService
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogsListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
new file mode 100644
index 0000000..6d73dcb
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import 'rxjs/add/operator/map';
+import {FilteringService} from '@app/services/filtering.service';
+
+@Component({
+  selector: 'logs-list',
+  templateUrl: './logs-list.component.html',
+  styleUrls: ['./logs-list.component.less']
+})
+export class LogsListComponent {
+
+  constructor(private filtering: FilteringService) {
+  }
+
+  @Input()
+  logs: any[] = [];
+
+  @Input()
+  totalCount: number = 0;
+
+  @Input()
+  displayedColumns: any[] = [];
+
+  readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message'];
+
+  timeFormat: string = 'DD/MM/YYYY HH:mm:ss';
+
+  get timeZone(): string {
+    return this.filtering.timeZone;
+  }
+
+  get filters(): any {
+    return this.filtering.filters;
+  }
+  
+  get filtersForm(): FormGroup {
+    return this.filtering.filtersForm;
+  }
+
+  isColumnDisplayed(key: string): boolean {
+    return this.displayedColumns.some(column => column.name === key);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
new file mode 100644
index 0000000..69b3887
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
@@ -0,0 +1,24 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<ng-template [ngTemplateOutlet]="template"></ng-template>
+<div *ngIf="isInitialLoading" class="text-center">
+  <span class="fa fa-spinner fa-spin"></span>
+</div>
+<login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form>
+<filters-panel *ngIf="isAuthorized" class="row"></filters-panel>
+<logs-container *ngIf="isAuthorized" logsType="serviceLogs"></logs-container>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
new file mode 100644
index 0000000..9736628
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 '../variables';
+
+:host {
+  .full-size;
+  overflow-x: hidden;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
new file mode 100644
index 0000000..42fba68
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {HttpModule} from '@angular/http';
+import {StoreModule} from '@ngrx/store';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
+import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
+import {HttpClientService} from '@app/services/http-client.service';
+
+import {MainContainerComponent} from './main-container.component';
+
+describe('MainContainerComponent', () => {
+  let component: MainContainerComponent;
+  let fixture: ComponentFixture<MainContainerComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [MainContainerComponent],
+      imports: [
+        HttpModule,
+        StoreModule.provideStore({
+          appState,
+          auditLogsFields,
+          serviceLogsFields
+        })
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
+      providers: [
+        AppStateService,
+        AuditLogsFieldsService,
+        ServiceLogsFieldsService,
+        HttpClientService
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MainContainerComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
new file mode 100644
index 0000000..53d58cf
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, ContentChild, TemplateRef} from '@angular/core';
+import {HttpClientService} from '@app/services/http-client.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service';
+import {AuditLogField} from '@app/models/audit-log-field.model';
+import {ServiceLogField} from '@app/models/service-log-field.model';
+
+@Component({
+  selector: 'main-container',
+  templateUrl: './main-container.component.html',
+  styleUrls: ['./main-container.component.less']
+})
+export class MainContainerComponent {
+
+  constructor(private httpClient: HttpClientService, private appState: AppStateService, private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService) {
+    this.loadColumnsNames();
+    appState.getParameter('isAuthorized').subscribe(value => this.isAuthorized = value);
+    appState.getParameter('isInitialLoading').subscribe(value => this.isInitialLoading = value);
+  }
+
+  @ContentChild(TemplateRef)
+  template;
+
+  isAuthorized: boolean = false;
+
+  isInitialLoading: boolean = false;
+
+  private loadColumnsNames(): void {
+    this.httpClient.get('serviceLogsFields').subscribe(response => {
+      const jsonResponse = response.json();
+      if (jsonResponse) {
+        this.serviceLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, ServiceLogField));
+      }
+    });
+    this.httpClient.get('auditLogsFields').subscribe(response => {
+      const jsonResponse = response.json();
+      if (jsonResponse) {
+        this.auditLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, AuditLogField));
+      }
+    });
+  }
+
+  private getColumnsArray(keysObject: any, fieldClass: any): any[] {
+    return Object.keys(keysObject).map(key => new fieldClass(key));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
new file mode 100644
index 0000000..2f05656
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
@@ -0,0 +1,28 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<div #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true}">
+  <a [ngClass]="iconClass + ' icon'" (mousedown)="onMouseDown($event)" (mouseup)="onMouseUp($event)"
+     (click)="$event.stopPropagation()"></a>
+  <a #dropdownToggle class="dropdown-toggle caret" data-toggle="dropdown" *ngIf="hasCaret"></a>
+  <br>
+  <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)"
+      [isMultipleChoice]="isMultipleChoice" [additionalLabelComponentSetter]="additionalLabelComponentSetter"
+      [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}"></ul>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
new file mode 100644
index 0000000..6a3a43d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+
+:host {
+  display: inline-block;
+  cursor: pointer;
+
+  a:hover, a:focus {
+    text-decoration: none;
+  }
+
+  .icon {
+    padding: 5px;
+  }
+
+  .unstyled-link {
+    color: inherit;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
new file mode 100644
index 0000000..6c9e021
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {ComponentActionsService} from '@app/services/component-actions.service';
+import {FilteringService} from '@app/services/filtering.service';
+
+import {MenuButtonComponent} from './menu-button.component';
+
+export function HttpLoaderFactory(http: Http) {
+  return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('MenuButtonComponent', () => {
+  let component: MenuButtonComponent;
+  let fixture: ComponentFixture<MenuButtonComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [MenuButtonComponent],
+      imports: [
+        StoreModule.provideStore({
+          appSettings
+        }),
+        TranslateModule.forRoot({
+          provide: TranslateLoader,
+          useFactory: HttpLoaderFactory,
+          deps: [Http]
+        })
+      ],
+      providers: [
+        AppSettingsService,
+        ComponentActionsService,
+        FilteringService
+      ],
+      schemas: [NO_ERRORS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MenuButtonComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+
+  describe('#hasSubItems', () => {
+    const cases = [
+      {
+        subItems: null,
+        hasSubItems: false,
+        title: 'no sub-items'
+      },
+      {
+        subItems: [],
+        hasSubItems: false,
+        title: 'empty sub-items array'
+      },
+      {
+        subItems: [{}],
+        hasSubItems: true,
+        title: 'sub-items present'
+      }
+    ];
+
+    cases.forEach((test) => {
+      it(test.title, () => {
+        component.subItems = test.subItems;
+        expect(component.hasSubItems).toEqual(test.hasSubItems);
+      });
+    });
+  });
+
+  describe('#hasCaret', () => {
+    const cases = [
+      {
+        subItems: null,
+        hasCaret: false,
+        title: 'no sub-items'
+      },
+      {
+        subItems: [],
+        hasCaret: false,
+        title: 'empty sub-items array'
+      },
+      {
+        subItems: [{}],
+        hideCaret: false,
+        hasCaret: true,
+        title: 'sub-items present, caret not hidden'
+      },
+      {
+        subItems: [{}],
+        hideCaret: true,
+        hasCaret: true,
+        title: 'sub-items present, caret hidden'
+      }
+    ];
+
+    cases.forEach((test) => {
+      it(test.title, () => {
+        component.subItems = test.subItems;
+        component.hideCaret = Boolean(test.hideCaret);
+        expect(component.hasSubItems).toEqual(test.hasCaret);
+      });
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
new file mode 100644
index 0000000..b674ec6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, Input, ViewChild, ElementRef} from '@angular/core';
+import {ComponentActionsService} from '@app/services/component-actions.service';
+import * as $ from 'jquery';
+
+@Component({
+  selector: 'menu-button',
+  templateUrl: './menu-button.component.html',
+  styleUrls: ['./menu-button.component.less']
+})
+export class MenuButtonComponent {
+
+  constructor(protected actions: ComponentActionsService) {
+  }
+
+  @ViewChild('dropdown')
+  dropdown: ElementRef;
+
+  @Input()
+  label?: string;
+
+  @Input()
+  action: string;
+
+  @Input()
+  iconClass: string;
+
+  @Input()
+  labelClass?: string;
+
+  @Input()
+  subItems?: any[];
+
+  @Input()
+  isMultipleChoice: boolean = false;
+
+  @Input()
+  hideCaret: boolean = false;
+
+  @Input()
+  isRightAlign: boolean = false;
+
+  @Input()
+  additionalLabelComponentSetter?: string;
+
+  get hasSubItems(): boolean {
+    return Boolean(this.subItems && this.subItems.length);
+  }
+
+  get hasCaret(): boolean {
+    return this.hasSubItems && !this.hideCaret;
+  }
+
+  private clickStartTime: number;
+
+  private readonly longClickInterval = 1000;
+
+  onMouseDown(event: MouseEvent): void {
+    if (this.action && event.button === 0) {
+      this.clickStartTime = (new Date()).getTime();
+    }
+  }
+
+  onMouseUp(event: MouseEvent): void {
+    if (event.button === 0) {
+      const clickEndTime = (new Date()).getTime();
+      if (this.hasSubItems && (!this.action || clickEndTime - this.clickStartTime >= this.longClickInterval)) {
+        $(this.dropdown.nativeElement).toggleClass('open');
+      } else if (this.action) {
+        this.actions[this.action]();
+      }
+      event.stopPropagation();
+    }
+  }
+  
+  updateValue(options: any) {
+    // TODO implement value change behaviour
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
new file mode 100644
index 0000000..abd7bc8
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
@@ -0,0 +1,40 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<div class="modal-backdrop in"></div>
+<div class="modal in">
+  <div [ngClass]="{'modal-dialog': true, 'modal-sm': isSmallModal, 'modal-lg': isLargeModal}">
+    <div class="modal-content">
+      <div *ngIf="showHeader" class="modal-header">
+        <button *ngIf="showCloseButton" type="button" class="close" data-dismiss="modal" (click)="onClose()">
+          <span>&times;</span>
+        </button>
+        <h4 *ngIf="title">{{title}}</h4>
+      </div>
+      <div class="modal-body">
+        <div *ngIf="bodyText">{{bodyText}}</div>
+        <ng-template *ngIf="bodyTemplate" [ngTemplateOutlet]="bodyTemplate"></ng-template>
+      </div>
+      <div *ngIf="showFooter" class="modal-footer">
+        <button *ngIf="showCancelButton" class="btn {{cancelButtonClassName}}"
+                (click)="onCancel()">{{cancelButtonLabel | translate}}</button>
+        <button *ngIf="showSubmitButton" class="btn {{submitButtonClassName}}"
+                (click)="onSubmit()">{{submitButtonLabel | translate}}</button>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
new file mode 100644
index 0000000..802bd13
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+
+import {ModalComponent} from './modal.component';
+
+export function HttpLoaderFactory(http: Http) {
+  return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('ModalComponent', () => {
+  let component: ModalComponent;
+  let fixture: ComponentFixture<ModalComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ModalComponent],
+      imports: [
+        TranslateModule.forRoot({
+          provide: TranslateLoader,
+          useFactory: HttpLoaderFactory,
+          deps: [Http]
+        })
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ModalComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
new file mode 100644
index 0000000..32f59f6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, AfterViewInit, ElementRef, Input, Output, ContentChild, TemplateRef, EventEmitter} from '@angular/core';
+import * as $ from 'jquery';
+
+@Component({
+  selector: 'modal',
+  templateUrl: './modal.component.html'
+})
+export class ModalComponent implements OnInit, AfterViewInit {
+
+  constructor(private element: ElementRef) {
+    this.rootElement = $(this.element.nativeElement);
+  }
+
+  ngOnInit() {
+    this.modalElements = this.rootElement.find('.in');
+    this.show();
+  }
+
+  ngAfterViewInit() {
+    this.init.emit();
+  }
+
+  private rootElement: JQuery;
+
+  private modalElements: JQuery;
+
+  @Input()
+  showHeader: boolean = true;
+
+  @Input()
+  title: string = '';
+
+  @Input()
+  showCloseButton: boolean = true;
+
+  @Input()
+  bodyText: string = '';
+
+  @Input()
+  showFooter: boolean = true;
+
+  @Input()
+  showSubmitButton: boolean = true;
+
+  @Input()
+  submitButtonLabel: string = 'modal.submit';
+
+  @Input()
+  submitButtonClassName: string = 'btn-success';
+
+  @Input()
+  showCancelButton: boolean = true;
+
+  @Input()
+  cancelButtonLabel: string = 'modal.cancel';
+
+  @Input()
+  cancelButtonClassName: string = 'btn-default';
+
+  @Input()
+  isSmallModal: boolean = false;
+
+  @Input()
+  isLargeModal: boolean = false;
+
+  @ContentChild(TemplateRef)
+  bodyTemplate;
+
+  @Output()
+  init: EventEmitter<any> = new EventEmitter();
+
+  @Output()
+  submit: EventEmitter<any> = new EventEmitter();
+
+  @Output()
+  cancel: EventEmitter<any> = new EventEmitter();
+
+  @Output()
+  close: EventEmitter<any> = new EventEmitter();
+
+  show(): void {
+    this.modalElements.show();
+  }
+
+  hide(): void {
+    this.modalElements.hide();
+  }
+
+  onSubmit(): void {
+    this.hide();
+    this.submit.emit();
+  }
+
+  onCancel(): void {
+    this.hide();
+    this.cancel.emit();
+  }
+
+  onClose(): void {
+    this.hide();
+    this.close.emit();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html
new file mode 100644
index 0000000..96c8619
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html
@@ -0,0 +1,19 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<div *ngFor="let item of data" class="bar-sector"
+     [ngStyle]="{'background-color': item.color, 'width': (item.value / totalCount * 100) + '%'}"></div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
new file mode 100644
index 0000000..b78b847
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+
+@bar-height: 8px;
+
+:host {
+  display: block;
+  width: 100%;
+
+  .bar-sector {
+    display: inline-block;
+    height: @bar-height;
+
+    &:first-child {
+      border-top-left-radius: @bar-height / 2;
+      border-bottom-left-radius: @bar-height / 2;
+    }
+
+    &:last-child {
+      border-top-right-radius: @bar-height / 2;
+      border-bottom-right-radius: @bar-height / 2;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
new file mode 100644
index 0000000..d47436e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {NodeBarComponent} from './node-bar.component';
+
+describe('NodeBarComponent', () => {
+  let component: NodeBarComponent;
+  let fixture: ComponentFixture<NodeBarComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [NodeBarComponent]
+    })
+      .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(NodeBarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
new file mode 100644
index 0000000..c7b3ead
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, Input} from '@angular/core';
+
+@Component({
+  selector: 'node-bar',
+  templateUrl: './node-bar.component.html',
+  styleUrls: ['./node-bar.component.less']
+})
+export class NodeBarComponent {
+
+  @Input()
+  data: any[] = [];
+
+  get totalCount(): number {
+    return this.data.reduce((currentValue, currentItem) => currentValue + Number(currentItem.value), 0);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
new file mode 100644
index 0000000..c227a2b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
@@ -0,0 +1,23 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<button class="btn btn-link" [disabled]="currentPage === 0" (click)="updateValue(true)">
+  <span class="pagination-control fa fa-chevron-left"></span>
+</button>
+<button class="btn btn-link" [disabled]="currentPage === pagesCount - 1" (click)="updateValue()">
+  <span class="pagination-control fa fa-chevron-right"></span>
+</button>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
new file mode 100644
index 0000000..8238eaf
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 '../variables';
+
+.pagination-control {
+  .clickable-item;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
new file mode 100644
index 0000000..489f79c
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {PaginationControlsComponent} from './pagination-controls.component';
+
+describe('PaginationControlsComponent', () => {
+  let component: PaginationControlsComponent;
+  let fixture: ComponentFixture<PaginationControlsComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [PaginationControlsComponent]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PaginationControlsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
new file mode 100644
index 0000000..c71844c
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, forwardRef, Input, Output, EventEmitter} from '@angular/core';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+@Component({
+  selector: 'pagination-controls',
+  templateUrl: './pagination-controls.component.html',
+  styleUrls: ['./pagination-controls.component.less'],
+  providers: [
+    {
+      provide: NG_VALUE_ACCESSOR,
+      useExisting: forwardRef(() => PaginationControlsComponent),
+      multi: true
+    }
+  ]
+})
+export class PaginationControlsComponent implements ControlValueAccessor {
+
+  private onChange: (fn: any) => void;
+
+  currentPage: number = 0;
+
+  @Input()
+  totalCount: number;
+
+  @Input()
+  pagesCount: number;
+
+  @Output()
+  currentPageChange: EventEmitter<number> = new EventEmitter();
+
+  get value(): number {
+    return this.currentPage;
+  }
+
+  set value(newValue: number) {
+    this.currentPage = newValue;
+    this.currentPageChange.emit(newValue);
+    this.onChange(newValue);
+  }
+
+  updateValue(isDecrement?: boolean) {
+    isDecrement? this.value-- : this.value++;
+  }
+
+  writeValue() {
+  }
+
+  registerOnChange(callback: any): void {
+    this.onChange = callback;
+  }
+
+  registerOnTouched() {
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
new file mode 100644
index 0000000..be6591b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
@@ -0,0 +1,24 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<form class="pagination-form col-md-12" [formGroup]="filtersForm">
+  <filter-dropdown [label]="filterInstance.label" formControlName="pageSize" [options]="filterInstance.options"
+                   [defaultLabel]="filterInstance.defaultLabel" [isRightAlign]="true" isDropup="true"></filter-dropdown>
+  <span>{{'pagination.numbers' | translate: numbersTranslateParams}}</span>
+  <pagination-controls formControlName="page" [totalCount]="totalCount" [pagesCount]="pagesCount"
+                       (currentPageChange)="setCurrentPage($event)"></pagination-controls>
+</form>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
new file mode 100644
index 0000000..df8ad2d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 '../variables';
+
+:host {
+  display: flex;
+
+  .pagination-form {
+    .flex-vertical-align;
+    justify-content: flex-end;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
new file mode 100644
index 0000000..7a15bbc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+
+import {PaginationComponent} from './pagination.component';
+
+export function HttpLoaderFactory(http: Http) {
+  return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('PaginationComponent', () => {
+  let component: PaginationComponent;
+  let fixture: ComponentFixture<PaginationComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        TranslateModule.forRoot({
+          provide: TranslateLoader,
+          useFactory: HttpLoaderFactory,
+          deps: [Http]
+        })
+      ],
+      declarations: [PaginationComponent],
+      schemas: [NO_ERRORS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PaginationComponent);
+    component = fixture.componentInstance;
+    component.filterInstance = {};
+    component.filtersForm = {
+      controls: {
+        pageSize: {
+          valueChanges: {
+            subscribe: () => {}
+          }
+        }
+      }
+    };
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
new file mode 100644
index 0000000..d38d0d8
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+  selector: 'pagination',
+  templateUrl: './pagination.component.html',
+  styleUrls: ['./pagination.component.less']
+})
+export class PaginationComponent implements OnInit {
+
+  ngOnInit() {
+    this.setPageSizeFromString(this.filterInstance.defaultValue);
+    this.filtersForm.controls.pageSize.valueChanges.subscribe(value => this.setPageSizeFromString(value));
+  }
+
+  @Input()
+  filtersForm: FormGroup;
+
+  @Input()
+  filterInstance: any;
+
+  @Input()
+  currentCount?: number;
+
+  @Input()
+  totalCount: number;
+
+  private pageSize: number = 0;
+
+  setPageSizeFromString(value: string) {
+    this.pageSize = parseInt(value);
+  }
+
+  private currentPage: number = 0;
+
+  get numbersTranslateParams(): any {
+    const pageSize = this.pageSize,
+      startIndex = (this.currentPage * pageSize) + 1;
+    return {
+      startIndex,
+      endIndex: startIndex + Math.min(pageSize, this.currentCount) - 1,
+      totalCount: this.totalCount
+    }
+  }
+
+  get pagesCount(): number {
+    return Math.ceil(this.totalCount / this.pageSize);
+  }
+
+  setCurrentPage(pageNumber: number) {
+    this.currentPage = pageNumber;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
new file mode 100644
index 0000000..299e46e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
@@ -0,0 +1,18 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<div #container></div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
new file mode 100644
index 0000000..d891862
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+
+/deep/ .axis {
+  .domain {
+    display: none;
+  }
+
+  .tick {
+    line {
+      display: none;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
new file mode 100644
index 0000000..9e056be
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+
+import {TimeHistogramComponent} from './time-histogram.component';
+
+describe('TimeHistogramComponent', () => {
+  let component: TimeHistogramComponent;
+  let fixture: ComponentFixture<TimeHistogramComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [TimeHistogramComponent],
+      imports: [
+        StoreModule.provideStore({
+          appSettings
+        })
+      ],
+      providers: [
+        AppSettingsService
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TimeHistogramComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create component', () => {
+    expect(component).toBeTruthy();
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
new file mode 100644
index 0000000..7856ecc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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, AfterViewInit, OnChanges, Input, ViewChild, ElementRef} from '@angular/core';
+import * as d3 from 'd3';
+import * as moment from 'moment-timezone';
+import {AppSettingsService} from '@app/services/storage/app-settings.service';
+
+@Component({
+  selector: 'time-histogram',
+  templateUrl: './time-histogram.component.html',
+  styleUrls: ['./time-histogram.component.less']
+})
+export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges {
+
+  constructor(private appSettings: AppSettingsService) {
+    appSettings.getParameter('timeZone').subscribe(value => {
+      this.timeZone = value;
+      this.createHistogram();
+    });
+  }
+
+  ngOnInit() {
+    Object.assign(this.options, this.defaultOptions, this.customOptions);
+  }
+
+  ngAfterViewInit() {
+    this.htmlElement = this.element.nativeElement;
+    this.host = d3.select(this.htmlElement);
+  }
+
+  ngOnChanges() {
+    this.createHistogram();
+  }
+
+  @ViewChild('container')
+  element: ElementRef;
+
+  @Input()
+  customOptions: any;
+
+  @Input()
+  data: any;
+
+  private readonly defaultOptions = {
+    margin: {
+      top: 20,
+      right: 20,
+      bottom: 40,
+      left: 50
+    },
+    height: 200,
+    tickPadding: 10,
+    columnWidth: 20
+  };
+
+  private options: any = {};
+
+  private timeZone: string;
+
+  private host;
+
+  private svg;
+
+  private width;
+
+  private xScale;
+
+  private yScale;
+
+  private colorScale;
+
+  private xAxis;
+
+  private yAxis;
+
+  private htmlElement: HTMLElement;
+
+  histogram: any;
+
+  private createHistogram(): void {
+    if (this.host) {
+      this.setup();
+      this.buildSVG();
+      this.populate();
+    }
+  }
+
+  private setup(): void {
+    const margin = this.options.margin,
+      keysWithColors = this.options.keysWithColors,
+      keys = Object.keys(keysWithColors),
+      colors = keys.reduce((array, key) => [...array, keysWithColors[key]], []);
+    this.width = this.htmlElement.clientWidth - margin.left - margin.right;
+    this.xScale = d3.scaleTime().range([0, this.width]);
+    this.yScale = d3.scaleLinear().range([this.options.height, 0]);
+    this.colorScale = d3.scaleOrdinal(colors);
+  }
+
+  private buildSVG(): void {
+    const margin = this.options.margin;
+    this.host.html('');
+    this.svg = this.host.append('svg').attr('width', this.width + margin.left + margin.right)
+      .attr('height', this.options.height + margin.top + margin.bottom).append('g')
+      .attr('transform', `translate(${margin.left},${margin.top})`);
+  }
+
+  private drawXAxis(): void {
+    this.xAxis = d3.axisBottom(this.xScale)
+      .tickFormat(tick => moment(tick).tz(this.timeZone).format('MM/DD HH:mm'))
+      .tickPadding(this.options.tickPadding);
+    this.svg.append('g').attr('class', 'axis').attr('transform', `translate(0,${this.options.height})`).call(this.xAxis);
+  }
+
+  private drawYAxis(): void {
+    this.yAxis = d3.axisLeft(this.yScale).tickFormat((tick: number) => {
+      if (Number.isInteger(tick)) {
+        return tick.toFixed(0);
+      } else {
+        return;
+      }
+    }).tickPadding(this.options.tickPadding);
+    this.svg.append('g').attr('class', 'axis').call(this.yAxis).append('text');
+  }
+
+  private populate(): void {
+    const keys = Object.keys(this.options.keysWithColors),
+      data = this.data,
+      timeStamps = Object.keys(data),
+      formattedData = timeStamps.map(timeStamp => Object.assign({
+        timeStamp: timeStamp
+      }, data[timeStamp])),
+      layers = (d3.stack().keys(keys)(formattedData)),
+      columnWidth = this.options.columnWidth;
+    this.xScale.domain(d3.extent(formattedData, item => item.timeStamp));
+    this.yScale.domain([0, d3.max(formattedData, item => keys.reduce((sum, key) => sum + item[key], 0))]);
+    this.drawXAxis();
+    this.drawYAxis();
+    const layer = this.svg.selectAll().data(d3.transpose<any>(layers)).enter().append('g');
+    layer.selectAll().data(item => item).enter().append('rect')
+      .attr('x', item => this.xScale(item.data.timeStamp) - columnWidth / 2).attr('y', item => this.yScale(item[1]))
+      .attr('height', item => this.yScale(item[0]) - this.yScale(item[1])).attr('width', columnWidth.toString())
+      .style('fill', (item, index) => this.colorScale(index));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
new file mode 100644
index 0000000..3cb196e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
@@ -0,0 +1,26 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  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.
+-->
+
+<button class="btn btn-link" (click)="setTimeZonePickerDisplay(true)">
+  {{timeZone | timeZoneAbbr}} <span class="caret"></span>
+</button>
+<modal *ngIf="isTimeZonePickerDisplayed" [showCloseButton]="false" [isLargeModal]="true"
+       (init)="initMap()" (cancel)="setTimeZonePickerDisplay(false)" (submit)="setTimeZone()">
+  <ng-template>
+    <div attr.id="{{mapElementId}}"></div>
+  </ng-template>
+</modal>

http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
new file mode 100644
index 0000000..4fa043d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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 '../variables';
+
+.btn-link {
+  // TODO implement actual colors
+  color: @submit-color;
+
+  &:hover {
+    color: @submit-hover-color;
+  }
+}
+
+/deep/ #timezone-map {
+  .Cbox {
+    .quickLink {
+      padding-top: 4px;
+    }
+  }
+
+  .hoverZone {
+    display: inline-block;
+
+    &:after {
+      content: '\007C\00a0\00a0';
+      visibility: hidden;
+    }
+  }
+}