Commit c4e67215969501693d89bc6a8659dbadc98fb458

Authored by Igor Kulikov
1 parent c2b4d9c8

UI: Device profile alarm rules improvements

Showing 27 changed files with 233 additions and 74 deletions
... ... @@ -24,7 +24,8 @@
24 24 </mat-option>
25 25 </mat-select>
26 26 </mat-form-field>
27   - <tb-filter-predicate-value fxFlex="60"
  27 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  28 + fxFlex="60"
28 29 [valueType]="valueTypeEnum.BOOLEAN"
29 30 formControlName="value">
30 31 </tb-filter-predicate-value>
... ...
... ... @@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
39 39
40 40 @Input() disabled: boolean;
41 41
  42 + @Input() allowUserDynamicSource = true;
  43 +
42 44 valueTypeEnum = EntityKeyValueType;
43 45
44 46 booleanFilterPredicateFormGroup: FormGroup;
... ...
... ... @@ -38,6 +38,7 @@
38 38 <tb-filter-predicate-list
39 39 [valueType]="data.valueType"
40 40 [displayUserParameters]="data.displayUserParameters"
  41 + [allowUserDynamicSource]="data.allowUserDynamicSource"
41 42 [operation]="complexFilterFormGroup.get('operation').value"
42 43 [key]="data.key"
43 44 formControlName="predicates">
... ...
... ... @@ -36,6 +36,7 @@ export interface ComplexFilterPredicateDialogData {
36 36 isAdd: boolean;
37 37 valueType: EntityKeyValueType;
38 38 displayUserParameters: boolean;
  39 + allowUserDynamicSource: boolean;
39 40 }
40 41
41 42 @Component({
... ...
... ... @@ -50,6 +50,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
50 50
51 51 @Input() displayUserParameters = true;
52 52
  53 + @Input() allowUserDynamicSource = true;
  54 +
53 55 private propagateChange = null;
54 56
55 57 private complexFilterPredicate: ComplexFilterPredicateInfo;
... ... @@ -86,7 +88,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
86 88 valueType: this.valueType,
87 89 isAdd: false,
88 90 key: this.key,
89   - displayUserParameters: this.displayUserParameters
  91 + displayUserParameters: this.displayUserParameters,
  92 + allowUserDynamicSource: this.allowUserDynamicSource
90 93 }
91 94 }).afterClosed().subscribe(
92 95 (result) => {
... ...
... ... @@ -52,6 +52,7 @@
52 52 fxFlex
53 53 [valueType]="valueType"
54 54 [displayUserParameters]="displayUserParameters"
  55 + [allowUserDynamicSource]="allowUserDynamicSource"
55 56 [key]="key"
56 57 [formControl]="predicateControl">
57 58 </tb-filter-predicate>
... ...
... ... @@ -64,6 +64,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni
64 64
65 65 @Input() displayUserParameters = true;
66 66
  67 + @Input() allowUserDynamicSource = true;
  68 +
67 69 filterListFormGroup: FormGroup;
68 70
69 71 valueTypeEnum = EntityKeyValueType;
... ... @@ -156,7 +158,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni
156 158 valueType: this.valueType,
157 159 key: this.key,
158 160 isAdd: true,
159   - displayUserParameters: this.displayUserParameters
  161 + displayUserParameters: this.displayUserParameters,
  162 + allowUserDynamicSource: this.allowUserDynamicSource
160 163 }
161 164 }).afterClosed().pipe(
162 165 map((result) => {
... ...
... ... @@ -55,7 +55,7 @@
55 55 {{'filter.no-dynamic-value' | translate}}
56 56 </mat-option>
57 57 <mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType">
58   - {{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}}
  58 + {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}}
59 59 </mat-option>
60 60 </mat-select>
61 61 </mat-form-field>
... ...
... ... @@ -47,12 +47,22 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
47 47 @Input() disabled: boolean;
48 48
49 49 @Input()
  50 + set allowUserDynamicSource(allow: boolean) {
  51 + this.dynamicValueSourceTypes = [DynamicValueSourceType.CURRENT_TENANT,
  52 + DynamicValueSourceType.CURRENT_CUSTOMER];
  53 + if (allow) {
  54 + this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER);
  55 + }
  56 + }
  57 +
  58 + @Input()
50 59 valueType: EntityKeyValueType;
51 60
52 61 valueTypeEnum = EntityKeyValueType;
53 62
54   - dynamicValueSourceTypes = Object.keys(DynamicValueSourceType);
55   - dynamicValueSourceTypeEnum = DynamicValueSourceType;
  63 + dynamicValueSourceTypes: DynamicValueSourceType[] = [DynamicValueSourceType.CURRENT_TENANT,
  64 + DynamicValueSourceType.CURRENT_CUSTOMER, DynamicValueSourceType.CURRENT_USER];
  65 +
56 66 dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap;
57 67
58 68 filterPredicateValueFormGroup: FormGroup;
... ...
... ... @@ -19,20 +19,27 @@
19 19 <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateFormGroup">
20 20 <div fxFlex fxLayout="column" [ngSwitch]="type">
21 21 <ng-template [ngSwitchCase]="filterPredicateType.STRING">
22   - <tb-string-filter-predicate formControlName="predicate">
  22 + <tb-string-filter-predicate
  23 + [allowUserDynamicSource]="allowUserDynamicSource"
  24 + formControlName="predicate">
23 25 </tb-string-filter-predicate>
24 26 </ng-template>
25 27 <ng-template [ngSwitchCase]="filterPredicateType.NUMERIC">
26   - <tb-numeric-filter-predicate [valueType]="valueType"
27   - formControlName="predicate">
  28 + <tb-numeric-filter-predicate
  29 + [allowUserDynamicSource]="allowUserDynamicSource"
  30 + [valueType]="valueType"
  31 + formControlName="predicate">
28 32 </tb-numeric-filter-predicate>
29 33 </ng-template>
30 34 <ng-template [ngSwitchCase]="filterPredicateType.BOOLEAN">
31   - <tb-boolean-filter-predicate formControlName="predicate">
  35 + <tb-boolean-filter-predicate
  36 + [allowUserDynamicSource]="allowUserDynamicSource"
  37 + formControlName="predicate">
32 38 </tb-boolean-filter-predicate>
33 39 </ng-template>
34 40 <ng-template [ngSwitchCase]="filterPredicateType.COMPLEX">
35 41 <tb-complex-filter-predicate
  42 + [allowUserDynamicSource]="allowUserDynamicSource"
36 43 [key]="key"
37 44 [valueType]="valueType"
38 45 [displayUserParameters]="displayUserParameters"
... ...
... ... @@ -43,6 +43,8 @@ export class FilterPredicateComponent implements ControlValueAccessor, OnInit {
43 43
44 44 @Input() displayUserParameters = true;
45 45
  46 + @Input() allowUserDynamicSource = true;
  47 +
46 48 filterPredicateFormGroup: FormGroup;
47 49
48 50 type: FilterPredicateType;
... ...
... ... @@ -70,6 +70,7 @@
70 70 </mat-form-field>
71 71 </section>
72 72 <tb-filter-predicate-list *ngIf="keyFilterFormGroup.get('valueType').value"
  73 + [allowUserDynamicSource]="data.allowUserDynamicSource"
73 74 [displayUserParameters]="data.displayUserParameters"
74 75 [valueType]="keyFilterFormGroup.get('valueType').value"
75 76 [key]="keyFilterFormGroup.get('key.key').value"
... ...
... ... @@ -40,6 +40,7 @@ export interface KeyFilterDialogData {
40 40 keyFilter: KeyFilterInfo;
41 41 isAdd: boolean;
42 42 displayUserParameters: boolean;
  43 + allowUserDynamicSource: boolean;
43 44 readonly: boolean;
44 45 telemetryKeysOnly: boolean;
45 46 }
... ...
... ... @@ -16,65 +16,77 @@
16 16
17 17 -->
18 18 <section fxLayout="column" [formGroup]="keyFilterListFormGroup">
19   - <mat-expansion-panel [expanded]="true">
20   - <mat-expansion-panel-header>
21   - <mat-panel-title>
22   - <div translate>filter.key-filters</div>
23   - </mat-panel-title>
24   - </mat-expansion-panel-header>
25   - <div fxLayout="row">
26   - <span fxFlex="8"></span>
27   - <div fxLayout="row" fxLayoutAlign="start center" fxFlex="92">
28   - <label fxFlex translate class="tb-title no-padding">filter.key-name</label>
29   - <label fxFlex translate class="tb-title no-padding">filter.key-type.key-type</label>
30   - <span [fxShow]="!disabled" style="min-width: 80px;">&nbsp;</span>
31   - <span [fxShow]="disabled" style="min-width: 40px;">&nbsp;</span>
32   - </div>
33   - </div>
34   - <mat-divider></mat-divider>
35   - <div class="key-filter-list">
36   - <div fxLayout="row" fxLayoutAlign="start center" style="max-height: 40px;"
37   - formArrayName="keyFilters"
38   - *ngFor="let keyFilterControl of keyFiltersFormArray().controls; let $index = index">
39   - <div fxFlex="8" class="filters-operation">
40   - <span *ngIf="$index > 0" translate>filter.operation.and</span>
  19 + <mat-accordion [multi]="true">
  20 + <mat-expansion-panel [expanded]="true">
  21 + <mat-expansion-panel-header>
  22 + <mat-panel-title>
  23 + <div translate>filter.key-filters</div>
  24 + </mat-panel-title>
  25 + </mat-expansion-panel-header>
  26 + <div fxLayout="row">
  27 + <span fxFlex="8"></span>
  28 + <div fxLayout="row" fxLayoutAlign="start center" fxFlex="92">
  29 + <label fxFlex translate class="tb-title no-padding">filter.key-name</label>
  30 + <label fxFlex translate class="tb-title no-padding">filter.key-type.key-type</label>
  31 + <span [fxShow]="!disabled" style="min-width: 80px;">&nbsp;</span>
  32 + <span [fxShow]="disabled" style="min-width: 40px;">&nbsp;</span>
41 33 </div>
42   - <div fxLayout="column" fxFlex="92">
43   - <div fxLayout="row" fxLayoutAlign="start center" fxFlex>
44   - <div fxFlex>{{ keyFilterControl.value.key.key }}</div>
45   - <div fxFlex translate>{{ entityKeyTypeTranslations.get(keyFilterControl.value.key.type) }}</div>
46   - <button mat-icon-button color="primary"
47   - type="button"
48   - (click)="editKeyFilter($index)"
49   - matTooltip="{{ (disabled ? 'filter.key-filter' : 'filter.edit-key-filter') | translate }}"
50   - matTooltipPosition="above">
51   - <mat-icon>{{disabled ? 'more_vert' : 'edit'}}</mat-icon>
52   - </button>
53   - <button mat-icon-button color="primary"
54   - [fxShow]="!disabled"
55   - type="button"
56   - (click)="removeKeyFilter($index)"
57   - matTooltip="{{ 'filter.remove-key-filter' | translate }}"
58   - matTooltipPosition="above">
59   - <mat-icon>close</mat-icon>
60   - </button>
  34 + </div>
  35 + <mat-divider></mat-divider>
  36 + <div class="key-filter-list">
  37 + <div fxLayout="row" fxLayoutAlign="start center" style="max-height: 40px;"
  38 + formArrayName="keyFilters"
  39 + *ngFor="let keyFilterControl of keyFiltersFormArray().controls; let $index = index">
  40 + <div fxFlex="8" class="filters-operation">
  41 + <span *ngIf="$index > 0" translate>filter.operation.and</span>
  42 + </div>
  43 + <div fxLayout="column" fxFlex="92">
  44 + <div fxLayout="row" fxLayoutAlign="start center" fxFlex>
  45 + <div fxFlex>{{ keyFilterControl.value.key.key }}</div>
  46 + <div fxFlex translate>{{ entityKeyTypeTranslations.get(keyFilterControl.value.key.type) }}</div>
  47 + <button mat-icon-button color="primary"
  48 + type="button"
  49 + (click)="editKeyFilter($index)"
  50 + matTooltip="{{ (disabled ? 'filter.key-filter' : 'filter.edit-key-filter') | translate }}"
  51 + matTooltipPosition="above">
  52 + <mat-icon>{{disabled ? 'more_vert' : 'edit'}}</mat-icon>
  53 + </button>
  54 + <button mat-icon-button color="primary"
  55 + [fxShow]="!disabled"
  56 + type="button"
  57 + (click)="removeKeyFilter($index)"
  58 + matTooltip="{{ 'filter.remove-key-filter' | translate }}"
  59 + matTooltipPosition="above">
  60 + <mat-icon>close</mat-icon>
  61 + </button>
  62 + </div>
  63 + <mat-divider></mat-divider>
61 64 </div>
62   - <mat-divider></mat-divider>
63 65 </div>
  66 + <span [fxShow]="!keyFiltersFormArray().length"
  67 + fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
  68 + class="no-data-found" translate>filter.no-key-filters</span>
  69 + </div>
  70 + <div style="margin-top: 16px;">
  71 + <button mat-button mat-raised-button color="primary"
  72 + [fxShow]="!disabled"
  73 + (click)="addKeyFilter()"
  74 + type="button"
  75 + matTooltip="{{ 'filter.add-key-filter' | translate }}"
  76 + matTooltipPosition="above">
  77 + {{ 'filter.add-key-filter' | translate }}
  78 + </button>
  79 + </div>
  80 + </mat-expansion-panel>
  81 + <mat-expansion-panel [expanded]="true">
  82 + <mat-expansion-panel-header>
  83 + <mat-panel-title>
  84 + <div translate>filter.preview</div>
  85 + </mat-panel-title>
  86 + </mat-expansion-panel-header>
  87 + <div class="tb-filter-preview">
  88 + <tb-filter-text [formControl]="keyFiltersControl"></tb-filter-text>
64 89 </div>
65   - <span [fxShow]="!keyFiltersFormArray().length"
66   - fxLayoutAlign="center center" [ngClass]="{'disabled': disabled}"
67   - class="no-data-found" translate>filter.no-key-filters</span>
68   - </div>
69   - <div style="margin-top: 16px;">
70   - <button mat-button mat-raised-button color="primary"
71   - [fxShow]="!disabled"
72   - (click)="addKeyFilter()"
73   - type="button"
74   - matTooltip="{{ 'filter.add-key-filter' | translate }}"
75   - matTooltipPosition="above">
76   - {{ 'filter.add-key-filter' | translate }}
77   - </button>
78   - </div>
79   - </mat-expansion-panel>
  90 + </mat-expansion-panel>
  91 + </mat-accordion>
80 92 </section>
... ...
... ... @@ -26,4 +26,17 @@
26 26 color: #666;
27 27 font-weight: 500;
28 28 }
  29 + .tb-filter-preview {
  30 + padding: 8px;
  31 + border: 1px groove rgba(0, 0, 0, .25);
  32 + border-radius: 4px;
  33 + }
  34 +}
  35 +
  36 +:host ::ng-deep {
  37 + .tb-filter-preview {
  38 + .tb-filter-text {
  39 + max-height: 200px;
  40 + }
  41 + }
29 42 }
... ...
... ... @@ -19,13 +19,18 @@ import {
19 19 AbstractControl,
20 20 ControlValueAccessor,
21 21 FormArray,
22   - FormBuilder,
  22 + FormBuilder, FormControl,
23 23 FormGroup,
24 24 NG_VALUE_ACCESSOR,
25 25 Validators
26 26 } from '@angular/forms';
27 27 import { Observable, Subscription } from 'rxjs';
28   -import { EntityKeyType, entityKeyTypeTranslationMap, KeyFilterInfo } from '@shared/models/query/query.models';
  28 +import {
  29 + EntityKeyType,
  30 + entityKeyTypeTranslationMap,
  31 + KeyFilter,
  32 + KeyFilterInfo, keyFilterInfosToKeyFilters
  33 +} from '@shared/models/query/query.models';
29 34 import { MatDialog } from '@angular/material/dialog';
30 35 import { deepClone } from '@core/utils';
31 36 import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component';
... ... @@ -48,12 +53,16 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
48 53
49 54 @Input() displayUserParameters = true;
50 55
  56 + @Input() allowUserDynamicSource = true;
  57 +
51 58 @Input() telemetryKeysOnly = false;
52 59
53 60 keyFilterListFormGroup: FormGroup;
54 61
55 62 entityKeyTypeTranslations = entityKeyTypeTranslationMap;
56 63
  64 + keyFiltersControl: FormControl;
  65 +
57 66 private propagateChange = null;
58 67
59 68 private valueChangeSubscription: Subscription = null;
... ... @@ -66,6 +75,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
66 75 this.keyFilterListFormGroup = this.fb.group({});
67 76 this.keyFilterListFormGroup.addControl('keyFilters',
68 77 this.fb.array([]));
  78 + this.keyFiltersControl = this.fb.control(null);
69 79 }
70 80
71 81 keyFiltersFormArray(): FormArray {
... ... @@ -83,8 +93,10 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
83 93 this.disabled = isDisabled;
84 94 if (this.disabled) {
85 95 this.keyFilterListFormGroup.disable({emitEvent: false});
  96 + this.keyFiltersControl.disable({emitEvent: false});
86 97 } else {
87 98 this.keyFilterListFormGroup.enable({emitEvent: false});
  99 + this.keyFiltersControl.enable({emitEvent: false});
88 100 }
89 101 }
90 102
... ... @@ -107,6 +119,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
107 119 } else {
108 120 this.keyFilterListFormGroup.enable({emitEvent: false});
109 121 }
  122 + const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters);
  123 + this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false});
110 124 }
111 125
112 126 public removeKeyFilter(index: number) {
... ... @@ -155,6 +169,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
155 169 isAdd,
156 170 readonly: this.disabled,
157 171 displayUserParameters: this.displayUserParameters,
  172 + allowUserDynamicSource: this.allowUserDynamicSource,
158 173 telemetryKeysOnly: this.telemetryKeysOnly
159 174 }
160 175 }).afterClosed();
... ... @@ -167,5 +182,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
167 182 } else {
168 183 this.propagateChange(null);
169 184 }
  185 + const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters);
  186 + this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false});
170 187 }
171 188 }
... ...
... ... @@ -24,7 +24,8 @@
24 24 </mat-option>
25 25 </mat-select>
26 26 </mat-form-field>
27   - <tb-filter-predicate-value fxFlex="60"
  27 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  28 + fxFlex="60"
28 29 [valueType]="valueType"
29 30 formControlName="value">
30 31 </tb-filter-predicate-value>
... ...
... ... @@ -40,6 +40,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
40 40
41 41 @Input() disabled: boolean;
42 42
  43 + @Input() allowUserDynamicSource = true;
  44 +
43 45 @Input() valueType: EntityKeyValueType;
44 46
45 47 numericFilterPredicateFormGroup: FormGroup;
... ...
... ... @@ -28,7 +28,8 @@
28 28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;">
29 29 </mat-checkbox>
30 30 </div>
31   - <tb-filter-predicate-value fxFlex="60"
  31 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  32 + fxFlex="60"
32 33 [valueType]="valueTypeEnum.STRING"
33 34 formControlName="value">
34 35 </tb-filter-predicate-value>
... ...
... ... @@ -40,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
40 40
41 41 @Input() disabled: boolean;
42 42
  43 + @Input() allowUserDynamicSource = true;
  44 +
43 45 valueTypeEnum = EntityKeyValueType;
44 46
45 47 stringFilterPredicateFormGroup: FormGroup;
... ...
... ... @@ -32,6 +32,7 @@
32 32 <div fxFlex fxLayout="column">
33 33 <tb-key-filter-list
34 34 [displayUserParameters]="false"
  35 + [allowUserDynamicSource]="false"
35 36 [telemetryKeysOnly]="true"
36 37 formControlName="keyFilters">
37 38 </tb-key-filter-list>
... ...
... ... @@ -25,7 +25,8 @@
25 25 <mat-select formControlName="severity"
26 26 required
27 27 placeholder="{{ 'device-profile.select-alarm-severity' | translate }}">
28   - <mat-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity">
  28 + <mat-option *ngFor="let alarmSeverity of alarmSeverities" [value]="alarmSeverity"
  29 + [disabled]="isDisabledSeverity(alarmSeverityEnum[alarmSeverity], $index)">
29 30 {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
30 31 </mat-option>
31 32 </mat-select>
... ...
... ... @@ -61,6 +61,8 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
61 61
62 62 createAlarmRulesFormGroup: FormGroup;
63 63
  64 + private usedSeverities: AlarmSeverity[] = [];
  65 +
64 66 private valueChangeSubscription: Subscription = null;
65 67
66 68 private propagateChange = (v: any) => { };
... ... @@ -121,6 +123,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
121 123 this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => {
122 124 this.updateModel();
123 125 });
  126 + this.updateUsedSeverities();
124 127 }
125 128
126 129 public removeCreateAlarmRule(index: number) {
... ... @@ -149,12 +152,26 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
149 152 };
150 153 }
151 154
  155 + public isDisabledSeverity(severity: AlarmSeverity, index: number): boolean {
  156 + const usedIndex = this.usedSeverities.indexOf(severity);
  157 + return usedIndex > -1 && usedIndex !== index;
  158 + }
  159 +
  160 + private updateUsedSeverities() {
  161 + this.usedSeverities = [];
  162 + const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value;
  163 + value.forEach((rule, index) => {
  164 + this.usedSeverities[index] = AlarmSeverity[rule.severity];
  165 + });
  166 + }
  167 +
152 168 private updateModel() {
153 169 const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value;
154 170 const createAlarmRules: {[severity: string]: AlarmRule} = {};
155 171 value.forEach(v => {
156 172 createAlarmRules[v.severity] = v.alarmRule;
157 173 });
  174 + this.updateUsedSeverities();
158 175 this.propagateChange(createAlarmRules);
159 176 }
160 177 }
... ...
... ... @@ -26,6 +26,7 @@
26 26 <mat-form-field floatLabel="always"
27 27 style="width: 600px;"
28 28 [fxShow]="expanded"
  29 + (keydown)="!disabled ? $event.stopPropagation() : null;"
29 30 (click)="!disabled ? $event.stopPropagation() : null;">
30 31 <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
31 32 <input required matInput formControlName="alarmType" placeholder="Enter alarm type">
... ... @@ -90,9 +91,28 @@
90 91 </div>
91 92 </mat-panel-title>
92 93 </mat-expansion-panel-header>
93   - <mat-checkbox formControlName="propagate" style="padding-bottom: 16px;">
  94 + <mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;">
94 95 {{ 'device-profile.propagate-alarm' | translate }}
95 96 </mat-checkbox>
96   - <div>TODO: Propagate relation types</div>
  97 + <section *ngIf="alarmFormGroup.get('propagate').value === true">
  98 + <mat-form-field floatLabel="always" class="mat-block">
  99 + <mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label>
  100 + <mat-chip-list #relationTypesChipList [disabled]="disabled">
  101 + <mat-chip
  102 + *ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;"
  103 + (removed)="removeRelationType(key)">
  104 + {{key}}
  105 + <mat-icon matChipRemove>close</mat-icon>
  106 + </mat-chip>
  107 + <input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}"
  108 + style="max-width: 200px;"
  109 + [matChipInputFor]="relationTypesChipList"
  110 + [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
  111 + (matChipInputTokenEnd)="addRelationType($event)"
  112 + [matChipInputAddOnBlur]="true">
  113 + </mat-chip-list>
  114 + <mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint>
  115 + </mat-form-field>
  116 + </section>
97 117 </mat-expansion-panel>
98 118 </mat-expansion-panel>
... ...
... ... @@ -27,6 +27,8 @@ import {
27 27 } from '@angular/forms';
28 28 import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models';
29 29 import { MatDialog } from '@angular/material/dialog';
  30 +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
  31 +import { MatChipInputEvent } from '@angular/material/chips';
30 32
31 33 @Component({
32 34 selector: 'tb-device-profile-alarm',
... ... @@ -53,6 +55,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
53 55 @Output()
54 56 removeAlarm = new EventEmitter();
55 57
  58 + separatorKeysCodes = [ENTER, COMMA, SEMICOLON];
  59 +
56 60 expanded = false;
57 61
58 62 private modelValue: DeviceProfileAlarm;
... ... @@ -124,6 +128,35 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
124 128 };
125 129 }
126 130
  131 + removeRelationType(key: string): void {
  132 + const keys: string[] = this.alarmFormGroup.get('propagateRelationTypes').value;
  133 + const index = keys.indexOf(key);
  134 + if (index >= 0) {
  135 + keys.splice(index, 1);
  136 + this.alarmFormGroup.get('propagateRelationTypes').setValue(keys, {emitEvent: true});
  137 + }
  138 + }
  139 +
  140 + addRelationType(event: MatChipInputEvent): void {
  141 + const input = event.input;
  142 + let value = event.value;
  143 + if ((value || '').trim()) {
  144 + value = value.trim();
  145 + let keys: string[] = this.alarmFormGroup.get('propagateRelationTypes').value;
  146 + if (!keys || keys.indexOf(value) === -1) {
  147 + if (!keys) {
  148 + keys = [];
  149 + }
  150 + keys.push(value);
  151 + this.alarmFormGroup.get('propagateRelationTypes').setValue(keys, {emitEvent: true});
  152 + }
  153 + }
  154 + if (input) {
  155 + input.value = '';
  156 + }
  157 + }
  158 +
  159 +
127 160 private updateModel() {
128 161 const value = this.alarmFormGroup.value;
129 162 this.modelValue = {...this.modelValue, ...value};
... ...
... ... @@ -453,6 +453,9 @@ function simpleKeyFilterPredicateToText(translate: TranslateService,
453 453 }
454 454
455 455 export function keyFilterInfosToKeyFilters(keyFilterInfos: Array<KeyFilterInfo>): Array<KeyFilter> {
  456 + if (!keyFilterInfos) {
  457 + return [];
  458 + }
456 459 const keyFilters: Array<KeyFilter> = [];
457 460 for (const keyFilterInfo of keyFilterInfos) {
458 461 const key = keyFilterInfo.key;
... ...
... ... @@ -834,6 +834,8 @@
834 834 "advanced-settings": "Advanced settings",
835 835 "alarm-rule-details": "Details",
836 836 "propagate-alarm": "Propagate alarm",
  837 + "alarm-rule-relation-types-list": "Relation types to propagate",
  838 + "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.",
837 839 "alarm-details": "Alarm details",
838 840 "alarm-rule-condition": "Alarm rule condition",
839 841 "enter-alarm-rule-condition-prompt": "Please add alarm rule condition",
... ... @@ -1302,6 +1304,7 @@
1302 1304 "ignore-case": "ignore case",
1303 1305 "value": "Value",
1304 1306 "remove-filter": "Remove filter",
  1307 + "preview": "Filter preview",
1305 1308 "no-filters": "No filters configured",
1306 1309 "add-filter": "Add filter",
1307 1310 "add-complex-filter": "Add complex filter",
... ...