From 0a58ff5e771fa1cea99dec9ddbf8ce9178ed8359 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 26 Dec 2025 14:17:19 +0200 Subject: [PATCH] UI: Improved alarm rules detail panel/page --- .../calculated-field-form.service.ts | 22 ++++++- .../alarm-rule-dialog.component.html | 33 +++------- .../alarm-rule-dialog.component.ts | 63 +++++-------------- .../alarm-rules/alarm-rules-table-config.ts | 23 ++++++- .../alarm-rules/alarm-rules.component.html | 1 + .../alarm-rules/alarm-rules.component.ts | 60 ++++++------------ .../cf-alarm-rule-condition.component.html | 4 +- .../cf-alarm-rule-condition.component.ts | 16 +++-- .../create-cf-alarm-rules.component.ts | 6 +- .../calculated-field.component.html | 11 +--- .../calculated-field.component.ts | 4 +- .../calculated-field-dialog.component.html | 1 + .../calculated-field-dialog.component.ts | 2 +- .../app/shared/models/alarm-rule.models.ts | 8 ++- 14 files changed, 109 insertions(+), 145 deletions(-) rename ui-ngx/src/app/{modules/home/components/calculated-fields => core/services}/calculated-field-form.service.ts (84%) diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts b/ui-ngx/src/app/core/services/calculated-field-form.service.ts similarity index 84% rename from ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts rename to ui-ngx/src/app/core/services/calculated-field-form.service.ts index 6ef02f0cf8..cc3e495c17 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field-form.service.ts +++ b/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 { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { EntityType } from '@shared/models/entity-type.models'; @Injectable({ providedIn: 'root' }) export class CalculatedFieldFormService { @@ -40,13 +39,32 @@ export class CalculatedFieldFormService { buildForm(): FormGroup { return this.fb.group({ 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], debugSettings: [], configuration: this.fb.control({} 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 { form.get('type').valueChanges.pipe( pairwise(), diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html index 869b707d98..1b116d0696 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html @@ -58,31 +58,14 @@ /> @if (!data.entityId) { -
- - - @if (fieldFormGroup.get('entityId.entityType').value) { - - } -
+ + } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts index a84aa52693..854f7fb1a0 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts @@ -14,16 +14,15 @@ /// 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 { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; import { CalculatedField, CalculatedFieldArgument, CalculatedFieldType } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; -import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ScriptLanguage } from '@shared/models/rule-node.models'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; @@ -39,13 +38,11 @@ import { } from "@shared/models/alarm-rule.models"; import { deepTrim } from "@core/utils"; import { combineLatest, Observable } from "rxjs"; -import { debounceTime, startWith, switchMap } 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 { debounceTime, startWith } from "rxjs/operators"; import { RelationTypes } from "@shared/models/relation.models"; import { StringItemsOption } from "@shared/components/string-items-list.component"; import { BaseData } from "@shared/models/base-data"; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; export interface AlarmRuleDialogData { value?: CalculatedField; @@ -67,24 +64,7 @@ export interface AlarmRuleDialogData { }) export class AlarmRuleDialogComponent extends DialogComponent { - fieldFormGroup = this.fb.group({ - name: ['', [Validators.required, Validators.pattern(oneSpaceInsideRegex), Validators.maxLength(255)]], - type: [CalculatedFieldType.ALARM], - debugSettings: [], - entityId: this.fb.group({ - entityType: this.fb.control(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], - }), - }); + fieldFormGroup: FormGroup ; additionalDebugActionConfig = this.data.value?.id ? { ...this.data.additionalDebugActionConfig, @@ -105,18 +85,15 @@ export class AlarmRuleDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: AlarmRuleDialogData, protected dialogRef: MatDialogRef, private calculatedFieldsService: CalculatedFieldsService, - private entityService: EntityService, private destroyRef: DestroyRef, - private fb: FormBuilder) { + private cfFormService: CalculatedFieldFormService) { super(store, router, dialogRef); + this.fieldFormGroup = this.cfFormService.buildAlarmRuleForm(); this.applyDialogData(); this.updateRulesValidators(); @@ -132,7 +109,7 @@ export class AlarmRuleDialogComponent extends DialogComponent { - const calculatedFieldId = this.data.value?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, args, false); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, null, false); + return this.cfFormService.testScript( + this.data.value?.id?.id, + this.fromGroupValue, + this.data.getTestScriptDialogFn, + this.destroyRef, + expression + ); } private updateRulesValidators(): void { @@ -250,5 +220,4 @@ export class AlarmRuleDialogComponent extends DialogComponent): void { this.entityName = entity.name; } - } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts index b0ac04aacb..ad718408e4 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts +++ b/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 { takeUntilDestroyed } from '@angular/core/rxjs-interop'; 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 { ArgumentEntityType, ArgumentType, @@ -186,7 +186,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig true, - onAction: ($event, entity) => this.openDebugEventsDialog($event, entity), + onAction: ($event, entity) => + this.pageMode ? this.openDebugTab($event, entity) : this.openDebugEventsDialog($event, entity), }, { name: '', @@ -308,6 +309,22 @@ export class AlarmRulesTableConfig extends EntityTableConfig 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 { $event?.stopPropagation(); this.importExportService.exportCalculatedField(calculatedField.id.id); @@ -361,7 +378,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig this.updateData()); } - private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, expression: string, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable { + private getTestScriptDialog(calculatedField: AlarmRuleTableEntity, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable { if (calculatedField.type === CalculatedFieldType.ALARM) { const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => { const type = calculatedField.configuration.arguments[key].refEntityKey.type; diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html index e304a78732..28695d9283 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.html @@ -65,6 +65,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts index 200cf09741..fb92f6c4fd 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules.component.ts @@ -14,11 +14,11 @@ /// 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 { AppState } from '@core/core.state'; -import { EntityComponent } from '../../components/entity/entity.component'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { EntityComponent } from '@home/components/entity/entity.component'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { EntityType } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { @@ -28,13 +28,9 @@ import { calculatedFieldsEntityTypeList, CalculatedFieldType } from '@shared/models/calculated-field.models'; -import { oneSpaceInsideRegex } from '@shared/models/regex.constants'; 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 { Observable } from 'rxjs'; -import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { CalculatedFieldsTableConfig, @@ -44,6 +40,7 @@ import { TenantId } from '@shared/models/id/tenant-id'; import { StringItemsOption } from '@shared/components/string-items-list.component'; import { RelationTypes } from '@shared/models/relation.models'; import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from '@shared/models/alarm-rule.models'; +import { CalculatedFieldFormService } from '@core/services/calculated-field-form.service'; @Component({ selector: 'tb-alarm-rules', @@ -58,20 +55,21 @@ export class AlarmRulesComponent extends EntityComponent, protected translate: TranslateService, @Inject('entity') protected entityValue: CalculatedFieldInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: CalculatedFieldsTableConfig, protected fb: FormBuilder, - protected cd: ChangeDetectorRef, - private destroyRef: DestroyRef, - private calculatedFieldsService: CalculatedFieldsService) { + protected cd: ChangeDetectorRef) { super(store, fb, entityValue, entitiesTableConfigValue, cd); } @@ -107,24 +105,8 @@ export class AlarmRulesComponent extends EntityComponent { - const calculatedFieldId = this.entity?.id?.id; - if (calculatedFieldId) { - return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true}) - .pipe( - switchMap(event => { - const args = event?.arguments ? JSON.parse(event.arguments) : null; - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), args, false, expression); - }), - takeUntilDestroyed(this.destroyRef) - ) - } - - return this.entitiesTableConfig.getTestScriptDialog(this.entityFormValue(), null, false, expression); + return this.cfFormService.testScript( + this.entity?.id?.id, + this.entityFormValue(), + this.entitiesTableConfig.getTestScriptDialog.bind(this.entitiesTableConfig), + this.destroyRef, + expression + ); } updateFormState() { diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html index 22fc01f9de..99ee0f9634 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
{{ 'alarm-rule.condition' | translate }}
- - - - - - - - - -
@@ -75,6 +65,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts index 01cc53609d..65595fe9b8 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-field.component.ts @@ -38,7 +38,7 @@ import { CalculatedFieldsTableEntity } from '@home/components/calculated-fields/calculated-fields-table-config'; 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({ selector: 'tb-calculated-field', @@ -55,8 +55,8 @@ export class CalculatedFieldComponent extends EntityComponent 20;' -export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, expression: string, argumentsObj?: Record, closeAllOnSave?: boolean) => Observable; +export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit?: boolean, expression?: string) => Observable; export function checkPredicates(predicates: any[], validSet: Set): boolean { for (const predicate of predicates) {