Commit 034af204f02aedbf5440ed4fe34b2ccddc88c746
1 parent
5d30243e
UI: Added key name autocomplete in alarms rules to device profile
Showing
20 changed files
with
188 additions
and
45 deletions
... | ... | @@ -69,4 +69,20 @@ export class DeviceProfileService { |
69 | 69 | return this.http.get<PageData<DeviceProfileInfo>>(url, defaultHttpOptionsFromConfig(config)); |
70 | 70 | } |
71 | 71 | |
72 | + public getDeviceProfileDevicesAttributesKeys(deviceProfileId?: string, config?: RequestConfig): Observable<Array<string>> { | |
73 | + let url = `/api/deviceProfile/devices/keys/attributes`; | |
74 | + if (isDefinedAndNotNull(deviceProfileId)) { | |
75 | + url += `?deviceProfileId=${deviceProfileId}`; | |
76 | + } | |
77 | + return this.http.get<Array<string>>(url, defaultHttpOptionsFromConfig(config)); | |
78 | + } | |
79 | + | |
80 | + public getDeviceProfileDevicesTimeseriesKeys(deviceProfileId?: string, config?: RequestConfig): Observable<Array<string>> { | |
81 | + let url = `/api/deviceProfile/devices/keys/timeseries`; | |
82 | + if (isDefinedAndNotNull(deviceProfileId)) { | |
83 | + url += `?deviceProfileId=${deviceProfileId}`; | |
84 | + } | |
85 | + return this.http.get<Array<string>>(url, defaultHttpOptionsFromConfig(config)); | |
86 | + } | |
87 | + | |
72 | 88 | } | ... | ... |
... | ... | @@ -40,11 +40,19 @@ |
40 | 40 | <mat-form-field fxFlex="60" class="mat-block"> |
41 | 41 | <mat-label translate>filter.key-name</mat-label> |
42 | 42 | <input matInput required formControlName="key" |
43 | - [matAutocomplete]="auto" | |
44 | - [matAutocompleteDisabled]="keyFilterFormGroup.get('key.type').value !== entityField"> | |
45 | - <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete"> | |
46 | - <mat-option *ngFor="let option of filteredEntityFields | async" [value]="option"> | |
47 | - {{option}} | |
43 | + #keyNameInput | |
44 | + (focusin)="onFocus()" | |
45 | + [matAutocomplete]="keyName" | |
46 | + [matAutocompleteDisabled]="!showAutocomplete"> | |
47 | + <button *ngIf="keyFilterFormGroup.get('key.key').value && showAutocomplete" | |
48 | + type="button" | |
49 | + matSuffix mat-button mat-icon-button aria-label="Clear" | |
50 | + (click)="clear()"> | |
51 | + <mat-icon class="material-icons">close</mat-icon> | |
52 | + </button> | |
53 | + <mat-autocomplete autoActiveFirstOption #keyName="matAutocomplete"> | |
54 | + <mat-option *ngFor="let keyName of filteredKeysName | async" [value]="keyName"> | |
55 | + <span [innerHTML]="keyName | highlight:searchText"></span> | |
48 | 56 | </mat-option> |
49 | 57 | </mat-autocomplete> |
50 | 58 | <mat-error *ngIf="keyFilterFormGroup.get('key.key').hasError('required')"> | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; | |
17 | +import { Component, ElementRef, Inject, OnDestroy, OnInit, SkipSelf, ViewChild } from '@angular/core'; | |
18 | 18 | import { ErrorStateMatcher } from '@angular/material/core'; |
19 | 19 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
20 | 20 | import { Store } from '@ngrx/store'; |
... | ... | @@ -32,9 +32,12 @@ import { |
32 | 32 | } from '@shared/models/query/query.models'; |
33 | 33 | import { DialogService } from '@core/services/dialog.service'; |
34 | 34 | import { TranslateService } from '@ngx-translate/core'; |
35 | -import { EntityField, entityFields } from '@shared/models/entity.models'; | |
36 | -import { Observable } from 'rxjs'; | |
37 | -import { filter, map, startWith } from 'rxjs/operators'; | |
35 | +import { entityFields } from '@shared/models/entity.models'; | |
36 | +import { Observable, of, Subject } from 'rxjs'; | |
37 | +import { filter, map, mergeMap, publishReplay, refCount, startWith, takeUntil } from 'rxjs/operators'; | |
38 | +import { isDefined } from '@core/utils'; | |
39 | +import { EntityId } from '@shared/models/id/entity-id'; | |
40 | +import { DeviceProfileService } from '@core/http/device-profile.service'; | |
38 | 41 | |
39 | 42 | export interface KeyFilterDialogData { |
40 | 43 | keyFilter: KeyFilterInfo; |
... | ... | @@ -43,6 +46,7 @@ export interface KeyFilterDialogData { |
43 | 46 | allowUserDynamicSource: boolean; |
44 | 47 | readonly: boolean; |
45 | 48 | telemetryKeysOnly: boolean; |
49 | + entityId?: EntityId; | |
46 | 50 | } |
47 | 51 | |
48 | 52 | @Component({ |
... | ... | @@ -53,7 +57,13 @@ export interface KeyFilterDialogData { |
53 | 57 | }) |
54 | 58 | export class KeyFilterDialogComponent extends |
55 | 59 | DialogComponent<KeyFilterDialogComponent, KeyFilterInfo> |
56 | - implements OnInit, ErrorStateMatcher { | |
60 | + implements OnInit, OnDestroy, ErrorStateMatcher { | |
61 | + | |
62 | + @ViewChild('keyNameInput', {static: true}) private keyNameInput: ElementRef; | |
63 | + | |
64 | + private dirty = false; | |
65 | + private entityKeysName: Observable<Array<string>>; | |
66 | + private destroy$ = new Subject(); | |
57 | 67 | |
58 | 68 | keyFilterFormGroup: FormGroup; |
59 | 69 | |
... | ... | @@ -72,19 +82,18 @@ export class KeyFilterDialogComponent extends |
72 | 82 | |
73 | 83 | submitted = false; |
74 | 84 | |
75 | - entityFields: { [fieldName: string]: EntityField }; | |
76 | - | |
77 | - entityFieldsList: string[]; | |
85 | + showAutocomplete = false; | |
78 | 86 | |
79 | - readonly entityField = EntityKeyType.ENTITY_FIELD; | |
87 | + filteredKeysName: Observable<Array<string>>; | |
80 | 88 | |
81 | - filteredEntityFields: Observable<string[]>; | |
89 | + searchText = ''; | |
82 | 90 | |
83 | 91 | constructor(protected store: Store<AppState>, |
84 | 92 | protected router: Router, |
85 | 93 | @Inject(MAT_DIALOG_DATA) public data: KeyFilterDialogData, |
86 | 94 | @SkipSelf() private errorStateMatcher: ErrorStateMatcher, |
87 | 95 | public dialogRef: MatDialogRef<KeyFilterDialogComponent, KeyFilterInfo>, |
96 | + private deviceProfileService: DeviceProfileService, | |
88 | 97 | private dialogs: DialogService, |
89 | 98 | private translate: TranslateService, |
90 | 99 | private fb: FormBuilder) { |
... | ... | @@ -104,7 +113,9 @@ export class KeyFilterDialogComponent extends |
104 | 113 | ); |
105 | 114 | |
106 | 115 | if (!this.data.readonly) { |
107 | - this.keyFilterFormGroup.get('valueType').valueChanges.subscribe((valueType: EntityKeyValueType) => { | |
116 | + this.keyFilterFormGroup.get('valueType').valueChanges.pipe( | |
117 | + takeUntil(this.destroy$) | |
118 | + ).subscribe((valueType: EntityKeyValueType) => { | |
108 | 119 | const prevValue: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; |
109 | 120 | const predicates: KeyFilterPredicate[] = this.keyFilterFormGroup.get('predicates').value; |
110 | 121 | if (prevValue && prevValue !== valueType && predicates && predicates.length) { |
... | ... | @@ -121,11 +132,26 @@ export class KeyFilterDialogComponent extends |
121 | 132 | } |
122 | 133 | }); |
123 | 134 | |
135 | + this.keyFilterFormGroup.get('key.type').valueChanges.pipe( | |
136 | + startWith(this.data.keyFilter.key.type), | |
137 | + takeUntil(this.destroy$) | |
138 | + ).subscribe((type: EntityKeyType) => { | |
139 | + if (type === EntityKeyType.ENTITY_FIELD || isDefined(this.data.entityId)) { | |
140 | + this.entityKeysName = null; | |
141 | + this.dirty = false; | |
142 | + this.showAutocomplete = true; | |
143 | + } else { | |
144 | + this.showAutocomplete = false; | |
145 | + } | |
146 | + }); | |
147 | + | |
124 | 148 | this.keyFilterFormGroup.get('key.key').valueChanges.pipe( |
125 | - filter((keyName) => this.keyFilterFormGroup.get('key.type').value === this.entityField && this.entityFields.hasOwnProperty(keyName)) | |
149 | + filter((keyName) => | |
150 | + this.keyFilterFormGroup.get('key.type').value === EntityKeyType.ENTITY_FIELD && entityFields.hasOwnProperty(keyName)), | |
151 | + takeUntil(this.destroy$) | |
126 | 152 | ).subscribe((keyName: string) => { |
127 | 153 | const prevValueType: EntityKeyValueType = this.keyFilterFormGroup.value.valueType; |
128 | - const newValueType = this.entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING; | |
154 | + const newValueType = entityFields[keyName]?.time ? EntityKeyValueType.DATE_TIME : EntityKeyValueType.STRING; | |
129 | 155 | if (prevValueType !== newValueType) { |
130 | 156 | this.keyFilterFormGroup.get('valueType').patchValue(newValueType, {emitEvent: false}); |
131 | 157 | } |
... | ... | @@ -133,18 +159,20 @@ export class KeyFilterDialogComponent extends |
133 | 159 | } else { |
134 | 160 | this.keyFilterFormGroup.disable({emitEvent: false}); |
135 | 161 | } |
162 | + } | |
136 | 163 | |
137 | - this.entityFields = entityFields; | |
138 | - this.entityFieldsList = Object.values(entityFields).map(entityField => entityField.keyName).sort(); | |
164 | + ngOnInit() { | |
165 | + this.filteredKeysName = this.keyFilterFormGroup.get('key.key').valueChanges | |
166 | + .pipe( | |
167 | + map(value => value ? value : ''), | |
168 | + mergeMap(name => this.fetchEntityName(name)) | |
169 | + ); | |
139 | 170 | } |
140 | 171 | |
141 | - ngOnInit(): void { | |
142 | - this.filteredEntityFields = this.keyFilterFormGroup.get('key.key').valueChanges.pipe( | |
143 | - startWith(''), | |
144 | - map(value => { | |
145 | - return this.entityFieldsList.filter(option => option.startsWith(value)); | |
146 | - }) | |
147 | - ); | |
172 | + ngOnDestroy() { | |
173 | + super.ngOnDestroy(); | |
174 | + this.destroy$.next(); | |
175 | + this.destroy$.complete(); | |
148 | 176 | } |
149 | 177 | |
150 | 178 | isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { |
... | ... | @@ -157,6 +185,21 @@ export class KeyFilterDialogComponent extends |
157 | 185 | this.dialogRef.close(null); |
158 | 186 | } |
159 | 187 | |
188 | + clear() { | |
189 | + this.keyFilterFormGroup.get('key.key').patchValue('', {emitEvent: true}); | |
190 | + setTimeout(() => { | |
191 | + this.keyNameInput.nativeElement.blur(); | |
192 | + this.keyNameInput.nativeElement.focus(); | |
193 | + }, 0); | |
194 | + } | |
195 | + | |
196 | + onFocus() { | |
197 | + if (!this.dirty && this.showAutocomplete) { | |
198 | + this.keyFilterFormGroup.get('key.key').updateValueAndValidity({onlySelf: true, emitEvent: true}); | |
199 | + this.dirty = true; | |
200 | + } | |
201 | + } | |
202 | + | |
160 | 203 | save(): void { |
161 | 204 | this.submitted = true; |
162 | 205 | if (this.keyFilterFormGroup.valid) { |
... | ... | @@ -164,4 +207,41 @@ export class KeyFilterDialogComponent extends |
164 | 207 | this.dialogRef.close(keyFilter); |
165 | 208 | } |
166 | 209 | } |
210 | + | |
211 | + private fetchEntityName(searchText?: string): Observable<Array<string>> { | |
212 | + this.searchText = searchText; | |
213 | + return this.getEntityKeys().pipe( | |
214 | + map(keys => searchText ? keys.filter(key => key.toUpperCase().startsWith(searchText.toUpperCase())) : keys) | |
215 | + ); | |
216 | + } | |
217 | + | |
218 | + private getEntityKeys(): Observable<Array<string>> { | |
219 | + if (!this.entityKeysName) { | |
220 | + let keyNameObservable: Observable<Array<string>>; | |
221 | + switch (this.keyFilterFormGroup.get('key.type').value) { | |
222 | + case EntityKeyType.ENTITY_FIELD: | |
223 | + keyNameObservable = of(Object.values(entityFields).map(entityField => entityField.keyName).sort()); | |
224 | + break; | |
225 | + case EntityKeyType.ATTRIBUTE: | |
226 | + keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesAttributesKeys( | |
227 | + this.data.entityId?.id, | |
228 | + {ignoreLoading: true} | |
229 | + ); | |
230 | + break; | |
231 | + case EntityKeyType.TIME_SERIES: | |
232 | + keyNameObservable = this.deviceProfileService.getDeviceProfileDevicesTimeseriesKeys( | |
233 | + this.data.entityId?.id, | |
234 | + {ignoreLoading: true} | |
235 | + ); | |
236 | + break; | |
237 | + default: | |
238 | + keyNameObservable = of([]); | |
239 | + } | |
240 | + this.entityKeysName = keyNameObservable.pipe( | |
241 | + publishReplay(1), | |
242 | + refCount() | |
243 | + ); | |
244 | + } | |
245 | + return this.entityKeysName; | |
246 | + } | |
167 | 247 | } | ... | ... |
... | ... | @@ -19,7 +19,8 @@ import { |
19 | 19 | AbstractControl, |
20 | 20 | ControlValueAccessor, |
21 | 21 | FormArray, |
22 | - FormBuilder, FormControl, | |
22 | + FormBuilder, | |
23 | + FormControl, | |
23 | 24 | FormGroup, |
24 | 25 | NG_VALUE_ACCESSOR, |
25 | 26 | Validators |
... | ... | @@ -28,12 +29,13 @@ import { Observable, Subscription } from 'rxjs'; |
28 | 29 | import { |
29 | 30 | EntityKeyType, |
30 | 31 | entityKeyTypeTranslationMap, |
31 | - KeyFilter, | |
32 | - KeyFilterInfo, keyFilterInfosToKeyFilters | |
32 | + KeyFilterInfo, | |
33 | + keyFilterInfosToKeyFilters | |
33 | 34 | } from '@shared/models/query/query.models'; |
34 | 35 | import { MatDialog } from '@angular/material/dialog'; |
35 | 36 | import { deepClone } from '@core/utils'; |
36 | 37 | import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; |
38 | +import { EntityId } from '@shared/models/id/entity-id'; | |
37 | 39 | |
38 | 40 | @Component({ |
39 | 41 | selector: 'tb-key-filter-list', |
... | ... | @@ -57,6 +59,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { |
57 | 59 | |
58 | 60 | @Input() telemetryKeysOnly = false; |
59 | 61 | |
62 | + @Input() entityId: EntityId; | |
63 | + | |
60 | 64 | keyFilterListFormGroup: FormGroup; |
61 | 65 | |
62 | 66 | entityKeyTypeTranslations = entityKeyTypeTranslationMap; |
... | ... | @@ -170,7 +174,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { |
170 | 174 | readonly: this.disabled, |
171 | 175 | displayUserParameters: this.displayUserParameters, |
172 | 176 | allowUserDynamicSource: this.allowUserDynamicSource, |
173 | - telemetryKeysOnly: this.telemetryKeysOnly | |
177 | + telemetryKeysOnly: this.telemetryKeysOnly, | |
178 | + entityId: this.entityId | |
174 | 179 | } |
175 | 180 | }).afterClosed(); |
176 | 181 | } | ... | ... |
... | ... | @@ -96,7 +96,8 @@ |
96 | 96 | {count: alarmRulesFormGroup.get('alarms').value ? |
97 | 97 | alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template> |
98 | 98 | <tb-device-profile-alarms |
99 | - formControlName="alarms"> | |
99 | + formControlName="alarms" | |
100 | + [deviceProfileId]="null"> | |
100 | 101 | </tb-device-profile-alarms> |
101 | 102 | </form> |
102 | 103 | </mat-step> | ... | ... |
... | ... | @@ -22,15 +22,16 @@ import { AppState } from '@core/core.state'; |
22 | 22 | import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; |
23 | 23 | import { Router } from '@angular/router'; |
24 | 24 | import { DialogComponent } from '@app/shared/components/dialog.component'; |
25 | -import { UtilsService } from '@core/services/utils.service'; | |
26 | 25 | import { TranslateService } from '@ngx-translate/core'; |
27 | -import { KeyFilter, keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/models/query/query.models'; | |
26 | +import { keyFilterInfosToKeyFilters, keyFiltersToKeyFilterInfos } from '@shared/models/query/query.models'; | |
28 | 27 | import { AlarmCondition, AlarmConditionType, AlarmConditionTypeTranslationMap } from '@shared/models/device.models'; |
29 | 28 | import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; |
29 | +import { EntityId } from '@shared/models/id/entity-id'; | |
30 | 30 | |
31 | 31 | export interface AlarmRuleConditionDialogData { |
32 | 32 | readonly: boolean; |
33 | 33 | condition: AlarmCondition; |
34 | + entityId?: EntityId; | |
34 | 35 | } |
35 | 36 | |
36 | 37 | @Component({ |
... | ... | @@ -50,6 +51,7 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule |
50 | 51 | |
51 | 52 | readonly = this.data.readonly; |
52 | 53 | condition = this.data.condition; |
54 | + entityId = this.data.entityId; | |
53 | 55 | |
54 | 56 | conditionFormGroup: FormGroup; |
55 | 57 | |
... | ... | @@ -61,7 +63,6 @@ export class AlarmRuleConditionDialogComponent extends DialogComponent<AlarmRule |
61 | 63 | @SkipSelf() private errorStateMatcher: ErrorStateMatcher, |
62 | 64 | public dialogRef: MatDialogRef<AlarmRuleConditionDialogComponent, AlarmCondition>, |
63 | 65 | private fb: FormBuilder, |
64 | - private utils: UtilsService, | |
65 | 66 | public translate: TranslateService) { |
66 | 67 | super(store, router, dialogRef); |
67 | 68 | ... | ... |
... | ... | @@ -33,6 +33,7 @@ import { |
33 | 33 | AlarmRuleConditionDialogData |
34 | 34 | } from '@home/components/profile/alarm/alarm-rule-condition-dialog.component'; |
35 | 35 | import { TimeUnit } from '@shared/models/time/time.models'; |
36 | +import { EntityId } from '@shared/models/id/entity-id'; | |
36 | 37 | |
37 | 38 | @Component({ |
38 | 39 | selector: 'tb-alarm-rule-condition', |
... | ... | @@ -56,6 +57,9 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit |
56 | 57 | @Input() |
57 | 58 | disabled: boolean; |
58 | 59 | |
60 | + @Input() | |
61 | + deviceProfileId: EntityId; | |
62 | + | |
59 | 63 | alarmRuleConditionFormGroup: FormGroup; |
60 | 64 | |
61 | 65 | specText = ''; |
... | ... | @@ -123,7 +127,8 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit |
123 | 127 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
124 | 128 | data: { |
125 | 129 | readonly: this.disabled, |
126 | - condition: this.disabled ? this.modelValue : deepClone(this.modelValue) | |
130 | + condition: this.disabled ? this.modelValue : deepClone(this.modelValue), | |
131 | + entityId: this.deviceProfileId | |
127 | 132 | } |
128 | 133 | }).afterClosed().subscribe((result) => { |
129 | 134 | if (result) { | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | --> |
18 | 18 | <div fxLayout="column" [formGroup]="alarmRuleFormGroup"> |
19 | - <tb-alarm-rule-condition formControlName="condition"> | |
19 | + <tb-alarm-rule-condition formControlName="condition" [deviceProfileId]="deviceProfileId"> | |
20 | 20 | </tb-alarm-rule-condition> |
21 | 21 | <tb-alarm-schedule-info formControlName="schedule"> |
22 | 22 | </tb-alarm-schedule-info> | ... | ... |
... | ... | @@ -33,6 +33,7 @@ import { |
33 | 33 | EditAlarmDetailsDialogComponent, |
34 | 34 | EditAlarmDetailsDialogData |
35 | 35 | } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; |
36 | +import { EntityId } from '@shared/models/id/entity-id'; | |
36 | 37 | |
37 | 38 | @Component({ |
38 | 39 | selector: 'tb-alarm-rule', |
... | ... | @@ -65,6 +66,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat |
65 | 66 | this.requiredValue = coerceBooleanProperty(value); |
66 | 67 | } |
67 | 68 | |
69 | + @Input() | |
70 | + deviceProfileId: EntityId; | |
71 | + | |
68 | 72 | private modelValue: AlarmRule; |
69 | 73 | |
70 | 74 | alarmRuleFormGroup: FormGroup; | ... | ... |
... | ... | @@ -35,7 +35,7 @@ |
35 | 35 | </mat-error> |
36 | 36 | </mat-form-field> |
37 | 37 | <mat-divider vertical></mat-divider> |
38 | - <tb-alarm-rule formControlName="alarmRule" required fxFlex> | |
38 | + <tb-alarm-rule formControlName="alarmRule" [deviceProfileId]="deviceProfileId" required fxFlex> | |
39 | 39 | </tb-alarm-rule> |
40 | 40 | </div> |
41 | 41 | <button *ngIf="!disabled" | ... | ... |
... | ... | @@ -30,7 +30,8 @@ import { |
30 | 30 | import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models'; |
31 | 31 | import { MatDialog } from '@angular/material/dialog'; |
32 | 32 | import { Subscription } from 'rxjs'; |
33 | -import { AlarmSeverity, alarmSeverityTranslations } from '../../../../../shared/models/alarm.models'; | |
33 | +import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models'; | |
34 | +import { EntityId } from '@shared/models/id/entity-id'; | |
34 | 35 | |
35 | 36 | @Component({ |
36 | 37 | selector: 'tb-create-alarm-rules', |
... | ... | @@ -59,6 +60,9 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, |
59 | 60 | @Input() |
60 | 61 | disabled: boolean; |
61 | 62 | |
63 | + @Input() | |
64 | + deviceProfileId: EntityId; | |
65 | + | |
62 | 66 | createAlarmRulesFormGroup: FormGroup; |
63 | 67 | |
64 | 68 | private usedSeverities: AlarmSeverity[] = []; | ... | ... |
... | ... | @@ -80,14 +80,16 @@ |
80 | 80 | </mat-expansion-panel> |
81 | 81 | <div fxFlex fxLayout="column"> |
82 | 82 | <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div> |
83 | - <tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;"> | |
83 | + <tb-create-alarm-rules formControlName="createRules" | |
84 | + style="padding-bottom: 16px;" | |
85 | + [deviceProfileId]="deviceProfileId"> | |
84 | 86 | </tb-create-alarm-rules> |
85 | 87 | <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.clear-alarm-rule</div> |
86 | 88 | <div fxLayout="row" fxLayoutGap="8px;" fxLayoutAlign="start center" |
87 | 89 | [fxShow]="alarmFormGroup.get('clearRule').value" |
88 | 90 | style="padding-bottom: 8px;"> |
89 | 91 | <div class="clear-alarm-rule" fxFlex fxLayout="row"> |
90 | - <tb-alarm-rule formControlName="clearRule" fxFlex> | |
92 | + <tb-alarm-rule formControlName="clearRule" fxFlex [deviceProfileId]="deviceProfileId"> | |
91 | 93 | </tb-alarm-rule> |
92 | 94 | </div> |
93 | 95 | <button *ngIf="!disabled" | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@sha |
29 | 29 | import { MatDialog } from '@angular/material/dialog'; |
30 | 30 | import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
31 | 31 | import { MatChipInputEvent } from '@angular/material/chips'; |
32 | +import { EntityId } from '@shared/models/id/entity-id'; | |
32 | 33 | |
33 | 34 | @Component({ |
34 | 35 | selector: 'tb-device-profile-alarm', |
... | ... | @@ -60,6 +61,9 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit |
60 | 61 | @Input() |
61 | 62 | expanded = false; |
62 | 63 | |
64 | + @Input() | |
65 | + deviceProfileId: EntityId; | |
66 | + | |
63 | 67 | private modelValue: DeviceProfileAlarm; |
64 | 68 | |
65 | 69 | alarmFormGroup: FormGroup; | ... | ... |
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | fxLayout="column" [ngStyle]="!isLast ? {paddingBottom: '8px'} : {}"> |
23 | 23 | <tb-device-profile-alarm [formControl]="alarmControl" |
24 | 24 | [expanded]="$index === 0" |
25 | + [deviceProfileId]="deviceProfileId" | |
25 | 26 | (removeAlarm)="removeAlarm($index)"> |
26 | 27 | </tb-device-profile-alarm> |
27 | 28 | </div> | ... | ... |
... | ... | @@ -34,6 +34,7 @@ import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/ |
34 | 34 | import { guid } from '@core/utils'; |
35 | 35 | import { Subscription } from 'rxjs'; |
36 | 36 | import { MatDialog } from '@angular/material/dialog'; |
37 | +import { EntityId } from '@shared/models/id/entity-id'; | |
37 | 38 | |
38 | 39 | @Component({ |
39 | 40 | selector: 'tb-device-profile-alarms', |
... | ... | @@ -68,6 +69,9 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni |
68 | 69 | @Input() |
69 | 70 | disabled: boolean; |
70 | 71 | |
72 | + @Input() | |
73 | + deviceProfileId: EntityId; | |
74 | + | |
71 | 75 | private valueChangeSubscription: Subscription = null; |
72 | 76 | |
73 | 77 | private propagateChange = (v: any) => { }; | ... | ... |
... | ... | @@ -116,7 +116,8 @@ |
116 | 116 | </mat-panel-title> |
117 | 117 | </mat-expansion-panel-header> |
118 | 118 | <tb-device-profile-alarms |
119 | - formControlName="alarms"> | |
119 | + formControlName="alarms" | |
120 | + [deviceProfileId]="deviceProfileId"> | |
120 | 121 | </tb-device-profile-alarms> |
121 | 122 | </mat-expansion-panel> |
122 | 123 | <mat-expansion-panel [expanded]="true"> | ... | ... |
... | ... | @@ -39,6 +39,7 @@ import { |
39 | 39 | import { EntityType } from '@shared/models/entity-type.models'; |
40 | 40 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
41 | 41 | import { ServiceType } from '@shared/models/queue.models'; |
42 | +import { EntityId } from '@shared/models/id/entity-id'; | |
42 | 43 | |
43 | 44 | @Component({ |
44 | 45 | selector: 'tb-device-profile', |
... | ... | @@ -66,6 +67,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { |
66 | 67 | |
67 | 68 | serviceType = ServiceType.TB_RULE_ENGINE; |
68 | 69 | |
70 | + deviceProfileId: EntityId; | |
71 | + | |
69 | 72 | constructor(protected store: Store<AppState>, |
70 | 73 | protected translate: TranslateService, |
71 | 74 | @Optional() @Inject('entity') protected entityValue: DeviceProfile, |
... | ... | @@ -83,6 +86,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { |
83 | 86 | } |
84 | 87 | |
85 | 88 | buildForm(entity: DeviceProfile): FormGroup { |
89 | + this.deviceProfileId = entity?.id ? entity.id : null; | |
86 | 90 | this.displayProfileConfiguration = entity && entity.type && |
87 | 91 | deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration; |
88 | 92 | this.displayTransportConfiguration = entity && entity.transportType && |
... | ... | @@ -157,6 +161,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { |
157 | 161 | } |
158 | 162 | |
159 | 163 | updateForm(entity: DeviceProfile) { |
164 | + this.deviceProfileId = entity.id; | |
160 | 165 | this.displayProfileConfiguration = entity.type && |
161 | 166 | deviceProfileTypeConfigurationInfoMap.get(entity.type).hasProfileConfiguration; |
162 | 167 | this.displayTransportConfiguration = entity.transportType && | ... | ... |
... | ... | @@ -76,7 +76,7 @@ |
76 | 76 | [required]="!createProfile" |
77 | 77 | [transportType]="deviceWizardFormGroup.get('transportType').value" |
78 | 78 | formControlName="deviceProfileId" |
79 | - [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" | |
79 | + [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" | |
80 | 80 | (deviceProfileChanged)="$event?.transportType ? deviceWizardFormGroup.get('transportType').patchValue($event?.transportType) : {}" |
81 | 81 | [addNewProfile]="false" |
82 | 82 | [selectDefaultProfile]="true" |
... | ... | @@ -133,7 +133,8 @@ |
133 | 133 | {count: alarmRulesFormGroup.get('alarms').value ? |
134 | 134 | alarmRulesFormGroup.get('alarms').value.length : 0} }}</ng-template> |
135 | 135 | <tb-device-profile-alarms |
136 | - formControlName="alarms"> | |
136 | + formControlName="alarms" | |
137 | + [deviceProfileId]="null"> | |
137 | 138 | </tb-device-profile-alarms> |
138 | 139 | </form> |
139 | 140 | </mat-step> | ... | ... |
... | ... | @@ -46,7 +46,7 @@ |
46 | 46 | }}" #alarmRules="matTab"> |
47 | 47 | <div class="mat-padding" [formGroup]="detailsForm"> |
48 | 48 | <div formGroupName="profileData"> |
49 | - <tb-device-profile-alarms formControlName="alarms"></tb-device-profile-alarms> | |
49 | + <tb-device-profile-alarms formControlName="alarms" [deviceProfileId]="entity.id"></tb-device-profile-alarms> | |
50 | 50 | </div> |
51 | 51 | </div> |
52 | 52 | </mat-tab> | ... | ... |