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,7 +24,8 @@
24 </mat-option> 24 </mat-option>
25 </mat-select> 25 </mat-select>
26 </mat-form-field> 26 </mat-form-field>
27 - <tb-filter-predicate-value fxFlex="60" 27 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  28 + fxFlex="60"
28 [valueType]="valueTypeEnum.BOOLEAN" 29 [valueType]="valueTypeEnum.BOOLEAN"
29 formControlName="value"> 30 formControlName="value">
30 </tb-filter-predicate-value> 31 </tb-filter-predicate-value>
@@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On @@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On
39 39
40 @Input() disabled: boolean; 40 @Input() disabled: boolean;
41 41
  42 + @Input() allowUserDynamicSource = true;
  43 +
42 valueTypeEnum = EntityKeyValueType; 44 valueTypeEnum = EntityKeyValueType;
43 45
44 booleanFilterPredicateFormGroup: FormGroup; 46 booleanFilterPredicateFormGroup: FormGroup;
@@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
38 <tb-filter-predicate-list 38 <tb-filter-predicate-list
39 [valueType]="data.valueType" 39 [valueType]="data.valueType"
40 [displayUserParameters]="data.displayUserParameters" 40 [displayUserParameters]="data.displayUserParameters"
  41 + [allowUserDynamicSource]="data.allowUserDynamicSource"
41 [operation]="complexFilterFormGroup.get('operation').value" 42 [operation]="complexFilterFormGroup.get('operation').value"
42 [key]="data.key" 43 [key]="data.key"
43 formControlName="predicates"> 44 formControlName="predicates">
@@ -36,6 +36,7 @@ export interface ComplexFilterPredicateDialogData { @@ -36,6 +36,7 @@ export interface ComplexFilterPredicateDialogData {
36 isAdd: boolean; 36 isAdd: boolean;
37 valueType: EntityKeyValueType; 37 valueType: EntityKeyValueType;
38 displayUserParameters: boolean; 38 displayUserParameters: boolean;
  39 + allowUserDynamicSource: boolean;
39 } 40 }
40 41
41 @Component({ 42 @Component({
@@ -50,6 +50,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On @@ -50,6 +50,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
50 50
51 @Input() displayUserParameters = true; 51 @Input() displayUserParameters = true;
52 52
  53 + @Input() allowUserDynamicSource = true;
  54 +
53 private propagateChange = null; 55 private propagateChange = null;
54 56
55 private complexFilterPredicate: ComplexFilterPredicateInfo; 57 private complexFilterPredicate: ComplexFilterPredicateInfo;
@@ -86,7 +88,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On @@ -86,7 +88,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On
86 valueType: this.valueType, 88 valueType: this.valueType,
87 isAdd: false, 89 isAdd: false,
88 key: this.key, 90 key: this.key,
89 - displayUserParameters: this.displayUserParameters 91 + displayUserParameters: this.displayUserParameters,
  92 + allowUserDynamicSource: this.allowUserDynamicSource
90 } 93 }
91 }).afterClosed().subscribe( 94 }).afterClosed().subscribe(
92 (result) => { 95 (result) => {
@@ -52,6 +52,7 @@ @@ -52,6 +52,7 @@
52 fxFlex 52 fxFlex
53 [valueType]="valueType" 53 [valueType]="valueType"
54 [displayUserParameters]="displayUserParameters" 54 [displayUserParameters]="displayUserParameters"
  55 + [allowUserDynamicSource]="allowUserDynamicSource"
55 [key]="key" 56 [key]="key"
56 [formControl]="predicateControl"> 57 [formControl]="predicateControl">
57 </tb-filter-predicate> 58 </tb-filter-predicate>
@@ -64,6 +64,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni @@ -64,6 +64,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni
64 64
65 @Input() displayUserParameters = true; 65 @Input() displayUserParameters = true;
66 66
  67 + @Input() allowUserDynamicSource = true;
  68 +
67 filterListFormGroup: FormGroup; 69 filterListFormGroup: FormGroup;
68 70
69 valueTypeEnum = EntityKeyValueType; 71 valueTypeEnum = EntityKeyValueType;
@@ -156,7 +158,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni @@ -156,7 +158,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni
156 valueType: this.valueType, 158 valueType: this.valueType,
157 key: this.key, 159 key: this.key,
158 isAdd: true, 160 isAdd: true,
159 - displayUserParameters: this.displayUserParameters 161 + displayUserParameters: this.displayUserParameters,
  162 + allowUserDynamicSource: this.allowUserDynamicSource
160 } 163 }
161 }).afterClosed().pipe( 164 }).afterClosed().pipe(
162 map((result) => { 165 map((result) => {
@@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
55 {{'filter.no-dynamic-value' | translate}} 55 {{'filter.no-dynamic-value' | translate}}
56 </mat-option> 56 </mat-option>
57 <mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType"> 57 <mat-option *ngFor="let sourceType of dynamicValueSourceTypes" [value]="sourceType">
58 - {{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}} 58 + {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}}
59 </mat-option> 59 </mat-option>
60 </mat-select> 60 </mat-select>
61 </mat-form-field> 61 </mat-form-field>
@@ -47,12 +47,22 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn @@ -47,12 +47,22 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
47 @Input() disabled: boolean; 47 @Input() disabled: boolean;
48 48
49 @Input() 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 valueType: EntityKeyValueType; 59 valueType: EntityKeyValueType;
51 60
52 valueTypeEnum = EntityKeyValueType; 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 dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; 66 dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap;
57 67
58 filterPredicateValueFormGroup: FormGroup; 68 filterPredicateValueFormGroup: FormGroup;
@@ -19,20 +19,27 @@ @@ -19,20 +19,27 @@
19 <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateFormGroup"> 19 <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px" [formGroup]="filterPredicateFormGroup">
20 <div fxFlex fxLayout="column" [ngSwitch]="type"> 20 <div fxFlex fxLayout="column" [ngSwitch]="type">
21 <ng-template [ngSwitchCase]="filterPredicateType.STRING"> 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 </tb-string-filter-predicate> 25 </tb-string-filter-predicate>
24 </ng-template> 26 </ng-template>
25 <ng-template [ngSwitchCase]="filterPredicateType.NUMERIC"> 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 </tb-numeric-filter-predicate> 32 </tb-numeric-filter-predicate>
29 </ng-template> 33 </ng-template>
30 <ng-template [ngSwitchCase]="filterPredicateType.BOOLEAN"> 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 </tb-boolean-filter-predicate> 38 </tb-boolean-filter-predicate>
33 </ng-template> 39 </ng-template>
34 <ng-template [ngSwitchCase]="filterPredicateType.COMPLEX"> 40 <ng-template [ngSwitchCase]="filterPredicateType.COMPLEX">
35 <tb-complex-filter-predicate 41 <tb-complex-filter-predicate
  42 + [allowUserDynamicSource]="allowUserDynamicSource"
36 [key]="key" 43 [key]="key"
37 [valueType]="valueType" 44 [valueType]="valueType"
38 [displayUserParameters]="displayUserParameters" 45 [displayUserParameters]="displayUserParameters"
@@ -43,6 +43,8 @@ export class FilterPredicateComponent implements ControlValueAccessor, OnInit { @@ -43,6 +43,8 @@ export class FilterPredicateComponent implements ControlValueAccessor, OnInit {
43 43
44 @Input() displayUserParameters = true; 44 @Input() displayUserParameters = true;
45 45
  46 + @Input() allowUserDynamicSource = true;
  47 +
46 filterPredicateFormGroup: FormGroup; 48 filterPredicateFormGroup: FormGroup;
47 49
48 type: FilterPredicateType; 50 type: FilterPredicateType;
@@ -70,6 +70,7 @@ @@ -70,6 +70,7 @@
70 </mat-form-field> 70 </mat-form-field>
71 </section> 71 </section>
72 <tb-filter-predicate-list *ngIf="keyFilterFormGroup.get('valueType').value" 72 <tb-filter-predicate-list *ngIf="keyFilterFormGroup.get('valueType').value"
  73 + [allowUserDynamicSource]="data.allowUserDynamicSource"
73 [displayUserParameters]="data.displayUserParameters" 74 [displayUserParameters]="data.displayUserParameters"
74 [valueType]="keyFilterFormGroup.get('valueType').value" 75 [valueType]="keyFilterFormGroup.get('valueType').value"
75 [key]="keyFilterFormGroup.get('key.key').value" 76 [key]="keyFilterFormGroup.get('key.key').value"
@@ -40,6 +40,7 @@ export interface KeyFilterDialogData { @@ -40,6 +40,7 @@ export interface KeyFilterDialogData {
40 keyFilter: KeyFilterInfo; 40 keyFilter: KeyFilterInfo;
41 isAdd: boolean; 41 isAdd: boolean;
42 displayUserParameters: boolean; 42 displayUserParameters: boolean;
  43 + allowUserDynamicSource: boolean;
43 readonly: boolean; 44 readonly: boolean;
44 telemetryKeysOnly: boolean; 45 telemetryKeysOnly: boolean;
45 } 46 }
@@ -16,65 +16,77 @@ @@ -16,65 +16,77 @@
16 16
17 --> 17 -->
18 <section fxLayout="column" [formGroup]="keyFilterListFormGroup"> 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 </div> 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 </div> 64 </div>
62 - <mat-divider></mat-divider>  
63 </div> 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 </div> 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 </section> 92 </section>
@@ -26,4 +26,17 @@ @@ -26,4 +26,17 @@
26 color: #666; 26 color: #666;
27 font-weight: 500; 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,13 +19,18 @@ import {
19 AbstractControl, 19 AbstractControl,
20 ControlValueAccessor, 20 ControlValueAccessor,
21 FormArray, 21 FormArray,
22 - FormBuilder, 22 + FormBuilder, FormControl,
23 FormGroup, 23 FormGroup,
24 NG_VALUE_ACCESSOR, 24 NG_VALUE_ACCESSOR,
25 Validators 25 Validators
26 } from '@angular/forms'; 26 } from '@angular/forms';
27 import { Observable, Subscription } from 'rxjs'; 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 import { MatDialog } from '@angular/material/dialog'; 34 import { MatDialog } from '@angular/material/dialog';
30 import { deepClone } from '@core/utils'; 35 import { deepClone } from '@core/utils';
31 import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; 36 import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component';
@@ -48,12 +53,16 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -48,12 +53,16 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
48 53
49 @Input() displayUserParameters = true; 54 @Input() displayUserParameters = true;
50 55
  56 + @Input() allowUserDynamicSource = true;
  57 +
51 @Input() telemetryKeysOnly = false; 58 @Input() telemetryKeysOnly = false;
52 59
53 keyFilterListFormGroup: FormGroup; 60 keyFilterListFormGroup: FormGroup;
54 61
55 entityKeyTypeTranslations = entityKeyTypeTranslationMap; 62 entityKeyTypeTranslations = entityKeyTypeTranslationMap;
56 63
  64 + keyFiltersControl: FormControl;
  65 +
57 private propagateChange = null; 66 private propagateChange = null;
58 67
59 private valueChangeSubscription: Subscription = null; 68 private valueChangeSubscription: Subscription = null;
@@ -66,6 +75,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -66,6 +75,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
66 this.keyFilterListFormGroup = this.fb.group({}); 75 this.keyFilterListFormGroup = this.fb.group({});
67 this.keyFilterListFormGroup.addControl('keyFilters', 76 this.keyFilterListFormGroup.addControl('keyFilters',
68 this.fb.array([])); 77 this.fb.array([]));
  78 + this.keyFiltersControl = this.fb.control(null);
69 } 79 }
70 80
71 keyFiltersFormArray(): FormArray { 81 keyFiltersFormArray(): FormArray {
@@ -83,8 +93,10 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -83,8 +93,10 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
83 this.disabled = isDisabled; 93 this.disabled = isDisabled;
84 if (this.disabled) { 94 if (this.disabled) {
85 this.keyFilterListFormGroup.disable({emitEvent: false}); 95 this.keyFilterListFormGroup.disable({emitEvent: false});
  96 + this.keyFiltersControl.disable({emitEvent: false});
86 } else { 97 } else {
87 this.keyFilterListFormGroup.enable({emitEvent: false}); 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,6 +119,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
107 } else { 119 } else {
108 this.keyFilterListFormGroup.enable({emitEvent: false}); 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 public removeKeyFilter(index: number) { 126 public removeKeyFilter(index: number) {
@@ -155,6 +169,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -155,6 +169,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
155 isAdd, 169 isAdd,
156 readonly: this.disabled, 170 readonly: this.disabled,
157 displayUserParameters: this.displayUserParameters, 171 displayUserParameters: this.displayUserParameters,
  172 + allowUserDynamicSource: this.allowUserDynamicSource,
158 telemetryKeysOnly: this.telemetryKeysOnly 173 telemetryKeysOnly: this.telemetryKeysOnly
159 } 174 }
160 }).afterClosed(); 175 }).afterClosed();
@@ -167,5 +182,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @@ -167,5 +182,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit {
167 } else { 182 } else {
168 this.propagateChange(null); 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,7 +24,8 @@
24 </mat-option> 24 </mat-option>
25 </mat-select> 25 </mat-select>
26 </mat-form-field> 26 </mat-form-field>
27 - <tb-filter-predicate-value fxFlex="60" 27 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  28 + fxFlex="60"
28 [valueType]="valueType" 29 [valueType]="valueType"
29 formControlName="value"> 30 formControlName="value">
30 </tb-filter-predicate-value> 31 </tb-filter-predicate-value>
@@ -40,6 +40,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On @@ -40,6 +40,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On
40 40
41 @Input() disabled: boolean; 41 @Input() disabled: boolean;
42 42
  43 + @Input() allowUserDynamicSource = true;
  44 +
43 @Input() valueType: EntityKeyValueType; 45 @Input() valueType: EntityKeyValueType;
44 46
45 numericFilterPredicateFormGroup: FormGroup; 47 numericFilterPredicateFormGroup: FormGroup;
@@ -28,7 +28,8 @@ @@ -28,7 +28,8 @@
28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;"> 28 <mat-checkbox fxLayout="row" fxLayoutAlign="center" formControlName="ignoreCase" style="min-width: 70px;">
29 </mat-checkbox> 29 </mat-checkbox>
30 </div> 30 </div>
31 - <tb-filter-predicate-value fxFlex="60" 31 + <tb-filter-predicate-value [allowUserDynamicSource]="allowUserDynamicSource"
  32 + fxFlex="60"
32 [valueType]="valueTypeEnum.STRING" 33 [valueType]="valueTypeEnum.STRING"
33 formControlName="value"> 34 formControlName="value">
34 </tb-filter-predicate-value> 35 </tb-filter-predicate-value>
@@ -40,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @@ -40,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI
40 40
41 @Input() disabled: boolean; 41 @Input() disabled: boolean;
42 42
  43 + @Input() allowUserDynamicSource = true;
  44 +
43 valueTypeEnum = EntityKeyValueType; 45 valueTypeEnum = EntityKeyValueType;
44 46
45 stringFilterPredicateFormGroup: FormGroup; 47 stringFilterPredicateFormGroup: FormGroup;
@@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
32 <div fxFlex fxLayout="column"> 32 <div fxFlex fxLayout="column">
33 <tb-key-filter-list 33 <tb-key-filter-list
34 [displayUserParameters]="false" 34 [displayUserParameters]="false"
  35 + [allowUserDynamicSource]="false"
35 [telemetryKeysOnly]="true" 36 [telemetryKeysOnly]="true"
36 formControlName="keyFilters"> 37 formControlName="keyFilters">
37 </tb-key-filter-list> 38 </tb-key-filter-list>
@@ -25,7 +25,8 @@ @@ -25,7 +25,8 @@
25 <mat-select formControlName="severity" 25 <mat-select formControlName="severity"
26 required 26 required
27 placeholder="{{ 'device-profile.select-alarm-severity' | translate }}"> 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 {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }} 30 {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }}
30 </mat-option> 31 </mat-option>
31 </mat-select> 32 </mat-select>
@@ -61,6 +61,8 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, @@ -61,6 +61,8 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
61 61
62 createAlarmRulesFormGroup: FormGroup; 62 createAlarmRulesFormGroup: FormGroup;
63 63
  64 + private usedSeverities: AlarmSeverity[] = [];
  65 +
64 private valueChangeSubscription: Subscription = null; 66 private valueChangeSubscription: Subscription = null;
65 67
66 private propagateChange = (v: any) => { }; 68 private propagateChange = (v: any) => { };
@@ -121,6 +123,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, @@ -121,6 +123,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
121 this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => { 123 this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => {
122 this.updateModel(); 124 this.updateModel();
123 }); 125 });
  126 + this.updateUsedSeverities();
124 } 127 }
125 128
126 public removeCreateAlarmRule(index: number) { 129 public removeCreateAlarmRule(index: number) {
@@ -149,12 +152,26 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, @@ -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 private updateModel() { 168 private updateModel() {
153 const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; 169 const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value;
154 const createAlarmRules: {[severity: string]: AlarmRule} = {}; 170 const createAlarmRules: {[severity: string]: AlarmRule} = {};
155 value.forEach(v => { 171 value.forEach(v => {
156 createAlarmRules[v.severity] = v.alarmRule; 172 createAlarmRules[v.severity] = v.alarmRule;
157 }); 173 });
  174 + this.updateUsedSeverities();
158 this.propagateChange(createAlarmRules); 175 this.propagateChange(createAlarmRules);
159 } 176 }
160 } 177 }
@@ -26,6 +26,7 @@ @@ -26,6 +26,7 @@
26 <mat-form-field floatLabel="always" 26 <mat-form-field floatLabel="always"
27 style="width: 600px;" 27 style="width: 600px;"
28 [fxShow]="expanded" 28 [fxShow]="expanded"
  29 + (keydown)="!disabled ? $event.stopPropagation() : null;"
29 (click)="!disabled ? $event.stopPropagation() : null;"> 30 (click)="!disabled ? $event.stopPropagation() : null;">
30 <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label> 31 <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
31 <input required matInput formControlName="alarmType" placeholder="Enter alarm type"> 32 <input required matInput formControlName="alarmType" placeholder="Enter alarm type">
@@ -90,9 +91,28 @@ @@ -90,9 +91,28 @@
90 </div> 91 </div>
91 </mat-panel-title> 92 </mat-panel-title>
92 </mat-expansion-panel-header> 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 {{ 'device-profile.propagate-alarm' | translate }} 95 {{ 'device-profile.propagate-alarm' | translate }}
95 </mat-checkbox> 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 </mat-expansion-panel> 117 </mat-expansion-panel>
98 </mat-expansion-panel> 118 </mat-expansion-panel>
@@ -27,6 +27,8 @@ import { @@ -27,6 +27,8 @@ import {
27 } from '@angular/forms'; 27 } from '@angular/forms';
28 import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models'; 28 import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models';
29 import { MatDialog } from '@angular/material/dialog'; 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 @Component({ 33 @Component({
32 selector: 'tb-device-profile-alarm', 34 selector: 'tb-device-profile-alarm',
@@ -53,6 +55,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit @@ -53,6 +55,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
53 @Output() 55 @Output()
54 removeAlarm = new EventEmitter(); 56 removeAlarm = new EventEmitter();
55 57
  58 + separatorKeysCodes = [ENTER, COMMA, SEMICOLON];
  59 +
56 expanded = false; 60 expanded = false;
57 61
58 private modelValue: DeviceProfileAlarm; 62 private modelValue: DeviceProfileAlarm;
@@ -124,6 +128,35 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit @@ -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 private updateModel() { 160 private updateModel() {
128 const value = this.alarmFormGroup.value; 161 const value = this.alarmFormGroup.value;
129 this.modelValue = {...this.modelValue, ...value}; 162 this.modelValue = {...this.modelValue, ...value};
@@ -453,6 +453,9 @@ function simpleKeyFilterPredicateToText(translate: TranslateService, @@ -453,6 +453,9 @@ function simpleKeyFilterPredicateToText(translate: TranslateService,
453 } 453 }
454 454
455 export function keyFilterInfosToKeyFilters(keyFilterInfos: Array<KeyFilterInfo>): Array<KeyFilter> { 455 export function keyFilterInfosToKeyFilters(keyFilterInfos: Array<KeyFilterInfo>): Array<KeyFilter> {
  456 + if (!keyFilterInfos) {
  457 + return [];
  458 + }
456 const keyFilters: Array<KeyFilter> = []; 459 const keyFilters: Array<KeyFilter> = [];
457 for (const keyFilterInfo of keyFilterInfos) { 460 for (const keyFilterInfo of keyFilterInfos) {
458 const key = keyFilterInfo.key; 461 const key = keyFilterInfo.key;
@@ -834,6 +834,8 @@ @@ -834,6 +834,8 @@
834 "advanced-settings": "Advanced settings", 834 "advanced-settings": "Advanced settings",
835 "alarm-rule-details": "Details", 835 "alarm-rule-details": "Details",
836 "propagate-alarm": "Propagate alarm", 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 "alarm-details": "Alarm details", 839 "alarm-details": "Alarm details",
838 "alarm-rule-condition": "Alarm rule condition", 840 "alarm-rule-condition": "Alarm rule condition",
839 "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", 841 "enter-alarm-rule-condition-prompt": "Please add alarm rule condition",
@@ -1302,6 +1304,7 @@ @@ -1302,6 +1304,7 @@
1302 "ignore-case": "ignore case", 1304 "ignore-case": "ignore case",
1303 "value": "Value", 1305 "value": "Value",
1304 "remove-filter": "Remove filter", 1306 "remove-filter": "Remove filter",
  1307 + "preview": "Filter preview",
1305 "no-filters": "No filters configured", 1308 "no-filters": "No filters configured",
1306 "add-filter": "Add filter", 1309 "add-filter": "Add filter",
1307 "add-complex-filter": "Add complex filter", 1310 "add-complex-filter": "Add complex filter",