2020-03-26 14:49:26 +02:00
|
|
|
///
|
2022-01-17 14:07:46 +02:00
|
|
|
/// Copyright © 2016-2022 The Thingsboard Authors
|
2020-03-26 14:49:26 +02:00
|
|
|
///
|
|
|
|
|
/// Licensed 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, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
|
|
|
|
|
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
|
|
|
import { Store } from '@ngrx/store';
|
|
|
|
|
import { AppState } from '@core/core.state';
|
|
|
|
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
|
|
|
import { ENTER } from '@angular/cdk/keycodes';
|
|
|
|
|
import { Observable, of } from 'rxjs';
|
|
|
|
|
import { filter, map, mergeMap, reduce, share, switchMap, tap } from 'rxjs/operators';
|
|
|
|
|
import { EntityService } from '@core/http/entity.service';
|
|
|
|
|
import { EntityType } from '@shared/models/entity-type.models';
|
|
|
|
|
import { Device } from '@shared/models/device.models';
|
|
|
|
|
import { DialogService } from '@core/services/dialog.service';
|
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
|
import { DeviceService } from '@core/http/device.service';
|
2020-04-28 18:47:20 +03:00
|
|
|
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
|
|
|
|
|
import { Authority } from '@shared/models/authority.enum';
|
2020-03-26 14:49:26 +02:00
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-entity-gateway-select',
|
|
|
|
|
templateUrl: './entity-gateway-select.component.html',
|
|
|
|
|
styleUrls: [],
|
|
|
|
|
providers: [{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => EntityGatewaySelectComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
}]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export class EntityGatewaySelectComponent implements ControlValueAccessor, OnInit {
|
|
|
|
|
get required(): boolean {
|
|
|
|
|
return this.requiredValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
set required(value: boolean) {
|
|
|
|
|
this.requiredValue = coerceBooleanProperty(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
set newGatewayType(value: string){
|
|
|
|
|
this.gatewayType = value;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-28 18:47:20 +03:00
|
|
|
@Input()
|
|
|
|
|
deviceName: string;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
isStateForm: boolean;
|
|
|
|
|
|
2020-03-26 14:49:26 +02:00
|
|
|
@Output()
|
|
|
|
|
private gatewayNameExist = new EventEmitter();
|
|
|
|
|
|
|
|
|
|
constructor(private store: Store<AppState>,
|
|
|
|
|
private entityService: EntityService,
|
|
|
|
|
private dialogService: DialogService,
|
|
|
|
|
private deviceService: DeviceService,
|
|
|
|
|
private translate: TranslateService,
|
|
|
|
|
private fb: FormBuilder) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private gatewayType = 'Gateway';
|
|
|
|
|
private dirty: boolean;
|
|
|
|
|
private requiredValue: boolean;
|
|
|
|
|
private gatewayList: Array<Device>;
|
|
|
|
|
|
|
|
|
|
searchText = '';
|
|
|
|
|
filteredGateways: Observable<Array<Device>>;
|
|
|
|
|
selectDeviceGatewayFormGroup: FormGroup;
|
|
|
|
|
modelValue: string | null;
|
|
|
|
|
|
|
|
|
|
@ViewChild('deviceGatewayInput', {static: true}) deviceGatewayInput: ElementRef<HTMLInputElement>;
|
|
|
|
|
private propagateChange = (v: any) => { };
|
|
|
|
|
|
|
|
|
|
registerOnChange(fn: any): void {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched(fn: any): void {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit() {
|
2020-04-28 18:47:20 +03:00
|
|
|
this.selectDeviceGatewayFormGroup = this.fb.group({
|
|
|
|
|
gateway: this.fb.control({value: null, disabled: this.isStateForm})
|
|
|
|
|
});
|
|
|
|
|
this.loadGatewayList();
|
2020-03-26 14:49:26 +02:00
|
|
|
this.filteredGateways = this.selectDeviceGatewayFormGroup.get('gateway').valueChanges
|
|
|
|
|
.pipe(
|
|
|
|
|
tap(value => {
|
|
|
|
|
let modelValue;
|
|
|
|
|
if (typeof value === 'string' || !value) {
|
|
|
|
|
modelValue = null;
|
|
|
|
|
} else {
|
|
|
|
|
modelValue = value.id.id;
|
|
|
|
|
}
|
|
|
|
|
this.updateView(modelValue);
|
|
|
|
|
if (value === null) {
|
|
|
|
|
this.clear();
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
|
|
|
|
|
mergeMap(name => this.fetchGateway(name) ),
|
|
|
|
|
share()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchGateway(searchText?: string): Observable<Array<Device>> {
|
|
|
|
|
this.searchText = searchText;
|
|
|
|
|
let result = [];
|
|
|
|
|
if (searchText && searchText.length) {
|
|
|
|
|
result = this.gatewayList.filter((gateway) => gateway.name.toLowerCase().includes(searchText.toLowerCase()));
|
|
|
|
|
} else {
|
|
|
|
|
result = this.gatewayList;
|
|
|
|
|
}
|
|
|
|
|
return of(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onFocus() {
|
|
|
|
|
if (this.dirty) {
|
|
|
|
|
this.selectDeviceGatewayFormGroup.get('gateway').updateValueAndValidity({onlySelf: true, emitEvent: true});
|
|
|
|
|
this.dirty = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
displayGatewayFn(gateway?: Device): string | undefined {
|
|
|
|
|
return gateway ? gateway.name : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setDisabledState(isDisabled: boolean): void {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeValue(value: string | null): void {
|
|
|
|
|
if(value === null){
|
|
|
|
|
this.searchText = '';
|
|
|
|
|
this.selectDeviceGatewayFormGroup.get('gateway').patchValue('', {emitEvent: false});
|
|
|
|
|
this.dirty = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clear(value: string = '', hideList?: boolean) {
|
|
|
|
|
this.searchText = value;
|
|
|
|
|
this.selectDeviceGatewayFormGroup.get('gateway').patchValue(value, {emitEvent: true});
|
|
|
|
|
if(!hideList) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.deviceGatewayInput.nativeElement.blur();
|
|
|
|
|
this.deviceGatewayInput.nativeElement.focus();
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
textIsNotEmpty(text: string): boolean {
|
|
|
|
|
return !!text && text.length > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gatewayNameEnter($event: KeyboardEvent) {
|
|
|
|
|
if ($event.keyCode === ENTER) {
|
|
|
|
|
if (!this.modelValue) {
|
|
|
|
|
this.createGateway($event, this.searchText);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
createGateway($event: Event, gatewayName: string) {
|
|
|
|
|
$event.preventDefault();
|
|
|
|
|
$event.stopPropagation();
|
|
|
|
|
const title = this.translate.instant('gateway.create-new-gateway');
|
|
|
|
|
const content = this.translate.instant('gateway.create-new-gateway-text', {gatewayName});
|
|
|
|
|
this.dialogService.confirm(title, content, null, null, true).subscribe(value => {
|
|
|
|
|
if(value){
|
|
|
|
|
this.createDeviceGateway(gatewayName);
|
|
|
|
|
} else {
|
|
|
|
|
this.clear('', true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private createDeviceGateway(gatewayName: string){
|
|
|
|
|
this.deviceService.findByName(gatewayName, {ignoreErrors: true}).subscribe(value => {
|
|
|
|
|
this.gatewayNameExist.emit(gatewayName)
|
|
|
|
|
}, () => {
|
|
|
|
|
const newGateway: Device = {
|
|
|
|
|
name: gatewayName,
|
|
|
|
|
label: null,
|
|
|
|
|
type: this.gatewayType,
|
|
|
|
|
additionalInfo: {
|
|
|
|
|
gateway: true
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.deviceService.saveDevice(newGateway).subscribe(
|
|
|
|
|
(device) => {
|
|
|
|
|
this.searchText = '';
|
|
|
|
|
this.gatewayList.push(device);
|
|
|
|
|
this.selectDeviceGatewayFormGroup.get('gateway').patchValue(device, {emitEvent: true});
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-28 18:47:20 +03:00
|
|
|
private loadGatewayList(): void {
|
|
|
|
|
let listObservable: Observable<any[]>;
|
|
|
|
|
if (getCurrentAuthUser(this.store).authority === Authority.SYS_ADMIN) {
|
|
|
|
|
listObservable = of([]);
|
|
|
|
|
} else {
|
|
|
|
|
const entityNameFilter = this.isStateForm && this.deviceName ? this.deviceName : '';
|
|
|
|
|
listObservable = this.entityService.getEntitiesByNameFilter(EntityType.DEVICE, entityNameFilter,
|
|
|
|
|
-1, '', {ignoreLoading: true});
|
|
|
|
|
}
|
|
|
|
|
listObservable.pipe(
|
|
|
|
|
map((devices) => devices ? devices.filter((device) =>
|
|
|
|
|
(device as Device)?.additionalInfo?.gateway): []),
|
2020-03-26 14:49:26 +02:00
|
|
|
).subscribe((devices) => {
|
|
|
|
|
this.gatewayList = devices;
|
2020-04-28 18:47:20 +03:00
|
|
|
if (!this.searchText) {
|
|
|
|
|
if (this.gatewayList.length) {
|
|
|
|
|
let foundGateway: Device = null;
|
|
|
|
|
if (this.deviceName) {
|
|
|
|
|
foundGateway = this.gatewayList.find((gateway) => gateway.name === this.deviceName);
|
|
|
|
|
}
|
|
|
|
|
if (!foundGateway) {
|
|
|
|
|
foundGateway = this.gatewayList[0];
|
|
|
|
|
}
|
|
|
|
|
if (foundGateway) {
|
|
|
|
|
this.selectDeviceGatewayFormGroup.get('gateway').patchValue(foundGateway, {emitEvent: true});
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-26 14:49:26 +02:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private updateView(modelValue: any) {
|
|
|
|
|
this.propagateChange(modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|