UI: Norification rule
This commit is contained in:
parent
0e83c8ac74
commit
7ea0c23c65
@ -24,7 +24,7 @@ import { Direction } from '@shared/models/page/sort-order';
|
|||||||
import {
|
import {
|
||||||
NotificationRule,
|
NotificationRule,
|
||||||
NotificationTarget,
|
NotificationTarget,
|
||||||
NotificationTargetConfigTypeTranslateMap
|
NotificationTargetConfigTypeTranslateMap, TriggerTypeTranslationMap
|
||||||
} from '@shared/models/notification.models';
|
} from '@shared/models/notification.models';
|
||||||
import { NotificationService } from '@core/http/notification.service';
|
import { NotificationService } from '@core/http/notification.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -62,8 +62,7 @@ export class RuleTableConfig extends EntityTableConfig<NotificationRule> {
|
|||||||
this.deleteEntityContent = () => this.translate.instant('notification.delete-rule-text');
|
this.deleteEntityContent = () => this.translate.instant('notification.delete-rule-text');
|
||||||
this.deleteEntity = id => this.notificationService.deleteNotificationRule(id.id);
|
this.deleteEntity = id => this.notificationService.deleteNotificationRule(id.id);
|
||||||
|
|
||||||
this.cellActionDescriptors = this.configureCellActions();
|
// this.cellActionDescriptors = this.configureCellActions();
|
||||||
|
|
||||||
this.headerComponent = RuleTableHeaderComponent;
|
this.headerComponent = RuleTableHeaderComponent;
|
||||||
this.onEntityAction = action => this.onTargetAction(action);
|
this.onEntityAction = action => this.onTargetAction(action);
|
||||||
|
|
||||||
@ -71,12 +70,12 @@ export class RuleTableConfig extends EntityTableConfig<NotificationRule> {
|
|||||||
|
|
||||||
this.columns.push(
|
this.columns.push(
|
||||||
new EntityTableColumn<NotificationRule>('name', 'notification.rule-name', '30%'),
|
new EntityTableColumn<NotificationRule>('name', 'notification.rule-name', '30%'),
|
||||||
new EntityTableColumn<NotificationRule>('templateId', 'notification.template', '30%',
|
new EntityTableColumn<NotificationRule>('templateId', 'notification.template', '15%',
|
||||||
(rule) => `${rule.templateId}`,
|
(rule) => `${rule.templateId.id}`,
|
||||||
() => ({}), false),
|
() => ({}), false),
|
||||||
new EntityTableColumn<NotificationRule>('configuration.description', 'notification.description', '40%',
|
new EntityTableColumn<NotificationRule>('triggerType', 'notification.trigger.trigger', '15%',
|
||||||
(rule) => rule.configuration.description || '',
|
(rule) => this.translate.instant(TriggerTypeTranslationMap.get(rule.triggerType)) || '',
|
||||||
() => ({}), false)
|
() => ({}), true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,22 +17,23 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<form [formGroup]="escalationFormGroup" fxLayout="column">
|
<form [formGroup]="escalationFormGroup" fxLayout="column">
|
||||||
<div fxLayout="row" style="align-items: center; min-height: 74px; border-radius: 8px; background: rgba(0,0,0,0.04);">
|
<div class="escalation" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center center" fxLayoutAlign.xs="start">
|
||||||
<div fxFlex fxLayout="row">
|
<div fxFlex fxLayout="row" ngStyle.xs="padding-top: 10px">
|
||||||
<div fxFlex *ngIf="systemEscalation" style="padding: 0 10px;" translate>notification.first-recipient</div>
|
<div fxFlex [fxShow]="systemEscalation" class="escalation-padding" translate>notification.first-recipient</div>
|
||||||
<div fxFlex *ngIf="!systemEscalation" fxLayout="row" style="align-items: center;">
|
<div fxFlex [fxShow]="!systemEscalation" fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center center">
|
||||||
<span style="padding: 0 10px;">After</span>
|
<span class="escalation-padding">After</span>
|
||||||
<tb-timeinterval
|
<tb-timeinterval
|
||||||
|
style="min-width: 100px;"
|
||||||
|
ngStyle.xs="padding: 0 10px;"
|
||||||
formControlName="delayInSec"
|
formControlName="delayInSec"
|
||||||
[disabledAdvanced]="true"
|
[disabledAdvanced]="true"></tb-timeinterval>
|
||||||
style="padding-top: 8px; width: 150px; min-width: 150px;"></tb-timeinterval>
|
<span fxFlex class="escalation-notify" translate>notification.notify</span>
|
||||||
<span fxFlex style="text-align: end; padding: 0 10px;" translate>notification.notify</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div fxFlex style="padding: 0 10px;">
|
<div fxFlex class="escalation-padding">
|
||||||
<tb-entity-list
|
<tb-entity-list
|
||||||
required
|
required
|
||||||
formControlName="notificationTargetId"
|
formControlName="targets"
|
||||||
[entityType]="entityType.NOTIFICATION_TARGET"
|
[entityType]="entityType.NOTIFICATION_TARGET"
|
||||||
placeholderText="notification.add-target">
|
placeholderText="notification.add-target">
|
||||||
</tb-entity-list>
|
</tb-entity-list>
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2022 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:host {
|
||||||
|
.escalation {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 74px;
|
||||||
|
max-height: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.escalation-padding {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.escalation-notify {
|
||||||
|
text-align: end;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,18 +22,20 @@ import {
|
|||||||
FormGroup,
|
FormGroup,
|
||||||
NG_VALIDATORS,
|
NG_VALIDATORS,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
Validator, Validators
|
Validator,
|
||||||
|
Validators
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import { UtilsService } from '@core/services/utils.service';
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
import { isDefinedAndNotNull } from '@core/utils';
|
import { isDefinedAndNotNull } from '@core/utils';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { NonConfirmedNotificationEscalation } from '@shared/models/notification.models';
|
import { NonConfirmedNotificationEscalation } from '@shared/models/notification.models';
|
||||||
import { EntityType } from '@shared/models/entity-type.models';
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-escalation-form',
|
selector: 'tb-escalation-form',
|
||||||
templateUrl: './escalation-form.component.html',
|
templateUrl: './escalation-form.component.html',
|
||||||
styleUrls: [],
|
styleUrls: ['./escalation-form.component.scss'],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
@ -59,10 +61,10 @@ export class EscalationFormComponent implements ControlValueAccessor, OnInit, On
|
|||||||
|
|
||||||
entityType = EntityType;
|
entityType = EntityType;
|
||||||
|
|
||||||
private modelValue: NonConfirmedNotificationEscalation;
|
private modelValue;
|
||||||
private propagateChange = null;
|
private propagateChange = null;
|
||||||
private propagateChangePending = false;
|
private propagateChangePending = false;
|
||||||
private valueChange$: Subscription = null;
|
private destroy$ = new Subject();
|
||||||
|
|
||||||
constructor(private utils: UtilsService,
|
constructor(private utils: UtilsService,
|
||||||
private fb: FormBuilder) {
|
private fb: FormBuilder) {
|
||||||
@ -84,19 +86,17 @@ export class EscalationFormComponent implements ControlValueAccessor, OnInit, On
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.escalationFormGroup = this.fb.group(
|
this.escalationFormGroup = this.fb.group(
|
||||||
{
|
{
|
||||||
delayInSec: [null],
|
delayInSec: [0],
|
||||||
notificationTargetId: [null, Validators.required],
|
targets: [null, Validators.required],
|
||||||
});
|
});
|
||||||
this.valueChange$ = this.escalationFormGroup.valueChanges.subscribe(() => {
|
this.escalationFormGroup.valueChanges.pipe(
|
||||||
this.updateModel();
|
takeUntil(this.destroy$)
|
||||||
});
|
).subscribe(() => this.updateModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (this.valueChange$) {
|
this.destroy$.next();
|
||||||
this.valueChange$.unsubscribe();
|
this.destroy$.complete();
|
||||||
this.valueChange$ = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisabledState(isDisabled: boolean): void {
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
|||||||
@ -30,11 +30,10 @@ import {
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@app/core/core.state';
|
import { AppState } from '@app/core/core.state';
|
||||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { QueueInfo } from '@shared/models/queue.models';
|
|
||||||
import { UtilsService } from '@core/services/utils.service';
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
import { guid } from '@core/utils';
|
|
||||||
import { NonConfirmedNotificationEscalation } from '@shared/models/notification.models';
|
import { NonConfirmedNotificationEscalation } from '@shared/models/notification.models';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-escalations-component',
|
selector: 'tb-escalations-component',
|
||||||
@ -71,11 +70,11 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
private mainEscalaion = {
|
private mainEscalaion = {
|
||||||
delayInSec: null,
|
delayInSec: 0,
|
||||||
notificationTargetId: null
|
targets: null
|
||||||
};
|
};
|
||||||
|
|
||||||
private valueChangeSubscription$: Subscription = null;
|
private destroy$ = new Subject();
|
||||||
|
|
||||||
private propagateChange = (v: any) => { };
|
private propagateChange = (v: any) => { };
|
||||||
|
|
||||||
@ -89,9 +88,8 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (this.valueChangeSubscription$) {
|
this.destroy$.next();
|
||||||
this.valueChangeSubscription$.unsubscribe();
|
this.destroy$.complete();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(fn: any): void {
|
registerOnTouched(fn: any): void {
|
||||||
@ -101,6 +99,10 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
this.escalationsFormGroup = this.fb.group({
|
this.escalationsFormGroup = this.fb.group({
|
||||||
escalations: this.fb.array([])
|
escalations: this.fb.array([])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.escalationsFormGroup.valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe(() => this.updateModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
get escalationsFormArray(): FormArray {
|
get escalationsFormArray(): FormArray {
|
||||||
@ -117,26 +119,24 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeValue(escalations: Array<NonConfirmedNotificationEscalation> | null): void {
|
writeValue(escalations: Array<NonConfirmedNotificationEscalation> | null): void {
|
||||||
if (this.valueChangeSubscription$) {
|
if (escalations?.length === this.escalationsFormArray.length) {
|
||||||
this.valueChangeSubscription$.unsubscribe();
|
this.escalationsFormArray.patchValue(escalations, {emitEvent: false});
|
||||||
}
|
|
||||||
const escalationsControls: Array<AbstractControl> = [];
|
|
||||||
if (escalations) {
|
|
||||||
escalations.forEach((escalation, index) => {
|
|
||||||
escalationsControls.push(this.fb.control(escalation, [Validators.required]));
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
escalationsControls.push(this.fb.control(this.mainEscalaion, [Validators.required]));
|
const escalationsControls: Array<AbstractControl> = [];
|
||||||
|
if (escalations) {
|
||||||
|
escalations.forEach((escalation, index) => {
|
||||||
|
escalationsControls.push(this.fb.control(escalation, [Validators.required]));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
escalationsControls.push(this.fb.control(this.mainEscalaion, [Validators.required]));
|
||||||
|
}
|
||||||
|
this.escalationsFormGroup.setControl('escalations', this.fb.array(escalationsControls), {emitEvent: false});
|
||||||
|
if (this.disabled) {
|
||||||
|
this.escalationsFormGroup.disable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
this.escalationsFormGroup.enable({emitEvent: false});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.escalationsFormGroup.setControl('escalations', this.fb.array(escalationsControls));
|
|
||||||
if (this.disabled) {
|
|
||||||
this.escalationsFormGroup.disable({emitEvent: false});
|
|
||||||
} else {
|
|
||||||
this.escalationsFormGroup.enable({emitEvent: false});
|
|
||||||
}
|
|
||||||
this.valueChangeSubscription$ = this.escalationsFormGroup.valueChanges.subscribe(() =>
|
|
||||||
this.updateModel()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeEscalation(index: number) {
|
public removeEscalation(index: number) {
|
||||||
@ -144,9 +144,9 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addEscalation() {
|
public addEscalation() {
|
||||||
const escalation: NonConfirmedNotificationEscalation = {
|
const escalation = {
|
||||||
delayInSec: null,
|
delayInSec: 0,
|
||||||
notificationTargetId: null
|
targets: null
|
||||||
};
|
};
|
||||||
this.newEscalation = true;
|
this.newEscalation = true;
|
||||||
const escalationArray = this.escalationsFormGroup.get('escalations') as FormArray;
|
const escalationArray = this.escalationsFormGroup.get('escalations') as FormArray;
|
||||||
@ -166,7 +166,7 @@ export class EscalationsComponent implements ControlValueAccessor, Validator, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateModel() {
|
private updateModel() {
|
||||||
const escalations: Array<NonConfirmedNotificationEscalation> = this.escalationsFormGroup.get('escalations').value;
|
const escalations = this.escalationsFormGroup.get('escalations').value;
|
||||||
this.propagateChange(escalations);
|
this.propagateChange(escalations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,224 +15,231 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
<section style="width: 800px; min-width: 100%; max-width: 100%">
|
||||||
<mat-toolbar color="primary">
|
<mat-toolbar color="primary">
|
||||||
<h2>{{'notification.add-rule' | translate }}</h2>
|
<h2>{{'notification.add-rule' | translate }}</h2>
|
||||||
<span fxFlex></span>
|
<span fxFlex></span>
|
||||||
<button mat-icon-button
|
<button mat-icon-button
|
||||||
(click)="cancel()"
|
(click)="cancel()"
|
||||||
type="button">
|
type="button">
|
||||||
<mat-icon class="material-icons">close</mat-icon>
|
<mat-icon class="material-icons">close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
<div mat-dialog-content>
|
<div mat-dialog-content ngStyle.xs="padding: 0;">
|
||||||
<mat-horizontal-stepper [linear]="true" labelPosition="end" #addNotificationRule [orientation]="(stepperOrientation | async)"
|
<mat-horizontal-stepper [linear]="true" labelPosition="end" #addNotificationRule [orientation]="(stepperOrientation | async)"
|
||||||
(selectionChange)="changeStep($event)">
|
(selectionChange)="changeStep($event)">
|
||||||
<ng-template matStepperIcon="edit">
|
<ng-template matStepperIcon="edit">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<mat-step optional [stepControl]="ruleNotificationForm">
|
<mat-step optional [stepControl]="ruleNotificationForm">
|
||||||
<ng-template matStepLabel>{{ 'notification.basic-settings' | translate }}</ng-template>
|
<ng-template matStepLabel>{{ 'notification.basic-settings' | translate }}</ng-template>
|
||||||
<form [formGroup]="ruleNotificationForm" style="padding-bottom: 16px;">
|
<form [formGroup]="ruleNotificationForm" style="padding-bottom: 16px;">
|
||||||
<mat-form-field class="mat-block">
|
<mat-form-field class="mat-block">
|
||||||
<mat-label translate>notification.rule-name</mat-label>
|
<mat-label translate>notification.rule-name</mat-label>
|
||||||
<input matInput formControlName="name" required>
|
<input matInput formControlName="name" required>
|
||||||
<mat-error *ngIf="ruleNotificationForm.get('name').hasError('required')">
|
<mat-error *ngIf="ruleNotificationForm.get('name').hasError('required')">
|
||||||
{{ 'notification.rule-name-required' | translate }}
|
{{ 'notification.rule-name-required' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
|
||||||
<tb-template-autocomplete
|
|
||||||
required
|
|
||||||
formControlName="templateId"
|
|
||||||
labelText="notification.template-name"
|
|
||||||
placeholderText="notification.template-name"
|
|
||||||
requiredText="notification.template-required">
|
|
||||||
</tb-template-autocomplete>
|
|
||||||
<mat-form-field class="mat-block">
|
|
||||||
<mat-label translate>notification.trigger.trigger</mat-label>
|
|
||||||
<mat-select formControlName="trigger" required>
|
|
||||||
<mat-option *ngFor="let trigger of triggerTypes" [value]="trigger">
|
|
||||||
{{ triggerTypeTranslationMap.get(trigger) | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
<mat-error *ngIf="ruleNotificationForm.get('trigger').hasError('required')">
|
|
||||||
{{ 'notification.trigger.trigger-required' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
</mat-form-field>
|
|
||||||
</form>
|
|
||||||
</mat-step>
|
|
||||||
<mat-step *ngIf="ruleNotificationForm.get('trigger').value === triggerType.ALARM"
|
|
||||||
[stepControl]="alarmTemplateForm">
|
|
||||||
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
|
||||||
<form [formGroup]="alarmTemplateForm">
|
|
||||||
<fieldset class="fields-group">
|
|
||||||
<span class="fields-group-title" translate>notification.filter</span>
|
|
||||||
<mat-form-field fxFlex class="mat-block" floatLabel="always">
|
|
||||||
<mat-label translate>alarm.alarm-type-list</mat-label>
|
|
||||||
<mat-chip-list #alarmTypeChipList formControlName="alarmTypeList">
|
|
||||||
<mat-chip *ngFor="let type of alarmTypeList()" [selectable]="true"
|
|
||||||
[removable]="true" (removed)="removeAlarmType(type)">
|
|
||||||
{{type}}
|
|
||||||
<mat-icon matChipRemove>cancel</mat-icon>
|
|
||||||
</mat-chip>
|
|
||||||
<input placeholder="{{ !alarmTemplateForm.get('alarmTypeList').value?.length ? ('alarm.any-type' | translate) : '' }}"
|
|
||||||
[matChipInputFor]="alarmTypeChipList"
|
|
||||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
|
||||||
matChipInputAddOnBlur
|
|
||||||
(matChipInputTokenEnd)="addAlarmType($event)">
|
|
||||||
</mat-chip-list>
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<tb-template-autocomplete
|
||||||
<mat-form-field fxFlex class="mat-block">
|
required
|
||||||
<mat-label translate>alarm.alarm-severity-list</mat-label>
|
formControlName="templateId"
|
||||||
<mat-chip-list #severitiesChipList
|
labelText="notification.template-name"
|
||||||
required>
|
placeholderText="notification.template-name"
|
||||||
<mat-chip *ngFor="let severity of alarmTemplateForm.get('alarmSeverityList').value"
|
requiredText="notification.template-required">
|
||||||
[removable]="true" (removed)="onSeverityRemoved(severity)">
|
</tb-template-autocomplete>
|
||||||
{{ alarmSeverityTranslationMap.get(severity) | translate }}
|
<mat-form-field class="mat-block">
|
||||||
<mat-icon matChipRemove>cancel</mat-icon>
|
<mat-label translate>notification.trigger.trigger</mat-label>
|
||||||
</mat-chip>
|
<mat-select formControlName="triggerType" required>
|
||||||
<input matInput
|
<mat-option *ngFor="let trigger of triggerTypes" [value]="trigger">
|
||||||
type="text"
|
{{ triggerTypeTranslationMap.get(trigger) | translate }}
|
||||||
placeholder="{{ !alarmTemplateForm.get('alarmSeverityList').value?.length ? ('alarm.any-severity' | translate) : '' }}"
|
|
||||||
style="max-width: 200px;"
|
|
||||||
#severityInput
|
|
||||||
(focusin)="onSeverityInputFocus()"
|
|
||||||
matAutocompleteOrigin
|
|
||||||
#origin="matAutocompleteOrigin"
|
|
||||||
(input)="severityInputChange.next(severityInput.value)"
|
|
||||||
[matAutocompleteConnectedTo]="origin"
|
|
||||||
[matAutocomplete]="severityAutocomplete"
|
|
||||||
[matChipInputFor]="severitiesChipList"
|
|
||||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
|
||||||
(matChipInputTokenEnd)="addSeverityFromChipInput($event)">
|
|
||||||
</mat-chip-list>
|
|
||||||
<mat-autocomplete #severityAutocomplete="matAutocomplete"
|
|
||||||
class="tb-autocomplete"
|
|
||||||
(optionSelected)="severitySelected($event)"
|
|
||||||
[displayWith]="displaySeverityFn.bind(this)">
|
|
||||||
<mat-option *ngFor="let severity of filteredDisplaySeverities | async" [value]="severity">
|
|
||||||
<span [innerHTML]="alarmSeverityTranslationMap.get(alarmSeverityEnum[severity]) | translate | highlight:severitySearchText"></span>
|
|
||||||
</mat-option>
|
|
||||||
<mat-option *ngIf="(filteredDisplaySeverities | async)?.length === 0" [value]="null" class="tb-not-found">
|
|
||||||
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
|
||||||
<div *ngIf="!textIsNotEmpty(severitySearchText); else searchNotEmpty">
|
|
||||||
<span translate>notification.no-severity-found</span>
|
|
||||||
</div>
|
|
||||||
<ng-template #searchNotEmpty>
|
|
||||||
<span>
|
|
||||||
{{ translate.get('notification.no-severity-matching',
|
|
||||||
{severity: truncate.transform(severitySearchText, true, 6, '...')}) | async }}
|
|
||||||
</span>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
</mat-option>
|
|
||||||
</mat-autocomplete>
|
|
||||||
</mat-form-field>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset class="fields-group">
|
|
||||||
<span class="fields-group-title" translate>notification.clear-rule</span>
|
|
||||||
<mat-form-field fxFlex class="mat-block" floatLabel="always">
|
|
||||||
<mat-label translate>alarm.alarm-status-list</mat-label>
|
|
||||||
<mat-select formControlName="alarmStatusList"
|
|
||||||
placeholder="{{ !alarmTemplateForm.get('alarmStatusList').value?.length ? ('alarm.any-status' | translate) : '' }}">
|
|
||||||
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
|
|
||||||
{{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
|
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
<mat-error *ngIf="ruleNotificationForm.get('triggerType').hasError('required')">
|
||||||
|
{{ 'notification.trigger.trigger-required' | translate }}
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</fieldset>
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
<mat-step optional [stepControl]="alarmTemplateForm"
|
||||||
|
*ngIf="ruleNotificationForm.get('triggerType').value === triggerType.ALARM">
|
||||||
|
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
||||||
|
<form [formGroup]="alarmTemplateForm">
|
||||||
|
<fieldset class="fields-group">
|
||||||
|
<span class="fields-group-title" translate>notification.filter</span>
|
||||||
|
<mat-form-field fxFlex class="mat-block" floatLabel="always">
|
||||||
|
<mat-label translate>alarm.alarm-type-list</mat-label>
|
||||||
|
<mat-chip-list #alarmTypeChipList formControlName="alarmTypes">
|
||||||
|
<mat-chip *ngFor="let type of alarmTypeList()" [selectable]="true"
|
||||||
|
[removable]="true" (removed)="removeAlarmType(type)">
|
||||||
|
{{type}}
|
||||||
|
<mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input placeholder="{{ !alarmTemplateForm.get('alarmTypes').value?.length ? ('alarm.any-type' | translate) : '' }}"
|
||||||
|
[matChipInputFor]="alarmTypeChipList"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
matChipInputAddOnBlur
|
||||||
|
(matChipInputTokenEnd)="addAlarmType($event)">
|
||||||
|
</mat-chip-list>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
<fieldset class="fields-group">
|
<mat-form-field fxFlex class="mat-block">
|
||||||
<span class="fields-group-title" translate>notification.hierarchy-of-receiving</span>
|
<mat-label translate>alarm.alarm-severity-list</mat-label>
|
||||||
<tb-escalations-component formControlName="escalationConfig"></tb-escalations-component>
|
<mat-chip-list #severitiesChipList formControlName="alarmSeverities"
|
||||||
</fieldset>
|
required>
|
||||||
|
<mat-chip *ngFor="let severity of alarmTemplateForm.get('alarmSeverities').value"
|
||||||
|
[removable]="true" (removed)="onSeverityRemoved(severity)">
|
||||||
|
{{ alarmSeverityTranslationMap.get(severity) | translate }}
|
||||||
|
<mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input matInput
|
||||||
|
type="text"
|
||||||
|
placeholder="{{ !alarmTemplateForm.get('alarmSeverities').value?.length ? ('alarm.any-severity' | translate) : '' }}"
|
||||||
|
style="max-width: 200px;"
|
||||||
|
#severityInput
|
||||||
|
(focusin)="onSeverityInputFocus()"
|
||||||
|
matAutocompleteOrigin
|
||||||
|
#origin="matAutocompleteOrigin"
|
||||||
|
(input)="severityInputChange.next(severityInput.value)"
|
||||||
|
[matAutocompleteConnectedTo]="origin"
|
||||||
|
[matAutocomplete]="severityAutocomplete"
|
||||||
|
[matChipInputFor]="severitiesChipList"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
(matChipInputTokenEnd)="addSeverityFromChipInput($event)">
|
||||||
|
</mat-chip-list>
|
||||||
|
<mat-autocomplete #severityAutocomplete="matAutocomplete"
|
||||||
|
class="tb-autocomplete"
|
||||||
|
(optionSelected)="severitySelected($event)"
|
||||||
|
[displayWith]="displaySeverityFn.bind(this)">
|
||||||
|
<mat-option *ngFor="let severity of filteredDisplaySeverities | async" [value]="severity">
|
||||||
|
<span [innerHTML]="alarmSeverityTranslationMap.get(alarmSeverityEnum[severity]) | translate | highlight:severitySearchText"></span>
|
||||||
|
</mat-option>
|
||||||
|
<mat-option *ngIf="(filteredDisplaySeverities | async)?.length === 0" [value]="null" class="tb-not-found">
|
||||||
|
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
|
||||||
|
<div *ngIf="!textIsNotEmpty(severitySearchText); else searchNotEmpty">
|
||||||
|
<span translate>notification.no-severity-found</span>
|
||||||
|
</div>
|
||||||
|
<ng-template #searchNotEmpty>
|
||||||
|
<span>
|
||||||
|
{{ translate.get('notification.no-severity-matching',
|
||||||
|
{severity: truncate.transform(severitySearchText, true, 6, '...')}) | async }}
|
||||||
|
</span>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
</mat-form-field>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<mat-form-field class="mat-block">
|
<fieldset class="fields-group" formGroupName="clearRule">
|
||||||
<mat-label translate>notification.description</mat-label>
|
<span class="fields-group-title" translate>notification.clear-rule</span>
|
||||||
<input matInput formControlName="description">
|
<mat-form-field fxFlex class="mat-block" floatLabel="always">
|
||||||
</mat-form-field>
|
<mat-label translate>alarm.alarm-status-list</mat-label>
|
||||||
</form>
|
<mat-select formControlName="alarmStatus"
|
||||||
</mat-step>
|
placeholder="{{ !alarmTemplateForm.get('clearRule.alarmStatus').value?.length ? ('alarm.any-status' | translate) : '' }}">
|
||||||
|
<mat-option *ngFor="let searchStatus of alarmSearchStatuses" [value]="searchStatus">
|
||||||
|
{{ alarmSearchStatusTranslationMap.get(searchStatus) | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<mat-step optional *ngIf="ruleNotificationForm.get('trigger').value === triggerType.DEVICE_INACTIVITY"
|
<fieldset class="fields-group">
|
||||||
[stepControl]="deviceInactivityTemplateForm">
|
<span class="fields-group-title" translate>notification.hierarchy-of-receiving</span>
|
||||||
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
<tb-escalations-component formControlName="escalationTable"></tb-escalations-component>
|
||||||
<form [formGroup]="deviceInactivityTemplateForm">
|
</fieldset>
|
||||||
<span translate>notification.filter-by</span>
|
|
||||||
<div fxFlex fxLayoutAlign="center center">
|
|
||||||
<mat-button-toggle-group class="tb-notification-unread-toggle-group"
|
|
||||||
style="width: 250px;"
|
|
||||||
formControlName="filterByDevice">
|
|
||||||
<mat-button-toggle fxFlex [value]=true>{{ 'notification.device' | translate }}</mat-button-toggle>
|
|
||||||
<mat-button-toggle fxFlex [value]=false>{{ 'notification.device-profile' | translate }}</mat-button-toggle>
|
|
||||||
</mat-button-toggle-group>
|
|
||||||
</div>
|
|
||||||
<tb-device-profile-autocomplete
|
|
||||||
*ngIf="!deviceInactivityTemplateForm.get('filterByDevice').value"
|
|
||||||
formControlName="deviceProfileId"
|
|
||||||
[editProfileEnabled]="false">
|
|
||||||
</tb-device-profile-autocomplete>
|
|
||||||
<tb-entity-autocomplete
|
|
||||||
*ngIf="deviceInactivityTemplateForm.get('filterByDevice').value"
|
|
||||||
formControlName="deviceId"
|
|
||||||
[entityType]="entityType.DEVICE">
|
|
||||||
</tb-entity-autocomplete>
|
|
||||||
<tb-entity-list
|
|
||||||
required
|
|
||||||
formControlName="notificationTargetId"
|
|
||||||
[entityType]="entityType.NOTIFICATION_TARGET"
|
|
||||||
placeholderText="notification.target">
|
|
||||||
</tb-entity-list>
|
|
||||||
<mat-form-field class="mat-block">
|
|
||||||
<mat-label translate>notification.description</mat-label>
|
|
||||||
<input matInput formControlName="description">
|
|
||||||
</mat-form-field>
|
|
||||||
</form>
|
|
||||||
</mat-step>
|
|
||||||
|
|
||||||
<mat-step optional *ngIf="ruleNotificationForm.get('trigger').value === triggerType.ENTITY_ACTION"
|
<mat-form-field class="mat-block">
|
||||||
[stepControl]="entityActionTemplateForm">
|
<mat-label translate>notification.description</mat-label>
|
||||||
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
<input matInput formControlName="description">
|
||||||
<form [formGroup]="entityActionTemplateForm">
|
</mat-form-field>
|
||||||
<fieldset class="fields-group">
|
</form>
|
||||||
<span class="fields-group-title" translate>notification.filter</span>
|
</mat-step>
|
||||||
<tb-entity-type-select required
|
|
||||||
showLabel
|
<mat-step optional *ngIf="ruleNotificationForm.get('triggerType').value === triggerType.DEVICE_INACTIVITY"
|
||||||
[allowedEntityTypes]="entityTypes"
|
[stepControl]="deviceInactivityTemplateForm">
|
||||||
formControlName="entityType">
|
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
||||||
</tb-entity-type-select>
|
<form [formGroup]="deviceInactivityTemplateForm">
|
||||||
<section formGroupName="status" fxLayout="column" fxLayoutGap="10px">
|
<span translate>notification.filter-by</span>
|
||||||
<span fxFlex translate>notification.status</span>
|
<div fxFlex fxLayoutAlign="center center">
|
||||||
<mat-checkbox fxFlex formControlName="created" translate>{{ 'notification.created' | translate }}</mat-checkbox>
|
<mat-button-toggle-group class="tb-notification-unread-toggle-group"
|
||||||
<mat-checkbox fxFlex formControlName="updated" translate>{{ 'notification.updated' | translate }}</mat-checkbox>
|
style="width: 250px;"
|
||||||
<mat-checkbox fxFlex formControlName="deleted" translate>{{ 'notification.deleted' | translate }}</mat-checkbox>
|
formControlName="filterByDevice">
|
||||||
</section>
|
<mat-button-toggle fxFlex [value]=true>{{ 'notification.device' | translate }}</mat-button-toggle>
|
||||||
</fieldset>
|
<mat-button-toggle fxFlex [value]=false>{{ 'notification.device-profile' | translate }}</mat-button-toggle>
|
||||||
<tb-entity-list
|
</mat-button-toggle-group>
|
||||||
required
|
</div>
|
||||||
formControlName="notificationTargetId"
|
<tb-entity-list
|
||||||
[entityType]="entityType.NOTIFICATION_TARGET"
|
*ngIf="deviceInactivityTemplateForm.get('filterByDevice').value"
|
||||||
placeholderText="notification.target">
|
required
|
||||||
</tb-entity-list>
|
formControlName="devices"
|
||||||
<mat-form-field class="mat-block">
|
[labelText]="translate.instant('notification.devices')"
|
||||||
<mat-label translate>notification.description</mat-label>
|
[placeholderText]="translate.instant('notification.device')"
|
||||||
<input matInput formControlName="description">
|
[entityType]="entityType.DEVICE">
|
||||||
</mat-form-field>
|
</tb-entity-list>
|
||||||
</form>
|
<tb-entity-list
|
||||||
</mat-step>
|
*ngIf="!deviceInactivityTemplateForm.get('filterByDevice').value"
|
||||||
</mat-horizontal-stepper>
|
required
|
||||||
</div>
|
formControlName="deviceProfiles"
|
||||||
<mat-divider></mat-divider>
|
[labelText]="translate.instant('notification.device-profiles')"
|
||||||
<div mat-dialog-actions fxLayout="row">
|
[placeholderText]="translate.instant('notification.device-profile')"
|
||||||
<button mat-stroked-button *ngIf="selectedIndex > 0"
|
[entityType]="entityType.DEVICE_PROFILE">
|
||||||
(click)="backStep()">{{ 'action.back' | translate }}</button>
|
</tb-entity-list>
|
||||||
<span fxFlex></span>
|
<tb-entity-list
|
||||||
<button mat-raised-button
|
required
|
||||||
color="primary"
|
formControlName="targets"
|
||||||
(click)="nextStep()">{{ nextStepLabel() | translate }}</button>
|
[entityType]="entityType.NOTIFICATION_TARGET"
|
||||||
</div>
|
placeholderText="notification.target">
|
||||||
|
</tb-entity-list>
|
||||||
|
<mat-form-field class="mat-block">
|
||||||
|
<mat-label translate>notification.description</mat-label>
|
||||||
|
<input matInput formControlName="description">
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step optional *ngIf="ruleNotificationForm.get('triggerType').value === triggerType.ENTITY_ACTION"
|
||||||
|
[stepControl]="entityActionTemplateForm">
|
||||||
|
<ng-template matStepLabel>{{ 'notification.type-settings' | translate }}</ng-template>
|
||||||
|
<form [formGroup]="entityActionTemplateForm">
|
||||||
|
<fieldset class="fields-group">
|
||||||
|
<span class="fields-group-title" translate>notification.filter</span>
|
||||||
|
<tb-entity-type-select required
|
||||||
|
showLabel
|
||||||
|
[allowedEntityTypes]="entityTypes"
|
||||||
|
formControlName="entityType">
|
||||||
|
</tb-entity-type-select>
|
||||||
|
<section fxLayout="column" fxLayoutGap="10px">
|
||||||
|
<span fxFlex translate>notification.status</span>
|
||||||
|
<mat-checkbox fxFlex formControlName="created" translate>{{ 'notification.created' | translate }}</mat-checkbox>
|
||||||
|
<mat-checkbox fxFlex formControlName="updated" translate>{{ 'notification.updated' | translate }}</mat-checkbox>
|
||||||
|
<mat-checkbox fxFlex formControlName="deleted" translate>{{ 'notification.deleted' | translate }}</mat-checkbox>
|
||||||
|
</section>
|
||||||
|
</fieldset>
|
||||||
|
<tb-entity-list
|
||||||
|
required
|
||||||
|
formControlName="targets"
|
||||||
|
[entityType]="entityType.NOTIFICATION_TARGET"
|
||||||
|
placeholderText="notification.target">
|
||||||
|
</tb-entity-list>
|
||||||
|
<mat-form-field class="mat-block">
|
||||||
|
<mat-label translate>notification.description</mat-label>
|
||||||
|
<input matInput formControlName="description">
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
</mat-horizontal-stepper>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
<div mat-dialog-actions fxLayout="row">
|
||||||
|
<button mat-stroked-button *ngIf="selectedIndex > 0"
|
||||||
|
(click)="backStep()">{{ 'action.back' | translate }}</button>
|
||||||
|
<span fxFlex></span>
|
||||||
|
<button mat-raised-button
|
||||||
|
color="primary"
|
||||||
|
(click)="nextStep()">{{ nextStepLabel() | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|||||||
@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
:host {
|
:host {
|
||||||
::ng-deep{
|
::ng-deep{
|
||||||
width: 100%;
|
//width: 100%;
|
||||||
min-width: 800px !important;
|
//min-width: 800px;
|
||||||
max-width: 100%;
|
//max-width: 100%;
|
||||||
|
|
||||||
.mat-button-toggle-group.tb-notification-unread-toggle-group {
|
.mat-button-toggle-group.tb-notification-unread-toggle-group {
|
||||||
&.mat-button-toggle-group-appearance-standard {
|
&.mat-button-toggle-group-appearance-standard {
|
||||||
|
|||||||
@ -23,10 +23,10 @@ import { Router } from '@angular/router';
|
|||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { NotificationService } from '@core/http/notification.service';
|
import { NotificationService } from '@core/http/notification.service';
|
||||||
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
import { deepTrim, isDefined } from '@core/utils';
|
import { deepTrim, isDefined } from '@core/utils';
|
||||||
import { Observable, of, Subject } from 'rxjs';
|
import { Observable, of, Subject } from 'rxjs';
|
||||||
import { map, mergeMap, share, startWith } from 'rxjs/operators';
|
import { map, mergeMap, share, startWith, takeUntil } from 'rxjs/operators';
|
||||||
import { StepperOrientation, StepperSelectionEvent } from '@angular/cdk/stepper';
|
import { StepperOrientation, StepperSelectionEvent } from '@angular/cdk/stepper';
|
||||||
import { MatStepper } from '@angular/material/stepper';
|
import { MatStepper } from '@angular/material/stepper';
|
||||||
import { MediaBreakpoints } from '@shared/models/constants';
|
import { MediaBreakpoints } from '@shared/models/constants';
|
||||||
@ -34,12 +34,11 @@ import { BreakpointObserver } from '@angular/cdk/layout';
|
|||||||
import { MatChipInputEvent, MatChipList } from '@angular/material/chips';
|
import { MatChipInputEvent, MatChipList } from '@angular/material/chips';
|
||||||
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
|
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
|
||||||
import {
|
import {
|
||||||
AlarmSearchStatus,
|
|
||||||
alarmSearchStatusTranslations,
|
|
||||||
AlarmSeverity,
|
AlarmSeverity,
|
||||||
alarmSeverityTranslations
|
alarmSeverityTranslations,
|
||||||
|
AlarmStatus,
|
||||||
|
alarmStatusTranslations
|
||||||
} from '@shared/models/alarm.models';
|
} from '@shared/models/alarm.models';
|
||||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
|
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
|
||||||
@ -80,11 +79,8 @@ export class RuleNotificationDialogComponent extends
|
|||||||
alarmSeverityEnum = AlarmSeverity;
|
alarmSeverityEnum = AlarmSeverity;
|
||||||
alarmSeverityTranslationMap = alarmSeverityTranslations;
|
alarmSeverityTranslationMap = alarmSeverityTranslations;
|
||||||
|
|
||||||
alarmSearchStatuses = [AlarmSearchStatus.ACTIVE,
|
alarmSearchStatuses = Object.values(AlarmStatus);
|
||||||
AlarmSearchStatus.CLEARED,
|
alarmSearchStatusTranslationMap = alarmStatusTranslations;
|
||||||
AlarmSearchStatus.ACK,
|
|
||||||
AlarmSearchStatus.UNACK];
|
|
||||||
alarmSearchStatusTranslationMap = alarmSearchStatusTranslations;
|
|
||||||
|
|
||||||
entityType = EntityType;
|
entityType = EntityType;
|
||||||
entityTypes = Object.values(EntityType);
|
entityTypes = Object.values(EntityType);
|
||||||
@ -97,7 +93,7 @@ export class RuleNotificationDialogComponent extends
|
|||||||
|
|
||||||
severityInputChange = new Subject<string>();
|
severityInputChange = new Subject<string>();
|
||||||
|
|
||||||
private readonly destroy$ = new Subject<void>();
|
private destroy$ = new Subject();
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@ -120,39 +116,54 @@ export class RuleNotificationDialogComponent extends
|
|||||||
this.ruleNotificationForm = this.fb.group({
|
this.ruleNotificationForm = this.fb.group({
|
||||||
name: [null, Validators.required],
|
name: [null, Validators.required],
|
||||||
templateId: [null, Validators.required],
|
templateId: [null, Validators.required],
|
||||||
trigger: [this.triggerType.ALARM, Validators.required],
|
triggerType: [null, Validators.required],
|
||||||
configuration: this.fb.group({
|
recipientsConfig: this.fb.group({
|
||||||
escalationConfig: this.fb.group({
|
triggerType: [],
|
||||||
escalations: [[]]
|
}),
|
||||||
}),
|
triggerConfig: this.fb.group({
|
||||||
description: [null]
|
triggerType: []
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.ruleNotificationForm.get('triggerType').valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe(
|
||||||
|
value => {
|
||||||
|
this.ruleNotificationForm.get('triggerConfig').patchValue({triggerType: value}, {emitEvent: false});
|
||||||
|
this.ruleNotificationForm.get('recipientsConfig').patchValue({triggerType: value}, {emitEvent: false});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.alarmTemplateForm = this.fb.group({
|
this.alarmTemplateForm = this.fb.group({
|
||||||
alarmTypeList: [[], Validators.required],
|
alarmTypes: [[], Validators.required],
|
||||||
alarmSeverityList: [[], Validators.required],
|
alarmSeverities: [[], Validators.required],
|
||||||
alarmStatusList: [[], Validators.required],
|
clearRule: this.fb.group({
|
||||||
escalationConfig: [],
|
alarmStatus: []
|
||||||
|
}),
|
||||||
|
escalationTable: [],
|
||||||
description: ['']
|
description: ['']
|
||||||
});
|
});
|
||||||
|
|
||||||
this.deviceInactivityTemplateForm = this.fb.group({
|
this.deviceInactivityTemplateForm = this.fb.group({
|
||||||
filterByDevice: [true],
|
filterByDevice: [true],
|
||||||
deviceId: [],
|
devices: [],
|
||||||
deviceProfileId: [],
|
deviceProfiles: [],
|
||||||
notificationTargetId: [],
|
targets: [[], Validators.required],
|
||||||
description: ['']
|
description: ['']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.deviceInactivityTemplateForm.get('filterByDevice').valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe(
|
||||||
|
value => this.deviceInactivityTemplateForm.get(value ? 'deviceProfiles' : 'devices').patchValue(null, {emitEvent: false})
|
||||||
|
);
|
||||||
|
|
||||||
this.entityActionTemplateForm = this.fb.group({
|
this.entityActionTemplateForm = this.fb.group({
|
||||||
entityType: [],
|
entityType: [],
|
||||||
status: this.fb.group({
|
created: [false],
|
||||||
created: [false],
|
updated: [false],
|
||||||
updated: [false],
|
deleted: [false],
|
||||||
deleted: [false]
|
targets: [[], Validators.required],
|
||||||
}),
|
|
||||||
notificationTargetId: [],
|
|
||||||
description: ['']
|
description: ['']
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -166,12 +177,12 @@ export class RuleNotificationDialogComponent extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSeverityRemoved(severity: string): void {
|
onSeverityRemoved(severity: string): void {
|
||||||
const severities: string[] = this.alarmTemplateForm.get('alarmSeverityList').value;
|
const severities: string[] = this.alarmTemplateForm.get('alarmSeverities').value;
|
||||||
const index = severities.indexOf(severity);
|
const index = severities.indexOf(severity);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
severities.splice(index, 1);
|
severities.splice(index, 1);
|
||||||
this.alarmTemplateForm.get('alarmSeverityList').setValue(severities);
|
this.alarmTemplateForm.get('alarmSeverities').setValue(severities);
|
||||||
this.alarmTemplateForm.get('alarmSeverityList').markAsDirty();
|
this.alarmTemplateForm.get('alarmSeverities').markAsDirty();
|
||||||
this.severitiesChipList.errorState = !severities.length;
|
this.severitiesChipList.errorState = !severities.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,12 +204,12 @@ export class RuleNotificationDialogComponent extends
|
|||||||
|
|
||||||
private addSeverity(existingSeverity: string): boolean {
|
private addSeverity(existingSeverity: string): boolean {
|
||||||
if (existingSeverity) {
|
if (existingSeverity) {
|
||||||
const displaySeverities: string[] = this.alarmTemplateForm.get('alarmSeverityList').value;
|
const displaySeverities: string[] = this.alarmTemplateForm.get('alarmSeverities').value;
|
||||||
const index = displaySeverities.indexOf(existingSeverity);
|
const index = displaySeverities.indexOf(existingSeverity);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
displaySeverities.push(existingSeverity);
|
displaySeverities.push(existingSeverity);
|
||||||
this.alarmTemplateForm.get('alarmSeverityList').setValue(displaySeverities);
|
this.alarmTemplateForm.get('alarmSeverities').setValue(displaySeverities);
|
||||||
this.alarmTemplateForm.get('alarmSeverityList').markAsDirty();
|
this.alarmTemplateForm.get('alarmSeverities').markAsDirty();
|
||||||
this.severitiesChipList.errorState = false;
|
this.severitiesChipList.errorState = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -239,16 +250,16 @@ export class RuleNotificationDialogComponent extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
public alarmTypeList(): string[] {
|
public alarmTypeList(): string[] {
|
||||||
return this.alarmTemplateForm.get('alarmTypeList').value;
|
return this.alarmTemplateForm.get('alarmTypes').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeAlarmType(type: string): void {
|
public removeAlarmType(type: string): void {
|
||||||
const types: string[] = this.alarmTemplateForm.get('alarmTypeList').value;
|
const types: string[] = this.alarmTemplateForm.get('alarmTypes').value;
|
||||||
const index = types.indexOf(type);
|
const index = types.indexOf(type);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
types.splice(index, 1);
|
types.splice(index, 1);
|
||||||
this.alarmTemplateForm.get('alarmTypeList').setValue(types);
|
this.alarmTemplateForm.get('alarmTypes').setValue(types);
|
||||||
this.alarmTemplateForm.get('alarmTypeList').markAsDirty();
|
this.alarmTemplateForm.get('alarmTypes').markAsDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,12 +267,12 @@ export class RuleNotificationDialogComponent extends
|
|||||||
const input = event.input;
|
const input = event.input;
|
||||||
const value = event.value;
|
const value = event.value;
|
||||||
|
|
||||||
const types: string[] = this.alarmTemplateForm.get('alarmTypeList').value;
|
const types: string[] = this.alarmTemplateForm.get('alarmTypes').value;
|
||||||
|
|
||||||
if ((value || '').trim()) {
|
if ((value || '').trim()) {
|
||||||
types.push(value.trim());
|
types.push(value.trim());
|
||||||
this.alarmTemplateForm.get('alarmTypeList').setValue(types);
|
this.alarmTemplateForm.get('alarmTypes').setValue(types);
|
||||||
this.alarmTemplateForm.get('alarmTypeList').markAsDirty();
|
this.alarmTemplateForm.get('alarmTypes').markAsDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
@ -307,22 +318,26 @@ export class RuleNotificationDialogComponent extends
|
|||||||
|
|
||||||
private add(): void {
|
private add(): void {
|
||||||
if (this.allValid()) {
|
if (this.allValid()) {
|
||||||
const formValue: NotificationRule = this.ruleNotificationForm.value;
|
const formValue = this.ruleNotificationForm.value;
|
||||||
// if (formValue.configuration.deliveryMethodsTemplates.PUSH.enabled) {
|
const triggerType = this.ruleNotificationForm.get('triggerType').value;
|
||||||
// Object.assign(formValue.configuration.deliveryMethodsTemplates.PUSH, this.pushTemplateForm.value);
|
if (triggerType === TriggerType.ALARM) {
|
||||||
// } else {
|
Object.assign(formValue.triggerConfig, this.alarmTemplateForm.value);
|
||||||
// delete formValue.configuration.deliveryMethodsTemplates.PUSH;
|
const parsedEscalationTable = {};
|
||||||
// }
|
this.alarmTemplateForm.get('escalationTable').value.forEach(
|
||||||
// if (formValue.configuration.deliveryMethodsTemplates.EMAIL.enabled) {
|
escalation => parsedEscalationTable[escalation.delayInSec] = escalation.targets
|
||||||
// Object.assign(formValue.configuration.deliveryMethodsTemplates.EMAIL, this.emailTemplateForm.value);
|
);
|
||||||
// } else {
|
formValue.recipientsConfig.escalationTable = parsedEscalationTable;
|
||||||
// delete formValue.configuration.deliveryMethodsTemplates.EMAIL;
|
delete formValue.triggerConfig.escalationTable;
|
||||||
// }
|
} else if (triggerType === TriggerType.DEVICE_INACTIVITY) {
|
||||||
// if (formValue.configuration.deliveryMethodsTemplates.SMS.enabled) {
|
Object.assign(formValue.triggerConfig, this.deviceInactivityTemplateForm.value);
|
||||||
// Object.assign(formValue.configuration.deliveryMethodsTemplates.SMS, this.smsTemplateForm.value);
|
delete formValue.triggerConfig.filterByDevice;
|
||||||
// } else {
|
} else {
|
||||||
// delete formValue.configuration.deliveryMethodsTemplates.SMS;
|
Object.assign(formValue.triggerConfig, this.entityActionTemplateForm.value);
|
||||||
// }
|
}
|
||||||
|
if (triggerType === TriggerType.DEVICE_INACTIVITY || triggerType === TriggerType.ENTITY_ACTION) {
|
||||||
|
formValue.recipientsConfig.targets = this.entityActionTemplateForm.get('targets').value;
|
||||||
|
delete formValue.triggerConfig.trigger;
|
||||||
|
}
|
||||||
this.notificationService.saveNotificationRule(deepTrim(formValue)).subscribe(
|
this.notificationService.saveNotificationRule(deepTrim(formValue)).subscribe(
|
||||||
(target) => this.dialogRef.close(target)
|
(target) => this.dialogRef.close(target)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,6 +24,8 @@ import { NotificationTargetId } from '@shared/models/id/notification-target-id';
|
|||||||
import { NotificationTemplateId } from '@shared/models/id/notification-template-id';
|
import { NotificationTemplateId } from '@shared/models/id/notification-template-id';
|
||||||
import { EntityId } from '@shared/models/id/entity-id';
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
import { NotificationRuleId } from '@shared/models/id/notification-rule-id';
|
import { NotificationRuleId } from '@shared/models/id/notification-rule-id';
|
||||||
|
import { AlarmSeverity, AlarmStatus } from '@shared/models/alarm.models';
|
||||||
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
|
|
||||||
export interface Notification {
|
export interface Notification {
|
||||||
readonly id: NotificationId;
|
readonly id: NotificationId;
|
||||||
@ -100,18 +102,26 @@ export interface SlackConversation {
|
|||||||
export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'label'>{
|
export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'label'>{
|
||||||
tenantId: TenantId;
|
tenantId: TenantId;
|
||||||
templateId: NotificationTemplateId;
|
templateId: NotificationTemplateId;
|
||||||
// deliveryMethods: Array<NotificationDeliveryMethod>;
|
triggerType: TriggerType;
|
||||||
configuration: NotificationRuleConfig;
|
triggerConfig: NotificationRuleTriggerConfig;
|
||||||
|
recipientConfig: NotificationRuleRecipientConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationRuleConfig {
|
export interface NotificationRuleTriggerConfig {
|
||||||
initialNotificationTargetId: NotificationTargetId;
|
alarmTypes?: Array<string>;
|
||||||
escalationConfig: NotificationEscalationConfig;
|
alarmSeverities?: Array<AlarmSeverity>;
|
||||||
description?: string;
|
clearRule?: AlarmStatus;
|
||||||
|
devices?: Array<string>;
|
||||||
|
devicesProfiles?: Array<string>;
|
||||||
|
entityType?: EntityType;
|
||||||
|
created?: boolean;
|
||||||
|
updated?: boolean;
|
||||||
|
deleted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationEscalationConfig {
|
export interface NotificationRuleRecipientConfig {
|
||||||
escalations: Array<NonConfirmedNotificationEscalation>;
|
targets?: Array<string>;
|
||||||
|
escalationTable?: {[key: string]: Array<string>};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NonConfirmedNotificationEscalation {
|
export interface NonConfirmedNotificationEscalation {
|
||||||
|
|||||||
@ -2805,7 +2805,9 @@
|
|||||||
"notify": "notify",
|
"notify": "notify",
|
||||||
"no-rule": "No rule configured",
|
"no-rule": "No rule configured",
|
||||||
"device": "Device",
|
"device": "Device",
|
||||||
|
"devices": "Devices",
|
||||||
"device-profile": "Device profile",
|
"device-profile": "Device profile",
|
||||||
|
"device-profiles": "Device profiles",
|
||||||
"filter-by": "Filter by",
|
"filter-by": "Filter by",
|
||||||
"entity-type": "Entity type",
|
"entity-type": "Entity type",
|
||||||
"created": "Created",
|
"created": "Created",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user