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 52ff7d15cb..d9bff11bf0 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 @@ -19,7 +19,9 @@

{{ 'alarm-rule.alarm-rule' | translate}}

+
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 ac4d0f9ed6..a84aa52693 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 @@ -30,21 +30,22 @@ import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { EntityId } from '@shared/models/id/entity-id'; import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model'; import { COMMA, ENTER, SEMICOLON } from "@angular/cdk/keycodes"; -import { MatChipInputEvent } from "@angular/material/chips"; import { AlarmRule, AlarmRuleConditionType, + alarmRuleEntityTypeList, AlarmRuleExpressionType, AlarmRuleTestScriptFn } from "@shared/models/alarm-rule.models"; import { deepTrim } from "@core/utils"; -import { Observable } from "rxjs"; -import { switchMap } from "rxjs/operators"; +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 { RelationTypes } from "@shared/models/relation.models"; import { StringItemsOption } from "@shared/components/string-items-list.component"; +import { BaseData } from "@shared/models/base-data"; export interface AlarmRuleDialogData { value?: CalculatedField; @@ -92,7 +93,7 @@ export class AlarmRuleDialogComponent extends DialogComponent { this.updateRulesValidators(); }); - if (!this.entityName) { - this.fieldFormGroup.get('entityId.id').valueChanges.pipe( - takeUntilDestroyed(this.destroyRef) - ).subscribe((entityId) => { - if (entityId && (this.fieldFormGroup.get('entityId.entityType').value === EntityType.DEVICE_PROFILE || - this.fieldFormGroup.get('entityId.entityType').value === EntityType.ASSET_PROFILE)) { - this.entityService.getEntity(this.fieldFormGroup.get('entityId.entityType').value as EntityType, entityId, {ignoreLoading: true, ignoreErrors: true}).subscribe( - value => { - this.entityName = value.name; - } - ) + if (!this.data.entityId) { + combineLatest([ + this.fieldFormGroup.get('entityId.id')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('entityId.id')!.value)), + this.fieldFormGroup.get('name')!.valueChanges.pipe(startWith(this.fieldFormGroup.get('name')!.value)) + ]).pipe( + debounceTime(50), + takeUntilDestroyed() + ).subscribe(([entityId, name]) => { + this.disabledArguments = !entityId || !name?.length; + const argsControl = this.fieldFormGroup.get('configuration.arguments')!; + if (this.disabledArguments) { + argsControl.disable({ emitEvent: false }); + } else { + argsControl.enable({ emitEvent: false }); } }); } @@ -174,12 +184,16 @@ export class AlarmRuleDialogComponent extends DialogComponent 0) { + this.isLoading = true; const alarmRule = { entityId: this.data.entityId, ...(this.data.value ?? {}), ...this.fromGroupValue}; alarmRule.configuration.type = CalculatedFieldType.ALARM; this.calculatedFieldsService.saveCalculatedField(alarmRule) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(calculatedField => this.dialogRef.close(calculatedField)); + .subscribe({ + next: calculatedField => this.dialogRef.close(calculatedField), + error: () => this.isLoading = false + }); } else { this.fieldFormGroup.get('name').markAsTouched(); this.entityTypeSelect?.markAsTouched(); @@ -215,7 +229,7 @@ export class AlarmRuleDialogComponent extends DialogComponent): void { + this.entityName = entity.name; + } + } diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts index a19fc688a8..da91aafb07 100644 --- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-filter-config.component.ts @@ -39,7 +39,7 @@ import { fromEvent, Subscription } from 'rxjs'; import { POSITION_MAP } from '@shared/models/overlay.models'; import { UtilsService } from '@core/services/utils.service'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { AlarmRuleFilterConfig } from "@shared/models/alarm-rule.models"; +import { alarmRuleEntityTypeList, AlarmRuleFilterConfig } from "@shared/models/alarm-rule.models"; export const ALARM_FILTER_CONFIG_DATA = new InjectionToken('AlarmRuleFilterConfigData'); @@ -100,7 +100,7 @@ export class AlarmRuleFilterConfigComponent implements OnInit, ControlValueAcces entityType = EntityType; - listEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]; + listEntityTypes = alarmRuleEntityTypeList; entityTypeTranslations = entityTypeTranslations; private alarmRuleFilterConfig: AlarmRuleFilterConfig; 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 b242fd9701..3784bc09bf 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 @@ -72,7 +72,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig { readonly tenantId = getCurrentAuthUser(this.store).tenantId; additionalDebugActionConfig = { title: this.translate.instant('calculated-fields.see-debug-events'), - action: (calculatedField: CalculatedField) => this.openDebugEventsDialog.call(this, calculatedField), + action: (calculatedField: CalculatedField) => this.openDebugEventsDialog.call(this, null, calculatedField), }; alarmRuleFilterConfig: CalculatedFieldsQuery; @@ -95,6 +95,13 @@ export class AlarmRulesTableConfig extends EntityTableConfig { super(); if (this.pageMode) { this.headerComponent = AlarmRuleTableHeaderComponent; + + this.rowPointer = true; + + this.handleRowClick = ($event, model) => { + this.editCalculatedField($event, model); + return true; + }; } this.tableTitle = this.pageMode ? '' : this.translate.instant('alarm-rule.alarm-rules'); this.detailsPanelEnabled = false; @@ -153,7 +160,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig { name: this.translate.instant('alarm-rule.copy'), icon: 'content_copy', isEnabled: () => true, - onAction: ($event, entity) => this.copyCalculatedField(entity) + onAction: ($event, entity) => this.copyCalculatedField($event, entity) } ); this.cellActionDescriptors.push( @@ -161,13 +168,13 @@ export class AlarmRulesTableConfig extends EntityTableConfig { name: this.translate.instant('action.export'), icon: 'file_download', isEnabled: () => true, - onAction: (event$, entity) => this.exportAlarmRule(event$, entity), + onAction: ($event, entity) => this.exportAlarmRule($event, entity), }, { name: this.translate.instant('entity-view.events'), icon: 'mdi:clipboard-text-clock', isEnabled: () => true, - onAction: (_, entity) => this.openDebugEventsDialog(entity), + onAction: ($event, entity) => this.openDebugEventsDialog($event, entity), }, { name: '', @@ -181,7 +188,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig { name: this.translate.instant('action.edit'), icon: 'edit', isEnabled: () => true, - onAction: (_, entity) => this.editCalculatedField(entity), + onAction: ($event, entity) => this.editCalculatedField($event, entity), } ); } @@ -193,14 +200,12 @@ export class AlarmRulesTableConfig extends EntityTableConfig { } onOpenDebugConfig($event: Event, calculatedField: CalculatedField): void { + $event?.stopPropagation(); const { debugSettings = {}, id } = calculatedField; const additionalActionConfig = { ...this.additionalDebugActionConfig, - action: () => this.openDebugEventsDialog(calculatedField) + action: () => this.openDebugEventsDialog($event, calculatedField) }; - if ($event) { - $event.stopPropagation(); - } const { viewContainerRef, renderer } = this.entityDebugSettingsService; if (!viewContainerRef || !renderer) { @@ -219,7 +224,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig { }, $event.target as Element); } - private editCalculatedField(calculatedField: CalculatedField, isDirty = false): void { + private editCalculatedField($event: Event, calculatedField: CalculatedField, isDirty = false): void { + $event?.stopPropagation(); this.getCalculatedAlarmDialog(calculatedField, 'action.apply', isDirty) .subscribe((res) => { if (res) { @@ -228,7 +234,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig { }); } - private copyCalculatedField(calculatedField: CalculatedField, isDirty = false): void { + private copyCalculatedField($event: Event, calculatedField: CalculatedField, isDirty = false): void { + $event?.stopPropagation(); const copyCalculatedAlarmRule = deepClone(calculatedField); if (this.pageMode) { copyCalculatedAlarmRule.entityId = null; @@ -243,13 +250,14 @@ export class AlarmRulesTableConfig extends EntityTableConfig { } private getCalculatedAlarmDialog(value?: CalculatedField, buttonTitle = 'action.add', isDirty = false): Observable { + const entityId = this.entityId || value?.entityId; return this.dialog.open(AlarmRuleDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { value, buttonTitle, - entityId: this.entityId, + entityId, tenantId: this.tenantId, entityName: this.entityName, ownerId: this.ownerId ?? {entityType: EntityType.TENANT, id: this.tenantId}, @@ -263,7 +271,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig { .pipe(filter(Boolean)); } - private openDebugEventsDialog(calculatedField: CalculatedField): void { + private openDebugEventsDialog($event: Event, calculatedField: CalculatedField): void { + $event?.stopPropagation(); this.dialog.open(EventsDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], @@ -282,9 +291,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig { } private exportAlarmRule($event: Event, calculatedField: CalculatedField): void { - if ($event) { - $event.stopPropagation(); - } + $event?.stopPropagation(); this.importExportService.exportCalculatedField(calculatedField.id.id); } @@ -361,7 +368,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig { filter(Boolean), tap(expression => { if (openCalculatedFieldEdit) { - this.editCalculatedField({ + this.editCalculatedField(null, { entityId: this.entityId, ...calculatedField, configuration: {...calculatedField.configuration, expression} as any }, true) diff --git a/ui-ngx/src/app/shared/models/alarm-rule.models.ts b/ui-ngx/src/app/shared/models/alarm-rule.models.ts index 3accaed95c..df137313be 100644 --- a/ui-ngx/src/app/shared/models/alarm-rule.models.ts +++ b/ui-ngx/src/app/shared/models/alarm-rule.models.ts @@ -23,6 +23,8 @@ import { EntityType } from "@shared/models/entity-type.models"; import { Observable } from "rxjs"; import { CalculatedField, CalculatedFieldArgument } from "@shared/models/calculated-field.models"; +export const alarmRuleEntityTypeList = [EntityType.DEVICE, EntityType.ASSET, EntityType.CUSTOMER, EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE]; + export enum AlarmRuleScheduleType { ANY_TIME = 'ANY_TIME', SPECIFIC_TIME = 'SPECIFIC_TIME', diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 2788aa52df..9253c0004a 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -219,7 +219,8 @@ export const HelpLinks = { aiModels: `${helpBaseUrl}/docs${docPlatformPrefix}/samples/analytics/ai-models/`, apiKeys: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/api-keys`, timewindowSettings: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/dashboards/#time-window`, - trendzSettings: `${helpBaseUrl}/docs/trendz/` + trendzSettings: `${helpBaseUrl}/docs/trendz/`, + alarmRules: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/alarm-rules/`, } }; /* eslint-enable max-len */