From ecdd49d6725cb92b8184bea2499125f208de9f9f Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 21 Mar 2023 15:03:30 +0200 Subject: [PATCH] UI: Add notification template and rule trigger config for entities limit --- .../notification-center.component.html | 2 +- .../notification-center.component.ts | 4 -- .../rule-notification-dialog.component.html | 38 +++++++++++++++ .../rule-notification-dialog.component.scss | 24 ++++++++++ .../rule-notification-dialog.component.ts | 47 ++++++++++++++++--- .../template-notification-dialog.component.ts | 15 ++++-- .../entity/entity-type-list.component.ts | 2 + .../app/shared/models/notification.models.ts | 24 ++++++++-- .../assets/locale/locale.constant-en_US.json | 5 ++ 9 files changed, 139 insertions(+), 22 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html index b67eec3ff9..9ea951a251 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.html @@ -46,7 +46,7 @@ [notificationType]="entityType.NOTIFICATION_TARGET"> - + diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts index 597c923c30..b78ad2b320 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/notification-center.component.ts @@ -68,10 +68,6 @@ export class NotificationCenterComponent extends PageComponent { .onEntityAction({event: $event, action: this.requestTab.isActive ? 'add' : 'add-without-update', entity: null}); } - public isTenantAdmin(): boolean { - return this.authUser.authority === Authority.TENANT_ADMIN; - } - public isCustomerUser(): boolean { return this.authUser.authority === Authority.CUSTOMER_USER; } diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html index e912a0eab6..0dae65583d 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.html @@ -392,6 +392,44 @@ + + + {{ 'notification.entities-limit-trigger-settings' | translate }} +
+
+ notification.filter + + +
+ +
+ + + + + + +
+
+
+
+
+
+ + notification.description + + +
+
+
+ diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss index a02b2e1f97..7e582d2b7d 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.scss @@ -66,6 +66,30 @@ } } + .limit-slider-container { + .limit-slider-value { + margin-left: 16px; + min-width: 25px; + max-width: 100px; + } + mat-form-field input[type=number] { + text-align: center; + } + } + + @media #{$mat-gt-sm} { + .history-time-input { + min-width: 364px; + } + .limit-slider-container { + > label { + margin-right: 16px; + width: min-content; + max-width: 40%; + } + } + } + ::ng-deep { .mat-mdc-dialog-content { diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts index 9dc829a53b..dd6ad5266c 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/rule-table/rule-notification-dialog.component.ts @@ -34,7 +34,7 @@ import { Router } from '@angular/router'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NotificationService } from '@core/http/notification.service'; -import { EntityType } from '@shared/models/entity-type.models'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; import { deepClone, deepTrim, isDefined } from '@core/utils'; import { Observable, Subject } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; @@ -54,6 +54,10 @@ import { TargetsNotificationDialogData } from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; import { MatButton } from '@angular/material/button'; +import { AuthState } from '@core/auth/auth.models'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { AuthUser } from '@shared/models/user.model'; +import { Authority } from '@shared/models/authority.enum'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -80,9 +84,10 @@ export class RuleNotificationDialogComponent extends alarmCommentTemplateForm: FormGroup; alarmAssignmentTemplateForm: FormGroup; ruleEngineEventsTemplateForm: FormGroup; + entitiesLimitTemplateForm: FormGroup; triggerType = TriggerType; - triggerTypes: TriggerType[] = Object.values(TriggerType); + triggerTypes: TriggerType[]; triggerTypeTranslationMap = TriggerTypeTranslationMap; alarmSearchStatuses = [ @@ -106,7 +111,7 @@ export class RuleNotificationDialogComponent extends componentLifecycleEventTranslationMap = ComponentLifecycleEventTranslationMap; entityType = EntityType; - entityTypes: EntityType[] = Object.values(EntityType); + entityTypes = Array.from(entityTypeTranslations.keys()).filter(type => !!this.entityType[type]); isAdd = true; selectedIndex = 0; @@ -118,6 +123,8 @@ export class RuleNotificationDialogComponent extends private readonly ruleNotification: NotificationRule; private triggerTypeFormsMap: Map; + private authState: AuthState = getCurrentAuthState(this.store); + private authUser: AuthUser = this.authState.authUser; constructor(protected store: Store, protected router: Router, @@ -130,6 +137,8 @@ export class RuleNotificationDialogComponent extends private dialog: MatDialog) { super(store, router, dialogRef); + this.triggerTypes = this.allowTriggerTypes(); + if (isDefined(data.isAdd)) { this.isAdd = data.isAdd; } @@ -140,10 +149,10 @@ export class RuleNotificationDialogComponent extends this.ruleNotificationForm = this.fb.group({ name: [null, Validators.required], templateId: [null, Validators.required], - triggerType: [TriggerType.ALARM, Validators.required], + triggerType: [this.isSysAdmin() ? TriggerType.ENTITIES_LIMIT : TriggerType.ALARM, Validators.required], recipientsConfig: this.fb.group({ - targets: [{value: null, disabled: true}, Validators.required], - escalationTable: [null, Validators.required] + targets: [{value: null, disabled: !this.isSysAdmin()}, Validators.required], + escalationTable: [{value: null, disabled: this.isSysAdmin()}, Validators.required] }), triggerConfig: [null], additionalConfig: this.fb.group({ @@ -243,13 +252,21 @@ export class RuleNotificationDialogComponent extends }) }); + this.entitiesLimitTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + entityTypes: [], + threshold: [.8, [Validators.min(0), Validators.max(1)]] + }) + }); + this.triggerTypeFormsMap = new Map([ [TriggerType.ALARM, this.alarmTemplateForm], [TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm], [TriggerType.DEVICE_INACTIVITY, this.deviceInactivityTemplateForm], [TriggerType.ENTITY_ACTION, this.entityActionTemplateForm], [TriggerType.ALARM_ASSIGNMENT, this.alarmAssignmentTemplateForm], - [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm] + [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm], + [TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm] ]); if (data.isAdd || data.isCopy) { @@ -368,4 +385,20 @@ export class RuleNotificationDialogComponent extends countRecipientsChainConfig(): number { return Object.keys(this.ruleNotificationForm.get('recipientsConfig.escalationTable').value ?? {}).length; } + + formatLabel(value: number): string { + const formatValue = (value * 100).toFixed(); + return `${formatValue}%`; + } + + private isSysAdmin(): boolean { + return this.authUser.authority === Authority.SYS_ADMIN; + } + + private allowTriggerTypes(): TriggerType[] { + if (this.isSysAdmin()) { + return [TriggerType.ENTITIES_LIMIT]; + } + return Object.values(TriggerType).filter(type => type !== TriggerType.ENTITIES_LIMIT); + } } diff --git a/ui-ngx/src/app/modules/home/pages/notification-center/template-table/template-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification-center/template-table/template-notification-dialog.component.ts index 18908dab65..571e7ea932 100644 --- a/ui-ngx/src/app/modules/home/pages/notification-center/template-table/template-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification-center/template-table/template-notification-dialog.component.ts @@ -57,7 +57,7 @@ export class TemplateNotificationDialogComponent dialogTitle = 'notification.edit-notification-template'; - notificationTypes = Object.keys(NotificationType) as NotificationType[]; + notificationTypes: NotificationType[]; selectedIndex = 0; hideSelectType = false; @@ -76,6 +76,8 @@ export class TemplateNotificationDialogComponent private translate: TranslateService) { super(store, router, dialogRef, fb); + this.notificationTypes = this.allowNotificationType(); + this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); @@ -84,10 +86,6 @@ export class TemplateNotificationDialogComponent this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvents: false}); } - if (this.isSysAdmin()) { - this.hideSelectType = true; - } - if (data.isAdd || data.isCopy) { this.dialogTitle = 'notification.add-notification-template'; } @@ -175,4 +173,11 @@ export class TemplateNotificationDialogComponent private isSysAdmin(): boolean { return this.authUser.authority === Authority.SYS_ADMIN; } + + private allowNotificationType(): NotificationType[] { + if (this.isSysAdmin()) { + return [NotificationType.GENERAL, NotificationType.ENTITIES_LIMIT]; + } + return Object.values(NotificationType).filter(type => type !== NotificationType.ENTITIES_LIMIT); + } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts index d0782fc7ce..e568abcc37 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-type-list.component.ts @@ -27,6 +27,7 @@ import { MatAutocomplete } from '@angular/material/autocomplete'; import { MatChipGrid } from '@angular/material/chips'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { SubscriptSizing } from '@angular/material/form-field'; +import { coerceBoolean } from '@shared/decorators/coerce-boolean'; interface EntityTypeInfo { name: string; @@ -74,6 +75,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af allowedEntityTypes: Array; @Input() + @coerceBoolean() ignoreAuthorityFilter: boolean; @ViewChild('entityTypeInput') entityTypeInput: ElementRef; diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 7753eef59c..72a32989e5 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -111,7 +111,7 @@ export interface NotificationRule extends Omit, 'la export type NotificationRuleTriggerConfig = Partial; + RuleEngineLifecycleEventNotificationRuleTriggerConfig & EntitiesLimitNotificationRuleTriggerConfig>; export interface AlarmNotificationRuleTriggerConfig { alarmTypes?: Array; @@ -160,6 +160,11 @@ export interface RuleEngineLifecycleEventNotificationRuleTriggerConfig { onlyRuleNodeLifecycleFailures: ComponentLifecycleEvent; } +export interface EntitiesLimitNotificationRuleTriggerConfig { + entityTypes: EntityType[]; + threshold: number; +} + export enum ComponentLifecycleEvent { STARTED = 'STARTED', UPDATED = 'UPDATED', @@ -402,7 +407,8 @@ export enum NotificationType { ENTITY_ACTION = 'ENTITY_ACTION', ALARM_COMMENT = 'ALARM_COMMENT', ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT', - RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT' + RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT', + ENTITIES_LIMIT = 'ENTITIES_LIMIT' } export const NotificationTypeIcons = new Map([ @@ -411,7 +417,8 @@ export const NotificationTypeIcons = new Map([ [NotificationType.ENTITY_ACTION, 'devices'], [NotificationType.ALARM_COMMENT, 'comment'], [NotificationType.ALARM_ASSIGNMENT, 'assignment_turned_in'], - [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'] + [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'], + [NotificationType.ENTITIES_LIMIT, 'data_thresholding'], ]); export const AlarmSeverityNotificationColors = new Map( @@ -481,7 +488,12 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -500,4 +513,5 @@ export const TriggerTypeTranslationMap = new Map([ [TriggerType.ALARM_COMMENT, 'notification.trigger.alarm-comment'], [TriggerType.ALARM_ASSIGNMENT, 'notification.trigger.alarm-assignment'], [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'notification.trigger.rule-engine-lifecycle-event'], + [TriggerType.ENTITIES_LIMIT, 'notification.trigger.entities-limit'], ]); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 31bc7f4062..faa938567f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2779,6 +2779,7 @@ "edit-rule": "Edit rule", "edit-template": "Edit template", "email-settings": "Email settings", + "entities-limit-trigger-settings": "Entities limit trigger settings", "entity-action-trigger-settings": "Entity action trigger settings", "entity-type": "Entity type", "failed-send": "Failed send", @@ -2890,6 +2891,7 @@ "alarm-assignment": "Available params: ${assigneeFirstName}, ${assigneeLastName}, ${assigneeEmail}, ${assigneeFirstName}, ${userName}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}", "alarm-comment": "Available params: ${comment}, ${alarmType}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}", "device-inactivity": "Available params: ${deviceName}, ${deviceLabel}, ${deviceType}, ${deviceId}", + "entities-limit": "Available params: ${entityType}, ${currentCount}, ${limit}", "entity-action": "Available params: ${actionType}, ${entityType}, ${entityName}, ${entityId}, ${originatorUserName}, ${originatorUserId}", "general": "Available params: ${recipientEmail}, ${recipientFirstName}, ${recipientLastName}", "rule-engine-lifecycle-event": "Available params: ${ruleChainName}, ${componentName}, ${eventType}, ${error}, ${ruleChainId}, ${componentId}" @@ -2901,6 +2903,7 @@ "alarm-assignment": "Alarm assignment", "alarm-comment": "Alarm comment", "device-inactivity": "Device inactivity", + "entities-limit": "Entities limit", "entity-action": "Entity action", "general": "General", "rule-engine-lifecycle-event": "Rule engine lifecycle event" @@ -2910,11 +2913,13 @@ "tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles", "time": "Time", "track-rule-node-events": "Track rule node events", + "threshold": "Threshold", "trigger": { "alarm": "Alarm", "alarm-assignment": "Alarm assignment", "alarm-comment": "Alarm comment", "device-inactivity": "Device inactivity", + "entities-limit": "Entities limit", "entity-action": "Entity action", "trigger": "Trigger", "trigger-required": "Trigger is required",