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">×</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"