blob: c1390968635bb7d8e06aff3347035c2b2f0f853c [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
import {Component, ViewChild, OnInit, Output, EventEmitter, OnDestroy} from '@angular/core';
import {map} from 'rxjs/operators';
import {View, ViewColumn} from './tableview.service';
import {NodeService} from '../navigator/node.service';
import {Node} from '../navigator/node';
import {SearchService, SearchAttribute} from '../search/search.service';
import {SearchattributeTreeComponent} from '../searchattribute-tree/searchattribute-tree.component';
import {MDMNotificationService} from '../core/mdm-notification.service';
import {TreeNode} from 'primeng/primeng';
import {ModalDirective} from 'ngx-bootstrap';
import {TypeaheadMatch} from 'ngx-bootstrap/typeahead';
import {classToClass} from 'class-transformer';
import { TranslateService } from '@ngx-translate/core';
import { MdmLocalizationService } from '@localization/mdm-localization.service';
import { MdmTranslatePipe } from '@localization/mdm-translate.pipe';
import { Subscription } from 'rxjs';
@Component({
selector: 'edit-view',
templateUrl: './editview.component.html',
styles: ['.remove {color:black; cursor: pointer; float: right}', '.icon { cursor: pointer; margin: 0px 5px; }']
})
export class EditViewComponent implements OnInit, OnDestroy {
@ViewChild('lgModal') public childModal: ModalDirective;
@ViewChild(SearchattributeTreeComponent) tree: SearchattributeTreeComponent;
environments: Node[] = [];
isReadOnly = false;
currentView: View = new View();
searchAttributes: { [env: string]: SearchAttribute[] } = {};
typeAheadValues: {label: string, group: string, attribute: SearchAttribute }[] = [];
selectedAttribute: SearchAttribute;
@Output()
coloumnsSubmitted = new EventEmitter<View>();
private allUniqueBoTypeTranslations: any[] = [];
private allAttribueTranslations: any[] = [];
private subscription = new Subscription();
constructor(private nodeService: NodeService,
private searchService: SearchService,
private notificationService: MDMNotificationService,
private translateService: TranslateService,
private localizationService: MdmLocalizationService) { }
ngOnInit() {
this.subscription.add(
this.tree.onNodeSelect$.subscribe(
node => this.selectNode(node),
error => this.notificationService.notifyError(this.translateService.instant('tableview.editview.err-cannot-select-node'), error)
)
);
this.subscription.add(
this.nodeService.getNodes()
.subscribe(
envs => {
this.searchService.loadSearchAttributesStructured(envs.map(e => e.sourceName)).pipe(
map(attrs => attrs['measurements']))
.subscribe(
attrs => this.refreshValues(attrs, envs),
error => this.notificationService.notifyError(
this.translateService.instant('tableview.editview.err-cannot-load-search-attributes'), error)
);
},
error => this.notificationService.notifyError(this.translateService.instant('tableview.editview.err-cannot-load-nodes'), error)
)
);
this.subscription.add(
this.translateService.onLangChange.subscribe(() => this.onLanguageChanged())
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
refreshValues(searchAttributes: { [env: string]: SearchAttribute[] }, environments: Node[]) {
this.searchAttributes = searchAttributes;
this.environments = environments;
this.onLanguageChanged();
}
private onLanguageChanged() {
this.allUniqueBoTypeTranslations = [];
}
asyncTranslateBoTypesAndAttributes() {
if (this.allUniqueBoTypeTranslations.length === 0) {
let allAttributes: SearchAttribute[] =
Object.keys(this.searchAttributes).map(env => this.searchAttributes[env])
.reduce((acc, value) => acc.concat(value), <SearchAttribute[]> []);
this.allAttribueTranslations = [];
let allBoTypes: any[] = allAttributes.map(sa => { return { 'key': sa.source + '.' + sa.boType, 'boType': sa.boType, 'label': sa.boType }; });
allBoTypes.sort((bo1, bo2) => bo1['key'].localeCompare(bo2['key']));
let allUniqueBoTypes: any[] = [];
allBoTypes.forEach(bo1 => {if (allUniqueBoTypes.findIndex(bo2 => bo1['key'].localeCompare(bo2['key']) === 0) === -1) {allUniqueBoTypes.push(bo1);}});
let translatePipe: MdmTranslatePipe = new MdmTranslatePipe(this.localizationService);
allUniqueBoTypes.forEach((bt, i) => {
translatePipe.transform(bt['boType'], bt['source'])
.subscribe(result => {
// for some keys we have 3 or more different translations
// and thus 3 or more iterations inside the subscription
// e.g. 'Channel' = 'Channel'; 'Channel' = 'Measurement quantity'
// take the last one like in attribute tree using Translate pipe
// to get the same displayed results
bt['label'] = result;
if (this.allUniqueBoTypeTranslations.length < allUniqueBoTypes.length) {
delete(bt['boType']);
this.allUniqueBoTypeTranslations.push(bt);
}
if (this.allUniqueBoTypeTranslations.length === allUniqueBoTypes.length && (allUniqueBoTypes.length - 1) === i) {
this.onTranslationDone();
}
}).unsubscribe();
});
allAttributes.forEach((attr, i) => {
translatePipe.transform(attr.boType, attr.source, attr.attrName)
.subscribe(result => {
if (this.allAttribueTranslations.length <= i) {
let item = {
'key': attr.source + '.' + attr.boType + '.' + attr.attrName
, 'label': result };
this.allAttribueTranslations.push(item);
if ((allAttributes.length - 1) === i) {
this.onTranslationDone();
}
}
}).unsubscribe();
});
}
}
/**
* this is called, when all subscriptions finished updating the
* asynchronous translation result for bo types or attributes
*/
private onTranslationDone() {
if (this.allUniqueBoTypeTranslations.length > 0 && this.allAttribueTranslations.length > 0) {
let allAttributes: SearchAttribute[] =
Object.keys(this.searchAttributes).map(env => this.searchAttributes[env])
.reduce((acc, value) => acc.concat(value), <SearchAttribute[]> []);
// space will not work with type-ahead values
let ar = allAttributes.map(sa => {
return { 'label': this.findBoTypeTranslation(sa) + '.' + this.findAttributeTranslation(sa),
'group': sa.boType, 'attribute': sa };
});
this.typeAheadValues = this.uniqueBy(ar, p => p.label);
}
}
private findBoTypeTranslation(attr: SearchAttribute): string {
let key = attr.source + '.' + attr.boType;
let retVal = this.allUniqueBoTypeTranslations.find(bt => bt['key'].localeCompare(key) === 0);
return (retVal === undefined) ? attr.boType : retVal['label'];
}
private findAttributeTranslation(attr: SearchAttribute): string {
let key = attr.source + '.' + attr.boType + '.' + attr.attrName;
let retVal = this.allAttribueTranslations.find(a => a['key'].localeCompare(key) === 0);
return (retVal === undefined) ? attr.attrName : retVal['label'];
}
showDialog(currentView: View) {
this.asyncTranslateBoTypesAndAttributes();
this.currentView = classToClass(currentView);
this.isNameReadOnly();
this.childModal.show();
return this.coloumnsSubmitted;
}
closeDialog() {
this.childModal.hide();
}
remove(col: ViewColumn) {
this.currentView.columns = this.currentView.columns.filter(c => c !== col);
}
isLast(col: ViewColumn) {
return this.currentView.columns.indexOf(col) === this.currentView.columns.length - 1;
}
isFirst(col: ViewColumn) {
return this.currentView.columns.indexOf(col) === 0;
}
moveUp(col: ViewColumn) {
if (!this.isFirst(col)) {
let oldIndex = this.currentView.columns.indexOf(col);
let otherCol = this.currentView.columns[oldIndex - 1];
this.currentView.columns[oldIndex] = otherCol;
this.currentView.columns[oldIndex - 1] = col;
}
}
moveDown(col: ViewColumn) {
if (!this.isLast(col)) {
let oldIndex = this.currentView.columns.indexOf(col);
let otherCol = this.currentView.columns[oldIndex + 1];
this.currentView.columns[oldIndex] = otherCol;
this.currentView.columns[oldIndex + 1] = col;
}
}
selectNode(node: TreeNode) {
if (node.type !== 'attribute') {
return;
}
this.pushViewCol(new ViewColumn(node.parent.label, node.label));
}
pushViewCol(viewCol: ViewColumn) {
if (viewCol && this.currentView.columns.findIndex(c => viewCol.equals(c)) === -1 ) {
this.currentView.columns.push(viewCol);
} else {
this.notificationService.notifyInfo(this.translateService.instant('tableview.editview.attribute-already-selected'), 'Info');
}
}
isAsc(col: ViewColumn) {
return col.sortOrder === 1;
}
isDesc(col: ViewColumn) {
return col.sortOrder === -1;
}
isNone(col: ViewColumn) {
return col.sortOrder === null;
}
toggleSort(col: ViewColumn) {
if (col.sortOrder === null) {
this.currentView.setSortOrder(col.type, col.name, 1);
} else if (col.sortOrder === 1) {
this.currentView.setSortOrder(col.type, col.name, -1);
} else if (col.sortOrder === -1) {
this.currentView.setSortOrder(col.type, col.name, null);
}
}
private uniqueBy<T>(a: T[], key: (T) => any) {
let seen = {};
return a.filter(function(item) {
let k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
public typeaheadOnSelect(match: TypeaheadMatch) {
this.pushViewCol(new ViewColumn(match.item.attribute.boType, match.item.attribute.attrName));
this.selectedAttribute = undefined;
}
private isNameReadOnly() {
return this.isReadOnly = (this.currentView.name === '') ? false : true;
}
applyChanges() {
this.coloumnsSubmitted.emit(this.currentView);
this.closeDialog();
}
}