http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts new file mode 100644 index 0000000..3e2a6c7 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts @@ -0,0 +1,105 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, OnInit, Input} from '@angular/core'; +import {FormGroup} from '@angular/forms'; +import {Observable} from 'rxjs/Observable'; +import 'rxjs/add/operator/map'; +import {FilteringService} from '@app/services/filtering.service'; +import {LogsContainerService} from '@app/services/logs-container.service'; +import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service'; +import {AppStateService} from '@app/services/storage/app-state.service'; +import {AuditLog} from '@app/models/audit-log.model'; +import {ServiceLog} from '@app/models/service-log.model'; +import {LogField} from '@app/models/log-field.model'; + +@Component({ + selector: 'logs-container', + templateUrl: './logs-container.component.html', + styleUrls: ['./logs-container.component.less'] +}) +export class LogsContainerComponent implements OnInit { + + constructor(private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService, private filtering: FilteringService, private logsContainer: LogsContainerService) { + serviceLogsHistogramStorage.getAll().subscribe(data => this.histogramData = this.logsContainer.getHistogramData(data)); + } + + ngOnInit() { + const fieldsModel = this.logsTypeMapObject.fieldsModel, + logsModel = this.logsTypeMapObject.logsModel; + this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe(value => this.isLogsSet = value); + this.availableColumns = fieldsModel.getAll().map(fields => { + return fields.filter(field => field.isAvailable).map(field => { + return { + value: field.name, + label: field.displayName || field.name, + isChecked: field.isDisplayed + }; + }); + }); + fieldsModel.getAll().subscribe(columns => { + const availableFields = columns.filter(field => field.isAvailable), + availableNames = availableFields.map(field => field.name); + if (availableNames.length && !this.isLogsSet) { + this.logs = logsModel.getAll().map(logs => logs.map(log => { + let logObject = availableNames.reduce((obj, key) => Object.assign(obj, { + [key]: log[key] + }), {}); + if (logObject.level) { + logObject.className = logObject.level.toLowerCase(); + } + return logObject; + })); + this.appState.setParameter(this.logsTypeMapObject.isSetFlag, true); + } + this.displayedColumns = columns.filter(column => column.isAvailable && column.isDisplayed); + }); + this.logsContainer.loadLogs(this.logsType); + this.filtersForm.valueChanges.subscribe(() => this.logsContainer.loadLogs(this.logsType)); + } + + @Input() + logsType: string; + + private isLogsSet: boolean = false; + + get logsTypeMapObject(): any { + return this.logsContainer.logsTypeMap[this.logsType]; + } + + get totalCount(): number { + return this.logsContainer.totalCount; + } + + logs: Observable<AuditLog[] | ServiceLog[]>; + + availableColumns: Observable<LogField[]>; + + displayedColumns: any[] = []; + + histogramData: any; + + readonly histogramOptions = { + keysWithColors: this.logsContainer.colors + }; + + private get filtersForm(): FormGroup { + return this.filtering.filtersForm; + } + +}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html new file mode 100644 index 0000000..888c524 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html @@ -0,0 +1,54 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="pull-right"> + <filter-dropdown [label]="filters.sorting.label" formControlName="sorting" [options]="filters.sorting.options" + [defaultLabel]="filters.sorting.defaultLabel" [isRightAlign]="true"></filter-dropdown> +</form> +<div class="col-md-12 text-center" *ngIf="logs && logs.length"> + <div class="logs-header"> + <div class="col-md-1">{{'logs.status' | translate}}</div> + <div class="col-md-11">{{'logs.details' | translate}}</div> + </div> +</div> +<accordion-panel *ngFor="let log of logs; let i = index" [toggleId]="'details-' + i" class="col-md-12"> + <ng-template> + <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + log.className"></div> + <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' + log.className">{{log.level}}</div> + <div *ngIf="isColumnDisplayed('type') || isColumnDisplayed('logtime')" class="col-md-3"> + <div *ngIf="isColumnDisplayed('type')" class="log-type">{{log.type}}</div> + <time *ngIf="isColumnDisplayed('logtime')" class="log-time"> + {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}} + </time> + </div> + <div class="col-md-6 log-content-wrapper"> + <div class="collapse log-actions" attr.id="details-{{i}}"> + <span class="action-icon fa fa-search"></span> + <span class="action-icon fa fa-external-link"></span> + <span class="action-icon fa fa-bullseye"></span> + </div> + <div class="log-content-inner-wrapper"> + <div class="log-content" *ngIf="isColumnDisplayed('log_message')">{{log.log_message}}</div> + </div> + </div> + <div *ngFor="let column of displayedColumns"> + <div *ngIf="customStyledColumns.indexOf(column.name) === -1" [innerHTML]="log[column.name]" class="col-md-1"></div> + </div> + </ng-template> +</accordion-panel> +<pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount" [filtersForm]="filtersForm" + [filterInstance]="filters.pageSize" [currentCount]="logs.length"></pagination> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less new file mode 100644 index 0000000..91d796f --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less @@ -0,0 +1,138 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../variables'; + +.logs-header { + // TODO get rid of magic numbers, base on actual design + margin: 10px 0; + padding: 5px 0; + background-color: @list-header-background-color; // TODO implement actual color + overflow: hidden; + text-transform: uppercase; +} + +/deep/ filter-dropdown { + justify-content: flex-end; +} + +.hexagon { + // TODO get rid of magic numbers, base on actual design + left: -7.5px; + + &.fatal { + .common-hexagon(15px, @fatal-color); + } + + &.error { + .common-hexagon(15px, @error-color); + } + + &.warn { + .common-hexagon(15px, @warning-color); + } + + &.info { + .common-hexagon(15px, @info-color); + } + + &.debug { + .common-hexagon(15px, @debug-color); + } + + &.trace { + .common-hexagon(15px, @trace-color); + } + + &.unknown { + .common-hexagon(15px, @unknown-color); + } +} + +.log-status { + text-transform: uppercase; + + &.fatal { + color: @fatal-color; + } + + &.error { + color: @error-color; + } + + &.warn { + color: @warning-color; + } + + &.info { + color: @info-color; + } + + &.debug { + color: @debug-color; + } + + &.trace { + color: @trace-color; + } + + &.unknown { + color: @unknown-color; + } +} + +.log-type { + color: @link-color; +} + +.log-time { + color: @grey-color; +} + +.log-content-wrapper { + position: relative; + + // TODO get rid of magic numbers, base on actual design + .log-content-inner-wrapper { + overflow: hidden; + max-height: @default-line-height * 2em; + padding-right: 65px; + + .log-content { + white-space: pre-wrap; + } + } + + .log-actions { + position: absolute; + right: 40px; + top: 0; + border: @input-border; + + &.collapsing + .log-content-inner-wrapper, &.collapse.in + .log-content-inner-wrapper { + min-height: 6em; + max-height: none; + overflow-x: auto; + } + + .action-icon { + .clickable-item; + display: block; + padding: 5px; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts new file mode 100644 index 0000000..02c3b23 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {NO_ERRORS_SCHEMA} from '@angular/core'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Http} from '@angular/http'; +import {TranslateModule, TranslateLoader} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; +import {StoreModule} from '@ngrx/store'; +import {MomentModule} from 'angular2-moment'; +import {MomentTimezoneModule} from 'angular-moment-timezone'; +import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service'; +import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service'; +import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service'; +import {ClustersService, clusters} from '@app/services/storage/clusters.service'; +import {ComponentsService, components} from '@app/services/storage/components.service'; +import {HostsService, hosts} from '@app/services/storage/hosts.service'; +import {HttpClientService} from '@app/services/http-client.service'; +import {FilteringService} from '@app/services/filtering.service'; +import {UtilsService} from '@app/services/utils.service'; + +import {LogsListComponent} from './logs-list.component'; + +export function HttpLoaderFactory(http: Http) { + return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); +} + +describe('LogsListComponent', () => { + let component: LogsListComponent; + let fixture: ComponentFixture<LogsListComponent>; + const httpClient = { + get: () => { + return { + subscribe: () => { + } + }; + } + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LogsListComponent], + imports: [ + StoreModule.provideStore({ + auditLogs, + serviceLogs, + appSettings, + clusters, + components, + hosts + }), + MomentModule, + MomentTimezoneModule, + TranslateModule.forRoot({ + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [Http] + }) + ], + providers: [ + { + provide: HttpClientService, + useValue: httpClient + }, + AuditLogsService, + ServiceLogsService, + AppSettingsService, + ClustersService, + ComponentsService, + HostsService, + FilteringService, + UtilsService + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LogsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts new file mode 100644 index 0000000..6d73dcb --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, Input} from '@angular/core'; +import {FormGroup} from '@angular/forms'; +import 'rxjs/add/operator/map'; +import {FilteringService} from '@app/services/filtering.service'; + +@Component({ + selector: 'logs-list', + templateUrl: './logs-list.component.html', + styleUrls: ['./logs-list.component.less'] +}) +export class LogsListComponent { + + constructor(private filtering: FilteringService) { + } + + @Input() + logs: any[] = []; + + @Input() + totalCount: number = 0; + + @Input() + displayedColumns: any[] = []; + + readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message']; + + timeFormat: string = 'DD/MM/YYYY HH:mm:ss'; + + get timeZone(): string { + return this.filtering.timeZone; + } + + get filters(): any { + return this.filtering.filters; + } + + get filtersForm(): FormGroup { + return this.filtering.filtersForm; + } + + isColumnDisplayed(key: string): boolean { + return this.displayedColumns.some(column => column.name === key); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html new file mode 100644 index 0000000..69b3887 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html @@ -0,0 +1,24 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<ng-template [ngTemplateOutlet]="template"></ng-template> +<div *ngIf="isInitialLoading" class="text-center"> + <span class="fa fa-spinner fa-spin"></span> +</div> +<login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form> +<filters-panel *ngIf="isAuthorized" class="row"></filters-panel> +<logs-container *ngIf="isAuthorized" logsType="serviceLogs"></logs-container> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less new file mode 100644 index 0000000..9736628 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less @@ -0,0 +1,24 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../variables'; + +:host { + .full-size; + overflow-x: hidden; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts new file mode 100644 index 0000000..42fba68 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {HttpModule} from '@angular/http'; +import {StoreModule} from '@ngrx/store'; +import {AppStateService, appState} from '@app/services/storage/app-state.service'; +import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service'; +import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service'; +import {HttpClientService} from '@app/services/http-client.service'; + +import {MainContainerComponent} from './main-container.component'; + +describe('MainContainerComponent', () => { + let component: MainContainerComponent; + let fixture: ComponentFixture<MainContainerComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MainContainerComponent], + imports: [ + HttpModule, + StoreModule.provideStore({ + appState, + auditLogsFields, + serviceLogsFields + }) + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [ + AppStateService, + AuditLogsFieldsService, + ServiceLogsFieldsService, + HttpClientService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MainContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts new file mode 100644 index 0000000..53d58cf --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, ContentChild, TemplateRef} from '@angular/core'; +import {HttpClientService} from '@app/services/http-client.service'; +import {AppStateService} from '@app/services/storage/app-state.service'; +import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service'; +import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service'; +import {AuditLogField} from '@app/models/audit-log-field.model'; +import {ServiceLogField} from '@app/models/service-log-field.model'; + +@Component({ + selector: 'main-container', + templateUrl: './main-container.component.html', + styleUrls: ['./main-container.component.less'] +}) +export class MainContainerComponent { + + constructor(private httpClient: HttpClientService, private appState: AppStateService, private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService) { + this.loadColumnsNames(); + appState.getParameter('isAuthorized').subscribe(value => this.isAuthorized = value); + appState.getParameter('isInitialLoading').subscribe(value => this.isInitialLoading = value); + } + + @ContentChild(TemplateRef) + template; + + isAuthorized: boolean = false; + + isInitialLoading: boolean = false; + + private loadColumnsNames(): void { + this.httpClient.get('serviceLogsFields').subscribe(response => { + const jsonResponse = response.json(); + if (jsonResponse) { + this.serviceLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, ServiceLogField)); + } + }); + this.httpClient.get('auditLogsFields').subscribe(response => { + const jsonResponse = response.json(); + if (jsonResponse) { + this.auditLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, AuditLogField)); + } + }); + } + + private getColumnsArray(keysObject: any, fieldClass: any): any[] { + return Object.keys(keysObject).map(key => new fieldClass(key)); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html new file mode 100644 index 0000000..2f05656 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html @@ -0,0 +1,28 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<div #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true}"> + <a [ngClass]="iconClass + ' icon'" (mousedown)="onMouseDown($event)" (mouseup)="onMouseUp($event)" + (click)="$event.stopPropagation()"></a> + <a #dropdownToggle class="dropdown-toggle caret" data-toggle="dropdown" *ngIf="hasCaret"></a> + <br> + <a *ngIf="label" (mousedown)="onMouseDown($event)" [ngClass]="labelClass" (mouseup)="onMouseUp($event)" + (click)="$event.stopPropagation()">{{label | translate}}</a> + <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="updateValue($event)" + [isMultipleChoice]="isMultipleChoice" [additionalLabelComponentSetter]="additionalLabelComponentSetter" + [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}"></ul> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less new file mode 100644 index 0000000..6a3a43d --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:host { + display: inline-block; + cursor: pointer; + + a:hover, a:focus { + text-decoration: none; + } + + .icon { + padding: 5px; + } + + .unstyled-link { + color: inherit; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts new file mode 100644 index 0000000..6c9e021 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts @@ -0,0 +1,133 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {NO_ERRORS_SCHEMA} from '@angular/core'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Http} from '@angular/http'; +import {TranslateModule, TranslateLoader} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; +import {StoreModule} from '@ngrx/store'; +import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service'; +import {ComponentActionsService} from '@app/services/component-actions.service'; +import {FilteringService} from '@app/services/filtering.service'; + +import {MenuButtonComponent} from './menu-button.component'; + +export function HttpLoaderFactory(http: Http) { + return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); +} + +describe('MenuButtonComponent', () => { + let component: MenuButtonComponent; + let fixture: ComponentFixture<MenuButtonComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MenuButtonComponent], + imports: [ + StoreModule.provideStore({ + appSettings + }), + TranslateModule.forRoot({ + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [Http] + }) + ], + providers: [ + AppSettingsService, + ComponentActionsService, + FilteringService + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); + + describe('#hasSubItems', () => { + const cases = [ + { + subItems: null, + hasSubItems: false, + title: 'no sub-items' + }, + { + subItems: [], + hasSubItems: false, + title: 'empty sub-items array' + }, + { + subItems: [{}], + hasSubItems: true, + title: 'sub-items present' + } + ]; + + cases.forEach((test) => { + it(test.title, () => { + component.subItems = test.subItems; + expect(component.hasSubItems).toEqual(test.hasSubItems); + }); + }); + }); + + describe('#hasCaret', () => { + const cases = [ + { + subItems: null, + hasCaret: false, + title: 'no sub-items' + }, + { + subItems: [], + hasCaret: false, + title: 'empty sub-items array' + }, + { + subItems: [{}], + hideCaret: false, + hasCaret: true, + title: 'sub-items present, caret not hidden' + }, + { + subItems: [{}], + hideCaret: true, + hasCaret: true, + title: 'sub-items present, caret hidden' + } + ]; + + cases.forEach((test) => { + it(test.title, () => { + component.subItems = test.subItems; + component.hideCaret = Boolean(test.hideCaret); + expect(component.hasSubItems).toEqual(test.hasCaret); + }); + }); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts new file mode 100644 index 0000000..b674ec6 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, Input, ViewChild, ElementRef} from '@angular/core'; +import {ComponentActionsService} from '@app/services/component-actions.service'; +import * as $ from 'jquery'; + +@Component({ + selector: 'menu-button', + templateUrl: './menu-button.component.html', + styleUrls: ['./menu-button.component.less'] +}) +export class MenuButtonComponent { + + constructor(protected actions: ComponentActionsService) { + } + + @ViewChild('dropdown') + dropdown: ElementRef; + + @Input() + label?: string; + + @Input() + action: string; + + @Input() + iconClass: string; + + @Input() + labelClass?: string; + + @Input() + subItems?: any[]; + + @Input() + isMultipleChoice: boolean = false; + + @Input() + hideCaret: boolean = false; + + @Input() + isRightAlign: boolean = false; + + @Input() + additionalLabelComponentSetter?: string; + + get hasSubItems(): boolean { + return Boolean(this.subItems && this.subItems.length); + } + + get hasCaret(): boolean { + return this.hasSubItems && !this.hideCaret; + } + + private clickStartTime: number; + + private readonly longClickInterval = 1000; + + onMouseDown(event: MouseEvent): void { + if (this.action && event.button === 0) { + this.clickStartTime = (new Date()).getTime(); + } + } + + onMouseUp(event: MouseEvent): void { + if (event.button === 0) { + const clickEndTime = (new Date()).getTime(); + if (this.hasSubItems && (!this.action || clickEndTime - this.clickStartTime >= this.longClickInterval)) { + $(this.dropdown.nativeElement).toggleClass('open'); + } else if (this.action) { + this.actions[this.action](); + } + event.stopPropagation(); + } + } + + updateValue(options: any) { + // TODO implement value change behaviour + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html new file mode 100644 index 0000000..abd7bc8 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html @@ -0,0 +1,40 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<div class="modal-backdrop in"></div> +<div class="modal in"> + <div [ngClass]="{'modal-dialog': true, 'modal-sm': isSmallModal, 'modal-lg': isLargeModal}"> + <div class="modal-content"> + <div *ngIf="showHeader" class="modal-header"> + <button *ngIf="showCloseButton" type="button" class="close" data-dismiss="modal" (click)="onClose()"> + <span>×</span> + </button> + <h4 *ngIf="title">{{title}}</h4> + </div> + <div class="modal-body"> + <div *ngIf="bodyText">{{bodyText}}</div> + <ng-template *ngIf="bodyTemplate" [ngTemplateOutlet]="bodyTemplate"></ng-template> + </div> + <div *ngIf="showFooter" class="modal-footer"> + <button *ngIf="showCancelButton" class="btn {{cancelButtonClassName}}" + (click)="onCancel()">{{cancelButtonLabel | translate}}</button> + <button *ngIf="showSubmitButton" class="btn {{submitButtonClassName}}" + (click)="onSubmit()">{{submitButtonLabel | translate}}</button> + </div> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts new file mode 100644 index 0000000..802bd13 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Http} from '@angular/http'; +import {TranslateModule, TranslateLoader} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; + +import {ModalComponent} from './modal.component'; + +export function HttpLoaderFactory(http: Http) { + return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); +} + +describe('ModalComponent', () => { + let component: ModalComponent; + let fixture: ComponentFixture<ModalComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ModalComponent], + imports: [ + TranslateModule.forRoot({ + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [Http] + }) + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts new file mode 100644 index 0000000..32f59f6 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts @@ -0,0 +1,122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, OnInit, AfterViewInit, ElementRef, Input, Output, ContentChild, TemplateRef, EventEmitter} from '@angular/core'; +import * as $ from 'jquery'; + +@Component({ + selector: 'modal', + templateUrl: './modal.component.html' +}) +export class ModalComponent implements OnInit, AfterViewInit { + + constructor(private element: ElementRef) { + this.rootElement = $(this.element.nativeElement); + } + + ngOnInit() { + this.modalElements = this.rootElement.find('.in'); + this.show(); + } + + ngAfterViewInit() { + this.init.emit(); + } + + private rootElement: JQuery; + + private modalElements: JQuery; + + @Input() + showHeader: boolean = true; + + @Input() + title: string = ''; + + @Input() + showCloseButton: boolean = true; + + @Input() + bodyText: string = ''; + + @Input() + showFooter: boolean = true; + + @Input() + showSubmitButton: boolean = true; + + @Input() + submitButtonLabel: string = 'modal.submit'; + + @Input() + submitButtonClassName: string = 'btn-success'; + + @Input() + showCancelButton: boolean = true; + + @Input() + cancelButtonLabel: string = 'modal.cancel'; + + @Input() + cancelButtonClassName: string = 'btn-default'; + + @Input() + isSmallModal: boolean = false; + + @Input() + isLargeModal: boolean = false; + + @ContentChild(TemplateRef) + bodyTemplate; + + @Output() + init: EventEmitter<any> = new EventEmitter(); + + @Output() + submit: EventEmitter<any> = new EventEmitter(); + + @Output() + cancel: EventEmitter<any> = new EventEmitter(); + + @Output() + close: EventEmitter<any> = new EventEmitter(); + + show(): void { + this.modalElements.show(); + } + + hide(): void { + this.modalElements.hide(); + } + + onSubmit(): void { + this.hide(); + this.submit.emit(); + } + + onCancel(): void { + this.hide(); + this.cancel.emit(); + } + + onClose(): void { + this.hide(); + this.close.emit(); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html new file mode 100644 index 0000000..96c8619 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html @@ -0,0 +1,19 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<div *ngFor="let item of data" class="bar-sector" + [ngStyle]="{'background-color': item.color, 'width': (item.value / totalCount * 100) + '%'}"></div> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less new file mode 100644 index 0000000..b78b847 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@bar-height: 8px; + +:host { + display: block; + width: 100%; + + .bar-sector { + display: inline-block; + height: @bar-height; + + &:first-child { + border-top-left-radius: @bar-height / 2; + border-bottom-left-radius: @bar-height / 2; + } + + &:last-child { + border-top-right-radius: @bar-height / 2; + border-bottom-right-radius: @bar-height / 2; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts new file mode 100644 index 0000000..d47436e --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {NodeBarComponent} from './node-bar.component'; + +describe('NodeBarComponent', () => { + let component: NodeBarComponent; + let fixture: ComponentFixture<NodeBarComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [NodeBarComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NodeBarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts new file mode 100644 index 0000000..c7b3ead --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'node-bar', + templateUrl: './node-bar.component.html', + styleUrls: ['./node-bar.component.less'] +}) +export class NodeBarComponent { + + @Input() + data: any[] = []; + + get totalCount(): number { + return this.data.reduce((currentValue, currentItem) => currentValue + Number(currentItem.value), 0); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html new file mode 100644 index 0000000..c227a2b --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html @@ -0,0 +1,23 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<button class="btn btn-link" [disabled]="currentPage === 0" (click)="updateValue(true)"> + <span class="pagination-control fa fa-chevron-left"></span> +</button> +<button class="btn btn-link" [disabled]="currentPage === pagesCount - 1" (click)="updateValue()"> + <span class="pagination-control fa fa-chevron-right"></span> +</button> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less new file mode 100644 index 0000000..8238eaf --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less @@ -0,0 +1,23 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../variables'; + +.pagination-control { + .clickable-item; +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts new file mode 100644 index 0000000..489f79c --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {PaginationControlsComponent} from './pagination-controls.component'; + +describe('PaginationControlsComponent', () => { + let component: PaginationControlsComponent; + let fixture: ComponentFixture<PaginationControlsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PaginationControlsComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PaginationControlsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts new file mode 100644 index 0000000..c71844c --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts @@ -0,0 +1,73 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, forwardRef, Input, Output, EventEmitter} from '@angular/core'; +import {ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'pagination-controls', + templateUrl: './pagination-controls.component.html', + styleUrls: ['./pagination-controls.component.less'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PaginationControlsComponent), + multi: true + } + ] +}) +export class PaginationControlsComponent implements ControlValueAccessor { + + private onChange: (fn: any) => void; + + currentPage: number = 0; + + @Input() + totalCount: number; + + @Input() + pagesCount: number; + + @Output() + currentPageChange: EventEmitter<number> = new EventEmitter(); + + get value(): number { + return this.currentPage; + } + + set value(newValue: number) { + this.currentPage = newValue; + this.currentPageChange.emit(newValue); + this.onChange(newValue); + } + + updateValue(isDecrement?: boolean) { + isDecrement? this.value-- : this.value++; + } + + writeValue() { + } + + registerOnChange(callback: any): void { + this.onChange = callback; + } + + registerOnTouched() { + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html new file mode 100644 index 0000000..be6591b --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html @@ -0,0 +1,24 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<form class="pagination-form col-md-12" [formGroup]="filtersForm"> + <filter-dropdown [label]="filterInstance.label" formControlName="pageSize" [options]="filterInstance.options" + [defaultLabel]="filterInstance.defaultLabel" [isRightAlign]="true" isDropup="true"></filter-dropdown> + <span>{{'pagination.numbers' | translate: numbersTranslateParams}}</span> + <pagination-controls formControlName="page" [totalCount]="totalCount" [pagesCount]="pagesCount" + (currentPageChange)="setCurrentPage($event)"></pagination-controls> +</form> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less new file mode 100644 index 0000000..df8ad2d --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../variables'; + +:host { + display: flex; + + .pagination-form { + .flex-vertical-align; + justify-content: flex-end; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts new file mode 100644 index 0000000..7a15bbc --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {NO_ERRORS_SCHEMA} from '@angular/core'; +import {Http} from '@angular/http'; +import {TranslateModule, TranslateLoader} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; + +import {PaginationComponent} from './pagination.component'; + +export function HttpLoaderFactory(http: Http) { + return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); +} + +describe('PaginationComponent', () => { + let component: PaginationComponent; + let fixture: ComponentFixture<PaginationComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [Http] + }) + ], + declarations: [PaginationComponent], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PaginationComponent); + component = fixture.componentInstance; + component.filterInstance = {}; + component.filtersForm = { + controls: { + pageSize: { + valueChanges: { + subscribe: () => {} + } + } + } + }; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts new file mode 100644 index 0000000..d38d0d8 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts @@ -0,0 +1,72 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, OnInit, Input} from '@angular/core'; +import {FormGroup} from '@angular/forms'; + +@Component({ + selector: 'pagination', + templateUrl: './pagination.component.html', + styleUrls: ['./pagination.component.less'] +}) +export class PaginationComponent implements OnInit { + + ngOnInit() { + this.setPageSizeFromString(this.filterInstance.defaultValue); + this.filtersForm.controls.pageSize.valueChanges.subscribe(value => this.setPageSizeFromString(value)); + } + + @Input() + filtersForm: FormGroup; + + @Input() + filterInstance: any; + + @Input() + currentCount?: number; + + @Input() + totalCount: number; + + private pageSize: number = 0; + + setPageSizeFromString(value: string) { + this.pageSize = parseInt(value); + } + + private currentPage: number = 0; + + get numbersTranslateParams(): any { + const pageSize = this.pageSize, + startIndex = (this.currentPage * pageSize) + 1; + return { + startIndex, + endIndex: startIndex + Math.min(pageSize, this.currentCount) - 1, + totalCount: this.totalCount + } + } + + get pagesCount(): number { + return Math.ceil(this.totalCount / this.pageSize); + } + + setCurrentPage(pageNumber: number) { + this.currentPage = pageNumber; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html new file mode 100644 index 0000000..299e46e --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html @@ -0,0 +1,18 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<div #container></div> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less new file mode 100644 index 0000000..d891862 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/deep/ .axis { + .domain { + display: none; + } + + .tick { + line { + display: none; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts new file mode 100644 index 0000000..9e056be --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {StoreModule} from '@ngrx/store'; +import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service'; + +import {TimeHistogramComponent} from './time-histogram.component'; + +describe('TimeHistogramComponent', () => { + let component: TimeHistogramComponent; + let fixture: ComponentFixture<TimeHistogramComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TimeHistogramComponent], + imports: [ + StoreModule.provideStore({ + appSettings + }) + ], + providers: [ + AppSettingsService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TimeHistogramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create component', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts new file mode 100644 index 0000000..7856ecc --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts @@ -0,0 +1,161 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Component, OnInit, AfterViewInit, OnChanges, Input, ViewChild, ElementRef} from '@angular/core'; +import * as d3 from 'd3'; +import * as moment from 'moment-timezone'; +import {AppSettingsService} from '@app/services/storage/app-settings.service'; + +@Component({ + selector: 'time-histogram', + templateUrl: './time-histogram.component.html', + styleUrls: ['./time-histogram.component.less'] +}) +export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges { + + constructor(private appSettings: AppSettingsService) { + appSettings.getParameter('timeZone').subscribe(value => { + this.timeZone = value; + this.createHistogram(); + }); + } + + ngOnInit() { + Object.assign(this.options, this.defaultOptions, this.customOptions); + } + + ngAfterViewInit() { + this.htmlElement = this.element.nativeElement; + this.host = d3.select(this.htmlElement); + } + + ngOnChanges() { + this.createHistogram(); + } + + @ViewChild('container') + element: ElementRef; + + @Input() + customOptions: any; + + @Input() + data: any; + + private readonly defaultOptions = { + margin: { + top: 20, + right: 20, + bottom: 40, + left: 50 + }, + height: 200, + tickPadding: 10, + columnWidth: 20 + }; + + private options: any = {}; + + private timeZone: string; + + private host; + + private svg; + + private width; + + private xScale; + + private yScale; + + private colorScale; + + private xAxis; + + private yAxis; + + private htmlElement: HTMLElement; + + histogram: any; + + private createHistogram(): void { + if (this.host) { + this.setup(); + this.buildSVG(); + this.populate(); + } + } + + private setup(): void { + const margin = this.options.margin, + keysWithColors = this.options.keysWithColors, + keys = Object.keys(keysWithColors), + colors = keys.reduce((array, key) => [...array, keysWithColors[key]], []); + this.width = this.htmlElement.clientWidth - margin.left - margin.right; + this.xScale = d3.scaleTime().range([0, this.width]); + this.yScale = d3.scaleLinear().range([this.options.height, 0]); + this.colorScale = d3.scaleOrdinal(colors); + } + + private buildSVG(): void { + const margin = this.options.margin; + this.host.html(''); + this.svg = this.host.append('svg').attr('width', this.width + margin.left + margin.right) + .attr('height', this.options.height + margin.top + margin.bottom).append('g') + .attr('transform', `translate(${margin.left},${margin.top})`); + } + + private drawXAxis(): void { + this.xAxis = d3.axisBottom(this.xScale) + .tickFormat(tick => moment(tick).tz(this.timeZone).format('MM/DD HH:mm')) + .tickPadding(this.options.tickPadding); + this.svg.append('g').attr('class', 'axis').attr('transform', `translate(0,${this.options.height})`).call(this.xAxis); + } + + private drawYAxis(): void { + this.yAxis = d3.axisLeft(this.yScale).tickFormat((tick: number) => { + if (Number.isInteger(tick)) { + return tick.toFixed(0); + } else { + return; + } + }).tickPadding(this.options.tickPadding); + this.svg.append('g').attr('class', 'axis').call(this.yAxis).append('text'); + } + + private populate(): void { + const keys = Object.keys(this.options.keysWithColors), + data = this.data, + timeStamps = Object.keys(data), + formattedData = timeStamps.map(timeStamp => Object.assign({ + timeStamp: timeStamp + }, data[timeStamp])), + layers = (d3.stack().keys(keys)(formattedData)), + columnWidth = this.options.columnWidth; + this.xScale.domain(d3.extent(formattedData, item => item.timeStamp)); + this.yScale.domain([0, d3.max(formattedData, item => keys.reduce((sum, key) => sum + item[key], 0))]); + this.drawXAxis(); + this.drawYAxis(); + const layer = this.svg.selectAll().data(d3.transpose<any>(layers)).enter().append('g'); + layer.selectAll().data(item => item).enter().append('rect') + .attr('x', item => this.xScale(item.data.timeStamp) - columnWidth / 2).attr('y', item => this.yScale(item[1])) + .attr('height', item => this.yScale(item[0]) - this.yScale(item[1])).attr('width', columnWidth.toString()) + .style('fill', (item, index) => this.colorScale(index)); + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html new file mode 100644 index 0000000..3cb196e --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html @@ -0,0 +1,26 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<button class="btn btn-link" (click)="setTimeZonePickerDisplay(true)"> + {{timeZone | timeZoneAbbr}} <span class="caret"></span> +</button> +<modal *ngIf="isTimeZonePickerDisplayed" [showCloseButton]="false" [isLargeModal]="true" + (init)="initMap()" (cancel)="setTimeZonePickerDisplay(false)" (submit)="setTimeZone()"> + <ng-template> + <div attr.id="{{mapElementId}}"></div> + </ng-template> +</modal> http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less ---------------------------------------------------------------------- diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less new file mode 100644 index 0000000..4fa043d --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../variables'; + +.btn-link { + // TODO implement actual colors + color: @submit-color; + + &:hover { + color: @submit-hover-color; + } +} + +/deep/ #timezone-map { + .Cbox { + .quickLink { + padding-top: 4px; + } + } + + .hoverZone { + display: inline-block; + + &:after { + content: '\007C\00a0\00a0'; + visibility: hidden; + } + } +}