2019-09-25 19:37:29 +03:00
|
|
|
///
|
2024-01-09 10:46:16 +02:00
|
|
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
2019-09-25 19:37:29 +03: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.
|
|
|
|
|
///
|
|
|
|
|
|
2023-05-30 12:37:47 +03:00
|
|
|
import {
|
|
|
|
|
ChangeDetectorRef,
|
|
|
|
|
Component,
|
|
|
|
|
ComponentRef,
|
|
|
|
|
forwardRef,
|
|
|
|
|
Input,
|
|
|
|
|
OnDestroy,
|
|
|
|
|
OnInit,
|
|
|
|
|
ViewChild,
|
|
|
|
|
ViewContainerRef
|
|
|
|
|
} from '@angular/core';
|
2019-09-25 19:37:29 +03:00
|
|
|
import { PageComponent } from '@shared/components/page.component';
|
|
|
|
|
import { Store } from '@ngrx/store';
|
|
|
|
|
import { AppState } from '@core/core.state';
|
|
|
|
|
import {
|
2024-04-19 16:22:39 +03:00
|
|
|
CellClickColumnInfo,
|
2019-10-10 13:00:29 +03:00
|
|
|
DataKey,
|
2023-01-30 16:35:19 +02:00
|
|
|
datasourcesHasAggregation,
|
2024-12-23 12:57:08 +02:00
|
|
|
datasourcesHasOnlyComparisonAggregation,
|
|
|
|
|
DynamicFormData,
|
2024-01-23 20:03:14 +02:00
|
|
|
TargetDevice,
|
2024-08-16 13:42:55 +03:00
|
|
|
targetDeviceValid,
|
2022-03-30 11:14:54 +03:00
|
|
|
Widget,
|
2023-05-25 17:03:52 +03:00
|
|
|
WidgetConfigMode,
|
2019-10-24 19:52:19 +03:00
|
|
|
widgetType
|
2019-09-25 19:37:29 +03:00
|
|
|
} from '@shared/models/widget.models';
|
2019-10-10 13:00:29 +03:00
|
|
|
import {
|
2023-11-08 17:01:47 +02:00
|
|
|
AsyncValidator,
|
2019-10-10 13:00:29 +03:00
|
|
|
ControlValueAccessor,
|
2023-11-08 17:01:47 +02:00
|
|
|
NG_ASYNC_VALIDATORS,
|
2023-04-21 18:00:56 +03:00
|
|
|
NG_VALUE_ACCESSOR,
|
2023-02-02 15:55:06 +02:00
|
|
|
UntypedFormBuilder,
|
|
|
|
|
UntypedFormControl,
|
|
|
|
|
UntypedFormGroup,
|
2023-11-08 17:01:47 +02:00
|
|
|
ValidationErrors,
|
2019-10-10 13:00:29 +03:00
|
|
|
Validators
|
|
|
|
|
} from '@angular/forms';
|
2019-09-25 19:37:29 +03:00
|
|
|
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
|
2023-01-30 16:35:19 +02:00
|
|
|
import { deepClone, genNextLabel, isDefined, isObject } from '@app/core/utils';
|
2023-04-21 18:00:56 +03:00
|
|
|
import { alarmFields, AlarmSearchStatus } from '@shared/models/alarm.models';
|
2019-09-25 19:37:29 +03:00
|
|
|
import { IAliasController } from '@core/api/widget-api.models';
|
2022-09-08 18:51:13 +03:00
|
|
|
import { EntityAlias } from '@shared/models/alias.models';
|
2019-10-10 13:00:29 +03:00
|
|
|
import { UtilsService } from '@core/services/utils.service';
|
|
|
|
|
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
|
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2019-10-11 19:22:03 +03:00
|
|
|
import { EntityType } from '@shared/models/entity-type.models';
|
2024-09-05 12:48:55 +03:00
|
|
|
import { merge, Observable, of, Subject, Subscription } from 'rxjs';
|
2023-05-30 12:37:47 +03:00
|
|
|
import {
|
|
|
|
|
IBasicWidgetConfigComponent,
|
|
|
|
|
WidgetConfigCallbacks
|
2023-06-02 19:37:16 +03:00
|
|
|
} from '@home/components/widget/config/widget-config.component.models';
|
2019-10-11 19:22:03 +03:00
|
|
|
import {
|
|
|
|
|
EntityAliasDialogComponent,
|
|
|
|
|
EntityAliasDialogData
|
|
|
|
|
} from '@home/components/alias/entity-alias-dialog.component';
|
2023-11-08 17:01:47 +02:00
|
|
|
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
|
2023-02-17 19:24:01 +02:00
|
|
|
import { MatDialog } from '@angular/material/dialog';
|
2019-10-17 18:23:53 +03:00
|
|
|
import { EntityService } from '@core/http/entity.service';
|
2022-09-08 18:51:13 +03:00
|
|
|
import { Dashboard } from '@shared/models/dashboard.models';
|
2020-02-21 19:04:49 +02:00
|
|
|
import { entityFields } from '@shared/models/entity.models';
|
2023-05-25 17:03:52 +03:00
|
|
|
import { Filter, singleEntityFilterFromDeviceId } from '@shared/models/query/query.models';
|
2020-06-30 19:37:50 +03:00
|
|
|
import { FilterDialogComponent, FilterDialogData } from '@home/components/filter/filter-dialog.component';
|
2023-05-16 20:00:53 +03:00
|
|
|
import { ToggleHeaderOption } from '@shared/components/toggle-header.component';
|
2023-05-25 17:03:52 +03:00
|
|
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
2023-06-02 19:37:16 +03:00
|
|
|
import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module';
|
2023-06-02 19:43:13 +03:00
|
|
|
import { TimewindowConfigData } from '@home/components/widget/config/timewindow-config-panel.component';
|
2024-03-01 19:58:00 +02:00
|
|
|
import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
|
2024-12-23 12:57:08 +02:00
|
|
|
import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models';
|
2024-08-16 13:42:55 +03:00
|
|
|
import Timeout = NodeJS.Timeout;
|
2019-10-21 19:57:18 +03:00
|
|
|
|
2019-09-25 19:37:29 +03:00
|
|
|
@Component({
|
|
|
|
|
selector: 'tb-widget-config',
|
|
|
|
|
templateUrl: './widget-config.component.html',
|
2023-06-06 17:20:35 +03:00
|
|
|
styleUrls: ['./widget-config.component.scss'],
|
2019-09-25 19:37:29 +03:00
|
|
|
providers: [
|
|
|
|
|
{
|
|
|
|
|
provide: NG_VALUE_ACCESSOR,
|
|
|
|
|
useExisting: forwardRef(() => WidgetConfigComponent),
|
|
|
|
|
multi: true
|
|
|
|
|
},
|
|
|
|
|
{
|
2023-11-08 17:01:47 +02:00
|
|
|
provide: NG_ASYNC_VALIDATORS,
|
2019-09-25 19:37:29 +03:00
|
|
|
useExisting: forwardRef(() => WidgetConfigComponent),
|
|
|
|
|
multi: true,
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
})
|
2023-11-08 17:01:47 +02:00
|
|
|
export class WidgetConfigComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor, AsyncValidator {
|
2023-05-30 12:37:47 +03:00
|
|
|
|
|
|
|
|
@ViewChild('basicModeContainer', {read: ViewContainerRef, static: false}) basicModeContainer: ViewContainerRef;
|
2019-09-25 19:37:29 +03:00
|
|
|
|
|
|
|
|
widgetTypes = widgetType;
|
|
|
|
|
|
2023-05-25 17:03:52 +03:00
|
|
|
widgetConfigModes = WidgetConfigMode;
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
entityTypes = EntityType;
|
|
|
|
|
|
2019-09-25 19:37:29 +03:00
|
|
|
@Input()
|
|
|
|
|
forceExpandDatasources: boolean;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
aliasController: IAliasController;
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
@Input()
|
2022-03-22 17:13:21 +02:00
|
|
|
dashboard: Dashboard;
|
2019-10-11 19:22:03 +03:00
|
|
|
|
2020-06-30 19:37:50 +03:00
|
|
|
@Input()
|
2022-03-22 17:13:21 +02:00
|
|
|
widget: Widget;
|
2020-06-30 19:37:50 +03:00
|
|
|
|
2019-09-25 19:37:29 +03:00
|
|
|
@Input()
|
|
|
|
|
functionsOnly: boolean;
|
|
|
|
|
|
2023-05-25 17:03:52 +03:00
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
hideHeader = false;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
hideToggleHeader = false;
|
|
|
|
|
|
2023-06-06 17:20:35 +03:00
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
isAdd = false;
|
|
|
|
|
|
2024-08-15 13:14:13 +03:00
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
2024-08-16 13:42:55 +03:00
|
|
|
showLayoutConfig = true;
|
|
|
|
|
|
|
|
|
|
@Input()
|
|
|
|
|
@coerceBoolean()
|
|
|
|
|
isDefaultBreakpoint = true;
|
2024-08-15 13:14:13 +03:00
|
|
|
|
2019-09-25 19:37:29 +03:00
|
|
|
@Input() disabled: boolean;
|
|
|
|
|
|
2023-05-25 17:03:52 +03:00
|
|
|
widgetConfigMode = WidgetConfigMode.advanced;
|
|
|
|
|
|
2019-10-10 13:00:29 +03:00
|
|
|
widgetType: widgetType;
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
widgetConfigCallbacks: WidgetConfigCallbacks = {
|
|
|
|
|
createEntityAlias: this.createEntityAlias.bind(this),
|
2024-11-29 16:23:27 +02:00
|
|
|
editEntityAlias: this.editEntityAlias.bind(this),
|
2020-06-30 19:37:50 +03:00
|
|
|
createFilter: this.createFilter.bind(this),
|
2019-10-17 18:23:53 +03:00
|
|
|
generateDataKey: this.generateDataKey.bind(this),
|
2023-05-25 17:03:52 +03:00
|
|
|
fetchEntityKeysForDevice: this.fetchEntityKeysForDevice.bind(this),
|
2019-10-24 19:52:19 +03:00
|
|
|
fetchEntityKeys: this.fetchEntityKeys.bind(this),
|
2024-04-19 16:22:39 +03:00
|
|
|
fetchDashboardStates: this.fetchDashboardStates.bind(this),
|
|
|
|
|
fetchCellClickColumns: this.fetchCellClickColumns.bind(this)
|
2019-10-11 19:22:03 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
widgetEditMode = this.utils.widgetEditMode;
|
|
|
|
|
|
2023-05-30 12:37:47 +03:00
|
|
|
basicModeDirectiveError: string;
|
|
|
|
|
|
2019-12-23 14:36:44 +02:00
|
|
|
modelValue: WidgetConfigComponentData;
|
2019-09-25 19:37:29 +03:00
|
|
|
|
|
|
|
|
private propagateChange = null;
|
|
|
|
|
|
2023-05-16 20:00:53 +03:00
|
|
|
headerOptions: ToggleHeaderOption[] = [];
|
|
|
|
|
selectedOption: string;
|
|
|
|
|
|
2023-02-02 15:55:06 +02:00
|
|
|
public dataSettings: UntypedFormGroup;
|
|
|
|
|
public targetDeviceSettings: UntypedFormGroup;
|
|
|
|
|
public widgetSettings: UntypedFormGroup;
|
|
|
|
|
public layoutSettings: UntypedFormGroup;
|
|
|
|
|
public advancedSettings: UntypedFormGroup;
|
|
|
|
|
public actionsSettings: UntypedFormGroup;
|
2022-03-28 13:39:26 +03:00
|
|
|
|
2023-05-30 12:37:47 +03:00
|
|
|
private createBasicModeComponentTimeout: Timeout;
|
|
|
|
|
private basicModeComponentRef: ComponentRef<IBasicWidgetConfigComponent>;
|
|
|
|
|
private basicModeComponent: IBasicWidgetConfigComponent;
|
2023-11-08 17:01:47 +02:00
|
|
|
private basicModeComponent$: Subject<IBasicWidgetConfigComponent> = null;
|
2023-05-30 12:37:47 +03:00
|
|
|
private basicModeComponentChangeSubscription: Subscription;
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private dataSettingsChangesSubscription: Subscription;
|
|
|
|
|
private targetDeviceSettingsSubscription: Subscription;
|
2019-10-24 19:52:19 +03:00
|
|
|
private widgetSettingsSubscription: Subscription;
|
|
|
|
|
private layoutSettingsSubscription: Subscription;
|
2019-10-21 19:57:18 +03:00
|
|
|
private advancedSettingsSubscription: Subscription;
|
2019-10-24 19:52:19 +03:00
|
|
|
private actionsSettingsSubscription: Subscription;
|
2019-10-10 13:00:29 +03:00
|
|
|
|
2023-06-02 19:37:16 +03:00
|
|
|
private defaultConfigFormsType: widgetType;
|
|
|
|
|
|
2019-10-10 13:00:29 +03:00
|
|
|
constructor(protected store: Store<AppState>,
|
|
|
|
|
private utils: UtilsService,
|
2019-10-17 18:23:53 +03:00
|
|
|
private entityService: EntityService,
|
2019-10-11 19:22:03 +03:00
|
|
|
private dialog: MatDialog,
|
2023-05-19 17:54:27 +03:00
|
|
|
public translate: TranslateService,
|
2023-05-25 17:03:52 +03:00
|
|
|
private fb: UntypedFormBuilder,
|
|
|
|
|
private cd: ChangeDetectorRef) {
|
2019-09-25 19:37:29 +03:00
|
|
|
super(store);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
2019-10-31 18:33:51 +02:00
|
|
|
this.dataSettings = this.fb.group({});
|
|
|
|
|
this.targetDeviceSettings = this.fb.group({});
|
|
|
|
|
this.advancedSettings = this.fb.group({});
|
2019-10-24 19:52:19 +03:00
|
|
|
this.widgetSettings = this.fb.group({
|
|
|
|
|
title: [null, []],
|
2023-08-04 19:07:55 +03:00
|
|
|
titleFont: [null, []],
|
|
|
|
|
titleColor: [null, []],
|
2019-10-24 19:52:19 +03:00
|
|
|
showTitleIcon: [null, []],
|
|
|
|
|
titleIcon: [null, []],
|
|
|
|
|
iconColor: [null, []],
|
|
|
|
|
iconSize: [null, []],
|
2020-02-25 19:11:25 +02:00
|
|
|
titleTooltip: [null, []],
|
2019-10-24 19:52:19 +03:00
|
|
|
showTitle: [null, []],
|
|
|
|
|
dropShadow: [null, []],
|
|
|
|
|
enableFullscreen: [null, []],
|
|
|
|
|
backgroundColor: [null, []],
|
|
|
|
|
color: [null, []],
|
|
|
|
|
padding: [null, []],
|
|
|
|
|
margin: [null, []],
|
2023-07-06 11:02:15 +03:00
|
|
|
borderRadius: [null, []],
|
2019-10-24 19:52:19 +03:00
|
|
|
widgetStyle: [null, []],
|
2021-12-22 17:15:10 +02:00
|
|
|
widgetCss: [null, []],
|
2019-10-24 19:52:19 +03:00
|
|
|
titleStyle: [null, []],
|
2022-05-16 16:35:07 +03:00
|
|
|
pageSize: [1024, [Validators.min(1), Validators.pattern(/^\d*$/)]],
|
2019-10-24 19:52:19 +03:00
|
|
|
units: [null, []],
|
|
|
|
|
decimals: [null, [Validators.min(0), Validators.max(15), Validators.pattern(/^\d*$/)]],
|
2023-05-16 20:00:53 +03:00
|
|
|
noDataDisplayMessage: [null, []]
|
2019-10-24 19:52:19 +03:00
|
|
|
});
|
2021-10-27 13:33:51 +03:00
|
|
|
|
2024-09-05 12:48:55 +03:00
|
|
|
merge(this.widgetSettings.get('showTitle').valueChanges,
|
|
|
|
|
this.widgetSettings.get('showTitleIcon').valueChanges).subscribe(() => {
|
2023-05-19 17:54:27 +03:00
|
|
|
this.updateWidgetSettingsEnabledState();
|
2019-10-24 19:52:19 +03:00
|
|
|
});
|
2023-05-19 17:54:27 +03:00
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
this.layoutSettings = this.fb.group({
|
2024-09-05 10:19:05 +03:00
|
|
|
resizable: [true],
|
|
|
|
|
preserveAspectRatio: [false],
|
2019-10-24 19:52:19 +03:00
|
|
|
mobileOrder: [null, [Validators.pattern(/^-?[0-9]+$/)]],
|
2023-02-08 11:06:01 +05:00
|
|
|
mobileHeight: [null, [Validators.min(1), Validators.pattern(/^\d*$/)]],
|
2022-11-20 00:32:32 +05:00
|
|
|
mobileHide: [false],
|
|
|
|
|
desktopHide: [false]
|
2019-10-24 19:52:19 +03:00
|
|
|
});
|
2024-09-05 12:48:55 +03:00
|
|
|
|
|
|
|
|
this.layoutSettings.get('resizable').valueChanges.subscribe(() => {
|
|
|
|
|
this.updateLayoutEnabledState();
|
|
|
|
|
});
|
|
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
this.actionsSettings = this.fb.group({
|
2023-05-30 12:37:47 +03:00
|
|
|
actions: [null, []]
|
2019-10-24 19:52:19 +03:00
|
|
|
});
|
2019-10-11 19:22:03 +03:00
|
|
|
}
|
|
|
|
|
|
2021-10-27 13:33:51 +03:00
|
|
|
ngOnDestroy(): void {
|
2023-05-30 12:37:47 +03:00
|
|
|
this.destroyBasicModeComponent();
|
2021-10-27 13:33:51 +03:00
|
|
|
this.removeChangeSubscriptions();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private removeChangeSubscriptions() {
|
|
|
|
|
if (this.dataSettingsChangesSubscription) {
|
|
|
|
|
this.dataSettingsChangesSubscription.unsubscribe();
|
|
|
|
|
this.dataSettingsChangesSubscription = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.targetDeviceSettingsSubscription) {
|
|
|
|
|
this.targetDeviceSettingsSubscription.unsubscribe();
|
|
|
|
|
this.targetDeviceSettingsSubscription = null;
|
|
|
|
|
}
|
2019-10-24 19:52:19 +03:00
|
|
|
if (this.widgetSettingsSubscription) {
|
|
|
|
|
this.widgetSettingsSubscription.unsubscribe();
|
|
|
|
|
this.widgetSettingsSubscription = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.layoutSettingsSubscription) {
|
|
|
|
|
this.layoutSettingsSubscription.unsubscribe();
|
|
|
|
|
this.layoutSettingsSubscription = null;
|
|
|
|
|
}
|
2019-10-21 19:57:18 +03:00
|
|
|
if (this.advancedSettingsSubscription) {
|
|
|
|
|
this.advancedSettingsSubscription.unsubscribe();
|
|
|
|
|
this.advancedSettingsSubscription = null;
|
|
|
|
|
}
|
2019-10-24 19:52:19 +03:00
|
|
|
if (this.actionsSettingsSubscription) {
|
|
|
|
|
this.actionsSettingsSubscription.unsubscribe();
|
|
|
|
|
this.actionsSettingsSubscription = null;
|
|
|
|
|
}
|
2019-10-11 19:22:03 +03:00
|
|
|
}
|
2019-10-10 13:00:29 +03:00
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private createChangeSubscriptions() {
|
|
|
|
|
this.dataSettingsChangesSubscription = this.dataSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateDataSettings()
|
|
|
|
|
);
|
|
|
|
|
this.targetDeviceSettingsSubscription = this.targetDeviceSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateTargetDeviceSettings()
|
|
|
|
|
);
|
2019-10-24 19:52:19 +03:00
|
|
|
this.widgetSettingsSubscription = this.widgetSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateWidgetSettings()
|
|
|
|
|
);
|
|
|
|
|
this.layoutSettingsSubscription = this.layoutSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateLayoutSettings()
|
|
|
|
|
);
|
2019-10-21 19:57:18 +03:00
|
|
|
this.advancedSettingsSubscription = this.advancedSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateAdvancedSettings()
|
|
|
|
|
);
|
2019-10-24 19:52:19 +03:00
|
|
|
this.actionsSettingsSubscription = this.actionsSettings.valueChanges.subscribe(
|
|
|
|
|
() => this.updateActionSettings()
|
|
|
|
|
);
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
private buildHeader() {
|
|
|
|
|
this.headerOptions.length = 0;
|
|
|
|
|
if (this.widgetType !== widgetType.static) {
|
|
|
|
|
this.headerOptions.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('widget-config.data'),
|
|
|
|
|
value: 'data'
|
|
|
|
|
}
|
|
|
|
|
);
|
2021-03-02 12:04:45 +02:00
|
|
|
}
|
2023-05-19 17:54:27 +03:00
|
|
|
if (this.displayAppearance) {
|
|
|
|
|
this.headerOptions.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('widget-config.appearance'),
|
|
|
|
|
value: 'appearance'
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
this.headerOptions.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('widget-config.widget-card'),
|
|
|
|
|
value: 'card'
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
this.headerOptions.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('widget-config.actions'),
|
|
|
|
|
value: 'actions'
|
|
|
|
|
}
|
|
|
|
|
);
|
2024-09-05 10:19:05 +03:00
|
|
|
this.headerOptions.push(
|
|
|
|
|
{
|
|
|
|
|
name: this.translate.instant('widget-config.layout'),
|
|
|
|
|
value: 'layout'
|
|
|
|
|
}
|
|
|
|
|
);
|
2023-05-25 17:03:52 +03:00
|
|
|
if (!this.selectedOption || !this.headerOptions.find(o => o.value === this.selectedOption)) {
|
|
|
|
|
this.selectedOption = this.headerOptions[0].value;
|
|
|
|
|
}
|
2023-05-19 17:54:27 +03:00
|
|
|
}
|
2021-12-23 14:16:22 +02:00
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
private buildForms() {
|
2019-10-10 13:00:29 +03:00
|
|
|
this.dataSettings = this.fb.group({});
|
2019-10-11 19:22:03 +03:00
|
|
|
this.targetDeviceSettings = this.fb.group({});
|
2019-10-21 19:57:18 +03:00
|
|
|
this.advancedSettings = this.fb.group({});
|
2022-09-08 18:51:13 +03:00
|
|
|
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) {
|
2023-06-02 19:37:16 +03:00
|
|
|
this.dataSettings.addControl('timewindowConfig', this.fb.control({
|
|
|
|
|
useDashboardTimewindow: true,
|
|
|
|
|
displayTimewindow: true,
|
2023-08-08 18:54:07 +03:00
|
|
|
timewindow: null,
|
|
|
|
|
timewindowStyle: null
|
2023-06-02 19:37:16 +03:00
|
|
|
}));
|
2019-10-10 13:00:29 +03:00
|
|
|
if (this.widgetType === widgetType.alarm) {
|
2023-04-21 18:00:56 +03:00
|
|
|
this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null));
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-21 19:57:18 +03:00
|
|
|
if (this.modelValue.isDataEnabled) {
|
2019-10-10 13:00:29 +03:00
|
|
|
if (this.widgetType !== widgetType.rpc &&
|
|
|
|
|
this.widgetType !== widgetType.alarm &&
|
|
|
|
|
this.widgetType !== widgetType.static) {
|
2023-05-19 17:54:27 +03:00
|
|
|
this.dataSettings.addControl('datasources', this.fb.control(null));
|
2019-10-11 19:22:03 +03:00
|
|
|
} else if (this.widgetType === widgetType.rpc) {
|
2024-01-23 20:03:14 +02:00
|
|
|
this.targetDeviceSettings.addControl('targetDevice',
|
|
|
|
|
this.fb.control(null, []));
|
2019-10-24 19:52:19 +03:00
|
|
|
} else if (this.widgetType === widgetType.alarm) {
|
2023-05-19 17:54:27 +03:00
|
|
|
this.dataSettings.addControl('alarmSource', this.fb.control(null));
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-21 19:57:18 +03:00
|
|
|
this.advancedSettings.addControl('settings',
|
|
|
|
|
this.fb.control(null, []));
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnChange(fn: any): void {
|
|
|
|
|
this.propagateChange = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnTouched(fn: any): void {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setDisabledState(isDisabled: boolean): void {
|
|
|
|
|
this.disabled = isDisabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeValue(value: WidgetConfigComponentData): void {
|
|
|
|
|
this.modelValue = value;
|
2023-06-02 19:37:16 +03:00
|
|
|
this.widgetType = this.modelValue?.widgetType;
|
2023-08-17 18:50:32 +03:00
|
|
|
this.widgetConfigMode = this.modelValue?.hasBasicMode ?
|
|
|
|
|
(this.modelValue?.config?.configMode || WidgetConfigMode.advanced) : WidgetConfigMode.advanced;
|
2023-06-06 17:20:35 +03:00
|
|
|
this.setupConfig(this.isAdd);
|
2023-05-30 12:37:47 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-17 18:50:32 +03:00
|
|
|
setWidgetConfigMode(widgetConfigMode: WidgetConfigMode) {
|
|
|
|
|
if (this.modelValue?.hasBasicMode && this.widgetConfigMode !== widgetConfigMode) {
|
|
|
|
|
this.widgetConfigMode = widgetConfigMode;
|
|
|
|
|
this.modelValue.config.configMode = widgetConfigMode;
|
|
|
|
|
if (this.hasBasicModeDirective) {
|
|
|
|
|
this.setupConfig();
|
|
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-06 17:20:35 +03:00
|
|
|
private setupConfig(isAdd = false) {
|
2023-06-02 19:37:16 +03:00
|
|
|
if (this.modelValue) {
|
|
|
|
|
this.destroyBasicModeComponent();
|
|
|
|
|
this.removeChangeSubscriptions();
|
|
|
|
|
if (this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic) {
|
2023-06-06 17:20:35 +03:00
|
|
|
this.setupBasicModeConfig(isAdd);
|
2023-06-02 19:37:16 +03:00
|
|
|
} else {
|
|
|
|
|
this.setupDefaultConfig();
|
|
|
|
|
}
|
2023-05-30 12:37:47 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-06 17:20:35 +03:00
|
|
|
private setupBasicModeConfig(isAdd = false) {
|
2023-05-30 12:37:47 +03:00
|
|
|
const componentType = basicWidgetConfigComponentsMap[this.modelValue.basicModeDirective];
|
|
|
|
|
if (!componentType) {
|
|
|
|
|
this.basicModeDirectiveError = this.translate.instant('widget-config.settings-component-not-found',
|
|
|
|
|
{selector: this.modelValue.basicModeDirective});
|
|
|
|
|
} else {
|
|
|
|
|
this.createBasicModeComponentTimeout = setTimeout(() => {
|
|
|
|
|
this.createBasicModeComponentTimeout = null;
|
2024-10-04 18:30:19 +03:00
|
|
|
this.basicModeComponentRef = this.basicModeContainer.createComponent(componentType);
|
2023-05-30 12:37:47 +03:00
|
|
|
this.basicModeComponent = this.basicModeComponentRef.instance;
|
2023-06-06 17:20:35 +03:00
|
|
|
this.basicModeComponent.isAdd = isAdd;
|
2023-05-30 12:37:47 +03:00
|
|
|
this.basicModeComponent.widgetConfig = this.modelValue;
|
|
|
|
|
this.basicModeComponentChangeSubscription = this.basicModeComponent.widgetConfigChanged.subscribe((data) => {
|
|
|
|
|
this.modelValue = data;
|
|
|
|
|
this.propagateChange(this.modelValue);
|
2023-10-10 17:23:12 +03:00
|
|
|
this.cd.markForCheck();
|
2023-05-30 12:37:47 +03:00
|
|
|
});
|
2023-11-08 17:01:47 +02:00
|
|
|
if (this.basicModeComponent$) {
|
|
|
|
|
this.basicModeComponent$.next(this.basicModeComponent);
|
|
|
|
|
this.basicModeComponent$.complete();
|
|
|
|
|
this.basicModeComponent$ = null;
|
|
|
|
|
}
|
2023-05-30 12:37:47 +03:00
|
|
|
this.cd.markForCheck();
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private destroyBasicModeComponent() {
|
|
|
|
|
this.basicModeDirectiveError = null;
|
|
|
|
|
if (this.basicModeComponentChangeSubscription) {
|
|
|
|
|
this.basicModeComponentChangeSubscription.unsubscribe();
|
|
|
|
|
this.basicModeComponentChangeSubscription = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.createBasicModeComponentTimeout) {
|
|
|
|
|
clearTimeout(this.createBasicModeComponentTimeout);
|
|
|
|
|
this.createBasicModeComponentTimeout = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.basicModeComponentRef) {
|
|
|
|
|
this.basicModeComponentRef.destroy();
|
|
|
|
|
this.basicModeComponentRef = null;
|
|
|
|
|
this.basicModeComponent = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.basicModeContainer) {
|
|
|
|
|
this.basicModeContainer.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private setupDefaultConfig() {
|
2023-06-02 19:37:16 +03:00
|
|
|
if (this.defaultConfigFormsType !== this.widgetType) {
|
|
|
|
|
this.defaultConfigFormsType = this.widgetType;
|
|
|
|
|
this.buildForms();
|
|
|
|
|
}
|
|
|
|
|
this.buildHeader();
|
|
|
|
|
const config = this.modelValue.config;
|
|
|
|
|
const layout = this.modelValue.layout;
|
|
|
|
|
if (config) {
|
|
|
|
|
const displayWidgetTitle = isDefined(config.showTitle) ? config.showTitle : false;
|
|
|
|
|
this.widgetSettings.patchValue({
|
|
|
|
|
title: config.title,
|
2023-08-04 19:07:55 +03:00
|
|
|
titleFont: config.titleFont,
|
|
|
|
|
titleColor: config.titleColor,
|
2023-06-02 19:37:16 +03:00
|
|
|
showTitleIcon: isDefined(config.showTitleIcon) && displayWidgetTitle ? config.showTitleIcon : false,
|
|
|
|
|
titleIcon: isDefined(config.titleIcon) ? config.titleIcon : '',
|
|
|
|
|
iconColor: isDefined(config.iconColor) ? config.iconColor : 'rgba(0, 0, 0, 0.87)',
|
|
|
|
|
iconSize: isDefined(config.iconSize) ? config.iconSize : '24px',
|
|
|
|
|
titleTooltip: isDefined(config.titleTooltip) ? config.titleTooltip : '',
|
|
|
|
|
showTitle: displayWidgetTitle,
|
|
|
|
|
dropShadow: isDefined(config.dropShadow) ? config.dropShadow : true,
|
|
|
|
|
enableFullscreen: isDefined(config.enableFullscreen) ? config.enableFullscreen : true,
|
|
|
|
|
backgroundColor: config.backgroundColor,
|
|
|
|
|
color: config.color,
|
|
|
|
|
padding: config.padding,
|
|
|
|
|
margin: config.margin,
|
2023-07-06 11:02:15 +03:00
|
|
|
borderRadius: config.borderRadius,
|
2023-06-02 19:37:16 +03:00
|
|
|
widgetStyle: isDefined(config.widgetStyle) ? config.widgetStyle : {},
|
|
|
|
|
widgetCss: isDefined(config.widgetCss) ? config.widgetCss : '',
|
|
|
|
|
titleStyle: isDefined(config.titleStyle) ? config.titleStyle : {
|
|
|
|
|
fontSize: '16px',
|
|
|
|
|
fontWeight: 400
|
|
|
|
|
},
|
|
|
|
|
pageSize: isDefined(config.pageSize) ? config.pageSize : 1024,
|
|
|
|
|
units: config.units,
|
|
|
|
|
decimals: config.decimals,
|
|
|
|
|
noDataDisplayMessage: isDefined(config.noDataDisplayMessage) ? config.noDataDisplayMessage : ''
|
|
|
|
|
},
|
|
|
|
|
{emitEvent: false}
|
|
|
|
|
);
|
|
|
|
|
this.updateWidgetSettingsEnabledState();
|
|
|
|
|
this.actionsSettings.patchValue(
|
|
|
|
|
{
|
|
|
|
|
actions: config.actions || {}
|
|
|
|
|
},
|
|
|
|
|
{emitEvent: false}
|
|
|
|
|
);
|
|
|
|
|
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) {
|
|
|
|
|
const useDashboardTimewindow = isDefined(config.useDashboardTimewindow) ?
|
|
|
|
|
config.useDashboardTimewindow : true;
|
|
|
|
|
this.dataSettings.get('timewindowConfig').patchValue({
|
|
|
|
|
useDashboardTimewindow,
|
|
|
|
|
displayTimewindow: isDefined(config.displayTimewindow) ?
|
|
|
|
|
config.displayTimewindow : true,
|
2023-08-08 18:54:07 +03:00
|
|
|
timewindow: config.timewindow,
|
|
|
|
|
timewindowStyle: config.timewindowStyle
|
2023-06-02 19:37:16 +03:00
|
|
|
}, {emitEvent: false});
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
2023-06-02 19:37:16 +03:00
|
|
|
if (this.modelValue.isDataEnabled) {
|
|
|
|
|
if (this.widgetType !== widgetType.rpc &&
|
|
|
|
|
this.widgetType !== widgetType.alarm &&
|
|
|
|
|
this.widgetType !== widgetType.static) {
|
|
|
|
|
this.dataSettings.patchValue({ datasources: config.datasources},
|
|
|
|
|
{emitEvent: false});
|
|
|
|
|
} else if (this.widgetType === widgetType.rpc) {
|
2024-01-23 20:03:14 +02:00
|
|
|
const targetDevice: TargetDevice = config.targetDevice;
|
2023-06-02 19:37:16 +03:00
|
|
|
this.targetDeviceSettings.patchValue({
|
2024-01-23 20:03:14 +02:00
|
|
|
targetDevice
|
2023-06-02 19:37:16 +03:00
|
|
|
}, {emitEvent: false});
|
|
|
|
|
} else if (this.widgetType === widgetType.alarm) {
|
|
|
|
|
this.dataSettings.patchValue(
|
|
|
|
|
{ alarmFilterConfig: isDefined(config.alarmFilterConfig) ?
|
|
|
|
|
config.alarmFilterConfig :
|
|
|
|
|
{ statusList: [AlarmSearchStatus.ACTIVE], searchPropagatedAlarms: true },
|
|
|
|
|
alarmSource: config.alarmSource }, {emitEvent: false}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 18:02:51 +02:00
|
|
|
this.updateAdvancedForm(config.settings);
|
2023-06-02 19:37:16 +03:00
|
|
|
|
|
|
|
|
if (layout) {
|
|
|
|
|
this.layoutSettings.patchValue(
|
|
|
|
|
{
|
2024-09-05 10:19:05 +03:00
|
|
|
resizable: isDefined(layout.resizable) ? layout.resizable : true,
|
|
|
|
|
preserveAspectRatio: layout.preserveAspectRatio,
|
2023-06-02 19:37:16 +03:00
|
|
|
mobileOrder: layout.mobileOrder,
|
|
|
|
|
mobileHeight: layout.mobileHeight,
|
|
|
|
|
mobileHide: layout.mobileHide,
|
|
|
|
|
desktopHide: layout.desktopHide
|
2019-10-24 19:52:19 +03:00
|
|
|
},
|
|
|
|
|
{emitEvent: false}
|
|
|
|
|
);
|
2023-06-02 19:37:16 +03:00
|
|
|
} else {
|
|
|
|
|
this.layoutSettings.patchValue(
|
2019-10-24 19:52:19 +03:00
|
|
|
{
|
2024-09-05 10:19:05 +03:00
|
|
|
resizable: true,
|
|
|
|
|
preserveAspectRatio: false,
|
2023-06-02 19:37:16 +03:00
|
|
|
mobileOrder: null,
|
|
|
|
|
mobileHeight: null,
|
|
|
|
|
mobileHide: false,
|
|
|
|
|
desktopHide: false
|
2019-10-24 19:52:19 +03:00
|
|
|
},
|
|
|
|
|
{emitEvent: false}
|
|
|
|
|
);
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
2024-09-05 12:48:55 +03:00
|
|
|
this.updateLayoutEnabledState();
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
2023-06-02 19:37:16 +03:00
|
|
|
this.createChangeSubscriptions();
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
private updateWidgetSettingsEnabledState() {
|
|
|
|
|
const showTitle: boolean = this.widgetSettings.get('showTitle').value;
|
|
|
|
|
const showTitleIcon: boolean = this.widgetSettings.get('showTitleIcon').value;
|
|
|
|
|
if (showTitle) {
|
|
|
|
|
this.widgetSettings.get('title').enable({emitEvent: false});
|
2023-08-04 19:07:55 +03:00
|
|
|
this.widgetSettings.get('titleFont').enable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('titleColor').enable({emitEvent: false});
|
2023-05-19 17:54:27 +03:00
|
|
|
this.widgetSettings.get('titleTooltip').enable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('titleStyle').enable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('showTitleIcon').enable({emitEvent: false});
|
2022-03-28 13:39:26 +03:00
|
|
|
} else {
|
2023-05-19 17:54:27 +03:00
|
|
|
this.widgetSettings.get('title').disable({emitEvent: false});
|
2023-08-04 19:07:55 +03:00
|
|
|
this.widgetSettings.get('titleFont').disable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('titleColor').disable({emitEvent: false});
|
2023-05-19 17:54:27 +03:00
|
|
|
this.widgetSettings.get('titleTooltip').disable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('titleStyle').disable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('showTitleIcon').disable({emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
if (showTitle && showTitleIcon) {
|
|
|
|
|
this.widgetSettings.get('titleIcon').enable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('iconColor').enable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('iconSize').enable({emitEvent: false});
|
|
|
|
|
} else {
|
|
|
|
|
this.widgetSettings.get('titleIcon').disable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('iconColor').disable({emitEvent: false});
|
|
|
|
|
this.widgetSettings.get('iconSize').disable({emitEvent: false});
|
2022-03-28 13:39:26 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 12:48:55 +03:00
|
|
|
private updateLayoutEnabledState() {
|
|
|
|
|
const resizable: boolean = this.layoutSettings.get('resizable').value;
|
|
|
|
|
if (resizable) {
|
|
|
|
|
this.layoutSettings.get('preserveAspectRatio').enable({emitEvent: false});
|
|
|
|
|
} else {
|
|
|
|
|
this.layoutSettings.get('preserveAspectRatio').disable({emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 18:02:51 +02:00
|
|
|
private updateAdvancedForm(settings?: any) {
|
|
|
|
|
const dynamicFormData: DynamicFormData = {};
|
|
|
|
|
dynamicFormData.model = settings || {};
|
|
|
|
|
if (this.modelValue.settingsForm?.length) {
|
|
|
|
|
dynamicFormData.settingsForm = this.modelValue.settingsForm;
|
|
|
|
|
} else {
|
|
|
|
|
dynamicFormData.settingsForm = [];
|
|
|
|
|
}
|
|
|
|
|
dynamicFormData.settingsDirective = this.modelValue.settingsDirective;
|
|
|
|
|
this.advancedSettings.patchValue({ settings: dynamicFormData }, {emitEvent: false});
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private updateDataSettings() {
|
2019-09-25 19:37:29 +03:00
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.config) {
|
2023-06-02 19:37:16 +03:00
|
|
|
let data = this.dataSettings.value;
|
|
|
|
|
if (data.timewindowConfig) {
|
|
|
|
|
const timewindowConfig: TimewindowConfigData = data.timewindowConfig;
|
|
|
|
|
data = {...data, ...timewindowConfig};
|
|
|
|
|
delete data.timewindowConfig;
|
|
|
|
|
}
|
|
|
|
|
Object.assign(this.modelValue.config, data);
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private updateTargetDeviceSettings() {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.config) {
|
2024-01-23 20:03:14 +02:00
|
|
|
this.modelValue.config.targetDevice = this.targetDeviceSettings.get('targetDevice').value;
|
2019-10-11 19:22:03 +03:00
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
private updateWidgetSettings() {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.config) {
|
|
|
|
|
Object.assign(this.modelValue.config, this.widgetSettings.value);
|
|
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private updateLayoutSettings() {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.layout) {
|
|
|
|
|
Object.assign(this.modelValue.layout, this.layoutSettings.value);
|
|
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-21 19:57:18 +03:00
|
|
|
private updateAdvancedSettings() {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.config) {
|
2023-05-19 17:54:27 +03:00
|
|
|
this.modelValue.config.settings = this.advancedSettings.get('settings').value?.model;
|
2019-10-21 19:57:18 +03:00
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
private updateActionSettings() {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
if (this.modelValue.config) {
|
2023-05-30 12:37:47 +03:00
|
|
|
this.modelValue.config.actions = this.actionsSettings.get('actions').value;
|
2019-10-24 19:52:19 +03:00
|
|
|
}
|
|
|
|
|
this.propagateChange(this.modelValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-30 12:37:47 +03:00
|
|
|
public get hasBasicModeDirective(): boolean {
|
|
|
|
|
return this.modelValue?.basicModeDirective?.length > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public get useDefinedBasicModeDirective(): boolean {
|
|
|
|
|
return this.modelValue?.basicModeDirective?.length && !this.basicModeDirectiveError;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public get displayAppearance(): boolean {
|
|
|
|
|
return this.displayAppearanceDataSettings || this.displayAdvancedAppearance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public get displayAdvancedAppearance(): boolean {
|
2024-12-23 12:57:08 +02:00
|
|
|
return !!this.modelValue && (!!this.modelValue.settingsForm && !!this.modelValue.settingsForm.length ||
|
2022-03-19 10:08:53 +02:00
|
|
|
!!this.modelValue.settingsDirective && !!this.modelValue.settingsDirective.length);
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public get displayTimewindowConfig(): boolean {
|
2022-09-08 18:51:13 +03:00
|
|
|
if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm) {
|
|
|
|
|
return true;
|
|
|
|
|
} else if (this.widgetType === widgetType.latest) {
|
|
|
|
|
const datasources = this.dataSettings.get('datasources').value;
|
|
|
|
|
return datasourcesHasAggregation(datasources);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public get displayLimits(): boolean {
|
|
|
|
|
return this.widgetType !== widgetType.rpc && this.widgetType !== widgetType.alarm &&
|
|
|
|
|
this.modelValue?.isDataEnabled && !this.modelValue?.typeParameters?.singleEntity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public get displayAppearanceDataSettings(): boolean {
|
2023-09-14 18:45:22 +03:00
|
|
|
return !this.modelValue?.typeParameters?.hideDataSettings && (this.displayUnitsConfig || this.displayNoDataDisplayMessageConfig);
|
2022-09-15 14:18:16 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public get displayUnitsConfig(): boolean {
|
|
|
|
|
return this.widgetType === widgetType.latest || this.widgetType === widgetType.timeseries;
|
2021-03-02 12:04:45 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public get displayNoDataDisplayMessageConfig(): boolean {
|
|
|
|
|
return this.widgetType !== widgetType.static && !this.modelValue?.typeParameters?.processNoDataByWidget;
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
|
2023-05-19 17:54:27 +03:00
|
|
|
public onlyHistoryTimewindow(): boolean {
|
|
|
|
|
if (this.widgetType === widgetType.latest) {
|
|
|
|
|
const datasources = this.dataSettings.get('datasources').value;
|
|
|
|
|
return datasourcesHasOnlyComparisonAggregation(datasources);
|
2019-10-10 13:00:29 +03:00
|
|
|
} else {
|
2023-05-19 17:54:27 +03:00
|
|
|
return false;
|
2022-03-28 13:39:26 +03:00
|
|
|
}
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-23 12:57:08 +02:00
|
|
|
public generateDataKey(chip: any, type: DataKeyType, dataKeySettingsForm: FormProperty[],
|
2024-03-01 19:58:00 +02:00
|
|
|
isLatestDataKey: boolean, dataKeySettingsFunction: DataKeySettingsFunction): DataKey {
|
2019-10-10 13:00:29 +03:00
|
|
|
if (isObject(chip)) {
|
|
|
|
|
(chip as DataKey)._hash = Math.random();
|
|
|
|
|
return chip;
|
|
|
|
|
} else {
|
|
|
|
|
let label: string = chip;
|
2020-02-21 19:04:49 +02:00
|
|
|
if (type === DataKeyType.alarm || type === DataKeyType.entityField) {
|
2020-07-02 15:42:58 +03:00
|
|
|
const keyField = type === DataKeyType.alarm ? alarmFields[label] : entityFields[chip];
|
2020-02-21 19:04:49 +02:00
|
|
|
if (keyField) {
|
|
|
|
|
label = this.translate.instant(keyField.name);
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-30 16:35:19 +02:00
|
|
|
const datasources = this.widgetType === widgetType.alarm ? [this.modelValue.config.alarmSource] : this.modelValue.config.datasources;
|
|
|
|
|
label = genNextLabel(label, datasources);
|
2019-10-10 13:00:29 +03:00
|
|
|
const result: DataKey = {
|
|
|
|
|
name: chip,
|
|
|
|
|
type,
|
|
|
|
|
label,
|
|
|
|
|
color: this.genNextColor(),
|
|
|
|
|
settings: {},
|
|
|
|
|
_hash: Math.random()
|
|
|
|
|
};
|
|
|
|
|
if (type === DataKeyType.function) {
|
|
|
|
|
result.name = 'f(x)';
|
|
|
|
|
result.funcBody = this.utils.getPredefinedFunctionBody(chip);
|
|
|
|
|
if (!result.funcBody) {
|
|
|
|
|
result.funcBody = 'return prevValue + 1;';
|
|
|
|
|
}
|
2021-03-02 12:04:45 +02:00
|
|
|
} else if (type === DataKeyType.count) {
|
|
|
|
|
result.name = 'count';
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
2024-12-23 12:57:08 +02:00
|
|
|
if (dataKeySettingsForm?.length) {
|
|
|
|
|
result.settings = defaultFormProperties(dataKeySettingsForm);
|
2024-03-01 19:58:00 +02:00
|
|
|
} else if (dataKeySettingsFunction) {
|
|
|
|
|
const settings = dataKeySettingsFunction(result, isLatestDataKey);
|
|
|
|
|
if (settings) {
|
|
|
|
|
result.settings = settings;
|
|
|
|
|
}
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private genNextColor(): string {
|
|
|
|
|
let i = 0;
|
|
|
|
|
const datasources = this.widgetType === widgetType.alarm ? [this.modelValue.config.alarmSource] : this.modelValue.config.datasources;
|
|
|
|
|
if (datasources) {
|
|
|
|
|
datasources.forEach((datasource) => {
|
2022-03-28 13:39:26 +03:00
|
|
|
if (datasource && (datasource.dataKeys || datasource.latestDataKeys)) {
|
|
|
|
|
i += ((datasource.dataKeys ? datasource.dataKeys.length : 0) +
|
|
|
|
|
(datasource.latestDataKeys ? datasource.latestDataKeys.length : 0));
|
2019-10-10 13:00:29 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return this.utils.getMaterialColor(i);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 19:22:03 +03:00
|
|
|
private createEntityAlias(alias: string, allowedEntityTypes: Array<EntityType>): Observable<EntityAlias> {
|
|
|
|
|
const singleEntityAlias: EntityAlias = {id: null, alias, filter: {resolveMultiple: false}};
|
|
|
|
|
return this.dialog.open<EntityAliasDialogComponent, EntityAliasDialogData,
|
|
|
|
|
EntityAlias>(EntityAliasDialogComponent, {
|
|
|
|
|
disableClose: true,
|
|
|
|
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
|
|
|
data: {
|
|
|
|
|
isAdd: true,
|
|
|
|
|
allowedEntityTypes,
|
2022-03-22 17:13:21 +02:00
|
|
|
entityAliases: this.dashboard.configuration.entityAliases,
|
2019-10-11 19:22:03 +03:00
|
|
|
alias: singleEntityAlias
|
|
|
|
|
}
|
|
|
|
|
}).afterClosed().pipe(
|
|
|
|
|
tap((entityAlias) => {
|
|
|
|
|
if (entityAlias) {
|
2022-03-22 17:13:21 +02:00
|
|
|
this.dashboard.configuration.entityAliases[entityAlias.id] = entityAlias;
|
|
|
|
|
this.aliasController.updateEntityAliases(this.dashboard.configuration.entityAliases);
|
2019-10-11 19:22:03 +03:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 16:23:27 +02:00
|
|
|
private editEntityAlias(alias: EntityAlias, allowedEntityTypes: Array<EntityType>): Observable<EntityAlias> {
|
|
|
|
|
return this.dialog.open<EntityAliasDialogComponent, EntityAliasDialogData,
|
|
|
|
|
EntityAlias>(EntityAliasDialogComponent, {
|
|
|
|
|
disableClose: true,
|
|
|
|
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
|
|
|
data: {
|
|
|
|
|
isAdd: false,
|
|
|
|
|
allowedEntityTypes,
|
|
|
|
|
entityAliases: this.dashboard.configuration.entityAliases,
|
|
|
|
|
alias: deepClone(alias)
|
|
|
|
|
}
|
|
|
|
|
}).afterClosed().pipe(
|
|
|
|
|
tap((entityAlias) => {
|
|
|
|
|
if (entityAlias) {
|
|
|
|
|
this.dashboard.configuration.entityAliases[entityAlias.id] = entityAlias;
|
|
|
|
|
this.aliasController.updateEntityAliases(this.dashboard.configuration.entityAliases);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-30 19:37:50 +03:00
|
|
|
private createFilter(filter: string): Observable<Filter> {
|
2020-07-01 20:09:25 +03:00
|
|
|
const singleFilter: Filter = {id: null, filter, keyFilters: [], editable: true};
|
2020-06-30 19:37:50 +03:00
|
|
|
return this.dialog.open<FilterDialogComponent, FilterDialogData,
|
|
|
|
|
Filter>(FilterDialogComponent, {
|
|
|
|
|
disableClose: true,
|
|
|
|
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
|
|
|
|
|
data: {
|
|
|
|
|
isAdd: true,
|
2022-03-22 17:13:21 +02:00
|
|
|
filters: this.dashboard.configuration.filters,
|
2020-07-02 15:42:58 +03:00
|
|
|
filter: singleFilter
|
2020-06-30 19:37:50 +03:00
|
|
|
}
|
|
|
|
|
}).afterClosed().pipe(
|
|
|
|
|
tap((result) => {
|
|
|
|
|
if (result) {
|
2022-03-22 17:13:21 +02:00
|
|
|
this.dashboard.configuration.filters[result.id] = result;
|
|
|
|
|
this.aliasController.updateFilters(this.dashboard.configuration.filters);
|
2020-06-30 19:37:50 +03:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-25 17:03:52 +03:00
|
|
|
private fetchEntityKeysForDevice(deviceId: string, dataKeyTypes: Array<DataKeyType>): Observable<Array<DataKey>> {
|
|
|
|
|
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
|
|
|
|
|
return this.entityService.getEntityKeysByEntityFilter(
|
|
|
|
|
entityFilter,
|
2023-06-06 17:20:35 +03:00
|
|
|
dataKeyTypes, [EntityType.DEVICE],
|
2023-05-25 17:03:52 +03:00
|
|
|
{ignoreLoading: true, ignoreErrors: true}
|
|
|
|
|
).pipe(
|
|
|
|
|
catchError(() => of([]))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-17 13:32:03 +02:00
|
|
|
private fetchEntityKeys(entityAliasId: string, dataKeyTypes: Array<DataKeyType>): Observable<Array<DataKey>> {
|
|
|
|
|
return this.aliasController.getAliasInfo(entityAliasId).pipe(
|
2023-05-16 20:00:53 +03:00
|
|
|
mergeMap((aliasInfo) => this.entityService.getEntityKeysByEntityFilter(
|
2020-12-17 13:32:03 +02:00
|
|
|
aliasInfo.entityFilter,
|
2023-06-06 17:20:35 +03:00
|
|
|
dataKeyTypes, [],
|
2020-12-17 13:32:03 +02:00
|
|
|
{ignoreLoading: true, ignoreErrors: true}
|
|
|
|
|
).pipe(
|
|
|
|
|
catchError(() => of([]))
|
2023-05-16 20:00:53 +03:00
|
|
|
)),
|
2020-11-24 10:57:59 +02:00
|
|
|
catchError(() => of([] as Array<DataKey>))
|
2019-10-17 18:23:53 +03:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
private fetchDashboardStates(query: string): Array<string> {
|
2022-03-22 17:13:21 +02:00
|
|
|
const stateIds = Object.keys(this.dashboard.configuration.states);
|
2019-10-24 19:52:19 +03:00
|
|
|
const result = query ? stateIds.filter(this.createFilterForDashboardState(query)) : stateIds;
|
|
|
|
|
if (result && result.length) {
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
return [query];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 16:22:39 +03:00
|
|
|
private fetchCellClickColumns(): Array<CellClickColumnInfo> {
|
|
|
|
|
if (this.modelValue) {
|
|
|
|
|
const configuredColumns = new Array<CellClickColumnInfo>();
|
|
|
|
|
if (this.modelValue.config?.datasources[0]?.dataKeys?.length) {
|
|
|
|
|
configuredColumns.push(...this.keysToCellClickColumns(this.modelValue.config.datasources[0].dataKeys));
|
|
|
|
|
}
|
|
|
|
|
if (this.modelValue.config?.alarmSource?.dataKeys?.length) {
|
|
|
|
|
configuredColumns.push(...this.keysToCellClickColumns(this.modelValue.config.alarmSource.dataKeys));
|
|
|
|
|
}
|
|
|
|
|
return configuredColumns;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private keysToCellClickColumns(dataKeys: Array<DataKey>): Array<CellClickColumnInfo> {
|
|
|
|
|
const result: Array<CellClickColumnInfo> = [];
|
|
|
|
|
for (const dataKey of dataKeys) {
|
|
|
|
|
result.push({
|
|
|
|
|
name: dataKey.name,
|
|
|
|
|
label: dataKey?.label
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-24 19:52:19 +03:00
|
|
|
private createFilterForDashboardState(query: string): (stateId: string) => boolean {
|
|
|
|
|
const lowercaseQuery = query.toLowerCase();
|
|
|
|
|
return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-08 17:01:47 +02:00
|
|
|
public validate(c: UntypedFormControl): Observable<ValidationErrors | null> {
|
|
|
|
|
const basicComponentMode = this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic;
|
|
|
|
|
let comp$: Observable<IBasicWidgetConfigComponent>;
|
|
|
|
|
if (basicComponentMode) {
|
|
|
|
|
if (this.basicModeComponent) {
|
|
|
|
|
comp$ = of(this.basicModeComponent);
|
|
|
|
|
} else {
|
|
|
|
|
if (this.useDefinedBasicModeDirective) {
|
|
|
|
|
this.basicModeComponent$ = new Subject();
|
|
|
|
|
comp$ = this.basicModeComponent$;
|
|
|
|
|
} else {
|
|
|
|
|
comp$ = of(null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
comp$ = of(null);
|
|
|
|
|
}
|
|
|
|
|
return comp$.pipe(
|
|
|
|
|
map((comp) => this.doValidate(basicComponentMode, comp))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private doValidate(basicComponentMode: boolean, basicModeComponent?: IBasicWidgetConfigComponent): ValidationErrors | null {
|
|
|
|
|
if (basicComponentMode) {
|
|
|
|
|
if (!basicModeComponent || !basicModeComponent.validateConfig()) {
|
2023-05-30 13:47:52 +03:00
|
|
|
return {
|
|
|
|
|
basicWidgetConfig: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-11-08 17:01:47 +02:00
|
|
|
} else {
|
|
|
|
|
if (!this.dataSettings.valid) {
|
|
|
|
|
return {
|
|
|
|
|
dataSettings: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} else if (!this.widgetSettings.valid) {
|
|
|
|
|
return {
|
|
|
|
|
widgetSettings: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} else if (!this.layoutSettings.valid) {
|
|
|
|
|
return {
|
|
|
|
|
widgetSettings: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} else if (!this.advancedSettings.valid) {
|
|
|
|
|
return {
|
|
|
|
|
advancedSettings: {
|
|
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.modelValue) {
|
2019-10-10 13:00:29 +03:00
|
|
|
const config = this.modelValue.config;
|
2019-10-21 19:57:18 +03:00
|
|
|
if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) {
|
2024-06-10 17:49:15 +03:00
|
|
|
if ((!this.widgetEditMode && !this.modelValue?.typeParameters.targetDeviceOptional) && !targetDeviceValid(config.targetDevice)) {
|
2019-10-10 13:00:29 +03:00
|
|
|
return {
|
2024-01-23 20:03:14 +02:00
|
|
|
targetDevice: {
|
2019-10-10 13:00:29 +03:00
|
|
|
valid: false
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|
2023-11-08 17:01:47 +02:00
|
|
|
|
2019-09-25 19:37:29 +03:00
|
|
|
}
|