Browse Source

UI: Improved alarm rules detail panel/page

pull/14692/head
Vladyslav_Prykhodko 9 months ago
parent
commit
0a58ff5e77
  1. 22
      ui-ngx/src/app/core/services/calculated-field-form.service.ts
  2. 33
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html
  3. 63
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts
  4. 23
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
  5. 1
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html
  6. 60
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts
  7. 4
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html
  8. 16
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts
  9. 6
      ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts
  10. 11
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html
  11. 4
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts
  12. 1
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html
  13. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
  14. 8
      ui-ngx/src/app/shared/models/alarm-rule.models.ts

22
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts → ui-ngx/src/app/core/services/calculated-field-form.service.ts

@ -30,7 +30,6 @@ import { isDefined } from '@core/utils';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EntityType } from '@shared/models/entity-type.models';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class CalculatedFieldFormService { export class CalculatedFieldFormService {
@ -40,13 +39,32 @@ export class CalculatedFieldFormService {
buildForm(): FormGroup { buildForm(): FormGroup {
return this.fb.group({ return this.fb.group({
name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]],
entityId: [{type: EntityType.DEVICE_PROFILE, id: null}, Validators.required], entityId: [null, Validators.required],
type: [CalculatedFieldType.SIMPLE], type: [CalculatedFieldType.SIMPLE],
debugSettings: [], debugSettings: [],
configuration: this.fb.control<CalculatedFieldConfiguration>({} as CalculatedFieldConfiguration), configuration: this.fb.control<CalculatedFieldConfiguration>({} as CalculatedFieldConfiguration),
}); });
} }
buildAlarmRuleForm(): FormGroup {
return this.fb.group({
name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]],
entityId: [null, Validators.required],
type: [CalculatedFieldType.ALARM],
debugSettings: [],
configuration: this.fb.group({
type: [CalculatedFieldType.ALARM],
arguments: this.fb.control({}, Validators.required),
propagate: [false],
propagateToOwner: [false],
propagateToTenant: [false],
propagateRelationTypes: [null],
createRules: [null, Validators.required],
clearRule: [null],
}),
});
}
setupTypeChange(form: FormGroup, destroyRef: DestroyRef, isEditActive?: () => boolean): void { setupTypeChange(form: FormGroup, destroyRef: DestroyRef, isEditActive?: () => boolean): void {
form.get('type').valueChanges.pipe( form.get('type').valueChanges.pipe(
pairwise(), pairwise(),

33
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html

@ -58,31 +58,14 @@
/> />
</div> </div>
@if (!data.entityId) { @if (!data.entityId) {
<div class="flex flex-row gap-4 xs:flex-col" formGroupName="entityId"> <tb-entity-select formControlName="entityId"
<tb-entity-type-select #entityTypeSelect appearance="outline"
appearance="outline" required
subscriptSizing="dynamic" [filterAllowedEntityTypes]="false"
class="flex-1" [allowedEntityTypes]="alarmRuleEntityTypeList"
showLabel [defaultEntityType]="EntityType.DEVICE_PROFILE"
required (entityChanged)="changeEntity($event)">
label="{{ 'entity.entity-type' | translate }}" </tb-entity-select>
[filterAllowedEntityTypes]="false"
[allowedEntityTypes]="alarmRuleEntityTypeList"
formControlName="entityType">
</tb-entity-type-select>
@if (fieldFormGroup.get('entityId.entityType').value) {
<tb-entity-autocomplete #entityAutocompleteComponent
formControlName="id"
appearance="outline"
subscriptSizing="dynamic"
class="flex-1"
[placeholder]="'action.set' | translate"
[required]="true"
[entityType]="fieldFormGroup.get('entityId.entityType').value"
(entityChanged)="changeEntity($event)"
/>
}
</div>
} }
</div> </div>
<ng-container formGroupName="configuration"> <ng-container formGroupName="configuration">

63
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts

@ -14,16 +14,15 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, DestroyRef, Inject, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, DestroyRef, Inject, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
import { CalculatedField, CalculatedFieldArgument, CalculatedFieldType } from '@shared/models/calculated-field.models'; import { CalculatedField, CalculatedFieldArgument, CalculatedFieldType } from '@shared/models/calculated-field.models';
import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ScriptLanguage } from '@shared/models/rule-node.models'; import { ScriptLanguage } from '@shared/models/rule-node.models';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
@ -39,13 +38,11 @@ import {
} from "@shared/models/alarm-rule.models"; } from "@shared/models/alarm-rule.models";
import { deepTrim } from "@core/utils"; import { deepTrim } from "@core/utils";
import { combineLatest, Observable } from "rxjs"; import { combineLatest, Observable } from "rxjs";
import { debounceTime, startWith, switchMap } from "rxjs/operators"; import { debounceTime, startWith } from "rxjs/operators";
import { EntityTypeSelectComponent } from "@shared/components/entity/entity-type-select.component";
import { EntityAutocompleteComponent } from "@shared/components/entity/entity-autocomplete.component";
import { EntityService } from "@core/http/entity.service";
import { RelationTypes } from "@shared/models/relation.models"; import { RelationTypes } from "@shared/models/relation.models";
import { StringItemsOption } from "@shared/components/string-items-list.component"; import { StringItemsOption } from "@shared/components/string-items-list.component";
import { BaseData } from "@shared/models/base-data"; import { BaseData } from "@shared/models/base-data";
import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service';
export interface AlarmRuleDialogData { export interface AlarmRuleDialogData {
value?: CalculatedField; value?: CalculatedField;
@ -67,24 +64,7 @@ export interface AlarmRuleDialogData {
}) })
export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogComponent, CalculatedField> { export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogComponent, CalculatedField> {
fieldFormGroup = this.fb.group({ fieldFormGroup: FormGroup ;
name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]],
type: [CalculatedFieldType.ALARM],
debugSettings: [],
entityId: this.fb.group({
entityType: this.fb.control<EntityType | AliasEntityType | null>(EntityType.DEVICE_PROFILE, Validators.required),
id: [null as null | string, Validators.required],
}),
configuration: this.fb.group({
arguments: this.fb.control({}, Validators.required),
propagate: [false],
propagateToOwner: [false],
propagateToTenant: [false],
propagateRelationTypes: [null],
createRules: [null, Validators.required],
clearRule: [null],
}),
});
additionalDebugActionConfig = this.data.value?.id ? { additionalDebugActionConfig = this.data.value?.id ? {
...this.data.additionalDebugActionConfig, ...this.data.additionalDebugActionConfig,
@ -105,18 +85,15 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
disabledArguments = false; disabledArguments = false;
isLoading = false; isLoading = false;
@ViewChild('entityTypeSelect') entityTypeSelect: EntityTypeSelectComponent;
@ViewChild('entityAutocompleteComponent') entityAutocompleteComponent: EntityAutocompleteComponent;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData, @Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData,
protected dialogRef: MatDialogRef<AlarmRuleDialogComponent, CalculatedField>, protected dialogRef: MatDialogRef<AlarmRuleDialogComponent, CalculatedField>,
private calculatedFieldsService: CalculatedFieldsService, private calculatedFieldsService: CalculatedFieldsService,
private entityService: EntityService,
private destroyRef: DestroyRef, private destroyRef: DestroyRef,
private fb: FormBuilder) { private cfFormService: CalculatedFieldFormService) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.fieldFormGroup = this.cfFormService.buildAlarmRuleForm();
this.applyDialogData(); this.applyDialogData();
this.updateRulesValidators(); this.updateRulesValidators();
@ -132,7 +109,7 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
if (!this.data.entityId) { if (!this.data.entityId) {
combineLatest([ combineLatest([
this.fieldFormGroup.get('entityId.id')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('entityId.id')!.value)), this.fieldFormGroup.get('entityId')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('entityId')!.value)),
this.fieldFormGroup.get('name')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('name')!.value)) this.fieldFormGroup.get('name')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('name')!.value))
]).pipe( ]).pipe(
debounceTime(50), debounceTime(50),
@ -196,8 +173,6 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
}); });
} else { } else {
this.fieldFormGroup.get('name').markAsTouched(); this.fieldFormGroup.get('name').markAsTouched();
this.entityTypeSelect?.markAsTouched();
this.entityAutocompleteComponent?.markAsTouched();
} }
} }
@ -207,18 +182,13 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
} }
onTestScript(expression: string): Observable<string> { onTestScript(expression: string): Observable<string> {
const calculatedFieldId = this.data.value?.id?.id; return this.cfFormService.testScript(
if (calculatedFieldId) { this.data.value?.id?.id,
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) this.fromGroupValue,
.pipe( this.data.getTestScriptDialogFn,
switchMap(event => { this.destroyRef,
const args = event?.arguments ? JSON.parse(event.arguments) : null; expression
return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, args, false); );
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, null, false);
} }
private updateRulesValidators(): void { private updateRulesValidators(): void {
@ -250,5 +220,4 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
changeEntity(entity: BaseData<EntityId>): void { changeEntity(entity: BaseData<EntityId>): void {
this.entityName = entity.name; this.entityName = entity.name;
} }
} }

23
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts

@ -36,7 +36,7 @@ import { DestroyRef, Renderer2 } from '@angular/core';
import { EntityDebugSettings } from '@shared/models/entity.models'; import { EntityDebugSettings } from '@shared/models/entity.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { catchError, filter, switchMap, tap } from 'rxjs/operators'; import { catchError, filter, first, switchMap, tap } from 'rxjs/operators';
import { import {
ArgumentEntityType, ArgumentEntityType,
ArgumentType, ArgumentType,
@ -186,7 +186,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig<AlarmRuleTableEntit
name: this.translate.instant('entity-view.events'), name: this.translate.instant('entity-view.events'),
icon: 'mdi:clipboard-text-clock', icon: 'mdi:clipboard-text-clock',
isEnabled: () => true, isEnabled: () => true,
onAction: ($event, entity) => this.openDebugEventsDialog($event, entity), onAction: ($event, entity) =>
this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity),
}, },
{ {
name: '', name: '',
@ -308,6 +309,22 @@ export class AlarmRulesTableConfig extends EntityTableConfig<AlarmRuleTableEntit
.subscribe(); .subscribe();
} }
private openDebugTab($event: Event, calculatedField: AlarmRuleTableEntity) {
const table = this.getTable();
if (!table.isDetailsOpen) {
table.toggleEntityDetails($event, calculatedField);
if (table.entityDetailsPanel.matTabGroup._tabs.length > 1) {
table.entityDetailsPanel.matTabGroup.selectedIndex = 1;
} else {
table.entityDetailsPanel.matTabGroup._tabs.changes.pipe(
first()
).subscribe(() => {
table.entityDetailsPanel.matTabGroup.selectedIndex = 1;
})
}
}
}
private exportAlarmRule($event: Event, calculatedField: AlarmRuleTableEntity): void { private exportAlarmRule($event: Event, calculatedField: AlarmRuleTableEntity): void {
$event?.stopPropagation(); $event?.stopPropagation();
this.importExportService.exportCalculatedField(calculatedField.id.id); this.importExportService.exportCalculatedField(calculatedField.id.id);
@ -361,7 +378,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig<AlarmRuleTableEntit
).subscribe(() => this.updateData()); ).subscribe(() => this.updateData());
} }
private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, expression: string, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable<string> { private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable<string> {
if (calculatedField.type === CalculatedFieldType.ALARM) { if (calculatedField.type === CalculatedFieldType.ALARM) {
const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => {
const type = calculatedField.configuration.arguments[key].refEntityKey.type; const type = calculatedField.configuration.arguments[key].refEntityKey.type;

1
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html

@ -65,6 +65,7 @@
</div> </div>
<tb-entity-select formControlName="entityId" <tb-entity-select formControlName="entityId"
appearance="outline" appearance="outline"
required
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="calculatedFieldsEntityTypeList" [allowedEntityTypes]="calculatedFieldsEntityTypeList"
(entityChanged)="changeEntity($event)"> (entityChanged)="changeEntity($event)">

60
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts

@ -14,11 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { ChangeDetectorRef, Component, DestroyRef, Inject, Input } from '@angular/core'; import { ChangeDetectorRef, Component, DestroyRef, inject, Inject, Input } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component'; import { EntityComponent } from '@home/components/entity/entity.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
@ -28,13 +28,9 @@ import {
calculatedFieldsEntityTypeList, calculatedFieldsEntityTypeList,
CalculatedFieldType CalculatedFieldType
} from '@shared/models/calculated-field.models'; } from '@shared/models/calculated-field.models';
import { oneSpaceInsideRegex } from '@shared/models/regex.constants';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { switchMap } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BaseData } from '@shared/models/base-data'; import { BaseData } from '@shared/models/base-data';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { import {
CalculatedFieldsTableConfig, CalculatedFieldsTableConfig,
@ -44,6 +40,7 @@ import { TenantId } from '@shared/models/id/tenant-id';
import { StringItemsOption } from '@shared/components/string-items-list.component'; import { StringItemsOption } from '@shared/components/string-items-list.component';
import { RelationTypes } from '@shared/models/relation.models'; import { RelationTypes } from '@shared/models/relation.models';
import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from '@shared/models/alarm-rule.models'; import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from '@shared/models/alarm-rule.models';
import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service';
@Component({ @Component({
selector: 'tb-alarm-rules', selector: 'tb-alarm-rules',
@ -58,20 +55,21 @@ export class AlarmRulesComponent extends EntityComponent<CalculatedFieldsTableEn
@Input() @Input()
entityName: string; entityName: string;
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
readonly ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId); readonly ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId);
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
readonly EntityType = EntityType; readonly EntityType = EntityType;
readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList;
readonly CalculatedFieldType = CalculatedFieldType; readonly CalculatedFieldType = CalculatedFieldType;
private cfFormService = inject(CalculatedFieldFormService);
private destroyRef = inject(DestroyRef);
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected translate: TranslateService, protected translate: TranslateService,
@Inject('entity') protected entityValue: CalculatedFieldInfo, @Inject('entity') protected entityValue: CalculatedFieldInfo,
@Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig,
protected fb: FormBuilder, protected fb: FormBuilder,
protected cd: ChangeDetectorRef, protected cd: ChangeDetectorRef) {
private destroyRef: DestroyRef,
private calculatedFieldsService: CalculatedFieldsService) {
super(store, fb, entityValue, entitiesTableConfigValue, cd); super(store, fb, entityValue, entitiesTableConfigValue, cd);
} }
@ -107,24 +105,8 @@ export class AlarmRulesComponent extends EntityComponent<CalculatedFieldsTableEn
this.entityName = entity?.name; this.entityName = entity?.name;
} }
buildForm(entity?: CalculatedFieldInfo): FormGroup { buildForm(_entity?: CalculatedFieldInfo): FormGroup {
const form = this.fb.group({ return inject(CalculatedFieldFormService).buildAlarmRuleForm();
name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]],
entityId: [null],
type: [CalculatedFieldType.ALARM],
debugSettings: [],
configuration: this.fb.group({
type: [CalculatedFieldType.ALARM],
arguments: this.fb.control({}, Validators.required),
propagate: [false],
propagateToOwner: [false],
propagateToTenant: [false],
propagateRelationTypes: [null],
createRules: [null, Validators.required],
clearRule: [null],
}),
});
return form;
} }
updateForm(entity: CalculatedFieldInfo) { updateForm(entity: CalculatedFieldInfo) {
@ -138,19 +120,13 @@ export class AlarmRulesComponent extends EntityComponent<CalculatedFieldsTableEn
} }
onTestScript(expression?: string): Observable<string> { onTestScript(expression?: string): Observable<string> {
const calculatedFieldId = this.entity?.id?.id; return this.cfFormService.testScript(
if (calculatedFieldId) { this.entity?.id?.id,
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) this.entityFormValue(),
.pipe( this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig),
switchMap(event => { this.destroyRef,
const args = event?.arguments ? JSON.parse(event.arguments) : null; expression
return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); );
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression);
} }
updateFormState() { updateFormState() {

4
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-alarm-rule-condition flex flex-col gap-4 min-w-0" [formGroup]="alarmRuleConditionFormGroup"> <div class="tb-alarm-rule-condition flex min-w-0 flex-col gap-4" [formGroup]="alarmRuleConditionFormGroup">
<div class="tb-form-row column-xs"> <div class="tb-form-row column-xs">
<div class="min-w-40 xs:min-w-fit">{{ 'alarm-rule.condition' | translate }}</div> <div class="min-w-40 xs:min-w-fit">{{ 'alarm-rule.condition' | translate }}</div>
<button [disabled]="disabled" <button [disabled]="disabled"
@ -23,7 +23,7 @@
class="tb-alarm-rule-condition-button" class="tb-alarm-rule-condition-button"
mat-stroked-button [color]="conditionSet() && this.filtersArgumentsValid ? 'primary' : 'warn'" mat-stroked-button [color]="conditionSet() && this.filtersArgumentsValid ? 'primary' : 'warn'"
(click)="openFilterDialog($event)"> (click)="openFilterDialog($event)">
<div class="flex items-center gap-2 justify-between"> <div class="flex items-center justify-between gap-2">
<tb-alarm-rule-filter-text [alarmRuleExpression]="alarmRuleConditionFormGroup.get('expression').value" <tb-alarm-rule-filter-text [alarmRuleExpression]="alarmRuleConditionFormGroup.get('expression').value"
[arguments]="arguments" [arguments]="arguments"
[disabled]="disabled" [disabled]="disabled"

16
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts

@ -137,15 +137,19 @@ export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Vali
writeValue(value: AlarmRuleCondition): void { writeValue(value: AlarmRuleCondition): void {
this.modelValue = value; this.modelValue = value;
this.updateConditionInfo(); this.updateConditionInfo();
if (value) { if (value && !this.disabled) {
this.onValidatorChange(); if (this.validate(this.alarmRuleConditionFormGroup)) {
this.onValidatorChange();
}
} }
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.arguments) { if (changes.arguments) {
if (changes.arguments && !changes.arguments.firstChange && this.modelValue) { if (changes.arguments && !changes.arguments.firstChange && this.modelValue && !this.disabled) {
this.onValidatorChange(); if (this.validate(this.alarmRuleConditionFormGroup)) {
this.onValidatorChange();
}
} }
} }
} }
@ -181,7 +185,7 @@ export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Vali
this.filtersArgumentsValid = this.areFilterAndPredicateArgumentsValid(this.modelValue, this.arguments); this.filtersArgumentsValid = this.areFilterAndPredicateArgumentsValid(this.modelValue, this.arguments);
this.schedulerArgumentsValid = this.isScheduleArgumentValid(this.modelValue, Object.keys(this.arguments)); this.schedulerArgumentsValid = this.isScheduleArgumentValid(this.modelValue, Object.keys(this.arguments));
this.onValidatorChange = () => { this.onValidatorChange = () => {
control.updateValueAndValidity({ emitEvent: true }); control.updateValueAndValidity({ emitEvent: !this.disabled });
}; };
return this.conditionSet() && this.filtersArgumentsValid && this.schedulerArgumentsValid ? null : { return this.conditionSet() && this.filtersArgumentsValid && this.schedulerArgumentsValid ? null : {
alarmRuleCondition: { alarmRuleCondition: {
@ -303,7 +307,7 @@ export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Vali
} }
private updateScheduleText() { private updateScheduleText() {
let schedule = this.modelValue?.schedule; const schedule = this.modelValue?.schedule;
this.scheduleText = ''; this.scheduleText = '';
if (isDefinedAndNotNull(schedule)) { if (isDefinedAndNotNull(schedule)) {
if (schedule.dynamicValueArgument) { if (schedule.dynamicValueArgument) {

6
ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts

@ -114,13 +114,13 @@ export class CreateCfAlarmRulesComponent implements ControlValueAccessor, Valida
} }
createAlarmRulesControls.push(this.fb.group({ createAlarmRulesControls.push(this.fb.group({
severity: [severity, Validators.required], severity: [severity, Validators.required],
alarmRule: [createAlarmRule, Validators.required] alarmRule: {value: [createAlarmRule, Validators.required], disabled: this.disabled}
})); }));
}); });
} }
const formArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray; const formArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray;
formArray.clear(); formArray.clear({emitEvent: false});
createAlarmRulesControls.forEach(c => formArray.push(c)); createAlarmRulesControls.forEach(c => formArray.push(c, {emitEvent: false}));
if (this.disabled) { if (this.disabled) {
this.createAlarmRulesFormGroup.disable({emitEvent: false}); this.createAlarmRulesFormGroup.disable({emitEvent: false});
} else { } else {

11
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.html

@ -34,16 +34,6 @@
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">
{{ 'action.delete' | translate }} {{ 'action.delete' | translate }}
</button> </button>
<!-- <div class="flex flex-row xs:flex-col">-->
<!-- <button mat-raised-button-->
<!-- ngxClipboard-->
<!-- (cbOnSuccess)="onAssetProfileIdCopied($event)"-->
<!-- [cbContent]="entity?.id?.id"-->
<!-- [class.!hidden]="isEdit">-->
<!-- <mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>-->
<!-- <span translate>asset-profile.copyId</span>-->
<!-- </button>-->
<!-- </div>-->
</div> </div>
<div class="mat-padding flex flex-1 flex-col" [formGroup]="entityForm"> <div class="mat-padding flex flex-1 flex-col" [formGroup]="entityForm">
<div class="tb-form-panel no-border no-padding"> <div class="tb-form-panel no-border no-padding">
@ -75,6 +65,7 @@
</div> </div>
<tb-entity-select formControlName="entityId" <tb-entity-select formControlName="entityId"
appearance="outline" appearance="outline"
required
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="calculatedFieldsEntityTypeList" [allowedEntityTypes]="calculatedFieldsEntityTypeList"
(entityChanged)="changeEntity($event)"> (entityChanged)="changeEntity($event)">

4
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts

@ -38,7 +38,7 @@ import {
CalculatedFieldsTableEntity CalculatedFieldsTableEntity
} from '@home/components/calculated-fields/calculated-fields-table-config'; } from '@home/components/calculated-fields/calculated-fields-table-config';
import { TenantId } from '@shared/models/id/tenant-id'; import { TenantId } from '@shared/models/id/tenant-id';
import { CalculatedFieldFormService } from '@home/components/calculated-fields/calculated-field-form.service'; import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service';
@Component({ @Component({
selector: 'tb-calculated-field', selector: 'tb-calculated-field',
@ -55,8 +55,8 @@ export class CalculatedFieldComponent extends EntityComponent<CalculatedFieldsTa
disabledConfiguration = false; disabledConfiguration = false;
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
readonly ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId); readonly ownerId = new TenantId(getCurrentAuthUser(this.store).tenantId);
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
readonly EntityType = EntityType; readonly EntityType = EntityType;
readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList; readonly calculatedFieldsEntityTypeList = calculatedFieldsEntityTypeList;
readonly CalculatedFieldType = CalculatedFieldType; readonly CalculatedFieldType = CalculatedFieldType;

1
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html

@ -58,6 +58,7 @@
@if (!data.entityId) { @if (!data.entityId) {
<tb-entity-select formControlName="entityId" <tb-entity-select formControlName="entityId"
appearance="outline" appearance="outline"
required
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="calculatedFieldsEntityTypeList" [allowedEntityTypes]="calculatedFieldsEntityTypeList"
[defaultEntityType]="EntityType.DEVICE_PROFILE" [defaultEntityType]="EntityType.DEVICE_PROFILE"

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts

@ -37,7 +37,7 @@ import { EntityId } from '@shared/models/id/entity-id';
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model'; import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model';
import { deepTrim } from '@core/utils'; import { deepTrim } from '@core/utils';
import { BaseData } from '@shared/models/base-data'; import { BaseData } from '@shared/models/base-data';
import { CalculatedFieldFormService } from '@home/components/calculated-fields/calculated-field-form.service'; import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
export interface CalculatedFieldDialogData { export interface CalculatedFieldDialogData {

8
ui-ngx/src/app/shared/models/alarm-rule.models.ts

@ -21,7 +21,11 @@ import { TimeUnit } from "@shared/models/time/time.models";
import { ComplexOperation, EntityKeyValueType, FilterPredicateType } from "@shared/models/query/query.models"; import { ComplexOperation, EntityKeyValueType, FilterPredicateType } from "@shared/models/query/query.models";
import { EntityType } from "@shared/models/entity-type.models"; import { EntityType } from "@shared/models/entity-type.models";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { CalculatedField, CalculatedFieldArgument } from "@shared/models/calculated-field.models"; import {
CalculatedField,
CalculatedFieldArgument,
CalculatedFieldEventArguments
} from "@shared/models/calculated-field.models";
export const alarmRuleEntityTypeList = [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]; export const alarmRuleEntityTypeList = [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE];
@ -244,7 +248,7 @@ export const alarmRuleDefaultScript =
'// Triggers when temperature is above 20 degrees\n' + '// Triggers when temperature is above 20 degrees\n' +
'return temperature > 20;' 'return temperature > 20;'
export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, expression: string, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>; export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable<string>;
export function checkPredicates(predicates: any[], validSet: Set<string>): boolean { export function checkPredicates(predicates: any[], validSet: Set<string>): boolean {
for (const predicate of predicates) { for (const predicate of predicates) {

Loading…
Cancel
Save