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 48f4e8a5af..de6b0ed243 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 @@ -56,23 +56,36 @@ /> @if (!data.entityId) { - - + @if (createNew) { + + + } @else { + + + } } {{ 'calculated-fields.arguments' | translate }} { - @ViewChild('entitySelect') entitySelect!: EntitySelectComponent; + @ViewChild('entitySelect') entitySelect!: EntitySelectComponent | EntityTypeListComponent; fieldFormGroup: FormGroup ; @@ -96,6 +97,7 @@ export class AlarmRuleDialogComponent extends DialogComponent, protected router: Router, @@ -120,6 +122,7 @@ export class AlarmRuleDialogComponent extends DialogComponent { - this.disabledArguments = !entityId || !name?.length; + this.disabledArguments = Array.isArray(entityId) ? !entityId.length : !entityId || !name?.length; const argsControl = this.fieldFormGroup.get('configuration.arguments')!; if (this.disabledArguments) { argsControl.disable({ emitEvent: false }); @@ -174,21 +177,34 @@ 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; + const entityIds = Array.isArray(this.fieldFormGroup.value.entityId) + ? this.fieldFormGroup.value.entityId + : [this.fieldFormGroup.value.entityId ?? this.data.entityId]; - this.alarmRulesService.saveAlarmRule(alarmRule) + const requests$ = entityIds.map((entityId: EntityId) => { + const alarmRule = { ...(this.data.value ?? {}), ...this.fromGroupValue, entityId }; + alarmRule.configuration.type = CalculatedFieldType.ALARM; + return this.alarmRulesService.saveAlarmRule(alarmRule); + }); + + forkJoin(requests$) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ - next: calculatedField => this.dialogRef.close(calculatedField), + next: (calculatedFields: CalculatedFieldAlarmRule) => this.dialogRef.close(calculatedFields), error: () => this.isLoading = false }); } else { this.fieldFormGroup.get('name').markAsTouched(); - this.entitySelect?.entityAutocompleteMarkAsTouched(); + if (this.entitySelect instanceof EntitySelectComponent) { + this.entitySelect?.entityAutocompleteMarkAsTouched(); + } } } + get selectedEntityId(): EntityId { + return Array.isArray(this.fieldFormGroup.get('entityId').value) ? this.fieldFormGroup.get('entityId').value : this.fieldFormGroup.get('entityId').value; + } + private applyDialogData(): void { const { configuration = {}, type = CalculatedFieldType.ALARM, debugSettings = { failuresEnabled: true, allEnabled: true }, entityId = this.data.entityId, ...value } = this.data.value ?? {}; this.fieldFormGroup.patchValue({ configuration, type, debugSettings, entityId, ...value }, {emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts index c4d4552999..38688594da 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts @@ -66,7 +66,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI @Input() buttonTitle: string; @Input() argument: CalculatedFieldArgumentValue; - @Input() entityId: EntityId; + @Input() entityId: EntityId | EntityId[]; @Input() tenantId: string; @Input() entityName: string; @Input() ownerId: EntityId; @@ -154,7 +154,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI get enableAttributeScopeSelection(): boolean { return this.entityType === ArgumentEntityType.Device || (this.entityType === ArgumentEntityType.Current - && (this.entityId.entityType === EntityType.DEVICE || this.entityId.entityType === EntityType.DEVICE_PROFILE)) + && (this.entityIdType === EntityType.DEVICE || this.entityIdType === EntityType.DEVICE_PROFILE)) } ngOnInit(): void { @@ -221,7 +221,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI private updatedArgumentType(): void { let argumentType = ArgumentEntityType.Current; if (this.argument.refDynamicSourceConfiguration?.type === ArgumentEntityType.Owner) { - this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE); + this.enableAutocomplete = (this.entityIdType === EntityType.DEVICE_PROFILE || this.entityIdType === EntityType.ASSET_PROFILE); argumentType = ArgumentEntityType.Owner; } else if (this.argument.refEntityId?.entityType) { argumentType = this.argument.refEntityId.entityType; @@ -294,7 +294,7 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI .pipe(distinctUntilChanged(), takeUntilDestroyed()) .subscribe(type => { this.argumentFormGroup.get('refEntityId').setValue(null); - this.enableAutocomplete = (this.entityId.entityType === EntityType.DEVICE_PROFILE || this.entityId.entityType === EntityType.ASSET_PROFILE) && type === ArgumentEntityType.Owner; + this.enableAutocomplete = (this.entityIdType === EntityType.DEVICE_PROFILE || this.entityIdType === EntityType.ASSET_PROFILE) && type === ArgumentEntityType.Owner; this.updatedRefEntityIdState(type); if (!this.enableAttributeScopeSelection) { this.refEntityKeyFormGroup.get('scope').setValue(AttributeScope.SERVER_SCOPE); @@ -341,4 +341,8 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI this.entityNameSubject.next(null); } } + + get entityIdType() { + return Array.isArray(this.entityId) ? this.entityId[0].entityType : this.entityId.entityType; + } } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts index 14cfde23fe..34f753d59c 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-arguments-table.component.ts @@ -84,7 +84,7 @@ import { BaseData } from '@shared/models/base-data'; }) export class CalculatedFieldArgumentsTableComponent implements ControlValueAccessor, Validator, OnChanges, AfterViewInit { - @Input() entityId: EntityId; + @Input() entityId: EntityId | EntityId[]; @Input() tenantId: string; @Input() entityName: string; @Input() ownerId: EntityId; diff --git a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html index 034f118b4d..8ddf26c8b1 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.html @@ -21,6 +21,7 @@ [inlineField]="inlineField" [class.flex-1]="inlineField && !modelValue.entityType" style="min-width: 100px; padding-right: 8px;" + [label]="entityTypeLabel ?? null" [showLabel]="true" [required]="required" subscriptSizing="dynamic" @@ -35,6 +36,7 @@ @if (modelValue.entityType) { 0" [required]="required" diff --git a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts index 1bf532af2e..20ccdfe5d8 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts @@ -16,7 +16,7 @@ import { booleanAttribute, Component, DestroyRef, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; +import { AliasEntityType, EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { EntityService } from '@core/http/entity.service'; import { EntityId } from '@shared/models/id/entity-id'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -76,8 +76,16 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit { @Input() appearance: MatFormFieldAppearance = 'fill'; + @Input() + entityTypeLabel: string; + + @Input({transform: booleanAttribute}) + displayEntityLabel: boolean; + displayEntityTypeSelect: boolean; + readonly entityTypeTranslations = entityTypeTranslations; + private defaultEntityType: EntityType | AliasEntityType = null; private propagateChange = (_v: any) => { }; diff --git a/ui-ngx/src/app/shared/models/calculated-field.models.ts b/ui-ngx/src/app/shared/models/calculated-field.models.ts index 552d2a3a95..7791dfaf0f 100644 --- a/ui-ngx/src/app/shared/models/calculated-field.models.ts +++ b/ui-ngx/src/app/shared/models/calculated-field.models.ts @@ -33,6 +33,7 @@ import { EntitySearchDirection } from '@shared/models/relation.models'; import { AbstractControl, FormControl, ValidationErrors, ValidatorFn } from '@angular/forms'; import { AlarmRule } from "@shared/models/alarm-rule.models"; import { AlarmSeverity } from "@shared/models/alarm.models"; +import { EntityFilter } from '@shared/models/query/query.models'; export const FORBIDDEN_NAMES = ['ctx', 'e', 'pi']; @@ -527,7 +528,14 @@ export const ArgumentEntityTypeParamsMap =new Map { +export const getCalculatedFieldCurrentEntityFilter = (entityName: string, entityId: EntityId | EntityId[]): EntityFilter => { + if (Array.isArray(entityId)) { + return { + type: AliasFilterType.entityList, + entityType: entityId[0].entityType as EntityType, + entityList: entityId.map(value => value.id) + } + } switch (entityId?.entityType) { case EntityType.ASSET_PROFILE: return {