|
|
|
@ -14,7 +14,7 @@ |
|
|
|
/// limitations under the License.
|
|
|
|
///
|
|
|
|
|
|
|
|
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; |
|
|
|
import { Component, ElementRef, Inject, OnDestroy, OnInit, SkipSelf, ViewChild } from '@angular/core'; |
|
|
|
import { ErrorStateMatcher } from '@angular/material/core'; |
|
|
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|
|
|
import { Store } from '@ngrx/store'; |
|
|
|
@ -32,9 +32,12 @@ import { |
|
|
|
} from '@shared/models/query/query.models'; |
|
|
|
import { DialogService } from '@core/services/dialog.service'; |
|
|
|
import { TranslateService } from '@ngx-translate/core'; |
|
|
|
import { EntityField, entityFields } from '@shared/models/entity.models'; |
|
|
|
import { Observable } from 'rxjs'; |
|
|
|
import { filter, map, startWith } from 'rxjs/operators'; |
|
|
|
import { entityFields } from '@shared/models/entity.models'; |
|
|
|
import { Observable, of, Subject } from 'rxjs'; |
|
|
|
import { filter, map, mergeMap, publishReplay, refCount, startWith, takeUntil } from 'rxjs/operators'; |
|
|
|
import { isDefined } from '@core/utils'; |
|
|
|
import { EntityId } from '@shared/models/id/entity-id'; |
|
|
|
import { DeviceProfileService } from '@core/http/device-profile.service'; |
|
|
|
|
|
|
|
export interface KeyFilterDialogData { |
|
|
|
keyFilter: KeyFilterInfo; |
|
|
|
@ -43,6 +46,7 @@ export interface KeyFilterDialogData { |
|
|
|
allowUserDynamicSource: boolean; |
|
|
|
readonly: boolean; |
|
|
|
telemetryKeysOnly: boolean; |
|
|
|
entityId?: EntityId; |
|
|
|
} |
|
|
|
|
|
|
|
@Component({ |
|
|
|
@ -53,7 +57,13 @@ export interface KeyFilterDialogData { |
|
|
|
}) |
|
|
|
export class KeyFilterDialogComponent extends |
|
|
|
DialogComponent<KeyFilterDialogComponent, KeyFilterInfo> |
|
|
|
implements OnInit, ErrorStateMatcher { |
|
|
|
implements OnInit, OnDestroy, ErrorStateMatcher { |
|
|
|
|
|
|
|
@ViewChild('keyNameInput', {static: true}) private keyNameInput: ElementRef; |
|
|
|
|
|
|
|
private dirty = false; |
|
|
|
private entityKeysName: Observable<Array<string>>; |
|
|
|
private destroy$ = new Subject(); |
|
|
|
|
|
|
|
keyFilterFormGroup: FormGroup; |
|
|
|
|
|
|
|
@ -72,19 +82,18 @@ export class KeyFilterDialogComponent extends |
|
|
|
|
|
|
|
submitted = false; |
|
|
|
|
|
|
|
entityFields: { [fieldName: string]: EntityField }; |
|
|
|
|
|
|
|
entityFieldsList: string[]; |
|
|
|
showAutocomplete = false; |
|
|
|
|
|
|
|
readonly entityField = EntityKeyType.ENTITY_FIELD; |
|
|
|
filteredKeysName: Observable<Array<string>>; |
|
|
|
|
|
|
|
filteredEntityFields: Observable<string[]>; |
|
|
|
searchText = ''; |
|
|
|
|
|
|
|
constructor(protected store: Store<AppState>, |
|
|
|
protected router: Router, |
|
|
|
@Inject(MAT_DIALOG_DATA) public data: KeyFilterDialogData, |
|
|
|
@SkipSelf() private errorStateMatcher: ErrorStateMatcher, |
|
|
|
public dialogRef: MatDialogRef<KeyFilterDialogComponent, KeyFilterInfo>, |
|
|
|
private deviceProfileService: DeviceProfileService, |
|
|
|
private dialogs: DialogService, |
|
|
|
private translate: TranslateService, |
|
|
|
private fb: FormBuilder) { |
|
|
|
@ -104,7 +113,9 @@ export class KeyFilterDialogComponent extends |
|
|
|
); |
|
|
|
|
|
|
|
if (!this.data.readonly) { |
|
|
|
this.keyFilterFormGroup.get('valueType').valueChanges.subscribe((valueType: EntityKeyValueType) => { |
|
|
|
this.keyFilterFormGroup.get('valueType').valueChanges.pipe( |
|
|
|
takeUntil(this.destroy$) |
|
|
|
).subscribe((valueType: EntityKeyValueType) => { |
|
|
|
const prevValue: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; |
|
|
|
const predicates: KeyFilterPredicate[] = this.keyFilterFormGroup.get('predicates').value; |
|
|
|
if (prevValue && prevValue !== valueType && predicates && predicates.length) { |
|
|
|
@ -121,11 +132,26 @@ export class KeyFilterDialogComponent extends |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
this.keyFilterFormGroup.get('key.type').valueChanges.pipe( |
|
|
|
startWith(this.data.keyFilter.key.type), |
|
|
|
takeUntil(this.destroy$) |
|
|
|
).subscribe((type: EntityKeyType) => { |
|
|
|
if (type === EntityKeyType.ENTITY_FIELD || isDefined(this.data.entityId)) { |
|
|
|
this.entityKeysName = null; |
|
|
|
this.dirty = false; |
|
|
|
this.showAutocomplete = true; |
|
|
|
} else { |
|
|
|
this.showAutocomplete = false; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
this.keyFilterFormGroup.get('key.key').valueChanges.pipe( |
|
|
|
filter((keyName) => this.keyFilterFormGroup.get('key.type').value === this.entityField && this.entityFields.hasOwnProperty(keyName)) |
|
|
|
filter((keyName) => |
|
|
|
this.keyFilterFormGroup.get('key.type').value === EntityKeyType.ENTITY_FIELD && entityFields.hasOwnProperty(keyName)), |
|
|
|
takeUntil(this.destroy$) |
|
|
|
).subscribe((keyName: string) => { |
|
|
|
const prevValueType: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; |
|
|
|
const newValueType = this.entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING; |
|
|
|
const newValueType = entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING; |
|
|
|
if (prevValueType !== newValueType) { |
|
|
|
this.keyFilterFormGroup.get('valueType').patchValue(newValueType, {emitEvent: false}); |
|
|
|
} |
|
|
|
@ -133,18 +159,20 @@ export class KeyFilterDialogComponent extends |
|
|
|
} else { |
|
|
|
this.keyFilterFormGroup.disable({emitEvent: false}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.entityFields = entityFields; |
|
|
|
this.entityFieldsList = Object.values(entityFields).map(entityField => entityField.keyName).sort(); |
|
|
|
ngOnInit() { |
|
|
|
this.filteredKeysName = this.keyFilterFormGroup.get('key.key').valueChanges |
|
|
|
.pipe( |
|
|
|
map(value => value ? value : ''), |
|
|
|
mergeMap(name => this.fetchEntityName(name)) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
ngOnInit(): void { |
|
|
|
this.filteredEntityFields = this.keyFilterFormGroup.get('key.key').valueChanges.pipe( |
|
|
|
startWith(''), |
|
|
|
map(value => { |
|
|
|
return this.entityFieldsList.filter(option => option.startsWith(value)); |
|
|
|
}) |
|
|
|
); |
|
|
|
ngOnDestroy() { |
|
|
|
super.ngOnDestroy(); |
|
|
|
this.destroy$.next(); |
|
|
|
this.destroy$.complete(); |
|
|
|
} |
|
|
|
|
|
|
|
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { |
|
|
|
@ -157,6 +185,21 @@ export class KeyFilterDialogComponent extends |
|
|
|
this.dialogRef.close(null); |
|
|
|
} |
|
|
|
|
|
|
|
clear() { |
|
|
|
this.keyFilterFormGroup.get('key.key').patchValue('', {emitEvent: true}); |
|
|
|
setTimeout(() => { |
|
|
|
this.keyNameInput.nativeElement.blur(); |
|
|
|
this.keyNameInput.nativeElement.focus(); |
|
|
|
}, 0); |
|
|
|
} |
|
|
|
|
|
|
|
onFocus() { |
|
|
|
if (!this.dirty && this.showAutocomplete) { |
|
|
|
this.keyFilterFormGroup.get('key.key').updateValueAndValidity({onlySelf: true, emitEvent: true}); |
|
|
|
this.dirty = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
save(): void { |
|
|
|
this.submitted = true; |
|
|
|
if (this.keyFilterFormGroup.valid) { |
|
|
|
@ -164,4 +207,41 @@ export class KeyFilterDialogComponent extends |
|
|
|
this.dialogRef.close(keyFilter); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fetchEntityName(searchText?: string): Observable<Array<string>> { |
|
|
|
this.searchText = searchText; |
|
|
|
return this.getEntityKeys().pipe( |
|
|
|
map(keys => searchText ? keys.filter(key => key.toUpperCase().startsWith(searchText.toUpperCase())) : keys) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private getEntityKeys(): Observable<Array<string>> { |
|
|
|
if (!this.entityKeysName) { |
|
|
|
let keyNameObservable: Observable<Array<string>>; |
|
|
|
switch (this.keyFilterFormGroup.get('key.type').value) { |
|
|
|
case EntityKeyType.ENTITY_FIELD: |
|
|
|
keyNameObservable = of(Object.values(entityFields).map(entityField => entityField.keyName).sort()); |
|
|
|
break; |
|
|
|
case EntityKeyType.ATTRIBUTE: |
|
|
|
keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesAttributesKeys( |
|
|
|
this.data.entityId?.id, |
|
|
|
{ignoreLoading: true} |
|
|
|
); |
|
|
|
break; |
|
|
|
case EntityKeyType.TIME_SERIES: |
|
|
|
keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesTimeseriesKeys( |
|
|
|
this.data.entityId?.id, |
|
|
|
{ignoreLoading: true} |
|
|
|
); |
|
|
|
break; |
|
|
|
default: |
|
|
|
keyNameObservable = of([]); |
|
|
|
} |
|
|
|
this.entityKeysName = keyNameObservable.pipe( |
|
|
|
publishReplay(1), |
|
|
|
refCount() |
|
|
|
); |
|
|
|
} |
|
|
|
return this.entityKeysName; |
|
|
|
} |
|
|
|
} |
|
|
|
|