This is an automated email from the ASF dual-hosted git repository.

tobiasistvan pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 544b07e  [AMBARI-24631] [Log Search UI] styles and layout fixes (#2339)
544b07e is described below

commit 544b07e74938584571c51ac0a109b2e6d4c33d78
Author: Istvan Tobias <tobias.ist...@gmail.com>
AuthorDate: Wed Sep 19 14:06:44 2018 +0200

    [AMBARI-24631] [Log Search UI] styles and layout fixes (#2339)
    
    * [AMBARI-24631] [Log Search UI] styles and layout fixes - autorefresh 
styling
    
    * [AMBARI-24631] [Log Search UI] styles and layout fixes
    
    Change-Id: I6d233019638464cf39c7af038b1b350d242279e8
    
    * [AMBARI-24631] [Log Search UI] styles and layout fixes - PR change 
requests
    
    Change-Id: Id3434b605929fc9e0129eeea341cab25a3bf1180
---
 ambari-logsearch/ambari-logsearch-web/package.json |   2 +-
 .../classes/components/graph/graph.component.ts    |   3 +
 .../components/graph/time-graph.component.less     |   2 +-
 .../action-menu/action-menu.component.less         |   6 +
 .../action-menu/action-menu.component.ts           |   4 +
 .../collapsible-panel.component.html               |   1 +
 .../collapsible-panel.component.less               |   2 +
 .../log-index-filter.component.less                |   3 +
 .../log-message/log-message.component.html         |   4 +-
 .../log-message/log-message.component.spec.ts      |   7 +-
 .../log-message/log-message.component.ts           |  41 ++---
 .../logs-container/logs-container.component.html   |  41 +++--
 .../logs-container/logs-container.component.less   |  43 ++++-
 .../logs-container.component.spec.ts               |   4 +-
 .../logs-container/logs-container.component.ts     |  24 ++-
 .../service-logs-table.component.html              |  17 +-
 .../service-logs-table.component.less              |  13 ++
 .../service-logs-table.component.ts                | 192 +++++++++++++--------
 .../time-histogram/time-histogram.component.html   |   3 +
 .../time-histogram/time-histogram.component.less   |   3 +
 .../time-range-picker.component.html               |   2 +-
 .../circle-progress-bar.component.html}            |  24 ++-
 .../circle-progress-bar.component.less}            |  45 ++---
 .../circle-progress-bar.component.spec.ts}         |  47 +++--
 .../circle-progress-bar.component.ts               |  86 +++++++++
 .../modal-dialog/modal-dialog.component.less       |  13 +-
 .../modal-dialog/modal-dialog.component.ts         |   4 +-
 .../src/app/modules/shared/shared.module.ts        |   7 +-
 .../src/app/modules/shared/variables.less          |  16 +-
 .../src/app/services/logs-container.service.ts     |  32 ++--
 .../ambari-logsearch-web/src/assets/i18n/en.json   |   6 +
 ambari-logsearch/ambari-logsearch-web/yarn.lock    |   6 +-
 32 files changed, 499 insertions(+), 204 deletions(-)

diff --git a/ambari-logsearch/ambari-logsearch-web/package.json 
b/ambari-logsearch/ambari-logsearch-web/package.json
index fb09f00..3639b54 100644
--- a/ambari-logsearch/ambari-logsearch-web/package.json
+++ b/ambari-logsearch/ambari-logsearch-web/package.json
@@ -39,7 +39,7 @@
     "jquery": "^1.12.4",
     "moment": "^2.18.1",
     "moment-timezone": "^0.5.13",
-    "ngx-bootstrap": "^1.9.3",
+    "ngx-bootstrap": "^2.0.5",
     "rxjs": "^5.4.3",
     "zone.js": "^0.8.4"
   },
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
index 7a0729a..af6a9db 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
@@ -63,6 +63,9 @@ export class GraphComponent implements AfterViewInit, 
OnChanges, OnInit, OnDestr
   @Input()
   labels: HomogeneousObject<string> = {};
 
+  @Input()
+  chartLabel: string;
+
   /**
    * Indicates whether the graph represents dependency on time
    * @type {boolean}
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
index 73f1870..bfafd40 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
@@ -24,7 +24,7 @@
     cursor: crosshair;
   }
 
-  .time-gap {
+  .chart-label, .time-gap {
     color: @base-font-color;
     font-size: 1.2rem;
     text-align: center;
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.less
index c1f3014..a8c6e05 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.less
@@ -111,4 +111,10 @@
       padding-right: 0;
     }
   }
+  /deep/ modal-dialog.log-index-filter .modal-header {
+    min-height: 4rem;
+    header {
+      margin-left: auto;
+    }
+  }
 }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
index fcf3587..a293e95 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
@@ -164,4 +164,8 @@ export class ActionMenuComponent  implements OnInit, 
OnDestroy {
     this.logsContainer.stopCaptureTimer();
   }
 
+  cancelCapture(): void {
+    this.logsContainer.cancelCapture();
+  }
+
 }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
index a671946..ce1e872 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
@@ -20,6 +20,7 @@
         <i [ngClass]="{'fa': true, 'fa-caret-down': !isCollapsed, 
'fa-caret-right': isCollapsed}"></i>
         {{((isCollapsed ? collapsedTitle : openTitle) || commonTitle) | 
translate}}
       </a>
+      <ng-content select="header"></ng-content>
     </div>
     <div class="panel-body" [attr.aria-collapsed]="isCollapsed">
       <ng-content></ng-content>
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
index 571e6a2..79d25f3 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
@@ -24,6 +24,8 @@
     background-color: @panel-heading;
     border: 0 none;
     color: @base-font-color;
+    display: flex;
+    flex-direction: row;
     font-size: 1.25rem;
     a, a:hover, a:visited {
       color: @base-font-color;
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less
index a7f48bc..a5c3957 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.less
@@ -27,6 +27,9 @@
         position: sticky;
         top: -1px;
         z-index: 10;
+        th {
+          padding: 8px 0;
+        }
       }
       .component-column {
         width: 25%;
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
index 9e05397..70c7ed7 100644
--- 
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
@@ -14,11 +14,11 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<div [ngClass]="{
+<div #wrapper [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">{{isOpen ? message : (message | 
replace: multiLineTestRegexp : ' ')}}</div>
+  <div #content class="log-message-content">{{ message }}</div>
 </div>
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
index d2339c9..eafb1aa 100644
--- 
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
@@ -48,7 +48,7 @@ describe('LogMessageComponent', () => {
   });
 
   it('event handler should call the toggleOpen method', () => {
-    let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+    const mockEvent: MouseEvent = document.createEvent('MouseEvent');
     mockEvent.initEvent('click', true, true);
     spyOn(component,'toggleOpen');
     component.onCaretClick(mockEvent);
@@ -56,7 +56,7 @@ describe('LogMessageComponent', () => {
   });
 
   it('event handler should prevent the default behaviour of the action', () => 
{
-    let mockEvent: MouseEvent = document.createEvent('MouseEvent');
+    const mockEvent: MouseEvent = document.createEvent('MouseEvent');
     mockEvent.initEvent('click', true, true);
     spyOn(mockEvent,'preventDefault');
     component.onCaretClick(mockEvent);
@@ -64,13 +64,14 @@ describe('LogMessageComponent', () => {
   });
 
   it('calling the toggleOpen method should negate the isOpen property', () => {
-    let currentState = component.isOpen;
+    const currentState = component.isOpen;
     component.toggleOpen();
     expect(component.isOpen).toEqual(!currentState);
   });
 
   it('should set the addCaret prop to TRUE if the message prop has new line 
character.', () => {
     component.message = messages.withNewLine;
+    component.reCalculateOnChange();
     component.checkAddCaret();
     expect(component['addCaret']).toEqual(true);
   });
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
index ac4fb31..10d1cca 100644
--- 
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
@@ -24,12 +24,11 @@ import {
   OnInit,
   OnDestroy,
   SimpleChanges,
-  HostListener,
   ChangeDetectorRef
 } from '@angular/core';
-import {Observable} from 'rxjs/Observable';
+import {Subject} from 'rxjs/Subject';
 import {Subscription} from 'rxjs/Subscription';
-import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/auditTime';
 
 /**
  * This is a simple UI component to display the log message. The goal is to be 
able to show one line and be collapsile
@@ -63,7 +62,7 @@ export class LogMessageComponent implements AfterViewInit, 
OnChanges, OnInit, On
    * LogMessageComponent should check if the caret should be visible or not.
    */
   @Input()
-  listenChangesOn: any;
+  refreshOn$: Subject<any>;
 
   /**
    * This will be shown as log message in the component
@@ -76,7 +75,9 @@ export class LogMessageComponent implements AfterViewInit, 
OnChanges, OnInit, On
    * the content container element. Handled by the @checkAddCaret method
    * @type {boolean}
    */
-  private addCaret = false;
+  addCaret = false;
+
+  private scrollWidth: number;
 
   /**
    * This is a regexp tester to check if the log message is multiline text or 
single line. Doing by checking the new
@@ -90,9 +91,7 @@ export class LogMessageComponent implements AfterViewInit, 
OnChanges, OnInit, On
    * caret to give a possibility to the user to see the message as it is 
(pre-wrapped).
    * @type {boolean}
    */
-  private get isMultiLineMessage(): boolean {
-    return this.multiLineTestRegexp.test(this.message);
-  }
+  isMultiLineMessage = false;
 
   /**
    * The array to collect all the subscriptions created by the instance in 
order to unsubscribe when the component
@@ -109,15 +108,17 @@ export class LogMessageComponent implements 
AfterViewInit, OnChanges, OnInit, On
    * @param {SimpleChanges} changes
    */
   ngOnChanges(changes: SimpleChanges): void {
-    if (changes.listenChangesOn !== undefined) {
+    if (changes.message !== undefined) {
+      this.message = this.message.trim();
+      this.reCalculateOnChange();
       this.checkAddCaret();
     }
   }
 
   ngOnInit() {
-    this.subscriptions.push(
-      Observable.fromEvent(window, 
'resize').debounceTime(100).subscribe(this.onWindowResize)
-    );
+    if (this.refreshOn$) {
+      this.subscriptions.push(this.refreshOn$.subscribe(this.checkAddCaret));
+    }
   }
 
   ngOnDestroy() {
@@ -128,16 +129,13 @@ export class LogMessageComponent implements 
AfterViewInit, OnChanges, OnInit, On
    * The goal is to perform a initial caret display check when the component 
has been initialized.
    */
   ngAfterViewInit(): void {
+    this.reCalculateOnChange();
     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.
-   */
-  onWindowResize = (): void => {
-    this.checkAddCaret();
+  reCalculateOnChange() {
+    this.isMultiLineMessage = this.multiLineTestRegexp.test(this.message);
+    this.scrollWidth = this.content.nativeElement.scrollWidth;
   }
 
   /**
@@ -145,10 +143,9 @@ export class LogMessageComponent implements AfterViewInit, 
OnChanges, OnInit, On
    * scrollHeight and the clientHeight.
    */
   checkAddCaret = (): void =>  {
-    const el = this.content.nativeElement;
-    this.addCaret = this.isMultiLineMessage || (el.scrollHeight > 
el.clientHeight) || (el.scrollWidth > el.clientWidth);
+    this.addCaret = this.isMultiLineMessage || (this.scrollWidth > 
this.content.nativeElement.clientWidth);
     this.cdRef.detectChanges();
-  };
+  }
 
   /**
    * This is the click event handler of the caret button element. It will only 
toggle the isOpen property so that the
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 7d0bb62..c319ca9 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
@@ -29,22 +29,20 @@
   [ngClass]="{'container-fluid': true, 'logs-container': true, 
'fixed-filterbar': isFilterPanelFixedPostioned}">
   <filters-panel class="row" [filtersForm]="filtersForm" 
#filtersPanel></filters-panel>
   <div class="row events-count">
-    <div *ngIf="autoRefreshRemainingSeconds" class="col-md-12">
-      <div class="auto-refresh-message pull-right">
-        {{'filter.capture.triggeringRefresh' | translate: 
autoRefreshMessageParams}}
-      </div>
+    <div *ngIf="captureTimeRangeCache" class="panel panel-default 
panel-capture-view col-md-2 col-md-offset-5">
+      <i class="fa fa-play"></i>
+      {{'filter.youAreInSnapshotView' | translate}}
+      <button type="button" class="close" [attr.aria-label]="'modal.close' | 
translate" (click)="clearCaptureTimeRangeCache()"
+        tooltip="{{'filter.closeSnapshotView' | translate}}" placement="right">
+        <span aria-hidden="true">&times;</span>
+      </button>
     </div>
-
-    <!-- TODO use plugin for singular/plural -->
-    <div *ngIf="logsType === 'serviceLogs'" class="logs-header col-md-12">{{
-      (!totalEventsFoundMessageParams.totalCount ? 'logs.noEventFound' :
-        (totalEventsFoundMessageParams.totalCount === 1 ? 'logs.oneEventFound' 
: 'logs.totalEventFound'))
-            | translate: totalEventsFoundMessageParams
-    }}</div>
   </div>
   <ng-container [ngSwitch]="logsType">
     <ng-container *ngSwitchCase="'serviceLogs'">
-      <collapsible-panel [class.hide]="isServiceLogsFileView$ | async" 
openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
+      <collapsible-panel [class.hide]="isServiceLogsFileView$ | async" 
openTitle="logs.hideGraph" collapsedTitle="logs.showGraph" 
class="service-logs-histogram">
+        <header>{{(!totalEventsFoundMessageParams.totalCount ? 
'logs.noEventFound' :
+            (totalEventsFoundMessageParams.totalCount === 1 ? 
'logs.oneEventFound' : 'logs.totalEventFound')) | translate: 
totalEventsFoundMessageParams}}</header>
         <time-histogram (selectArea)="setCustomTimeRange($event[0], 
$event[1])" [data]="serviceLogsHistogramData"
                         [colors]="serviceLogsHistogramColors" 
[allowFractionalYTicks]="false"
                         svgId="service-logs-histogram"></time-histogram>
@@ -65,3 +63,22 @@
   <log-context *ngIf="isServiceLogContextView" [id]="activeLog.id" 
[hostName]="activeLog.host_name"
                [componentName]="activeLog.component_name"></log-context>
 </div>
+<modal-dialog
+  title="{{'filter.capture' | translate}}"
+  class="capture-dialog"
+  [visible]="autoRefreshRemainingSeconds"
+  (onCloseRequest)="cancelCapture()">
+  <span class="info">{{'filter.refreshingLogListIn' | translate}}</span>
+  <circle-progress-bar radius="50" strokeWidth="5" strokeColor="black"
+  [percent]="(autoRefreshRemainingSeconds / (autoRefreshInterval / 1000)) * 
100">
+    <span>
+      <span class="remaining-seconds">
+        {{autoRefreshRemainingSeconds}}
+        <span class="unit">{{'filter.capture.sec' | translate}}</span>
+      </span>
+    </span>
+  </circle-progress-bar>
+  <footer>
+      <button class="btn btn-secondary" 
(click)="cancelCapture()">{{'modal.cancel' | translate}}</button>
+  </footer>
+</modal-dialog>
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 5056003..ef61abe 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
@@ -17,6 +17,7 @@
  */
 
 @import '../../modules/shared/mixins';
+@import '../../modules/shared/variables';
 
 :host {
   display: block;
@@ -40,7 +41,7 @@
 
   .fixed-filterbar {
     filters-panel {
-      background-color: fadeout(@filters-panel-background-color, 10%);
+      background-color: fadeout(@filters-panel-background-color, 5%);
       box-shadow: 0 2px 2px rgba(0,0,0,.1);
       left: 0;
       margin: 0;
@@ -55,4 +56,44 @@
     margin-top: @block-margin-top;
   }
 
+  /deep/ collapsible-panel.service-logs-histogram {
+    .panel-heading {
+      header {
+        margin-left: auto;
+      }
+    }
+  }
+
+  /deep/ modal-dialog.capture-dialog {
+    .modal-dialog {
+      max-width: 350px;
+    }
+    .modal-body {
+      display: flex;
+      flex-direction: column;
+      /deep/ circle-progress-bar {
+        display: inline-block;
+        align-self: center;
+        label {
+          font-size: 3rem;
+          font-weight: normal;
+          .unit {
+            color: @fluid-gray-2;
+            font-size: 1.2rem;
+          }
+        }
+      }
+    }
+  }
+
+  .panel-capture-view {
+    padding: 1rem;
+    i {
+      color: @fluid-gray-1;
+      &.fa-play {
+        padding-right: 1rem;
+      }
+    }
+  }
+
 }
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 015273c..78245e4 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
@@ -19,6 +19,7 @@
 import {async, ComponentFixture, TestBed} from '@angular/core/testing';
 import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
 import {StoreModule} from '@ngrx/store';
+import {TooltipModule} from 'ngx-bootstrap';
 import {MockHttpRequestModules, TranslationModules} from 
'@app/test-config.spec';
 import {AppSettingsService, appSettings} from 
'@app/services/storage/app-settings.service';
 import {AppStateService, appState} from 
'@app/services/storage/app-state.service';
@@ -75,7 +76,8 @@ describe('LogsContainerComponent', () => {
           hosts,
           serviceLogsTruncated
         }),
-        ...TranslationModules
+        ...TranslationModules,
+        TooltipModule.forRoot(),
       ],
       providers: [
         ...MockHttpRequestModules,
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 e8d2ee0..6b983fc 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
@@ -58,7 +58,7 @@ export class LogsContainerComponent implements OnInit, 
OnDestroy {
     });
   });
 
-  private logsType: LogsType;
+  logsType: LogsType;
 
   serviceLogsHistogramData: HomogeneousObject<HomogeneousObject<number>>;
 
@@ -87,7 +87,7 @@ export class LogsContainerComponent implements OnInit, 
OnDestroy {
   private subscriptions: Subscription[] = [];
   private paramsSyncInProgress: BehaviorSubject<boolean> = new 
BehaviorSubject<boolean>(false);
 
-  private isServiceLogsFileView$: Observable<boolean> = 
this.appState.getParameter('isServiceLogsFileView');
+  isServiceLogsFileView$: Observable<boolean> = 
this.appState.getParameter('isServiceLogsFileView');
 
   constructor(
     private appState: AppStateService,
@@ -176,6 +176,12 @@ export class LogsContainerComponent implements OnInit, 
OnDestroy {
   get autoRefreshRemainingSeconds(): number {
     return this.logsContainerService.autoRefreshRemainingSeconds;
   }
+  get autoRefreshInterval(): number {
+    return this.logsContainerService.autoRefreshInterval;
+  }
+  get captureTimeRangeCache(): ListItem {
+    return this.logsContainerService.captureTimeRangeCache;
+  }
 
   get autoRefreshMessageParams(): object {
     return {
@@ -361,4 +367,18 @@ export class LogsContainerComponent implements OnInit, 
OnDestroy {
       this.router.navigate(['/logs', 
...this.logsFilteringUtilsService.getNavigationForTab(newActiveTab)]);
     }
   }
+  //
+  // CAPTURE FEATURES
+  //
+  cancelCapture(): void {
+    this.logsContainerService.cancelCapture();
+  }
+
+  clearCaptureTimeRangeCache(): void {
+    if (this.captureTimeRangeCache) {
+      this.filtersForm.controls.timeRange.setValue(this.captureTimeRangeCache);
+      this.logsContainerService.captureTimeRangeCache = null;
+    }
+  }
+
 }
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
index fb8a9b4..31375ed 100644
--- 
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
@@ -18,6 +18,11 @@
 <div class="panel panel-default">
   <div [ngClass]="{'panel-body': true, 'layout-flex': layout==='FLEX', 
'layout-table': layout==='TABLE'}">
     <div class="service-logs-table-controls">
+      <div class="total-event-info">
+        <ng-container 
*ngIf="!totalEventsFoundMessageParams.totalCount">{{'logs.noEventFound' | 
translate}}</ng-container>
+        <ng-container *ngIf="totalEventsFoundMessageParams.totalCount === 
1">{{'logs.oneEventFound' | translate}}</ng-container>
+        <ng-container *ngIf="totalEventsFoundMessageParams.totalCount > 
1">{{'logs.totalEventFound' | translate: 
totalEventsFoundMessageParams}}</ng-container>
+      </div>
       <div *ngIf="tooManyColumnsSelected" class="list-layout-warning">
         <i class="fa fa-warning"></i>
         {{'logs.brokenListLayoutMessage' | translate}}
@@ -103,9 +108,9 @@
               <td *ngIf="isColumnDisplayed('path')" [ngClass]="'log-path'">
                 {{log.path}}
               </td>
-              <td *ngIf="isColumnDisplayed('log_message')" 
[ngClass]="'log-message'" width="*"
+              <td *ngIf="isColumnDisplayed('log_message')" class="log-message" 
width="*"
                   (contextmenu)="openMessageContextMenu($event)">
-                <log-message [listenChangesOn]="displayedColumns" 
[message]="log.log_message"></log-message>
+                <log-message [refreshOn$]="tableRefresh$" 
[message]="log.log_message"></log-message>
               </td>
             </tr>
           </ng-container>
@@ -113,9 +118,9 @@
         <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>
+              <pagination *ngIf="logs && logs.length" [totalCount]="totalCount"
+                [filtersForm]="filtersForm" [filterInstance]="filters.pageSize"
+                [currentCount]="logs.length"></pagination>
             </td>
           </tr>
         </tfoot>
@@ -158,7 +163,7 @@
               </div>
             </div>
             <div *ngIf="isColumnDisplayed('log_message')" class="log-message" 
(contextmenu)="openMessageContextMenu($event)">
-              <log-message [listenChangesOn]="displayedColumns" 
[message]="log.log_message"></log-message>
+              <log-message [refreshOn$]="tableRefresh$" 
[message]="log.log_message"></log-message>
             </div>
           </div>
         </ng-container>
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less
index 5d13bac..0ff490b 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.less
@@ -20,9 +20,16 @@
 :host {
 
   .service-logs-table-controls {
+    align-items: center;
     display: flex;
     flex-wrap: wrap;
     justify-content: flex-end;
+    .total-event-info {
+      margin-right: auto;
+    }
+    pagination {
+      margin-right: auto;
+    }
     .layout-btn-group {
       display: flex;
       align-items: center;
@@ -145,6 +152,12 @@
       }
       &.log-message {
         width: 100%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        &.log-message-open {
+          white-space: pre-wrap;
+        }
       }
       &.log-event_count {
         width: 3em;
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
index 7dfb9af..757b4a0 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
@@ -16,7 +16,22 @@
  * limitations under the License.
  */
 
-import {Component, AfterViewChecked, ViewChild, ElementRef, Input, 
ChangeDetectorRef} from '@angular/core';
+import {
+  Component,
+  OnInit,
+  OnDestroy,
+  AfterViewChecked,
+  ViewChild,
+  ElementRef,
+  Input,
+  ChangeDetectorRef,
+  SimpleChanges
+} from '@angular/core';
+
+import { Subject } from 'rxjs/Subject';
+import { Subscription } from 'rxjs/Subscription';
+import { Observable } from 'rxjs/Observable';
+import { auditTime } from 'rxjs/operator/auditTime';
 
 import {ListItem} from '@app/classes/list-item';
 import {LogsTableComponent} from 
'@app/classes/components/logs-table/logs-table-component';
@@ -35,21 +50,7 @@ export enum ListLayout {
   templateUrl: './service-logs-table.component.html',
   styleUrls: ['./service-logs-table.component.less']
 })
-export class ServiceLogsTableComponent extends LogsTableComponent implements 
AfterViewChecked {
-
-  constructor(
-    private logsContainer: LogsContainerService,
-    private utils: UtilsService,
-    private cdRef: ChangeDetectorRef,
-    private notificationService: NotificationService
-  ) {
-    super();
-  }
-
-  ngAfterViewChecked() {
-    this.checkListLayout();
-    this.cdRef.detectChanges();
-  }
+export class ServiceLogsTableComponent extends LogsTableComponent implements 
AfterViewChecked, OnInit, OnDestroy {
 
   /**
    * The element reference is used to check if the table is broken or not.
@@ -72,21 +73,21 @@ export class ServiceLogsTableComponent extends 
LogsTableComponent implements Aft
    * @type {boolean}
    */
   @Input()
-  showLabels: boolean = false;
+  showLabels = false;
 
   /**
    * The minimum width for the log message column. It is used when we check if 
the layout is broken or not.
    * @type {number}
    */
   @Input()
-  logMessageColumnMinWidth: number = 175;
+  logMessageColumnMinWidth = 175;
 
   /**
    * We use this property in the broken table layout check process when the 
log message is displayed.
    * @type {string}
    */
   @Input()
-  logMessageColumnCssSelector: string = 'tbody tr td.log-message';
+  logMessageColumnCssSelector = 'tbody tr td.log-message';
 
   /**
    * Set the layout for the list.
@@ -99,9 +100,91 @@ export class ServiceLogsTableComponent extends 
LogsTableComponent implements Aft
   @Input()
   layout: ListLayout = ListLayout.Table;
 
-  readonly dateFormat: string = 'dddd, MMMM Do';
+  readonly dateFormat = 'dddd, MMMM Do';
+
+  readonly timeFormat = 'h:mm:ss A';
+
+  readonly customStyledColumns: string[] = ['level', 'type', 'logtime', 
'log_message', 'path'];
+
+  private readonly messageFilterParameterName = 'log_message';
+
+  private readonly logsType = 'serviceLogs';
+
+  private selectedText = '';
+
+  /**
+   * This is a private flag to store the table layout check result. It is used 
to show user notifications about
+   * non-visible information.
+   * @type {boolean}
+   */
+  tooManyColumnsSelected = false;
+
+  get contextMenuItems(): ListItem[] {
+    return this.logsContainer.queryContextMenuItems;
+  }
+
+  get timeZone(): string {
+    return this.logsContainer.timeZone;
+  }
+
+  get filters(): any {
+    return this.logsContainer.filters;
+  }
+
+  get logsTypeMapObject(): object {
+    return this.logsContainer.logsTypeMap.serviceLogs;
+  }
 
-  readonly timeFormat: string = 'h:mm:ss A';
+  get isContextMenuDisplayed(): boolean {
+    return Boolean(this.selectedText);
+  };
+
+  /**
+   * 'left' CSS property value for context menu dropdown
+   * @type {number}
+   */
+  contextMenuLeft = 0;
+
+  /**
+   * 'top' CSS property value for context menu dropdown
+   * @type {number}
+   */
+  contextMenuTop = 0;
+
+  tableRefresh$ = new Subject();
+
+  subscriptions: Subscription[] = [];
+
+  constructor(
+    private logsContainer: LogsContainerService,
+    private utils: UtilsService,
+    private cdRef: ChangeDetectorRef,
+    private notificationService: NotificationService
+  ) {
+    super();
+  }
+
+  ngOnInit() {
+    this.subscriptions.push(
+      Observable.fromEvent(window, 
'resize').auditTime(300).subscribe(this.onWindowResize)
+    );
+  }
+
+  ngAfterViewChecked() {
+    this.checkListLayout();
+    this.cdRef.detectChanges();
+  }
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes.hasOwnProperty('columns')) {
+      this.displayedColumns = this.columns.filter((column: ListItem): boolean 
=> column.isChecked);
+      this.tableRefresh$.next(Date.now());
+    }
+  }
+
+  ngOnDestroy() {
+    this.subscriptions.forEach((subscription: Subscription) => 
subscription.unsubscribe());
+  }
 
   private copyLog = (log: ServiceLog): void => {
     if (document.queryCommandSupported('copy')) {
@@ -144,15 +227,19 @@ export class ServiceLogsTableComponent extends 
LogsTableComponent implements Aft
         message: 'logs.copy.notSupported'
       });
     }
-  };
+  }
+
+  private onWindowResize = () => {
+    this.tableRefresh$.next(Date.now());
+  }
 
   private openLog = (log: ServiceLog): void => {
     this.logsContainer.openServiceLog(log);
-  };
+  }
 
   private openContext = (log: ServiceLog): void => {
     this.logsContainer.loadLogContext(log.id, log.host, log.type);
-  };
+  }
 
   readonly logActions = [
     {
@@ -172,53 +259,6 @@ export class ServiceLogsTableComponent extends 
LogsTableComponent implements Aft
     }
   ];
 
-  readonly customStyledColumns: string[] = ['level', 'type', 'logtime', 
'log_message', 'path'];
-
-  private readonly messageFilterParameterName: string = 'log_message';
-
-  private readonly logsType: string = 'serviceLogs';
-
-  private selectedText: string = '';
-
-  /**
-   * This is a private flag to store the table layout check result. It is used 
to show user notifications about
-   * non-visible information.
-   * @type {boolean}
-   */
-  private tooManyColumnsSelected: boolean = false;
-
-  get contextMenuItems(): ListItem[] {
-    return this.logsContainer.queryContextMenuItems;
-  }
-
-  get timeZone(): string {
-    return this.logsContainer.timeZone;
-  }
-
-  get filters(): any {
-    return this.logsContainer.filters;
-  }
-
-  get logsTypeMapObject(): object {
-    return this.logsContainer.logsTypeMap.serviceLogs;
-  }
-
-  get isContextMenuDisplayed(): boolean {
-    return Boolean(this.selectedText);
-  };
-
-  /**
-   * 'left' CSS property value for context menu dropdown
-   * @type {number}
-   */
-  contextMenuLeft: number = 0;
-
-  /**
-   * 'top' CSS property value for context menu dropdown
-   * @type {number}
-   */
-  contextMenuTop: number = 0;
-
   isDifferentDates(dateA, dateB): boolean {
     return this.utils.isDifferentDates(dateA, dateB, this.timeZone);
   }
@@ -319,4 +359,14 @@ export class ServiceLogsTableComponent extends 
LogsTableComponent implements Aft
     this.logsContainer.updateSelectedColumns(columns, this.logsType);
   }
 
+  /**
+   * The goal is to provide the single source for the parameters of 'xyz 
events found' message.
+   * @returns {Object}
+   */
+  get totalEventsFoundMessageParams(): {totalCount: number} {
+    return {
+      totalCount: this.logsContainer.totalCount
+    };
+  }
+
 }
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
index be68d08..3b66327 100644
--- 
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
@@ -24,6 +24,9 @@
       <graph-legend [ngClass]="{'col-md-5 text-right': true, 'md-offset-7': 
!chartTimeGap}"
                     [items]="legendItems"></graph-legend>
     </div>
+    <div *ngIf="chartLabel" class="row chart-label">
+      <div class="col-md-2 col-md-offset-5">{{ chartLabel }}</div>
+    </div>
   </div>
 </header>
 <div #graphContainer></div>
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
index 4859af9..a0b8551 100644
--- 
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
@@ -22,4 +22,7 @@
   header {
     padding: @graph-padding;
   }
+  .chart-label {
+    text-align: center;
+  }
 }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.html
 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.html
index c3f0b6a..e2393ec 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.html
@@ -26,7 +26,7 @@
     <date-picker class="col-md-12 row" [time]="startTime" 
(timeChange)="setStartTime($event)"></date-picker>
     <div class="col-md-12 row text-uppercase">{{'filter.timeRange.to' | 
translate}}</div>
     <date-picker class="col-md-12 row" [time]="endTime" 
(timeChange)="setEndTime($event)"></date-picker>
-    <button class="btn btn-success pull-right" type="button" 
(click)="setCustomTimeRange()"
+    <button class="btn btn-success" type="button" 
(click)="setCustomTimeRange()"
             [disabled]="!startTime || !endTime || startTime >= endTime">
       {{'modal.apply' | translate}}
     </button>
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/modules/shared/components/circle-progress-bar/circle-progress-bar.component.html
similarity index 62%
copy from 
ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
copy to 
ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.html
index 9e05397..6f83974 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-message/log-message.component.html
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.html
@@ -14,11 +14,19 @@
   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">{{isOpen ? message : (message | 
replace: multiLineTestRegexp : ' ')}}</div>
-</div>
+<svg [attr.height]="radius * 2" [attr.width]="radius * 2">
+    <circle class="full-circle"
+    [attr.stroke-width]="strokeWidth"
+    [attr.r]="normalizedRadius"
+    [attr.cx]="radius"
+    [attr.cy]="radius"/>
+  <circle #circle
+    [attr.stroke-dasharray]="circumference + ' ' + circumference"
+    [attr.stroke-width]="strokeWidth"
+    [attr.r]="normalizedRadius"
+    [attr.cx]="radius"
+    [attr.cy]="radius"/>
+</svg>
+<label>
+  <ng-content></ng-content>
+</label>
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.less
similarity index 61%
copy from 
ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
copy to 
ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.less
index 571e6a2..74e8f73 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.less
@@ -14,29 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-@import '../../modules/shared/mixins';
-
-.panel-collapsible {
+ @import '../../variables';
+:host {
+  display: inline-block;
   position: relative;
-  .panel-heading {
-    .clickable-item;
-    background-color: @panel-heading;
-    border: 0 none;
-    color: @base-font-color;
-    font-size: 1.25rem;
-    a, a:hover, a:visited {
-      color: @base-font-color;
-      text-decoration: none;
-    }
-  }
-  .panel-body {
-    padding: 5px;
+  label {
+    align-items: center;
+    align-content: center;
+    background-color: transparent;
+    display: flex;
+    height: 100%;
+    justify-content: center;
+    left: 0;
+    position: absolute;
+    top: 0;
+    width: 100%;
   }
-  &.panel-collapsed {
-    .panel-body {
-      height: 0;
-      overflow: hidden;
+  svg {
+    circle {
+      fill: transparent;
+      stroke: @blue;
+      transition: stroke-dashoffset 0.35s;
+      transform: rotate(-90deg);
+      transform-origin: 50% 50%;
+      &.full-circle {
+        stroke: @grey;
+      }
     }
   }
 }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.spec.ts
similarity index 53%
copy from 
ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
copy to 
ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.spec.ts
index 73f1870..b6cf4ca 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/time-graph.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.spec.ts
@@ -14,35 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-@import '../../../modules/shared/mixins';
+import { CircleProgressBarComponent } from './circle-progress-bar.component';
 
-:host {
-  background: #FFF; // TODO add style according to actual design
+describe('CircleProgressBarComponent', () => {
+  let component: CircleProgressBarComponent;
+  let fixture: ComponentFixture<CircleProgressBarComponent>;
 
-  /deep/ svg {
-    cursor: crosshair;
-  }
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ CircleProgressBarComponent ]
+    })
+    .compileComponents();
+  }));
 
-  .time-gap {
-    color: @base-font-color;
-    font-size: 1.2rem;
-    text-align: center;
-  }
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CircleProgressBarComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
 
-  footer {
-    .default-flex;
-    font-size: 1.2rem;
-    color: @base-font-color;
-    padding: 0 1em .5em;
-  }
-
-  /deep/ rect.drag-area {
-    fill: #fff;
-  }
-
-  /deep/ rect.unselected-drag-area {
-    fill: @graph-invert-selection-background;
-    opacity: .4;
-  }
-}
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.ts
new file mode 100644
index 0000000..c19e4c7
--- /dev/null
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/circle-progress-bar/circle-progress-bar.component.ts
@@ -0,0 +1,86 @@
+/**
+ * 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,
+  OnChanges,
+  Input,
+  ViewChild,
+  ElementRef,
+  SimpleChanges,
+  SimpleChange
+} from '@angular/core';
+
+@Component({
+  selector: 'circle-progress-bar',
+  templateUrl: './circle-progress-bar.component.html',
+  styleUrls: ['./circle-progress-bar.component.less']
+})
+export class CircleProgressBarComponent implements OnInit, OnChanges {
+
+  @Input()
+  radius: number;
+
+  @Input()
+  strokeColor = 'white';
+
+  @Input()
+  strokeWidth: number;
+
+  @Input()
+  fill = 'transparent';
+
+  @Input()
+  percent = 0;
+
+  @Input()
+  label: string;
+
+  @ViewChild('circle')
+  circleRef: ElementRef;
+
+  get normalizedRadius(): number {
+    return this.radius - this.strokeWidth;
+  }
+
+  get circumference(): number {
+    return this.normalizedRadius * 2 * Math.PI;
+  }
+
+  get strokeDashoffset(): number {
+    return this.circumference - (this.percent / 100 * this.circumference);
+  }
+
+  constructor() { }
+
+  ngOnInit() {
+    this.setProgress(this.percent);
+  }
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes.percent) {
+      this.setProgress(this.percent);
+    }
+  }
+
+  setProgress(percent = this.percent) {
+    if (this.circleRef) {
+      this.circleRef.nativeElement.style.strokeDashoffset = 
this.strokeDashoffset;
+    }
+  }
+
+}
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.less
 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.less
index 10f5e1c..dea2c31 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.less
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+ @import '../../variables';
 :host {
   .modal-backdrop {
     opacity: .5;
@@ -29,27 +30,29 @@
       flex-direction: column;
       max-height: 90vh;
       overflow: hidden;
-      padding: 1.42rem;
+      padding: @modal-dialog-content-padding;
       .modal-header {
-        display: block;
+        display: flex;
         flex-shrink: 1;
         line-height: 1.42rem;
+        padding: @modal-dialog-header-padding;
         position: relative;
         &> * {
           display: inline-block;
         }
         .close {
-          position: absolute;
-          top: 0;
-          right: 1em;
+          order: 1;
+          margin-left: auto;
         }
       }
       .modal-body {
         flex: 1;
         overflow: auto;
+        padding: @modal-dialog-body-padding;
       }
       .modal-footer {
         flex-shrink: 1;
+        padding: @modal-dialog-footer-padding;
       }
     }
   }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.ts
index 21a4e7e..362f34f 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/components/modal-dialog/modal-dialog.component.ts
@@ -74,7 +74,9 @@ export class ModalDialogComponent implements AfterViewInit {
     if (this.showCloseBtn) {
       totalBuiltInHeaderElement += 1;
     }
-    this.showHeader = this.headerElementRef && 
(this.headerElementRef.nativeElement.children.length - 
totalBuiltInHeaderElement > 0);
+    this.showHeader = this.showCloseBtn || !!this.title || (
+      this.headerElementRef && 
(this.headerElementRef.nativeElement.children.length - 
totalBuiltInHeaderElement > 0)
+    );
     this.showFooter = this.footerElementRef && 
this.footerElementRef.nativeElement.children.length;
     this.cdRef.detectChanges();
   }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/shared.module.ts 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/shared.module.ts
index 833ef85..8269852 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/shared.module.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/shared.module.ts
@@ -40,6 +40,7 @@ import {ModalComponent} from 
'./components/modal/modal.component';
 import { DataLoadingIndicatorComponent } from 
'@app/modules/shared/components/data-loading-indicator/data-loading-indicator.component';
 import { ModalDialogComponent } from 
'./components/modal-dialog/modal-dialog.component';
 import { LoadingIndicatorComponent } from 
'./components/loading-indicator/loading-indicator.component';
+import { CircleProgressBarComponent } from 
'./components/circle-progress-bar/circle-progress-bar.component';
 
 @NgModule({
   imports: [
@@ -64,7 +65,8 @@ import { LoadingIndicatorComponent } from 
'./components/loading-indicator/loadin
     ModalComponent,
     DataLoadingIndicatorComponent,
     ModalDialogComponent,
-    LoadingIndicatorComponent
+    LoadingIndicatorComponent,
+    CircleProgressBarComponent
   ],
   providers: [
     Title,
@@ -80,7 +82,8 @@ import { LoadingIndicatorComponent } from 
'./components/loading-indicator/loadin
     ModalComponent,
     DataLoadingIndicatorComponent,
     ModalDialogComponent,
-    LoadingIndicatorComponent
+    LoadingIndicatorComponent,
+    CircleProgressBarComponent
   ]
 })
 export class SharedModule { }
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
index fb76842..7ffd20c 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less
@@ -17,6 +17,14 @@
  */
 
 // Variables
+@blue: #1491C1;
+@grey: #DDD;
+
+@fluid-gray-1: #ccc;
+@fluid-gray-2: #999;
+@fluid-gray-3: #666;
+@fluid-gray-4: #333;
+
 @base-font-color: #666;
 @navbar-background-color: #323544;
 @navbar-logo-background-color: #303d54;
@@ -26,8 +34,8 @@
 @input-border: @input-border-width solid #CFD3D7;
 @input-group-addon-padding: 6px 12px 6px 0;
 @block-margin-top: 20px;
-@link-color: #1491C1;
-@link-hover-color: #23527C;
+@link-color: @blue;
+@link-hover-color: darken(@blue, 10%);
 @grey-color: #DDD;
 @default-line-height: 1.42857143;
 @main-background-color: #ECECEC;
@@ -87,6 +95,10 @@
 
 // Modals
 @large-modal-width: 1200px;
+@modal-dialog-content-padding: 2rem;
+@modal-dialog-header-padding: 0 0 1rem 0;
+@modal-dialog-body-padding: 0;
+@modal-dialog-footer-padding: 1rem 0 0 0;
 
 // Notifications
 @notification-color: @base-font-color;
diff --git 
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
 
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
index efae027..9fba951 100644
--- 
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
+++ 
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
@@ -59,8 +59,7 @@ import {NodeItem} from '@app/classes/models/node-item';
 import {CommonEntry} from '@app/classes/models/common-entry';
 import {ClusterSelectionService} from 
'@app/services/storage/cluster-selection.service';
 import {ActivatedRoute, Router} from '@angular/router';
-import {RoutingUtilsService} from '@app/services/routing-utils.service';
-import {LogsFilteringUtilsService, timeRangeFilterOptions} from 
'@app/services/logs-filtering-utils.service';
+import {LogsFilteringUtilsService} from 
'@app/services/logs-filtering-utils.service';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import {LogsStateService} from '@app/services/storage/logs-state.service';
 import {LogLevelComponent} from 
'@app/components/log-level/log-level.component';
@@ -244,11 +243,11 @@ export class LogsContainerService {
     users: ['userList']
   };
 
-  readonly customTimeRangeKey: string = 'filter.timeRange.custom';
+  readonly customTimeRangeKey = 'filter.timeRange.custom';
 
-  readonly topResourcesCount: string = '10';
+  readonly topResourcesCount = '10';
 
-  readonly topUsersCount: string = '6';
+  readonly topUsersCount = '6';
 
   readonly logsTypeMap = {
     auditLogs: {
@@ -289,16 +288,16 @@ export class LogsContainerService {
 
   timeZone: string = this.defaultTimeZone;
 
-  totalCount: number = 0;
+  totalCount = 0;
 
   /**
    * A configurable property to indicate the maximum capture time in 
milliseconds.
    * @type {number}
    * @default 600000 (10 minutes)
    */
-  private readonly maximumCaptureTimeLimit: number = 600000;
+  readonly maximumCaptureTimeLimit = 600000;
 
-  isServiceLogsFileView: boolean = false;
+  isServiceLogsFileView = false;
 
   filtersForm: FormGroup;
 
@@ -336,16 +335,18 @@ export class LogsContainerService {
 
   private stopAutoRefreshCountdown: Subject<void> = new Subject();
 
-  captureSeconds: number = 0;
+  captureSeconds = 0;
 
-  private readonly autoRefreshInterval: number = 30000;
+  readonly autoRefreshInterval = 30000;
 
-  autoRefreshRemainingSeconds: number = 0;
+  autoRefreshRemainingSeconds = 0;
 
   private startCaptureTime: number;
 
   private stopCaptureTime: number;
 
+  captureTimeRangeCache: ListItem;
+
   topUsersGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
 
   topResourcesGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
@@ -757,16 +758,23 @@ export class LogsContainerService {
     this.stopCaptureTime = new Date().valueOf();
     this.captureSeconds = 0;
     this.stopTimer.next();
-    this.setCustomTimeRange(this.startCaptureTime, this.stopCaptureTime);
     Observable.timer(0, 
1000).takeUntil(this.stopAutoRefreshCountdown).subscribe((seconds: number): 
void => {
       this.autoRefreshRemainingSeconds = autoRefreshIntervalSeconds - seconds;
       if (!this.autoRefreshRemainingSeconds) {
         this.stopAutoRefreshCountdown.next();
+        this.captureTimeRangeCache = this.filtersForm.controls.timeRange.value;
         this.setCustomTimeRange(this.startCaptureTime, this.stopCaptureTime);
       }
     });
   }
 
+  cancelCapture(): void {
+    this.stopTimer.next();
+    this.stopAutoRefreshCountdown.next();
+    this.autoRefreshRemainingSeconds = 0;
+    this.captureSeconds = 0;
+  }
+
   loadClusters(): void {
 
   }
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json 
b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index 66b68cb..a0796ac 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -47,7 +47,13 @@
   "filter.users": "Users",
 
   "filter.capture": "Capture",
+  "filter.captureSnapshot": "Snapshot",
+  "filter.refreshingLogListIn": "Refreshing log list in...",
+  "filter.capture.min": "Min",
+  "filter.capture.sec": "Sec",
   "filter.capture.triggeringRefresh": "Triggering auto-refresh in 
{{remainingSeconds}} sec",
+  "filter.youAreInSnapshotView": "You are in snapshot view",
+  "filter.closeSnapshotView": "Close snapshot view",
 
   "filters.clear": "Clear",
 
diff --git a/ambari-logsearch/ambari-logsearch-web/yarn.lock 
b/ambari-logsearch/ambari-logsearch-web/yarn.lock
index deb8288..ae4bb5a 100644
--- a/ambari-logsearch/ambari-logsearch-web/yarn.lock
+++ b/ambari-logsearch/ambari-logsearch-web/yarn.lock
@@ -4192,9 +4192,9 @@ negotiator@0.6.1:
   version "0.6.1"
   resolved 
"https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9";
 
-ngx-bootstrap@^1.9.3:
-  version "1.9.3"
-  resolved 
"https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-1.9.3.tgz#28e75d14fb1beaee609383d7694de4eb3ba03b26";
+ngx-bootstrap@^2.0.5:
+  version "2.0.5"
+  resolved 
"https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-2.0.5.tgz#83aab39d1e4fe811fad2b34f7927f9ce19d68daa";
 
 no-case@^2.2.0:
   version "2.3.1"

Reply via email to