Commit c4e67215969501693d89bc6a8659dbadc98fb458
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;"> </span> | ||
31 | - <span [fxShow]="disabled" style="min-width: 40px;"> </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;"> </span> | ||
32 | + <span [fxShow]="disabled" style="min-width: 40px;"> </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", |