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 | 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"> | ... | ... |
... | ... | @@ -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) => { | ... | ... |
... | ... | @@ -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" | ... | ... |
... | ... | @@ -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;"> </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 | 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; | ... | ... |
... | ... | @@ -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", | ... | ... |