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/11/27 23:29:31 UTC

[19/30] ambari git commit: Merge trunk with feature branch and fix some UT compilation issues (mradhakrishnan)

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
index 4e9bdc9..7bd87ad 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
@@ -34,7 +34,6 @@ import {TranslationModules} from '@app/test-config.spec';
 import {ModalComponent} from '@app/components/modal/modal.component';
 import {LogsContainerService} from '@app/services/logs-container.service';
 import {HttpClientService} from '@app/services/http-client.service';
-import {FilteringService} from '@app/services/filtering.service';
 
 import {LogContextComponent} from './log-context.component';
 
@@ -90,8 +89,7 @@ describe('LogContextComponent', () => {
         {
           provide: HttpClientService,
           useValue: httpClient
-        },
-        FilteringService
+        }
       ],
       schemas: [CUSTOM_ELEMENTS_SCHEMA]
     })

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.html
new file mode 100644
index 0000000..d72c9d33
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.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.
+-->
+<i class="fa {{cssClass}}"></i>
+{{logEntry.level}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.ts
new file mode 100644
index 0000000..c13d373
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.spec.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 {DebugElement} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {LogLevelComponent} from './log-level.component';
+import {By} from '@angular/platform-browser';
+
+describe('LogLevelComponent', () => {
+  let component: LogLevelComponent;
+  let fixture: ComponentFixture<LogLevelComponent>;
+  let de: DebugElement;
+  let el: HTMLElement;
+  let logLevelMap = {
+    warn: 'fa-exclamation-triangle',
+    fatal: 'fa-exclamation-circle',
+    error: 'fa-exclamation-circle',
+    info: 'fa-info-circle',
+    debug: 'fa-bug',
+    trace: 'fa-random',
+    unknown: 'fa-question-circle'
+  };
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LogLevelComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogLevelComponent);
+    component = fixture.componentInstance;
+    component.logEntry = {level: 'unknown'};
+    fixture.detectChanges();
+    de = fixture.debugElement.query(By.css('i.fa'));
+    el = de.nativeElement;
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  Object.keys(logLevelMap).forEach((level) => {
+    describe(level, () => {
+      beforeEach(() => {
+        component.logEntry = {level: level};
+        fixture.detectChanges();
+      });
+      it(`should return with the ${logLevelMap[level]} css class for ${level} log level`, () => {
+        expect(component.cssClass).toEqual(logLevelMap[level]);
+      });
+      it(`should set the ${logLevelMap[level]} css class on the icon element`, () => {
+        expect(el.classList).toContain(logLevelMap[level]);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
new file mode 100644
index 0000000..8542770
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-level/log-level.component.ts
@@ -0,0 +1,52 @@
+/**
+ * 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';
+
+/**
+ * This is a simple UI component to display the log message. The goal is to be able to show one line and be collapsile
+ * to show the full log message with new lines.
+ * @class LogMessageComponent
+ */
+@Component({
+  selector: 'log-level',
+  templateUrl: './log-level.component.html',
+  styleUrls: []
+})
+export class LogLevelComponent {
+
+  /**
+   * This is the log entry object
+   * @type {object}
+   */
+  @Input()
+  logEntry: any;
+
+  private classMap: object = {
+    warn: 'fa-exclamation-triangle',
+    fatal: 'fa-exclamation-circle',
+    error: 'fa-exclamation-circle',
+    info: 'fa-info-circle',
+    debug: 'fa-bug',
+    trace: 'fa-random',
+    unknown: 'fa-question-circle'
+  };
+
+  get cssClass() {
+    return this.classMap[((this.logEntry && this.logEntry.level) || 'unknown').toLowerCase()];
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
new file mode 100644
index 0000000..d4c2902
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.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.
+-->
+<div [ngClass]="{
+  'log-message-container': true,
+  'log-message-container-collapsible': addCaret,
+  'log-message-container-open': isOpen
+  }">
+  <button *ngIf="addCaret" (click)="onCaretClick($event)"><i class="caret"></i></button>
+  <div #content class="log-message-content"><ng-content></ng-content></div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
new file mode 100644
index 0000000..602d7bd
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.less
@@ -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 '../variables';
+:host {
+  .log-message-container {
+    display: block;
+    margin: 0;
+    padding: 0;
+
+    .caret {
+      margin-top: -3px;
+      transition: transform 250ms;
+      transform: rotate(-90deg);
+    }
+    &.log-message-container-open .caret {
+      transform: rotate(0deg);
+    }
+
+    .log-message-content {
+      max-height: calc(20em/14); // from Bootstrap
+      overflow: hidden;
+      padding-left: 1em;
+      position: relative;
+    }
+    &.log-message-container-open .log-message-content {
+      max-height: none;
+      white-space: pre-wrap;
+      &:before {
+        display: none;
+      }
+    }
+    &.log-message-container-collapsible {
+      .log-message-content {
+        padding-left: 0;
+        &:before {
+          content: "...";
+          float: right;
+          margin-left: 1em;
+        }
+      }
+
+    }
+
+    button, button:active {
+      background: none transparent;
+      border: none transparent;
+      color: @base-font-color;
+      cursor: pointer;
+      float: left;
+      height: 1em;
+      outline: none;
+      padding: 0 .15em;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
new file mode 100644
index 0000000..edc2515
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.spec.ts
@@ -0,0 +1,64 @@
+/**
+ * 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 {LogMessageComponent} from './log-message.component';
+
+describe('LogMessageComponent', () => {
+  let component: LogMessageComponent;
+  let fixture: ComponentFixture<LogMessageComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ LogMessageComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LogMessageComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('event handler should call the toggleOpen method', () => {
+    let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+    mockEvent.initEvent('click', true, true);
+    spyOn(component,'toggleOpen');
+    component.onCaretClick(mockEvent);
+    expect(component.toggleOpen).toHaveBeenCalled();
+  });
+
+  it('event handler should prevent the default behaviour of the action', () => {
+    let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+    mockEvent.initEvent('click', true, true);
+    spyOn(mockEvent,'preventDefault');
+    component.onCaretClick(mockEvent);
+    expect(mockEvent.preventDefault).toHaveBeenCalled();
+  });
+
+  it('calling the toggleOpen method should negate the isOpen property', () => {
+    let currentState = component.isOpen;
+    component.toggleOpen();
+    expect(component.isOpen).toEqual(!currentState);
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
new file mode 100644
index 0000000..b8be61b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.ts
@@ -0,0 +1,129 @@
+/**
+ * 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, AfterViewInit, ElementRef, ViewChild, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef} from '@angular/core';
+
+/**
+ * This is a simple UI component to display the log message. The goal is to be able to show one line and be collapsile
+ * to show the full log message with new lines.
+ * @class LogMessageComponent
+ */
+@Component({
+  selector: 'log-message',
+  templateUrl: './log-message.component.html',
+  styleUrls: ['./log-message.component.less']
+})
+export class LogMessageComponent implements AfterViewInit, OnChanges {
+
+  /**
+   * This is the element reference to the message log container element. So that we can calculate if the caret should be
+   * displayed or not.
+   * @type ElementRef
+   */
+  @ViewChild('content') content: ElementRef;
+
+  /**
+   * This is the flag property to indicate if the content container is open or not.
+   * @type {boolean}
+   */
+  @Input()
+  isOpen: boolean = false;
+
+  /**
+   * This is a helper property to handle the changes on the parent component. The goal of this input is to be able to
+   * react when the parent component (currently the log-list component) has changed (its size) in a way that the
+   * LogMessageComponent should check if the caret should be visible or not.
+   */
+  @Input()
+  listenChangesOn: any;
+
+  /**
+   * This is a private flag to check if it should display the caret or not, it depends on the size of the size of
+   * the content container element. Handled by the @checkAddCaret method
+   * @type {boolean}
+   */
+  private addCaret: boolean = false;
+
+  /**
+   * This is a primary check if the message content does contain new line (/n) characters. If so than we display the
+   * caret to give a possibility to the user to see the message as it is (pre-wrapped).
+   * @type {boolean}
+   */
+  private isMultiLineMessage: boolean = false;
+
+  constructor(private cdRef:ChangeDetectorRef) {}
+
+  /**
+   * This change handler's goal is to check if we should add the caret or not. Mainly it is because currently we have
+   * the LogListComponent where columns can be added or removed and we have to recheck the visibility of the caret every
+   * changes of the displayed columns.
+   * @param {SimpleChanges} changes
+   */
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes.listenChangesOn !== undefined) {
+      this.checkAddCaret();
+    }
+  }
+
+  /**
+   * The goal is to perform a initial caret display check when the component has been initialized.
+   */
+  ngAfterViewInit(): void {
+    let text = this.content.nativeElement.textContent;
+    let newLinePos = text.indexOf('\n');
+    this.isMultiLineMessage = ((text.length - 1) > newLinePos) && (newLinePos > 0);
+    this.checkAddCaret();
+  }
+
+  /**
+   * Since the size of the column is depends on the window size we have to listen the resize event and show/hide the
+   * caret corresponding the new size of the content container element.
+   * Using the arrow function will keep the instance scope.
+   */
+  @HostListener('window:resize', ['$event'])
+  onWindowResize = (): void => {
+    this.isMultiLineMessage || this.checkAddCaret();
+  };
+
+  /**
+   * The goal is to perform a height check on the content container element. It is based on the comparison of the
+   * scrollHeight and the clientHeight.
+   */
+  checkAddCaret = (): void =>  {
+    let el = this.content.nativeElement;
+    this.addCaret = this.isMultiLineMessage || (el.scrollHeight > el.clientHeight);
+    this.cdRef.detectChanges();
+  };
+
+  /**
+   * This is the click event handler of the caret button element. It will only toggle the isOpen property so that the
+   * component element css classes will follow its state.
+   * @param ev {MouseEvent}
+   */
+  onCaretClick(ev:MouseEvent) {
+    ev.preventDefault();
+    this.toggleOpen();
+  }
+
+  /**
+   * This is a simple property toggle method of the @isOpen property.
+   * The goal is to separate this logic from the event handling and give a way to call it from anywhere.
+   */
+  toggleOpen():void {
+    this.isOpen = !this.isOpen;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts
index fb5c2a0..ac9f3a8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts
@@ -22,6 +22,7 @@ import {TranslationModules} from '@app/test-config.spec';
 import {StoreModule} from '@ngrx/store';
 import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {AuthService} from '@app/services/auth.service';
 
 import {LoginFormComponent} from './login-form.component';
 
@@ -58,7 +59,8 @@ describe('LoginFormComponent', () => {
         {
           provide: HttpClientService,
           useValue: httpClient
-        }
+        },
+        AuthService
       ]
     })
     .compileComponents();
@@ -101,9 +103,6 @@ describe('LoginFormComponent', () => {
           expect(component.isLoginAlertDisplayed).toEqual(test.isLoginAlertDisplayed);
         });
 
-        it('isLoginInProgress', () => {
-          expect(component.isLoginInProgress).toEqual(false);
-        });
       });
     });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts
index 2bc45404..39a4975 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts
@@ -17,9 +17,10 @@
  */
 
 import {Component} from '@angular/core';
+import {Response} from '@angular/http';
 import 'rxjs/add/operator/finally';
-import {HttpClientService} from '@app/services/http-client.service';
 import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuthService} from '@app/services/auth.service';
 
 @Component({
   selector: 'login-form',
@@ -28,7 +29,7 @@ import {AppStateService} from '@app/services/storage/app-state.service';
 })
 export class LoginFormComponent {
 
-  constructor(private httpClient: HttpClientService, private appState: AppStateService) {
+  constructor(private authService: AuthService, private appState: AppStateService) {
     appState.getParameter('isLoginInProgress').subscribe(value => this.isLoginInProgress = value);
   }
 
@@ -40,20 +41,25 @@ export class LoginFormComponent {
 
   isLoginInProgress: boolean;
 
-  private setIsAuthorized(value: boolean): void {
-    this.appState.setParameters({
-      isAuthorized: value,
-      isLoginInProgress: false
-    });
-    this.isLoginAlertDisplayed = !value;
-  }
+  /**
+   * Handling the response from the login action. Actually the goal only to show or hide the login error alert.
+   * When it gets error response it shows.
+   * @param {Response} resp
+   */
+  private onLoginError = (resp: Response): void => {
+    this.isLoginAlertDisplayed = true;
+  };
+  /**
+   * Handling the response from the login action. Actually the goal only to show or hide the login error alert.
+   * When it gets success response it hides.
+   * @param {Response} resp
+   */
+  private onLoginSuccess = (resp: Response): void => {
+    this.isLoginAlertDisplayed = false;
+  };
 
   login() {
-    this.appState.setParameter('isLoginInProgress', true);
-    this.httpClient.postFormData('login', {
-      username: this.username,
-      password: this.password
-    }).subscribe(() => this.setIsAuthorized(true), () => this.setIsAuthorized(false));
+    this.authService.login(this.username,this.password).subscribe(this.onLoginSuccess, this.onLoginError);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
index 70150a5..f34dd15 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
@@ -15,30 +15,41 @@
   limitations under the License.
 -->
 
-<div class="tabs-container row">
-  <tabs class="col-md-12" [items]="tabs | async" (tabSwitched)="onSwitchTab($event)"
-        (tabClosed)="onCloseTab($event[0], $event[1])"></tabs>
+<div class="tabs-container container-fluid">
+  <div class="row">
+    <div class="col-md-12">
+      <tabs class="pull-left" [items]="tabs | async" (tabSwitched)="onSwitchTab($event)"
+            (tabClosed)="onCloseTab($event[0], $event[1])"></tabs>
+      <action-menu class="pull-right"></action-menu>
+    </div>
+  </div>
 </div>
-<filters-panel class="row" [filtersForm]="filtersForm"></filters-panel>
-<div *ngIf="autoRefreshRemainingSeconds" class="col-md-12">
-  <div class="auto-refresh-message pull-right">
-    {{'filter.capture.triggeringRefresh' | translate: autoRefreshMessageParams}}
+<div class="container-fluid">
+  <filters-panel class="row" [filtersForm]="filtersForm"></filters-panel>
+  <div class="row">
+    <div *ngIf="autoRefreshRemainingSeconds" class="col-md-12">
+      <div class="auto-refresh-message pull-right">
+        {{'filter.capture.triggeringRefresh' | translate: autoRefreshMessageParams}}
+      </div>
+    </div>
+
+    <!-- TODO use plugin for singular/plural -->
+    <div class="logs-header col-md-12">{{
+      (!totalEventsFoundMessageParams.totalCount ? 'logs.noEventFound' :
+        (totalEventsFoundMessageParams.totalCount === 1 ? 'logs.oneEventFound' : 'logs.totalEventFound'))
+            | translate: totalEventsFoundMessageParams
+    }}</div>
   </div>
+  <collapsible-panel openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
+    <time-histogram [data]="histogramData" [customOptions]="histogramOptions" svgId="service-logs-histogram"
+                    (selectArea)="setCustomTimeRange($event[0], $event[1])"></time-histogram>
+  </collapsible-panel>
+  <ng-container [ngSwitch]="logsType">
+    <service-logs-table *ngSwitchCase="'serviceLogs'" [totalCount]="totalCount" [logs]="serviceLogs | async"
+                        [columns]="serviceLogsColumns | async" [filtersForm]="filtersForm"></service-logs-table>
+    <audit-logs-table *ngSwitchCase="'auditLogs'" [totalCount]="totalCount" [logs]="auditLogs | async"
+                      [columns]="auditLogsColumns | async" [filtersForm]="filtersForm"></audit-logs-table>
+  </ng-container>
+  <log-context *ngIf="isServiceLogContextView" [id]="activeLog.id" [hostName]="activeLog.host_name"
+               [componentName]="activeLog.component_name"></log-context>
 </div>
-<!-- TODO use plugin for singular/plural -->
-<div class="logs-header">{{
-  (!totalEventsFoundMessageParams.totalCount ? 'logs.noEventFound' :
-    (totalEventsFoundMessageParams.totalCount === 1 ? 'logs.oneEventFound' : 'logs.totalEventFound'))
-        | translate: totalEventsFoundMessageParams
-}}</div>
-<collapsible-panel openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
-  <time-histogram [data]="histogramData" [customOptions]="histogramOptions" svgId="service-logs-histogram"
-                  (selectArea)="setCustomTimeRange($event[0], $event[1])"></time-histogram>
-</collapsible-panel>
-<dropdown-button *ngIf="!isServiceLogsFileView" class="pull-right" label="logs.columns"
-                 [options]="availableColumns | async" [isRightAlign]="true" [isMultipleChoice]="true"
-                 action="updateSelectedColumns" [additionalArgs]="logsTypeMapObject.fieldsModel"></dropdown-button>
-<logs-list [logs]="logs | async" [totalCount]="totalCount" [displayedColumns]="displayedColumns"
-           [isServiceLogsFileView]="isServiceLogsFileView" [filtersForm]="filtersForm"></logs-list>
-<log-context *ngIf="isServiceLogContextView" [hostName]="activeLog.host_name" [componentName]="activeLog.component_name"
-             [id]="activeLog.id"></log-context>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less
index 23d5f92..9902b79 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less
@@ -25,6 +25,9 @@
   .tabs-container, .auto-refresh-message {
     background-color: @filters-panel-background-color;
   }
+  .tabs-container {
+    border-bottom: 1px solid @table-border-color;
+  }
 
   filters-panel {
     margin-bottom: @block-margin-top;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
index 0a9418f..2bb8731 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
@@ -33,7 +33,6 @@ import {HostsService, hosts} from '@app/services/storage/hosts.service';
 import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {TabsService, tabs} from '@app/services/storage/tabs.service';
 import {HttpClientService} from '@app/services/http-client.service';
-import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
 import {TabsComponent} from '@app/components/tabs/tabs.component';
@@ -92,7 +91,6 @@ describe('LogsContainerComponent', () => {
         HostsService,
         ServiceLogsTruncatedService,
         TabsService,
-        FilteringService,
         UtilsService,
         LogsContainerService
       ],

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 21949f1..b06cfa4 100644
--- 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
@@ -19,17 +19,12 @@
 import {Component} from '@angular/core';
 import {FormGroup} from '@angular/forms';
 import {Observable} from 'rxjs/Observable';
-import {Subject} from 'rxjs/Subject';
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/takeUntil';
-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 {TabsService} from '@app/services/storage/tabs.service';
 import {AuditLog} from '@app/classes/models/audit-log';
 import {ServiceLog} from '@app/classes/models/service-log';
-import {LogField} from '@app/classes/models/log-field';
 import {Tab} from '@app/classes/models/tab';
 import {BarGraph} from '@app/classes/models/bar-graph';
 import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry';
@@ -43,43 +38,12 @@ import {ListItem} from '@app/classes/list-item';
 })
 export class LogsContainerComponent {
 
-  constructor(private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService, private tabsStorage: TabsService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
+  constructor(
+    private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService,
+    private tabsStorage: TabsService, private logsContainer: LogsContainerService
+  ) {
     this.logsContainer.loadColumnsNames();
-    this.logsTypeChange.first().subscribe(() => this.logsContainer.loadLogs());
-    appState.getParameter('activeLogsType').subscribe((value: string): void => {
-      this.logsType = value;
-      this.logsTypeChange.next();
-      const fieldsModel = this.logsTypeMapObject.fieldsModel,
-        logsModel = this.logsTypeMapObject.logsModel;
-      this.availableColumns = fieldsModel.getAll().takeUntil(this.logsTypeChange).map((fields: LogField[]): ListItem[] => {
-        return fields.filter((field: LogField): boolean => field.isAvailable).map((field: LogField): ListItem => {
-          return {
-            value: field.name,
-            label: field.displayName || field.name,
-            isChecked: field.isDisplayed
-          };
-        });
-      });
-      fieldsModel.getAll().takeUntil(this.logsTypeChange).subscribe(columns => {
-        const availableFields = columns.filter((field: LogField): boolean => field.isAvailable),
-          availableNames = availableFields.map((field: LogField): string => field.name);
-        if (availableNames.length) {
-          this.logs = logsModel.getAll().map((logs: (AuditLog | ServiceLog)[]): (AuditLog | ServiceLog)[] => {
-            return logs.map((log: AuditLog | ServiceLog): AuditLog | ServiceLog => {
-              return availableNames.reduce((obj, key) => Object.assign(obj, {
-                [key]: log[key]
-              }), {});
-            });
-          });
-        }
-        this.displayedColumns = columns.filter((column: LogField): boolean => column.isAvailable && column.isDisplayed);
-      });
-    });
-    appState.getParameter('activeFiltersForm').subscribe((form: FormGroup): void => {
-      this.filtersFormChange.next();
-      form.valueChanges.takeUntil(this.filtersFormChange).subscribe(() => this.logsContainer.loadLogs());
-      this.filtersForm = form;
-    });
+    appState.getParameter('activeLogsType').subscribe((value: string) => this.logsType = value);
     serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => {
       this.histogramData = this.logsContainer.getHistogramData(data);
     });
@@ -88,28 +52,16 @@ export class LogsContainerComponent {
 
   tabs: Observable<Tab[]> = this.tabsStorage.getAll();
 
-  filtersForm: FormGroup;
+  get filtersForm(): FormGroup {
+    return this.logsContainer.filtersForm;
+  };
 
   private logsType: string;
 
-  private filtersFormChange: Subject<any> = new Subject();
-
-  private logsTypeChange: Subject<any> = new Subject();
-
-  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: {[key: string]: number};
 
   readonly histogramOptions: HistogramOptions = {
@@ -117,10 +69,10 @@ export class LogsContainerComponent {
   };
 
   get autoRefreshRemainingSeconds(): number {
-    return this.filtering.autoRefreshRemainingSeconds;
+    return this.logsContainer.autoRefreshRemainingSeconds;
   }
 
-  get autoRefreshMessageParams(): any {
+  get autoRefreshMessageParams(): object {
     return {
       remainingSeconds: this.autoRefreshRemainingSeconds
     };
@@ -133,7 +85,7 @@ export class LogsContainerComponent {
   get totalEventsFoundMessageParams(): object {
     return {
       totalCount: this.totalCount
-    }
+    };
   }
 
   isServiceLogContextView: boolean = false;
@@ -146,8 +98,24 @@ export class LogsContainerComponent {
     return this.logsContainer.activeLog;
   }
 
+  get auditLogs(): Observable<AuditLog[]> {
+    return this.logsContainer.auditLogs;
+  }
+
+  get auditLogsColumns(): Observable<ListItem[]> {
+    return this.logsContainer.auditLogsColumns;
+  }
+
+  get serviceLogs(): Observable<ServiceLog[]> {
+    return this.logsContainer.serviceLogs;
+  }
+
+  get serviceLogsColumns(): Observable<ListItem[]> {
+    return this.logsContainer.serviceLogsColumns;
+  }
+
   setCustomTimeRange(startTime: number, endTime: number): void {
-    this.filtering.setCustomTimeRange(startTime, endTime);
+    this.logsContainer.setCustomTimeRange(startTime, endTime);
   }
 
   onSwitchTab(activeTab: Tab): void {

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
deleted file mode 100644
index 1e0f49c..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!--
-  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="row pull-right">
-  <filter-dropdown [label]="filters.sorting.label" formControlName="sorting" [options]="filters.sorting.options"
-                   [defaultLabel]="filters.sorting.defaultLabel" [isRightAlign]="true"
-                   class="col-md-12"></filter-dropdown>
-</form>
-<div *ngFor="let log of logs; let i = index" class="row">
-  <div class="logs-header col-md-12"
-       *ngIf="!isServiceLogsFileView && (i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime))">
-    <div class="col-md-12">{{log.logtime | amTz: timeZone | amDateFormat: dateFormat}}</div>
-  </div>
-  <accordion-panel *ngIf="!isServiceLogsFileView" [toggleId]="'details-' + i" class="col-md-12">
-    <ng-template>
-      <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + (log.level ? log.level.toLowerCase() : '')"></div>
-      <div class="col-md-1">
-        <dropdown-button iconClass="fa fa-ellipsis-h" [hideCaret]="true" [options]="logActions"
-                         [additionalArgs]="[log]"></dropdown-button>
-      </div>
-      <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' + (log.level ? log.level.toLowerCase() : '')">
-        {{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}}">
-          <!-- TODO remove after restyling the table -->
-        </div>
-        <div class="log-content-inner-wrapper">
-          <div class="log-content" *ngIf="isColumnDisplayed('log_message')"
-               (contextmenu)="openMessageContextMenu($event)">{{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>
-  <log-file-entry *ngIf="isServiceLogsFileView" [time]="log.logtime" [level]="log.level"
-                  [fileName]="log.file" [lineNumber]="log.line_number" [message]="log.log_message"></log-file-entry>
-</div>
-<ul #contextmenu *ngIf="!isServiceLogsFileView" data-component="dropdown-list" class="dropdown-menu context-menu"
-    [items]="contextMenuItems" (selectedItemChange)="updateQuery($event)"></ul>
-<pagination class="pull-right" *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/e83bf1bd/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
deleted file mode 100644
index 67d0615..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * 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 '../mixins';
-
-.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;
-}
-
-/deep/ filter-dropdown {
-  justify-content: flex-end;
-}
-
-.hexagon {
-  // TODO remove, since it's not a part of updated 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;
-  .log-colors;
-}
-
-.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 {
-    &.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;
-    }
-  }
-}
-
-.context-menu {
-  position: fixed;
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
deleted file mode 100644
index 8ee4ca3..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * 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 {TranslationModules} from '@app/test-config.spec';
-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 {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 {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';
-
-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,
-          appState,
-          clusters,
-          components,
-          hosts
-        }),
-        MomentModule,
-        MomentTimezoneModule,
-        ...TranslationModules
-      ],
-      providers: [
-        {
-          provide: HttpClientService,
-          useValue: httpClient
-        },
-        AuditLogsService,
-        ServiceLogsService,
-        AppSettingsService,
-        AppStateService,
-        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/e83bf1bd/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
deleted file mode 100644
index 017bc82..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * 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, AfterViewInit, Input, ViewChild, ElementRef} from '@angular/core';
-import {FormGroup} from '@angular/forms';
-import 'rxjs/add/operator/map';
-import {FilteringService} from '@app/services/filtering.service';
-import {UtilsService} from '@app/services/utils.service';
-import {AuditLog} from '@app/classes/models/audit-log';
-import {ServiceLog} from '@app/classes/models/service-log';
-import {LogField} from '@app/classes/models/log-field';
-
-@Component({
-  selector: 'logs-list',
-  templateUrl: './logs-list.component.html',
-  styleUrls: ['./logs-list.component.less']
-})
-export class LogsListComponent implements AfterViewInit {
-
-  constructor(private filtering: FilteringService, private utils: UtilsService) {
-  }
-
-  ngAfterViewInit() {
-    if (this.contextMenu) {
-      this.contextMenuElement = this.contextMenu.nativeElement;
-    }
-  }
-
-  @Input()
-  logs: (AuditLog| ServiceLog)[] = [];
-
-  @Input()
-  totalCount: number = 0;
-
-  @Input()
-  displayedColumns: LogField[] = [];
-
-  @Input()
-  isServiceLogsFileView: boolean = false;
-
-  @Input()
-  filtersForm: FormGroup;
-
-  @ViewChild('contextmenu', {
-    read: ElementRef
-  })
-  contextMenu: ElementRef;
-
-  private contextMenuElement: HTMLElement;
-
-  private selectedText: string = '';
-
-  private readonly messageFilterParameterName = 'log_message';
-
-  readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message'];
-
-  readonly contextMenuItems = [
-    {
-      label: 'logs.addToQuery',
-      iconClass: 'fa fa-search-plus',
-      value: false // 'isExclude' is false
-    },
-    {
-      label: 'logs.excludeFromQuery',
-      iconClass: 'fa fa-search-minus',
-      value: true // 'isExclude' is true
-    }
-  ];
-
-  readonly logActions = [
-    {
-      label: 'logs.copy',
-      iconClass: 'fa fa-files-o',
-      action: 'copyLog'
-    },
-    {
-      label: 'logs.open',
-      iconClass: 'fa fa-external-link',
-      action: 'openLog'
-    },
-    {
-      label: 'logs.context',
-      iconClass: 'fa fa-crosshairs',
-      action: 'openContext'
-    }
-  ];
-
-  readonly dateFormat: string = 'dddd, MMMM Do';
-
-  readonly timeFormat: string = 'h:mm:ss A';
-
-  get timeZone(): string {
-    return this.filtering.timeZone;
-  }
-
-  get filters(): any {
-    return this.filtering.filters;
-  }
-
-  isDifferentDates(dateA, dateB): boolean {
-    return this.utils.isDifferentDates(dateA, dateB, this.timeZone);
-  }
-
-  isColumnDisplayed(key: string): boolean {
-    return this.displayedColumns.some((column: LogField): boolean  => column.name === key);
-  }
-
-  openMessageContextMenu(event: MouseEvent): void {
-    const selectedText = getSelection().toString();
-    if (selectedText) {
-      let contextMenuStyle = this.contextMenuElement.style;
-      Object.assign(contextMenuStyle, {
-        left: `${event.clientX}px`,
-        top: `${event.clientY}px`,
-        display: 'block'
-      });
-      this.selectedText = selectedText;
-      document.body.addEventListener('click', this.dismissContextMenu);
-      event.preventDefault();
-    }
-  }
-
-  updateQuery(event: any) {
-    this.filtering.queryParameterAdd.next({
-      name: this.messageFilterParameterName,
-      value: this.selectedText,
-      isExclude: event.value
-    });
-  }
-
-  private dismissContextMenu = (): void => {
-    this.selectedText = '';
-    this.contextMenuElement.style.display = 'none';
-    document.body.removeEventListener('click', this.dismissContextMenu);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 2061582..95dd238 100644
--- 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
@@ -20,4 +20,4 @@
   <span class="fa fa-spinner fa-spin"></span>
 </div>
 <login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form>
-<logs-container *ngIf="isAuthorized" class="col-md-12"></logs-container>
+<logs-container *ngIf="isAuthorized"></logs-container>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index ca70927..5e2b15f 100644
--- 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
@@ -15,14 +15,15 @@
   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}}</a>
-  <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="updateValue($event)"
+<div #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true, 'open': dropdownIsOpen}">
+  <a class="dropdown-toggle" [ngClass]="(labelClass || '') + (hasCaret ? ' has-caret' : '')"
+    (click)="onMouseClick($event)"
+    (mousedown)="onMouseDown($event)">
+    <i *ngIf="iconClass" [ngClass]="['icon', iconClass]"></i>
+    <i *ngIf="hasCaret" [ngClass]="['fa ', caretClass ]"></i>
+    <span *ngIf="label" class="menu-button-label">{{label}}</span>
+  </a>
+  <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="onDropdownItemChange($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/e83bf1bd/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
index 615db24..0207561 100644
--- 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
@@ -21,14 +21,26 @@
   cursor: pointer;
   display: inline-block;
   position: relative;
-  a:hover, a:focus {
+  a {
+    text-align: center;
     text-decoration: none;
+    i {
+      color: @link-color;
+      display: inline-block;
+      position: relative;
+      &.fa-caret-down {
+        padding: 0 .25em;
+      }
+    }
+    .menu-button-label {
+      display: block;
+    }
   }
-
-  .icon {
-    padding: @icon-padding;
+  a:hover, a:focus {
+    i {
+      color: @link-hover-color;
+    }
   }
-
   .unstyled-link {
     color: inherit;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 261e213..3836e7a 100644
--- 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
@@ -33,9 +33,9 @@ import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/se
 import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {TabsService, tabs} from '@app/services/storage/tabs.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
-import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
+import {AuthService} from '@app/services/auth.service';
 
 import {MenuButtonComponent} from './menu-button.component';
 
@@ -85,12 +85,12 @@ describe('MenuButtonComponent', () => {
         ServiceLogsTruncatedService,
         TabsService,
         ComponentActionsService,
-        FilteringService,
         {
           provide: HttpClientService,
           useValue: httpClient
         },
-        LogsContainerService
+        LogsContainerService,
+        AuthService
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 0aa7c7e..ca89935 100644
--- 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
@@ -19,7 +19,6 @@
 import {Component, Input, ViewChild, ElementRef} from '@angular/core';
 import {ListItem} from '@app/classes/list-item';
 import {ComponentActionsService} from '@app/services/component-actions.service';
-import * as $ from 'jquery';
 
 @Component({
   selector: 'menu-button',
@@ -64,6 +63,37 @@ export class MenuButtonComponent {
   @Input()
   badge: string;
 
+  @Input()
+  caretClass: string = 'fa-caret-down';
+
+  /**
+   * The minimum time to handle a mousedown as a longclick. Default is 500 ms (0.5sec)
+   * @default 500
+   * @type {number}
+   */
+  @Input()
+  minLongClickDelay: number = 500;
+
+  /**
+   * The maximum milliseconds to wait for longclick ends. The default is 0 which means no upper limit.
+   * @default 0
+   * @type {number}
+   */
+  @Input()
+  maxLongClickDelay: number = 0;
+
+  /**
+   * This is a private property to indicate the mousedown timestamp, so that we can check it when teh click event
+   * has been triggered.
+   */
+  private mouseDownTimestamp: number;
+
+  /**
+   * Indicates if the dropdown list is open or not. So that we use internal state to display or hide the dropdown.
+   * @type {boolean}
+   */
+  private dropdownIsOpen: boolean = false;
+
   get hasSubItems(): boolean {
     return Boolean(this.subItems && this.subItems.length);
   }
@@ -72,29 +102,111 @@ export class MenuButtonComponent {
     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();
+  /**
+   * Handling the click event on the component element.
+   * Two goal:
+   * - check if we have a 'longclick' event and open the dropdown (if any) when longclick event happened
+   * - trigger the action or the dropdown open depending on the target element (caret will open the dropdown otherwise
+   * trigger the action.
+   * @param {MouseEvent} event
+   */
+  onMouseClick(event: MouseEvent): void {
+    let el = <HTMLElement>event.target;
+    let now = Date.now();
+    let mdt = this.mouseDownTimestamp; // mousedown time
+    let isLongClick = mdt && mdt + this.minLongClickDelay <= now && (
+      !this.maxLongClickDelay || mdt + this.maxLongClickDelay >= now
+    );
+    let openDropdown = this.hasSubItems && (
+      el.classList.contains(this.caretClass) || isLongClick || !this.actions[this.action]
+    );
+    if (openDropdown && this.dropdown) {
+      if (this.toggleDropdown()) {
+        this.listenToClickOut();
+      }
+    } else if (this.action) {
+      this.actions[this.action]();
     }
+    this.mouseDownTimestamp = 0;
+    event.preventDefault();
   }
 
-  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]();
+  /**
+   * Listening the click event on the document so that we can hide our dropdown list if the event source is not the
+   * component.
+   */
+  private listenToClickOut = (): void => {
+    this.dropdownIsOpen && document.addEventListener('click', this.onDocumentMouseClick);
+  };
+
+  /**
+   * Handling the click event on the document to hide the dropdown list if it needs.
+   * @param {MouseEvent} event
+   */
+  private onDocumentMouseClick = (event: MouseEvent): void => {
+    let el = <HTMLElement>event.target;
+    if (!this.dropdown.nativeElement.contains(el)) {
+      this.closeDropdown();
+      this.removeDocumentClickListener()
+    }
+  };
+
+  /**
+   * Handling the mousedown event, so that we can check the long clicks and open the dropdown if any.
+   * @param {MouseEvent} event
+   */
+  onMouseDown = (event: MouseEvent): void => {
+    if (this.hasSubItems) {
+      let el = <HTMLElement>event.target;
+      if (!el.classList.contains(this.caretClass)) {
+        this.mouseDownTimestamp = Date.now();
       }
-      event.stopPropagation();
     }
+  };
+
+  /**
+   * The goal is to have one and only one place where we open the dropdown. So that later if we need to change the way
+   * how we do, it will be easier.
+   */
+  private openDropdown():void {
+    this.dropdownIsOpen = true;
+  }
+
+  /**
+   * The goal is to have one and only one place where we close the dropdown. So that later if we need to change the way
+   * how we do, it will be easier.
+   */
+  private closeDropdown():void {
+    this.dropdownIsOpen = false;
+  }
+
+  /**
+   * Just a simple helper method to make the dropdown toggle more easy.
+   * @returns {boolean} It will return the open state of the dropdown;
+   */
+  private toggleDropdown(): boolean {
+    this[this.dropdownIsOpen ? 'closeDropdown' : 'openDropdown']();
+    return this.dropdownIsOpen;
+  }
+
+  /**
+   * The goal is to simply remove the click event listeners from the document.
+   */
+  private removeDocumentClickListener(): void {
+    document.removeEventListener('click', this.onDocumentMouseClick);
+  }
+
+  /**
+   * The main goal if this function is tho handle the item change event on the child dropdown list.
+   * Should update the value and close the dropdown if it is not multiple choice type.
+   * @param {ListItem} options The selected item(s) from the dropdown list.
+   */
+  onDropdownItemChange(options: ListItem) {
+    this.updateSelection(options);
+    !this.isMultipleChoice && this.closeDropdown();
   }
 
-  updateValue(options: ListItem) {
+  updateSelection(options: ListItem) {
     // TODO implement value change behaviour
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
index 2e46213..5fa265b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
@@ -28,6 +28,11 @@
   justify-content: space-between;
 }
 
+.stretch-flex {
+  align-items: stretch;
+  display: flex;
+}
+
 .common-hexagon(@side, @color) {
   display: block;
   position: absolute;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index c227a2b..370d1c7 100644
--- 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
@@ -14,10 +14,15 @@
   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 class="btn btn-link" [disabled]="!hasPreviousPage()" (click)="setFirstPage()">
+  <span class="pagination-control fa fa-angle-double-left"></span>
+</button>
+<button class="btn btn-link" [disabled]="!hasPreviousPage()" (click)="setPreviousPage()">
+  <span class="pagination-control fa fa-angle-left"></span>
+</button>
+<button class="btn btn-link" [disabled]="!hasNextPage()" (click)="setNextPage()">
+  <span class="pagination-control fa fa-angle-right"></span>
 </button>
-<button class="btn btn-link" [disabled]="currentPage === pagesCount - 1" (click)="updateValue()">
-  <span class="pagination-control fa fa-chevron-right"></span>
+<button class="btn btn-link" [disabled]="!hasNextPage()" (click)="setLastPage()">
+  <span class="pagination-control fa fa-angle-double-right"></span>
 </button>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 489f79c..999609c 100644
--- 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
@@ -34,10 +34,111 @@ describe('PaginationControlsComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PaginationControlsComponent);
     component = fixture.componentInstance;
+    component.registerOnChange(() => {});
+    component.pagesCount = 3;
+    component.totalCount = 30;
     fixture.detectChanges();
   });
 
   it('should create component', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should the hasNextPage function return true when the currentPage is less than the pagesCount', () => {
+    component.pagesCount = 3;
+    component.totalCount = 30;
+    fixture.detectChanges();
+    expect(component.hasNextPage()).toBe(true);
+  });
+  it('should the hasNextPage function return false when the currentPage is equal than the pagesCount', () => {
+    component.currentPage = 3;
+    fixture.detectChanges();
+    expect(component.hasNextPage()).toBe(false);
+  });
+  it('should the hasNextPage function return false when the pagesCount is 0', () => {
+    component.pagesCount = 0;
+    component.totalCount = 0;
+    component.currentPage = 0;
+    fixture.detectChanges();
+    expect(component.hasNextPage()).toBe(false);
+  });
+
+  it('should the hasPreviousPage function return true when the currentPage is greater than 0 and the pagesCount is greater than 0', () => {
+    component.currentPage = 1;
+    fixture.detectChanges();
+    expect(component.hasPreviousPage()).toBe(true);
+  });
+  it('should the hasPreviousPage function return false when the currentPage is equal to 0', () => {
+    component.currentPage = 0;
+    fixture.detectChanges();
+    expect(component.hasPreviousPage()).toBe(false);
+  });
+  it('should the hasPreviousPage function return false when the pagesCount is 0', () => {
+    component.pagesCount = 0;
+    component.totalCount = 0;
+    fixture.detectChanges();
+    expect(component.hasPreviousPage()).toBe(false);
+  });
+
+  it('should the setNextPage function increment the value/currentPage when it is less then the pagesCount', () => {
+    let initialPage = 0;
+    let pagesCount = 3;
+    component.pagesCount = pagesCount;
+    component.totalCount = 30;
+    component.currentPage = initialPage;
+    fixture.detectChanges();
+    component.setNextPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(initialPage + 1);
+  });
+
+  it('should not the setNextPage function increment the value/currentPage when it is on the last page', () => {
+    let pagesCount = 3;
+    component.pagesCount = pagesCount;
+    component.totalCount = 30;
+    component.currentPage = pagesCount - 1;
+    fixture.detectChanges();
+    component.setNextPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(pagesCount - 1);
+  });
+
+  it('should the setPreviousPage function decrement the value/currentPage', () => {
+    let initialPage = 1;
+    component.pagesCount = 3;
+    component.totalCount = 30;
+    component.currentPage = initialPage;
+    fixture.detectChanges();
+    component.setPreviousPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(initialPage - 1);
+  });
+
+  it('should not the setPreviousPage function decrement the value/currentPage when it is equal to 0', () => {
+    component.pagesCount = 3;
+    component.totalCount = 30;
+    component.currentPage = 0;
+    fixture.detectChanges();
+    component.setPreviousPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(0);
+  });
+
+  it('should the setFirstPage set the value/currentPage to 0', () => {
+    component.pagesCount = 3;
+    component.totalCount = 30;
+    component.currentPage = 1;
+    fixture.detectChanges();
+    component.setFirstPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(0);
+  });
+
+
+  it('should the setLastPage set the value/currentPage to the value of pagesCount', () => {
+    component.setLastPage();
+    fixture.detectChanges();
+    expect(component.currentPage).toEqual(component.pagesCount - 1);
+  });
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index c71844c..5f85da7 100644
--- 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
@@ -51,16 +51,80 @@ export class PaginationControlsComponent implements ControlValueAccessor {
   }
 
   set value(newValue: number) {
-    this.currentPage = newValue;
-    this.currentPageChange.emit(newValue);
-    this.onChange(newValue);
+    if (this.isValidValue(newValue)) { // this is the last validation check
+      this.currentPage = newValue;
+      this.currentPageChange.emit(newValue);
+      if (this.onChange) {
+        this.onChange(newValue);
+      }
+    } else {
+      throw new Error(`Invalid value ${newValue}. The currentPage should be between 0 and ${this.pagesCount}.`);
+    }
+  }
+
+  /**
+   * A simple check if the given value is valid for the current pagination instance
+   * @param {number} value The new value to test
+   * @returns {boolean}
+   */
+  private isValidValue(value: number): boolean {
+    return value <= this.pagesCount || value >= 0;
+  }
+
+  /**
+   * The goal is to set the value to the first page... obviously to zero. It is just to have a centralized api for that.
+   */
+  setFirstPage(): void {
+    this.value = 0;
+  }
+
+  /**
+   * The goal is to set the value to the last page which is the pagesCount property anyway.
+   */
+  setLastPage(): void {
+    this.value = this.pagesCount - 1;
+  }
+
+  /**
+   * The goal is to decrease the value (currentPage) property if it is possible (checking with 'hasPreviousPage').
+   * @returns {number} The new value of the currentPage
+   */
+  setPreviousPage(): number {
+    if (this.hasPreviousPage()) {
+      this.value -= 1;
+    }
+    return this.value;
+  }
+
+  /**
+   * The goal is to increase the value (currentPage) property if it is possible (checking with 'hasNextPage').
+   * @returns {number} The new value of the currentPage
+   */
+  setNextPage(): number {
+    if (this.hasNextPage()){
+      this.value += 1;
+    }
+    return this.value;
+  }
+
+  /**
+   * The goal is to have a single source of true to check if we can set a next page or not.
+   * @returns {boolean}
+   */
+  hasNextPage(): boolean {
+    return this.pagesCount > 0 && this.value < this.pagesCount - 1;
   }
 
-  updateValue(isDecrement?: boolean) {
-    isDecrement? this.value-- : this.value++;
+  /**
+   * The goal is to have a single source of true to check if we can set a previous page or not.
+   * @returns {boolean}
+   */
+  hasPreviousPage(): boolean {
+    return this.pagesCount > 0 && this.value > 0;
   }
 
-  writeValue() {
+  writeValue(value: number) {
+    this.value = value;
   }
 
   registerOnChange(callback: any): void {

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index 679a7e5..4be0a47 100644
--- 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
@@ -17,7 +17,7 @@
 
 <form class="pagination-form" [formGroup]="filtersForm">
   <filter-dropdown [label]="filterInstance.label" formControlName="pageSize" [options]="filterInstance.options"
-                   [defaultLabel]="filterInstance.defaultLabel" [isRightAlign]="true" isDropup="true"></filter-dropdown>
+                   [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>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index ff8675d..c820027 100644
--- 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
@@ -39,7 +39,14 @@ describe('PaginationComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(PaginationComponent);
     component = fixture.componentInstance;
-    component.filterInstance = {};
+    component.filterInstance = {
+      defaultSelection: [
+        {
+          label: '10',
+          value: '10'
+        }
+      ]
+    };
     component.filtersForm = new FormGroup({
       pageSize: new FormControl()
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
index cc5589f..890c2ee 100644
--- 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
@@ -18,6 +18,8 @@
 
 import {Component, OnInit, Input} from '@angular/core';
 import {FormGroup} from '@angular/forms';
+import {ListItem} from '@app/classes/list-item';
+import {FilterCondition} from '@app/classes/filtering';
 
 @Component({
   selector: 'pagination',
@@ -27,9 +29,9 @@ import {FormGroup} from '@angular/forms';
 export class PaginationComponent implements OnInit {
 
   ngOnInit() {
-    this.setPageSizeFromString(this.filterInstance.defaultValue);
-    this.filtersForm.controls.pageSize.valueChanges.subscribe((value: string): void => {
-      this.setPageSizeFromString(value);
+    this.setPageSizeFromString(this.filterInstance.defaultSelection[0].value);
+    this.filtersForm.controls.pageSize.valueChanges.subscribe((selection: ListItem): void => {
+      this.setPageSizeFromString(selection[0].value);
     });
   }
 
@@ -37,7 +39,7 @@ export class PaginationComponent implements OnInit {
   filtersForm: FormGroup;
 
   @Input()
-  filterInstance: any;
+  filterInstance: FilterCondition;
 
   @Input()
   currentCount?: number;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
index 5520310..18ff715 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
@@ -159,7 +159,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
     this.isValueInput = true;
     this.currentValue = '';
     setTimeout(() => this.valueInput.focus(), 0);
-  }
+  };
 
   onParameterValueChange(event: KeyboardEvent): void {
     if (this.utils.isEnterPressed(event) && this.currentValue) {
@@ -187,7 +187,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
       isExclude: options.isExclude
     });
     this.updateValue();
-  }
+  };
 
   removeParameter(event: MouseEvent, id: number): void {
     this.parameters = this.parameters.filter(parameter => parameter.id !== id);
@@ -196,10 +196,14 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
   }
 
   updateValue() {
-    this.onChange(this.parameters);
+    if (this.onChange) {
+      this.onChange(this.parameters);
+    }
   }
 
-  writeValue() {
+  writeValue(parameters: any [] = []) {
+    this.parameters = parameters;
+    this.updateValue();
   }
 
   registerOnChange(callback: any): void {

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
new file mode 100644
index 0000000..a2e666e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
@@ -0,0 +1,76 @@
+<!--
+  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.
+-->
+
+<dropdown-button class="pull-right" label="logs.columns" [options]="columns" [isRightAlign]="true"
+                 [isMultipleChoice]="true" action="updateSelectedColumns"
+                 [additionalArgs]="logsTypeMapObject.fieldsModel"></dropdown-button>
+<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="row pull-right">
+  <filter-dropdown class="col-md-12" [label]="filters.serviceLogsSorting.label" formControlName="serviceLogsSorting"
+                   [options]="filters.serviceLogsSorting.options" [isRightAlign]="true"></filter-dropdown>
+</form>
+<div class="panel panel-default">
+  <div class="panel-body">
+    <table class="table table-hover">
+      <tbody>
+        <ng-container *ngFor="let log of logs; let i = index">
+          <tr *ngIf="i === 0 || isDifferentDates(log.logtime, logs[i - 1].logtime)" class="log-date-row">
+            <th attr.colspan="{{displayedColumns.length + 1}}">
+              {{log.logtime | amTz: timeZone | amDateFormat: dateFormat}}
+            </th>
+          </tr>
+          <tr class="log-item-row">
+            <td class="log-action">
+              <dropdown-button iconClass="fa fa-ellipsis-h action" [hideCaret]="true" [options]="logActions"
+                               [additionalArgs]="[log]"></dropdown-button>
+            </td>
+            <td *ngIf="isColumnDisplayed('logtime')" class="log-time">
+              <time>
+                {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}}
+              </time>
+            </td>
+            <td *ngIf="isColumnDisplayed('level')" [ngClass]="'log-level ' + log.level.toLowerCase()">
+              <log-level [logEntry]="log"></log-level>
+            </td>
+            <td *ngIf="isColumnDisplayed('type')" [ngClass]="'log-type'">
+              {{log.type}}
+            </td>
+            <td *ngIf="isColumnDisplayed('log_message')" [ngClass]="'log-message'" width="*"
+                (contextmenu)="openMessageContextMenu($event)">
+              <log-message [listenChangesOn]="displayedColumns">{{log.log_message}}</log-message>
+            </td>
+            <ng-container *ngFor="let column of displayedColumns">
+              <td *ngIf="customStyledColumns.indexOf(column.value) === -1" [ngClass]="'log-' + column.value">
+                {{log[column.value]}}
+              </td>
+            </ng-container>
+          </tr>
+        </ng-container>
+      </tbody>
+      <tfoot>
+        <tr>
+          <td attr.colspan="{{displayedColumns.length + 1}}">
+            <pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount"
+                        [filtersForm]="filtersForm" [filterInstance]="filters.pageSize"
+                        [currentCount]="logs.length"></pagination>
+          </td>
+        </tr>
+      </tfoot>
+    </table>
+    <ul #contextmenu data-component="dropdown-list" class="dropdown-menu context-menu" [items]="contextMenuItems"
+        (selectedItemChange)="updateQuery($event)"></ul>
+  </div>
+</div>