Merge branch 'develop/3.5.2' into feature/widget-bundles

This commit is contained in:
Igor Kulikov 2023-08-28 18:55:00 +03:00
commit 9a0b0fbefa
24 changed files with 130 additions and 93 deletions

View File

@ -42,6 +42,7 @@ import {
EntityCountCmd, EntityCountCmd,
EntityDataCmd, EntityDataCmd,
IndexedSubscriptionData, IndexedSubscriptionData,
NOT_SUPPORTED,
SubscriptionData, SubscriptionData,
TelemetrySubscriber TelemetrySubscriber
} from '@shared/models/telemetry/telemetry.models'; } from '@shared/models/telemetry/telemetry.models';
@ -786,7 +787,7 @@ export class EntityDataSubscription {
private reportNotSupported(keys: AggKey[], isUpdate: boolean) { private reportNotSupported(keys: AggKey[], isUpdate: boolean) {
const indexedData: IndexedSubscriptionData = []; const indexedData: IndexedSubscriptionData = [];
for (const key of keys) { for (const key of keys) {
indexedData[key.id] = [[0, 'Not supported!']]; indexedData[key.id] = [[0, NOT_SUPPORTED]];
} }
for (let dataIndex = 0; dataIndex < this.pageData.data.length; dataIndex++) { for (let dataIndex = 0; dataIndex < this.pageData.data.length; dataIndex++) {
this.onIndexedData(indexedData, dataIndex, true, this.onIndexedData(indexedData, dataIndex, true,

View File

@ -81,6 +81,7 @@ import { distinct, filter, map, switchMap, takeUntil } from 'rxjs/operators';
import { AlarmDataListener } from '@core/api/alarm-data.service'; import { AlarmDataListener } from '@core/api/alarm-data.service';
import { RpcStatus } from '@shared/models/rpc.models'; import { RpcStatus } from '@shared/models/rpc.models';
import { EventEmitter } from '@angular/core'; import { EventEmitter } from '@angular/core';
import { NOT_SUPPORTED } from '@shared/models/telemetry/telemetry.models';
const moment = moment_; const moment = moment_;
@ -1541,7 +1542,7 @@ export class WidgetSubscription implements IWidgetSubscription {
} else if (prevData && prevData[0] && prevData[0].length > 1 && data.data.length > 0) { } else if (prevData && prevData[0] && prevData[0].length > 1 && data.data.length > 0) {
const prevTs = prevData[0][0]; const prevTs = prevData[0][0];
const prevValue = prevData[0][1]; const prevValue = prevData[0][1];
if (prevTs === data.data[0][0] && prevValue === data.data[0][1]) { if (prevTs === data.data[0][0] && prevValue === data.data[0][1] && data.data[0][1] !== NOT_SUPPORTED) {
update = false; update = false;
} }
} }

View File

@ -161,12 +161,14 @@ import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard';
}) })
export class DashboardPageComponent extends PageComponent implements IDashboardController, HasDirtyFlag, OnInit, AfterViewInit, OnDestroy { export class DashboardPageComponent extends PageComponent implements IDashboardController, HasDirtyFlag, OnInit, AfterViewInit, OnDestroy {
private forcePristine = false;
get isDirty(): boolean { get isDirty(): boolean {
return this.isEdit; return this.isEdit && !this.forcePristine;
} }
set isDirty(value: boolean) { set isDirty(value: boolean) {
this.forcePristine = !value;
} }
authState: AuthState = getCurrentAuthState(this.store); authState: AuthState = getCurrentAuthState(this.store);

View File

@ -43,6 +43,11 @@
(click)="editAssetProfile($event)"> (click)="editAssetProfile($event)">
<mat-icon class="material-icons">edit</mat-icon> <mat-icon class="material-icons">edit</mat-icon>
</button> </button>
<button mat-button color="primary" matSuffix
(click)="createAssetProfile($event, '')"
*ngIf="!selectAssetProfileFormGroup.get('assetProfile').value && !disabled && addNewProfile">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
</button>
<mat-autocomplete <mat-autocomplete
class="tb-autocomplete" class="tb-autocomplete"
(closed)="onPanelClosed()" (closed)="onPanelClosed()"

View File

@ -19,3 +19,9 @@
color: inherit; color: inherit;
} }
} }
:host ::ng-deep {
.mat-mdc-form-field-icon-suffix {
display: flex;
}
}

View File

@ -46,6 +46,7 @@ import { AssetProfile, AssetProfileInfo } from '@shared/models/asset.models';
import { AssetProfileService } from '@core/http/asset-profile.service'; import { AssetProfileService } from '@core/http/asset-profile.service';
import { AssetProfileDialogComponent, AssetProfileDialogData } from './asset-profile-dialog.component'; import { AssetProfileDialogComponent, AssetProfileDialogData } from './asset-profile-dialog.component';
import { SubscriptSizing } from '@angular/material/form-field'; import { SubscriptSizing } from '@angular/material/form-field';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-asset-profile-autocomplete', selector: 'tb-asset-profile-autocomplete',
@ -84,14 +85,9 @@ export class AssetProfileAutocompleteComponent implements ControlValueAccessor,
@Input() @Input()
showDetailsPageLink = false; showDetailsPageLink = false;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input() @Input()
set required(value: boolean) { @coerceBoolean()
this.requiredValue = coerceBooleanProperty(value); required = false;
}
@Input() @Input()
disabled: boolean; disabled: boolean;

View File

@ -45,7 +45,7 @@
</button> </button>
<button mat-button color="primary" matSuffix <button mat-button color="primary" matSuffix
(click)="createDeviceProfile($event, '')" (click)="createDeviceProfile($event, '')"
*ngIf="!selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled && addNewProfile && showCreateNewButton"> *ngIf="!selectDeviceProfileFormGroup.get('deviceProfile').value && !disabled && addNewProfile">
<span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span> <span style="white-space: nowrap">{{ 'notification.create-new' | translate }}</span>
</button> </button>
<mat-autocomplete <mat-autocomplete

View File

@ -88,10 +88,6 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor,
@coerceBoolean() @coerceBoolean()
addNewProfile = true; addNewProfile = true;
@Input()
@coerceBoolean()
showCreateNewButton = false;
@Input() @Input()
showDetailsPageLink = false; showDetailsPageLink = false;

View File

@ -70,7 +70,7 @@
[indeterminate]="alarmsDatasource.selection.hasValue() && !(alarmsDatasource.isAllSelected() | async)"> [indeterminate]="alarmsDatasource.selection.hasValue() && !(alarmsDatasource.isAllSelected() | async)">
</mat-checkbox> </mat-checkbox>
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let alarm"> <mat-cell *matCellDef="let alarm; let row = index" [style]="rowStyle(alarm, row)">
<mat-checkbox (click)="$event.stopPropagation();" <mat-checkbox (click)="$event.stopPropagation();"
(change)="$event ? alarmsDatasource.toggleSelection(alarm) : null" (change)="$event ? alarmsDatasource.toggleSelection(alarm) : null"
[checked]="alarmsDatasource.isSelected(alarm)"> [checked]="alarmsDatasource.isSelected(alarm)">
@ -122,7 +122,7 @@
maxWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px', maxWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px',
width: (alarmsDatasource.countCellButtonAction * 48) + 'px' }"> width: (alarmsDatasource.countCellButtonAction * 48) + 'px' }">
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let alarm" [ngStyle.gt-md]="{ minWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px', <mat-cell *matCellDef="let alarm; let row = index" [style]="rowStyle(alarm, row)" [ngStyle.gt-md]="{ minWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px',
maxWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px', maxWidth: (alarmsDatasource.countCellButtonAction * 48) + 'px',
width: (alarmsDatasource.countCellButtonAction * 48) + 'px' }"> width: (alarmsDatasource.countCellButtonAction * 48) + 'px' }">
<div [fxHide]="showCellActionsMenu" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end"> <div [fxHide]="showCellActionsMenu" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">

View File

@ -52,7 +52,7 @@
maxWidth: (entityDatasource.countCellButtonAction * 48) + 'px', maxWidth: (entityDatasource.countCellButtonAction * 48) + 'px',
width: (entityDatasource.countCellButtonAction * 48) + 'px' }"> width: (entityDatasource.countCellButtonAction * 48) + 'px' }">
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let entity" [ngStyle.gt-md]="{ minWidth: (entityDatasource.countCellButtonAction * 48) + 'px', <mat-cell *matCellDef="let entity; let row = index" [style]="rowStyle(entity, row)" [ngStyle.gt-md]="{ minWidth: (entityDatasource.countCellButtonAction * 48) + 'px',
maxWidth: (entityDatasource.countCellButtonAction * 48) + 'px', maxWidth: (entityDatasource.countCellButtonAction * 48) + 'px',
width: (entityDatasource.countCellButtonAction * 48) + 'px' }"> width: (entityDatasource.countCellButtonAction * 48) + 'px' }">
<div [fxHide]="showCellActionsMenu && entityDatasource.countCellButtonAction !== 1" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end"> <div [fxHide]="showCellActionsMenu && entityDatasource.countCellButtonAction !== 1" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">

View File

@ -452,7 +452,7 @@ export function constructTableCssString(widgetConfig: WidgetConfig): string {
'.mat-mdc-table .mat-mdc-row .mat-mdc-cell.mat-mdc-table-sticky, .mat-mdc-table .mat-mdc-header-cell.mat-mdc-table-sticky {\n' + '.mat-mdc-table .mat-mdc-row .mat-mdc-cell.mat-mdc-table-sticky, .mat-mdc-table .mat-mdc-header-cell.mat-mdc-table-sticky {\n' +
'background-color: ' + origBackgroundColor + ';\n' + 'background-color: ' + origBackgroundColor + ';\n' +
'}\n' + '}\n' +
'.mat-mdc-table .mat-mdc-cell {\n' + '.mat-mdc-table .mat-mdc-row {\n' +
'color: ' + mdDark + ';\n' + 'color: ' + mdDark + ';\n' +
'background-color: rgba(0, 0, 0, 0);\n' + 'background-color: rgba(0, 0, 0, 0);\n' +
'}\n' + '}\n' +

View File

@ -64,7 +64,7 @@
maxWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px', maxWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px',
width: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px' }"> width: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px' }">
</mat-header-cell> </mat-header-cell>
<mat-cell *matCellDef="let row" [ngStyle.gt-md]="{ minWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px', <mat-cell *matCellDef="let entity; let row = index" [style]="rowStyle(source, entity, row)" [ngStyle.gt-md]="{ minWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px',
maxWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px', maxWidth: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px',
width: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px' }"> width: (source.timeseriesDatasource.countCellButtonAction * 48) + 'px' }">
<div [fxHide]="showCellActionsMenu && source.timeseriesDatasource.countCellButtonAction !== 1" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end"> <div [fxHide]="showCellActionsMenu && source.timeseriesDatasource.countCellButtonAction !== 1" fxShow.gt-md fxFlex fxLayout="row" fxLayoutAlign="end">
@ -74,22 +74,22 @@
mat-icon-button [disabled]="isLoading$ | async" mat-icon-button [disabled]="isLoading$ | async"
matTooltip="{{ actionDescriptor.displayName }}" matTooltip="{{ actionDescriptor.displayName }}"
matTooltipPosition="above" matTooltipPosition="above"
(click)="onActionButtonClick($event, row, actionDescriptor)"> (click)="onActionButtonClick($event, entity, actionDescriptor)">
<mat-icon>{{actionDescriptor.icon}}</mat-icon> <mat-icon>{{actionDescriptor.icon}}</mat-icon>
</button> </button>
</ng-container> </ng-container>
</div> </div>
<div fxHide [fxShow.lt-lg]="showCellActionsMenu && source.timeseriesDatasource.countCellButtonAction !== 1" *ngIf="row.hasActions"> <div fxHide [fxShow.lt-lg]="showCellActionsMenu && source.timeseriesDatasource.countCellButtonAction !== 1" *ngIf="entity.hasActions">
<button mat-icon-button <button mat-icon-button
(click)="$event.stopPropagation(); ctx.detectChanges();" (click)="$event.stopPropagation(); ctx.detectChanges();"
[matMenuTriggerFor]="cellActionsMenu"> [matMenuTriggerFor]="cellActionsMenu">
<mat-icon class="material-icons">more_vert</mat-icon> <mat-icon class="material-icons">more_vert</mat-icon>
</button> </button>
<mat-menu #cellActionsMenu="matMenu" xPosition="before"> <mat-menu #cellActionsMenu="matMenu" xPosition="before">
<ng-container *ngFor="let actionDescriptor of row.actionCellButtons; trackBy: trackByActionCellDescriptionId"> <ng-container *ngFor="let actionDescriptor of entity.actionCellButtons; trackBy: trackByActionCellDescriptionId">
<button mat-menu-item *ngIf="actionDescriptor.icon" <button mat-menu-item *ngIf="actionDescriptor.icon"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
(click)="onActionButtonClick($event, row, actionDescriptor)"> (click)="onActionButtonClick($event, entity, actionDescriptor)">
<mat-icon>{{actionDescriptor.icon}}</mat-icon> <mat-icon>{{actionDescriptor.icon}}</mat-icon>
<span>{{ actionDescriptor.displayName }}</span> <span>{{ actionDescriptor.displayName }}</span>
</button> </button>

View File

@ -146,11 +146,8 @@ export class DeviceWizardDialogComponent extends DialogComponent<DeviceWizardDia
overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value, overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value,
description: this.deviceWizardFormGroup.get('description').value description: this.deviceWizardFormGroup.get('description').value
}, },
customerId: null customerId: this.deviceWizardFormGroup.get('customerId').value
}; };
if (this.deviceWizardFormGroup.get('customerId').value) {
device.customerId = new CustomerId(this.deviceWizardFormGroup.get('customerId').value);
}
if (this.addDeviceWizardStepper.steps.last.completed || this.addDeviceWizardStepper.selectedIndex > 0) { if (this.addDeviceWizardStepper.steps.last.completed || this.addDeviceWizardStepper.selectedIndex > 0) {
return this.deviceService.saveDeviceWithCredentials(deepTrim(device), deepTrim(this.credentialsFormGroup.value.credential)).pipe( return this.deviceService.saveDeviceWithCredentials(deepTrim(device), deepTrim(this.credentialsFormGroup.value.credential)).pipe(
catchError((e: HttpErrorResponse) => { catchError((e: HttpErrorResponse) => {

View File

@ -86,13 +86,6 @@
{{ 'asset.name-max-length' | translate }} {{ 'asset.name-max-length' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<tb-asset-profile-autocomplete
[selectDefaultProfile]="isAdd"
required
formControlName="assetProfileId"
[showDetailsPageLink]="true"
(assetProfileUpdated)="onAssetProfileUpdated()">
</tb-asset-profile-autocomplete>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>asset.label</mat-label> <mat-label translate>asset.label</mat-label>
<input matInput formControlName="label"> <input matInput formControlName="label">
@ -100,6 +93,20 @@
{{ 'asset.label-max-length' | translate }} {{ 'asset.label-max-length' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<tb-asset-profile-autocomplete
[selectDefaultProfile]="isAdd"
required
formControlName="assetProfileId"
[showDetailsPageLink]="true"
(assetProfileUpdated)="onAssetProfileUpdated()">
</tb-asset-profile-autocomplete>
<tb-entity-autocomplete
*ngIf="isAdd"
useFullEntityId
formControlName="customerId"
labelText="asset.assign-to-customer"
[entityType]="entityType.CUSTOMER">
</tb-entity-autocomplete>
<div formGroupName="additionalInfo"> <div formGroupName="additionalInfo">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>asset.description</mat-label> <mat-label translate>asset.description</mat-label>

View File

@ -69,6 +69,7 @@ export class AssetComponent extends EntityComponent<AssetInfo> {
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
assetProfileId: [entity ? entity.assetProfileId : null, [Validators.required]], assetProfileId: [entity ? entity.assetProfileId : null, [Validators.required]],
label: [entity ? entity.label : '', Validators.maxLength(255)], label: [entity ? entity.label : '', Validators.maxLength(255)],
customerId: [entity ? entity.customerId : ''],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],
@ -82,6 +83,7 @@ export class AssetComponent extends EntityComponent<AssetInfo> {
this.entityForm.patchValue({name: entity.name}); this.entityForm.patchValue({name: entity.name});
this.entityForm.patchValue({assetProfileId: entity.assetProfileId}); this.entityForm.patchValue({assetProfileId: entity.assetProfileId});
this.entityForm.patchValue({label: entity.label}); this.entityForm.patchValue({label: entity.label});
this.entityForm.patchValue({customerId: entity.customerId});
this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}});
} }

View File

@ -29,16 +29,12 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar> </mat-progress-bar>
<div mat-dialog-content> <div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async"> <tb-rule-node #tbRuleNode
<tb-rule-node #tbRuleNode [ruleNode]="ruleNode"
[ruleNode]="ruleNode" [ruleChainId]="ruleChainId"
[ruleChainId]="ruleChainId" [ruleChainType]="ruleChainType"
[ruleChainType]="ruleChainType" isAdd>
[isEdit]="true" </tb-rule-node>
[isAdd]="true"
[isReadOnly]="false">
</tb-rule-node>
</fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-button color="primary" <button mat-button color="primary"

View File

@ -15,7 +15,7 @@
*/ */
:host { :host {
display: block; display: block;
margin-bottom: 16px;
tb-json-object-edit.tb-rule-node-configuration-json { tb-json-object-edit.tb-rule-node-configuration-json {
display: block; display: block;
height: 300px; height: 300px;

View File

@ -165,6 +165,9 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On
} else { } else {
this.ruleNodeConfigFormGroup.enable({emitEvent: false}); this.ruleNodeConfigFormGroup.enable({emitEvent: false});
} }
if (this.definedConfigComponent) {
this.definedConfigComponent.disabled = this.disabled;
}
} }
writeValue(value: RuleNodeConfiguration): void { writeValue(value: RuleNodeConfiguration): void {
@ -222,6 +225,7 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On
this.definedConfigComponent.ruleChainId = this.ruleChainId; this.definedConfigComponent.ruleChainId = this.ruleChainId;
this.definedConfigComponent.ruleChainType = this.ruleChainType; this.definedConfigComponent.ruleChainType = this.ruleChainType;
this.definedConfigComponent.configuration = this.configuration; this.definedConfigComponent.configuration = this.configuration;
this.definedConfigComponent.disabled = this.disabled;
this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => { this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => {
this.updateModel(configuration); this.updateModel(configuration);
}); });

View File

@ -22,44 +22,40 @@
</button> </button>
</div> </div>
<form [formGroup]="ruleNodeFormGroup" class="mat-padding"> <form [formGroup]="ruleNodeFormGroup" class="mat-padding">
<fieldset [disabled]="(isLoading$ | async) || !isEdit || isReadOnly"> <section class="title-row">
<section> <mat-form-field fxFlex class="mat-block">
<section class="title-row"> <mat-label translate>rulenode.name</mat-label>
<mat-form-field fxFlex class="mat-block"> <input matInput formControlName="name" required>
<mat-label translate>rulenode.name</mat-label> <mat-error *ngIf="ruleNodeFormGroup.get('name').hasError('required')
<input matInput formControlName="name" required>
<mat-error *ngIf="ruleNodeFormGroup.get('name').hasError('required')
|| ruleNodeFormGroup.get('name').hasError('pattern')"> || ruleNodeFormGroup.get('name').hasError('pattern')">
{{ 'rulenode.name-required' | translate }} {{ 'rulenode.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="ruleNodeFormGroup.get('name').hasError('maxlength')"> <mat-error *ngIf="ruleNodeFormGroup.get('name').hasError('maxlength')">
{{ 'rulenode.name-max-length' | translate }} {{ 'rulenode.name-max-length' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<section class="node-setting"> <section class="node-setting">
<mat-slide-toggle formControlName="debugMode"> <mat-slide-toggle formControlName="debugMode">
{{ 'rulenode.debug-mode' | translate }} {{ 'rulenode.debug-mode' | translate }}
</mat-slide-toggle> </mat-slide-toggle>
<mat-slide-toggle *ngIf="isSingletonEditAllowed()" formControlName="singletonMode"> <mat-slide-toggle *ngIf="isSingletonEditAllowed()" formControlName="singletonMode">
{{ 'rulenode.singleton-mode' | translate }} {{ 'rulenode.singleton-mode' | translate }}
</mat-slide-toggle > </mat-slide-toggle>
</section>
</section>
<tb-rule-node-config #ruleNodeConfigComponent
formControlName="configuration"
[ruleNodeId]="ruleNode.ruleNodeId?.id"
[ruleChainId]="ruleChainId"
[ruleChainType]="ruleChainType"
[nodeDefinition]="ruleNode.component.configurationDescriptor.nodeDefinition"
(initRuleNode)="initRuleNode.emit($event)"
(changeScript)="changeScript.emit($event)">
</tb-rule-node-config>
<div formGroupName="additionalInfo" fxLayout="column" class="description-block">
<mat-form-field class="mat-block">
<mat-label translate>rulenode.rule-node-description</mat-label>
<textarea matInput formControlName="description" rows="1"></textarea>
</mat-form-field>
</div>
</section> </section>
</fieldset> </section>
<tb-rule-node-config #ruleNodeConfigComponent
formControlName="configuration"
[ruleNodeId]="ruleNode.ruleNodeId?.id"
[ruleChainId]="ruleChainId"
[ruleChainType]="ruleChainType"
[nodeDefinition]="ruleNode.component.configurationDescriptor.nodeDefinition"
(initRuleNode)="initRuleNode.emit($event)"
(changeScript)="changeScript.emit($event)">
</tb-rule-node-config>
<div formGroupName="additionalInfo" fxLayout="column" class="description-block">
<mat-form-field class="mat-block">
<mat-label translate>rulenode.rule-node-description</mat-label>
<textarea matInput formControlName="description" rows="1"></textarea>
</mat-form-field>
</div>
</form> </form>

View File

@ -22,11 +22,11 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms
import { FcRuleNode, RuleNodeType } from '@shared/models/rule-node.models'; import { FcRuleNode, RuleNodeType } from '@shared/models/rule-node.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { RuleChainService } from '@core/http/rule-chain.service';
import { RuleNodeConfigComponent } from './rule-node-config.component'; import { RuleNodeConfigComponent } from './rule-node-config.component';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { RuleChainType } from '@app/shared/models/rule-chain.models'; import { RuleChainType } from '@app/shared/models/rule-chain.models';
import { ComponentClusteringMode } from '@shared/models/component-descriptor.models'; import { ComponentClusteringMode } from '@shared/models/component-descriptor.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({ @Component({
selector: 'tb-rule-node', selector: 'tb-rule-node',
@ -47,12 +47,11 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
ruleChainType: RuleChainType; ruleChainType: RuleChainType;
@Input() @Input()
isEdit: boolean; @coerceBoolean()
disabled = false;
@Input()
isReadOnly: boolean;
@Input() @Input()
@coerceBoolean()
isAdd = false; isAdd = false;
@Output() @Output()
@ -70,7 +69,6 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private ruleChainService: RuleChainService,
private router: Router) { private router: Router) {
super(store); super(store);
this.ruleNodeFormGroup = this.fb.group({}); this.ruleNodeFormGroup = this.fb.group({});
@ -99,6 +97,9 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
} else { } else {
this.ruleNodeFormGroup = this.fb.group({}); this.ruleNodeFormGroup = this.fb.group({});
} }
if (this.disabled) {
this.ruleNodeFormGroup.disable({emitEvent: false});
}
} }
private updateRuleNode() { private updateRuleNode() {
@ -108,6 +109,9 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
} }
ngOnInit(): void { ngOnInit(): void {
if (this.disabled) {
this.ruleNodeFormGroup.disable({emitEvent: false});
}
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {

View File

@ -110,8 +110,6 @@
[ruleNode]="editingRuleNode" [ruleNode]="editingRuleNode"
[ruleChainId]="ruleChain.id?.id" [ruleChainId]="ruleChain.id?.id"
[ruleChainType]="ruleChainType" [ruleChainType]="ruleChainType"
[isEdit]="true"
[isReadOnly]="false"
(initRuleNode)="onRuleNodeInit()" (initRuleNode)="onRuleNodeInit()"
(changeScript)="switchToFirstTab()"> (changeScript)="switchToFirstTab()">
</tb-rule-node> </tb-rule-node>

View File

@ -74,6 +74,7 @@ export interface IRuleNodeConfigurationComponent {
ruleNodeId: string; ruleNodeId: string;
ruleChainId: string; ruleChainId: string;
hasScript: boolean; hasScript: boolean;
disabled: boolean;
testScriptLabel?: string; testScriptLabel?: string;
changeScript?: EventEmitter<void>; changeScript?: EventEmitter<void>;
ruleChainType: RuleChainType; ruleChainType: RuleChainType;
@ -101,6 +102,14 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple
private configurationSet = false; private configurationSet = false;
set disabled(value: boolean) {
if (value) {
this.configForm().disable({emitEvent: false});
} else {
this.configForm().enable({emitEvent: false});
}
};
set configuration(value: RuleNodeConfiguration) { set configuration(value: RuleNodeConfiguration) {
this.configurationValue = value; this.configurationValue = value;
if (!this.configurationSet) { if (!this.configurationSet) {

View File

@ -37,6 +37,8 @@ import { isUndefined } from '@core/utils';
import { CmdWrapper, WsSubscriber } from '@shared/models/websocket/websocket.models'; import { CmdWrapper, WsSubscriber } from '@shared/models/websocket/websocket.models';
import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
export const NOT_SUPPORTED = 'Not supported!';
export enum DataKeyType { export enum DataKeyType {
timeseries = 'timeseries', timeseries = 'timeseries',
attribute = 'attribute', attribute = 'attribute',

View File

@ -692,6 +692,21 @@ mat-label {
margin: 0; margin: 0;
} }
.tb-table-widget {
.mat-mdc-table {
.mat-mdc-cell {
background: inherit;
color: inherit;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
letter-spacing: inherit;
text-transform: inherit;
}
}
}
.mat-mdc-footer-row::after, .mat-mdc-header-row::after, .mat-mdc-row::after { .mat-mdc-footer-row::after, .mat-mdc-header-row::after, .mat-mdc-row::after {
content: none; content: none;
} }