Commit 93fc099ee3622d6c47aecec76f91aa34d483ea56
Merge branch 'vvlladd28-improvement/device-profile/refactoring'
Showing
28 changed files
with
581 additions
and
542 deletions
@@ -387,3 +387,19 @@ export function sortObjectKeys<T>(obj: T): T { | @@ -387,3 +387,19 @@ export function sortObjectKeys<T>(obj: T): T { | ||
387 | return acc; | 387 | return acc; |
388 | }, {} as T); | 388 | }, {} as T); |
389 | } | 389 | } |
390 | + | ||
391 | +export function deepTrim<T>(obj: T): T { | ||
392 | + if (isNumber(obj) || isUndefined(obj) || isString(obj) || obj === null) { | ||
393 | + return obj; | ||
394 | + } | ||
395 | + return Object.keys(obj).reduce((acc, curr) => { | ||
396 | + if (isString(obj[curr])) { | ||
397 | + acc[curr] = obj[curr].trim(); | ||
398 | + } else if (isObject(obj[curr])) { | ||
399 | + acc[curr] = deepTrim(obj[curr]); | ||
400 | + } else { | ||
401 | + acc[curr] = obj[curr]; | ||
402 | + } | ||
403 | + return acc; | ||
404 | + }, (Array.isArray(obj) ? [] : {}) as T); | ||
405 | +} |
@@ -23,7 +23,7 @@ import { AppState } from '@core/core.state'; | @@ -23,7 +23,7 @@ import { AppState } from '@core/core.state'; | ||
23 | import { EntityAction } from '@home/models/entity/entity-component.models'; | 23 | import { EntityAction } from '@home/models/entity/entity-component.models'; |
24 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; | 24 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
25 | import { PageLink } from '@shared/models/page/page-link'; | 25 | import { PageLink } from '@shared/models/page/page-link'; |
26 | -import { isObject, isString } from '@core/utils'; | 26 | +import { deepTrim } from '@core/utils'; |
27 | 27 | ||
28 | // @dynamic | 28 | // @dynamic |
29 | @Directive() | 29 | @Directive() |
@@ -115,20 +115,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>, | @@ -115,20 +115,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>, | ||
115 | } | 115 | } |
116 | 116 | ||
117 | prepareFormValue(formValue: any): any { | 117 | prepareFormValue(formValue: any): any { |
118 | - return this.deepTrim(formValue); | ||
119 | - } | ||
120 | - | ||
121 | - private deepTrim(obj: object): object { | ||
122 | - return Object.keys(obj).reduce((acc, curr) => { | ||
123 | - if (isString(obj[curr])) { | ||
124 | - acc[curr] = obj[curr].trim(); | ||
125 | - } else if (isObject(obj[curr])) { | ||
126 | - acc[curr] = this.deepTrim(obj[curr]); | ||
127 | - } else { | ||
128 | - acc[curr] = obj[curr]; | ||
129 | - } | ||
130 | - return acc; | ||
131 | - }, Array.isArray(obj) ? [] : {}); | 118 | + return deepTrim(formValue); |
132 | } | 119 | } |
133 | 120 | ||
134 | protected setEntitiesTableConfig(entitiesTableConfig: C) { | 121 | protected setEntitiesTableConfig(entitiesTableConfig: C) { |
@@ -76,7 +76,6 @@ | @@ -76,7 +76,6 @@ | ||
76 | type="button" | 76 | type="button" |
77 | matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" | 77 | matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" |
78 | matTooltipPosition="above" | 78 | matTooltipPosition="above" |
79 | - *ngIf="allow" | ||
80 | (click)="dynamicMode = !dynamicMode"> | 79 | (click)="dynamicMode = !dynamicMode"> |
81 | <mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon> | 80 | <mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon> |
82 | </button> | 81 | </button> |
@@ -54,7 +54,7 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | @@ -54,7 +54,7 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn | ||
54 | if (allow) { | 54 | if (allow) { |
55 | this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER); | 55 | this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER); |
56 | } else { | 56 | } else { |
57 | - this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_DEVICE); | 57 | + this.dynamicValueSourceTypes = [DynamicValueSourceType.CURRENT_DEVICE]; |
58 | } | 58 | } |
59 | } | 59 | } |
60 | 60 |
@@ -90,7 +90,6 @@ import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.co | @@ -90,7 +90,6 @@ import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.co | ||
90 | import { TenantProfileDataComponent } from './profile/tenant-profile-data.component'; | 90 | import { TenantProfileDataComponent } from './profile/tenant-profile-data.component'; |
91 | import { DefaultDeviceProfileConfigurationComponent } from './profile/device/default-device-profile-configuration.component'; | 91 | import { DefaultDeviceProfileConfigurationComponent } from './profile/device/default-device-profile-configuration.component'; |
92 | import { DeviceProfileConfigurationComponent } from './profile/device/device-profile-configuration.component'; | 92 | import { DeviceProfileConfigurationComponent } from './profile/device/device-profile-configuration.component'; |
93 | -import { DeviceProfileDataComponent } from './profile/device-profile-data.component'; | ||
94 | import { DeviceProfileComponent } from './profile/device-profile.component'; | 93 | import { DeviceProfileComponent } from './profile/device-profile.component'; |
95 | import { DefaultDeviceProfileTransportConfigurationComponent } from './profile/device/default-device-profile-transport-configuration.component'; | 94 | import { DefaultDeviceProfileTransportConfigurationComponent } from './profile/device/default-device-profile-transport-configuration.component'; |
96 | import { DeviceProfileTransportConfigurationComponent } from './profile/device/device-profile-transport-configuration.component'; | 95 | import { DeviceProfileTransportConfigurationComponent } from './profile/device/device-profile-transport-configuration.component'; |
@@ -195,7 +194,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen | @@ -195,7 +194,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen | ||
195 | AlarmRuleConditionComponent, | 194 | AlarmRuleConditionComponent, |
196 | DeviceProfileAlarmComponent, | 195 | DeviceProfileAlarmComponent, |
197 | DeviceProfileAlarmsComponent, | 196 | DeviceProfileAlarmsComponent, |
198 | - DeviceProfileDataComponent, | ||
199 | DeviceProfileComponent, | 197 | DeviceProfileComponent, |
200 | DeviceProfileDialogComponent, | 198 | DeviceProfileDialogComponent, |
201 | AddDeviceProfileDialogComponent, | 199 | AddDeviceProfileDialogComponent, |
@@ -277,7 +275,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen | @@ -277,7 +275,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen | ||
277 | AlarmRuleConditionComponent, | 275 | AlarmRuleConditionComponent, |
278 | DeviceProfileAlarmComponent, | 276 | DeviceProfileAlarmComponent, |
279 | DeviceProfileAlarmsComponent, | 277 | DeviceProfileAlarmsComponent, |
280 | - DeviceProfileDataComponent, | ||
281 | DeviceProfileComponent, | 278 | DeviceProfileComponent, |
282 | DeviceProfileDialogComponent, | 279 | DeviceProfileDialogComponent, |
283 | AddDeviceProfileDialogComponent, | 280 | AddDeviceProfileDialogComponent, |
@@ -29,7 +29,7 @@ | @@ -29,7 +29,7 @@ | ||
29 | </mat-progress-bar> | 29 | </mat-progress-bar> |
30 | <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> | 30 | <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> |
31 | <div mat-dialog-content> | 31 | <div mat-dialog-content> |
32 | - <mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="selectedIndex = $event.selectedIndex"> | 32 | + <mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="changeStep($event)"> |
33 | <mat-step [stepControl]="deviceProfileDetailsFormGroup"> | 33 | <mat-step [stepControl]="deviceProfileDetailsFormGroup"> |
34 | <form [formGroup]="deviceProfileDetailsFormGroup" style="padding-bottom: 16px;"> | 34 | <form [formGroup]="deviceProfileDetailsFormGroup" style="padding-bottom: 16px;"> |
35 | <ng-template matStepLabel>{{ 'device-profile.device-profile-details' | translate }}</ng-template> | 35 | <ng-template matStepLabel>{{ 'device-profile.device-profile-details' | translate }}</ng-template> |
@@ -45,7 +45,7 @@ | @@ -45,7 +45,7 @@ | ||
45 | labelText="device-profile.default-rule-chain" | 45 | labelText="device-profile.default-rule-chain" |
46 | formControlName="defaultRuleChainId"> | 46 | formControlName="defaultRuleChainId"> |
47 | </tb-rule-chain-autocomplete> | 47 | </tb-rule-chain-autocomplete> |
48 | - <mat-form-field class="mat-block"> | 48 | + <mat-form-field fxHide class="mat-block"> |
49 | <mat-label translate>device-profile.type</mat-label> | 49 | <mat-label translate>device-profile.type</mat-label> |
50 | <mat-select formControlName="type" required> | 50 | <mat-select formControlName="type" required> |
51 | <mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> | 51 | <mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> |
@@ -63,7 +63,7 @@ | @@ -63,7 +63,7 @@ | ||
63 | </fieldset> | 63 | </fieldset> |
64 | </form> | 64 | </form> |
65 | </mat-step> | 65 | </mat-step> |
66 | - <mat-step [stepControl]="transportConfigFormGroup"> | 66 | + <mat-step [stepControl]="transportConfigFormGroup" [optional]="true"> |
67 | <form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;"> | 67 | <form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;"> |
68 | <ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template> | 68 | <ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template> |
69 | <mat-form-field class="mat-block"> | 69 | <mat-form-field class="mat-block"> |
@@ -73,6 +73,9 @@ | @@ -73,6 +73,9 @@ | ||
73 | {{deviceTransportTypeTranslations.get(type) | translate}} | 73 | {{deviceTransportTypeTranslations.get(type) | translate}} |
74 | </mat-option> | 74 | </mat-option> |
75 | </mat-select> | 75 | </mat-select> |
76 | + <mat-hint *ngIf="transportConfigFormGroup.get('transportType').value"> | ||
77 | + {{deviceTransportTypeHints.get(transportConfigFormGroup.get('transportType').value) | translate}} | ||
78 | + </mat-hint> | ||
76 | <mat-error *ngIf="transportConfigFormGroup.get('transportType').hasError('required')"> | 79 | <mat-error *ngIf="transportConfigFormGroup.get('transportType').hasError('required')"> |
77 | {{ 'device-profile.transport-type-required' | translate }} | 80 | {{ 'device-profile.transport-type-required' | translate }} |
78 | </mat-error> | 81 | </mat-error> |
@@ -83,7 +86,7 @@ | @@ -83,7 +86,7 @@ | ||
83 | </tb-device-profile-transport-configuration> | 86 | </tb-device-profile-transport-configuration> |
84 | </form> | 87 | </form> |
85 | </mat-step> | 88 | </mat-step> |
86 | - <mat-step [stepControl]="alarmRulesFormGroup"> | 89 | + <mat-step [stepControl]="alarmRulesFormGroup" [optional]="true"> |
87 | <form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;"> | 90 | <form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;"> |
88 | <ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate: | 91 | <ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate: |
89 | {count: alarmRulesFormGroup.get('alarms').value ? | 92 | {count: alarmRulesFormGroup.get('alarms').value ? |
@@ -95,19 +98,28 @@ | @@ -95,19 +98,28 @@ | ||
95 | </mat-step> | 98 | </mat-step> |
96 | </mat-horizontal-stepper> | 99 | </mat-horizontal-stepper> |
97 | </div> | 100 | </div> |
98 | - <div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center"> | ||
99 | - <button mat-button *ngIf="selectedIndex > 0" | ||
100 | - [disabled]="(isLoading$ | async)" | ||
101 | - (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
102 | - <span *ngIf="selectedIndex <= 0"></span> | ||
103 | - <div fxLayout="row wrap" fxLayoutGap="20px"> | 101 | + <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;"> |
102 | + <div fxFlex fxLayout="row" fxLayoutAlign="end"> | ||
103 | + <button mat-raised-button | ||
104 | + *ngIf="showNext" | ||
105 | + [disabled]="(isLoading$ | async)" | ||
106 | + (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button> | ||
107 | + </div> | ||
108 | + <div fxFlex fxLayout="row"> | ||
104 | <button mat-button | 109 | <button mat-button |
110 | + color="primary" | ||
105 | [disabled]="(isLoading$ | async)" | 111 | [disabled]="(isLoading$ | async)" |
106 | (click)="cancel()">{{ 'action.cancel' | translate }}</button> | 112 | (click)="cancel()">{{ 'action.cancel' | translate }}</button> |
107 | - <button mat-raised-button | ||
108 | - [disabled]="(isLoading$ | async) || selectedForm().invalid" | ||
109 | - color="primary" | ||
110 | - (click)="nextStep()">{{ (selectedIndex === 2 ? 'action.add' : 'action.continue') | translate }}</button> | 113 | + <span fxFlex></span> |
114 | + <div fxLayout="row wrap" fxLayoutGap="8px"> | ||
115 | + <button mat-raised-button *ngIf="selectedIndex > 0" | ||
116 | + [disabled]="(isLoading$ | async)" | ||
117 | + (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
118 | + <button mat-raised-button | ||
119 | + [disabled]="(isLoading$ | async)" | ||
120 | + color="primary" | ||
121 | + (click)="add()">{{ 'action.add' | translate }}</button> | ||
122 | + </div> | ||
111 | </div> | 123 | </div> |
112 | </div> | 124 | </div> |
113 | </div> | 125 | </div> |
@@ -13,25 +13,51 @@ | @@ -13,25 +13,51 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -:host { | ||
17 | - .mat-dialog-content { | ||
18 | - display: flex; | ||
19 | - flex-direction: column; | ||
20 | - overflow: hidden; | 16 | +@import "../../../../../scss/constants"; |
21 | 17 | ||
22 | - .mat-stepper-horizontal { | ||
23 | - display: flex; | ||
24 | - flex-direction: column; | ||
25 | - overflow: hidden; | 18 | +:host-context(.tb-fullscreen-dialog .mat-dialog-container) { |
19 | + @media #{$mat-lt-sm} { | ||
20 | + .mat-dialog-content { | ||
21 | + max-height: 75vh; | ||
26 | } | 22 | } |
27 | } | 23 | } |
28 | } | 24 | } |
29 | 25 | ||
30 | :host ::ng-deep { | 26 | :host ::ng-deep { |
31 | .mat-dialog-content { | 27 | .mat-dialog-content { |
28 | + display: flex; | ||
29 | + flex-direction: column; | ||
30 | + height: 100%; | ||
31 | + padding: 24px 24px 8px !important; | ||
32 | + | ||
32 | .mat-stepper-horizontal { | 33 | .mat-stepper-horizontal { |
34 | + display: flex; | ||
35 | + flex-direction: column; | ||
36 | + height: 100%; | ||
37 | + overflow: hidden; | ||
38 | + @media #{$mat-lt-sm} { | ||
39 | + .mat-step-label { | ||
40 | + white-space: normal; | ||
41 | + overflow: visible; | ||
42 | + .mat-step-text-label { | ||
43 | + overflow: visible; | ||
44 | + } | ||
45 | + } | ||
46 | + } | ||
33 | .mat-horizontal-content-container { | 47 | .mat-horizontal-content-container { |
34 | - overflow: auto; | 48 | + height: 350px; |
49 | + max-height: 100%; | ||
50 | + width: 100%;; | ||
51 | + overflow-y: auto; | ||
52 | + @media #{$mat-gt-sm} { | ||
53 | + min-width: 800px; | ||
54 | + } | ||
55 | + } | ||
56 | + .mat-horizontal-stepper-content[aria-expanded=true] { | ||
57 | + height: 100%; | ||
58 | + form { | ||
59 | + height: 100%; | ||
60 | + } | ||
35 | } | 61 | } |
36 | } | 62 | } |
37 | } | 63 | } |
@@ -36,13 +36,15 @@ import { | @@ -36,13 +36,15 @@ import { | ||
36 | DeviceProfile, | 36 | DeviceProfile, |
37 | DeviceProfileType, | 37 | DeviceProfileType, |
38 | deviceProfileTypeTranslationMap, | 38 | deviceProfileTypeTranslationMap, |
39 | - DeviceTransportType, | 39 | + DeviceTransportType, deviceTransportTypeHintMap, |
40 | deviceTransportTypeTranslationMap | 40 | deviceTransportTypeTranslationMap |
41 | } from '@shared/models/device.models'; | 41 | } from '@shared/models/device.models'; |
42 | import { DeviceProfileService } from '@core/http/device-profile.service'; | 42 | import { DeviceProfileService } from '@core/http/device-profile.service'; |
43 | import { EntityType } from '@shared/models/entity-type.models'; | 43 | import { EntityType } from '@shared/models/entity-type.models'; |
44 | import { MatHorizontalStepper } from '@angular/material/stepper'; | 44 | import { MatHorizontalStepper } from '@angular/material/stepper'; |
45 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; | 45 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
46 | +import { StepperSelectionEvent } from '@angular/cdk/stepper'; | ||
47 | +import { deepTrim } from '@core/utils'; | ||
46 | 48 | ||
47 | export interface AddDeviceProfileDialogData { | 49 | export interface AddDeviceProfileDialogData { |
48 | deviceProfileName: string; | 50 | deviceProfileName: string; |
@@ -62,12 +64,16 @@ export class AddDeviceProfileDialogComponent extends | @@ -62,12 +64,16 @@ export class AddDeviceProfileDialogComponent extends | ||
62 | 64 | ||
63 | selectedIndex = 0; | 65 | selectedIndex = 0; |
64 | 66 | ||
67 | + showNext = true; | ||
68 | + | ||
65 | entityType = EntityType; | 69 | entityType = EntityType; |
66 | 70 | ||
67 | deviceProfileTypes = Object.keys(DeviceProfileType); | 71 | deviceProfileTypes = Object.keys(DeviceProfileType); |
68 | 72 | ||
69 | deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; | 73 | deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; |
70 | 74 | ||
75 | + deviceTransportTypeHints = deviceTransportTypeHintMap; | ||
76 | + | ||
71 | deviceTransportTypes = Object.keys(DeviceTransportType); | 77 | deviceTransportTypes = Object.keys(DeviceTransportType); |
72 | 78 | ||
73 | deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; | 79 | deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; |
@@ -150,25 +156,63 @@ export class AddDeviceProfileDialogComponent extends | @@ -150,25 +156,63 @@ export class AddDeviceProfileDialogComponent extends | ||
150 | } | 156 | } |
151 | } | 157 | } |
152 | 158 | ||
153 | - private add(): void { | ||
154 | - const deviceProfile: DeviceProfile = { | ||
155 | - name: this.deviceProfileDetailsFormGroup.get('name').value, | ||
156 | - type: this.deviceProfileDetailsFormGroup.get('type').value, | ||
157 | - transportType: this.transportConfigFormGroup.get('transportType').value, | ||
158 | - description: this.deviceProfileDetailsFormGroup.get('description').value, | ||
159 | - profileData: { | ||
160 | - configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), | ||
161 | - transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, | ||
162 | - alarms: this.alarmRulesFormGroup.get('alarms').value | 159 | + add(): void { |
160 | + if (this.allValid()) { | ||
161 | + const deviceProfile: DeviceProfile = { | ||
162 | + name: this.deviceProfileDetailsFormGroup.get('name').value, | ||
163 | + type: this.deviceProfileDetailsFormGroup.get('type').value, | ||
164 | + transportType: this.transportConfigFormGroup.get('transportType').value, | ||
165 | + description: this.deviceProfileDetailsFormGroup.get('description').value, | ||
166 | + profileData: { | ||
167 | + configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), | ||
168 | + transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, | ||
169 | + alarms: this.alarmRulesFormGroup.get('alarms').value | ||
170 | + } | ||
171 | + }; | ||
172 | + if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) { | ||
173 | + deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value); | ||
163 | } | 174 | } |
164 | - }; | ||
165 | - if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) { | ||
166 | - deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value); | 175 | + this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).subscribe( |
176 | + (savedDeviceProfile) => { | ||
177 | + this.dialogRef.close(savedDeviceProfile); | ||
178 | + } | ||
179 | + ); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + getFormLabel(index: number): string { | ||
184 | + switch (index) { | ||
185 | + case 0: | ||
186 | + return 'device-profile.device-profile-details'; | ||
187 | + case 1: | ||
188 | + return 'device-profile.transport-configuration'; | ||
189 | + case 2: | ||
190 | + return 'device-profile.alarm-rules'; | ||
191 | + } | ||
192 | + } | ||
193 | + | ||
194 | + changeStep($event: StepperSelectionEvent): void { | ||
195 | + this.selectedIndex = $event.selectedIndex; | ||
196 | + if (this.selectedIndex === this.maxStepperIndex) { | ||
197 | + this.showNext = false; | ||
198 | + } else { | ||
199 | + this.showNext = true; | ||
167 | } | 200 | } |
168 | - this.deviceProfileService.saveDeviceProfile(deviceProfile).subscribe( | ||
169 | - (savedDeviceProfile) => { | ||
170 | - this.dialogRef.close(savedDeviceProfile); | 201 | + } |
202 | + | ||
203 | + private get maxStepperIndex(): number { | ||
204 | + return this.addDeviceProfileStepper?._steps?.length - 1; | ||
205 | + } | ||
206 | + | ||
207 | + allValid(): boolean { | ||
208 | + return !this.addDeviceProfileStepper.steps.find((item, index) => { | ||
209 | + if (item.stepControl.invalid) { | ||
210 | + item.interacted = true; | ||
211 | + this.addDeviceProfileStepper.selectedIndex = index; | ||
212 | + return true; | ||
213 | + } else { | ||
214 | + return false; | ||
171 | } | 215 | } |
172 | - ); | 216 | + }); |
173 | } | 217 | } |
174 | } | 218 | } |
@@ -21,74 +21,72 @@ | @@ -21,74 +21,72 @@ | ||
21 | <tb-alarm-rule-condition fxFlex class="row" | 21 | <tb-alarm-rule-condition fxFlex class="row" |
22 | formControlName="condition"> | 22 | formControlName="condition"> |
23 | </tb-alarm-rule-condition> | 23 | </tb-alarm-rule-condition> |
24 | - <section class="row"> | ||
25 | - <div formGroupName="spec"> | ||
26 | - <mat-form-field class="mat-block" hideRequiredMarker> | ||
27 | - <mat-label translate>device-profile.condition-type</mat-label> | ||
28 | - <mat-select formControlName="type" required> | ||
29 | - <mat-option *ngFor="let alarmConditionType of alarmConditionTypes" [value]="alarmConditionType"> | ||
30 | - {{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }} | 24 | + <section formGroupName="spec" class="row"> |
25 | + <mat-form-field class="mat-block" hideRequiredMarker> | ||
26 | + <mat-label translate>device-profile.condition-type</mat-label> | ||
27 | + <mat-select formControlName="type" required> | ||
28 | + <mat-option *ngFor="let alarmConditionType of alarmConditionTypes" [value]="alarmConditionType"> | ||
29 | + {{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }} | ||
30 | + </mat-option> | ||
31 | + </mat-select> | ||
32 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.type').hasError('required')"> | ||
33 | + {{ 'device-profile.condition-type-required' | translate }} | ||
34 | + </mat-error> | ||
35 | + </mat-form-field> | ||
36 | + <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.DURATION"> | ||
37 | + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
38 | + <mat-label></mat-label> | ||
39 | + <input type="number" required | ||
40 | + step="1" min="1" max="2147483647" matInput | ||
41 | + placeholder="{{ 'device-profile.condition-duration-value' | translate }}" | ||
42 | + formControlName="value"> | ||
43 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('required')"> | ||
44 | + {{ 'device-profile.condition-duration-value-required' | translate }} | ||
45 | + </mat-error> | ||
46 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('min')"> | ||
47 | + {{ 'device-profile.condition-duration-value-range' | translate }} | ||
48 | + </mat-error> | ||
49 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('max')"> | ||
50 | + {{ 'device-profile.condition-duration-value-range' | translate }} | ||
51 | + </mat-error> | ||
52 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('pattern')"> | ||
53 | + {{ 'device-profile.condition-duration-value-pattern' | translate }} | ||
54 | + </mat-error> | ||
55 | + </mat-form-field> | ||
56 | + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
57 | + <mat-label></mat-label> | ||
58 | + <mat-select formControlName="unit" | ||
59 | + required | ||
60 | + placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}"> | ||
61 | + <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit"> | ||
62 | + {{ timeUnitTranslations.get(timeUnit) | translate }} | ||
31 | </mat-option> | 63 | </mat-option> |
32 | </mat-select> | 64 | </mat-select> |
33 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.type').hasError('required')"> | ||
34 | - {{ 'device-profile.condition-type-required' | translate }} | 65 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.unit').hasError('required')"> |
66 | + {{ 'device-profile.condition-duration-time-unit-required' | translate }} | ||
67 | + </mat-error> | ||
68 | + </mat-form-field> | ||
69 | + </div> | ||
70 | + <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.REPEATING"> | ||
71 | + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
72 | + <mat-label></mat-label> | ||
73 | + <input type="number" required | ||
74 | + step="1" min="1" max="2147483647" matInput | ||
75 | + placeholder="{{ 'device-profile.condition-repeating-value' | translate }}" | ||
76 | + formControlName="count"> | ||
77 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('required')"> | ||
78 | + {{ 'device-profile.condition-repeating-value-required' | translate }} | ||
79 | + </mat-error> | ||
80 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('min')"> | ||
81 | + {{ 'device-profile.condition-repeating-value-range' | translate }} | ||
82 | + </mat-error> | ||
83 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('max')"> | ||
84 | + {{ 'device-profile.condition-repeating-value-range' | translate }} | ||
85 | + </mat-error> | ||
86 | + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('pattern')"> | ||
87 | + {{ 'device-profile.condition-repeating-value-pattern' | translate }} | ||
35 | </mat-error> | 88 | </mat-error> |
36 | </mat-form-field> | 89 | </mat-form-field> |
37 | - <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.DURATION"> | ||
38 | - <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
39 | - <mat-label></mat-label> | ||
40 | - <input type="number" required | ||
41 | - step="1" min="1" max="2147483647" matInput | ||
42 | - placeholder="{{ 'device-profile.condition-duration-value' | translate }}" | ||
43 | - formControlName="value"> | ||
44 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('required')"> | ||
45 | - {{ 'device-profile.condition-duration-value-required' | translate }} | ||
46 | - </mat-error> | ||
47 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('min')"> | ||
48 | - {{ 'device-profile.condition-duration-value-range' | translate }} | ||
49 | - </mat-error> | ||
50 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('max')"> | ||
51 | - {{ 'device-profile.condition-duration-value-range' | translate }} | ||
52 | - </mat-error> | ||
53 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('pattern')"> | ||
54 | - {{ 'device-profile.condition-duration-value-pattern' | translate }} | ||
55 | - </mat-error> | ||
56 | - </mat-form-field> | ||
57 | - <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
58 | - <mat-label></mat-label> | ||
59 | - <mat-select formControlName="unit" | ||
60 | - required | ||
61 | - placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}"> | ||
62 | - <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit"> | ||
63 | - {{ timeUnitTranslations.get(timeUnit) | translate }} | ||
64 | - </mat-option> | ||
65 | - </mat-select> | ||
66 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.unit').hasError('required')"> | ||
67 | - {{ 'device-profile.condition-duration-time-unit-required' | translate }} | ||
68 | - </mat-error> | ||
69 | - </mat-form-field> | ||
70 | - </div> | ||
71 | - <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.REPEATING"> | ||
72 | - <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always"> | ||
73 | - <mat-label></mat-label> | ||
74 | - <input type="number" required | ||
75 | - step="1" min="1" max="2147483647" matInput | ||
76 | - placeholder="{{ 'device-profile.condition-repeating-value' | translate }}" | ||
77 | - formControlName="count"> | ||
78 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('required')"> | ||
79 | - {{ 'device-profile.condition-repeating-value-required' | translate }} | ||
80 | - </mat-error> | ||
81 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('min')"> | ||
82 | - {{ 'device-profile.condition-repeating-value-range' | translate }} | ||
83 | - </mat-error> | ||
84 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('max')"> | ||
85 | - {{ 'device-profile.condition-repeating-value-range' | translate }} | ||
86 | - </mat-error> | ||
87 | - <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('pattern')"> | ||
88 | - {{ 'device-profile.condition-repeating-value-pattern' | translate }} | ||
89 | - </mat-error> | ||
90 | - </mat-form-field> | ||
91 | - </div> | ||
92 | </div> | 90 | </div> |
93 | </section> | 91 | </section> |
94 | </mat-tab> | 92 | </mat-tab> |
@@ -29,6 +29,7 @@ import { AlarmConditionType, AlarmConditionTypeTranslationMap, AlarmRule } from | @@ -29,6 +29,7 @@ import { AlarmConditionType, AlarmConditionTypeTranslationMap, AlarmRule } from | ||
29 | import { MatDialog } from '@angular/material/dialog'; | 29 | import { MatDialog } from '@angular/material/dialog'; |
30 | import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; | 30 | import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models'; |
31 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 31 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
32 | +import { isUndefined } from '@core/utils'; | ||
32 | 33 | ||
33 | @Component({ | 34 | @Component({ |
34 | selector: 'tb-alarm-rule', | 35 | selector: 'tb-alarm-rule', |
@@ -117,10 +118,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat | @@ -117,10 +118,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat | ||
117 | 118 | ||
118 | writeValue(value: AlarmRule): void { | 119 | writeValue(value: AlarmRule): void { |
119 | this.modelValue = value; | 120 | this.modelValue = value; |
120 | - if (this.modelValue?.condition?.spec === null) { | ||
121 | - this.modelValue.condition.spec = { | ||
122 | - type: AlarmConditionType.SIMPLE | ||
123 | - }; | 121 | + if (this.modelValue !== null && isUndefined(this.modelValue?.condition?.spec)) { |
122 | + this.modelValue = Object.assign(this.modelValue, {condition: {spec: {type: AlarmConditionType.SIMPLE}}}); | ||
124 | } | 123 | } |
125 | this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false}); | 124 | this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false}); |
126 | this.updateValidators(this.modelValue?.condition?.spec?.type); | 125 | this.updateValidators(this.modelValue?.condition?.spec?.type); |
@@ -35,7 +35,7 @@ | @@ -35,7 +35,7 @@ | ||
35 | </tb-timezone-select> | 35 | </tb-timezone-select> |
36 | <section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.SPECIFIC_TIME"> | 36 | <section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.SPECIFIC_TIME"> |
37 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div> | 37 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div> |
38 | - <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="16px" style="padding-bottom: 16px;"> | 38 | + <div fxLayout="column" fxLayout.gt-md="row" fxLayoutGap="16px" style="padding-bottom: 16px;"> |
39 | <div fxLayout="row" fxLayoutGap="16px"> | 39 | <div fxLayout="row" fxLayoutGap="16px"> |
40 | <mat-checkbox [formControl]="weeklyRepeatControl(0)"> | 40 | <mat-checkbox [formControl]="weeklyRepeatControl(0)"> |
41 | {{ 'device-profile.schedule-day.monday' | translate }} | 41 | {{ 'device-profile.schedule-day.monday' | translate }} |
@@ -64,158 +64,189 @@ | @@ -64,158 +64,189 @@ | ||
64 | </div> | 64 | </div> |
65 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-time</div> | 65 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-time</div> |
66 | <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | 66 | <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
67 | - <mat-form-field fxFlex> | ||
68 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
69 | - <mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle> | ||
70 | - <mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | ||
71 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker"> | ||
72 | - </mat-form-field> | ||
73 | - <mat-form-field fxFlex> | ||
74 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
75 | - <mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle> | ||
76 | - <mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | ||
77 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker"> | ||
78 | - </mat-form-field> | 67 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex.gt-md> |
68 | + <mat-form-field fxFlex.xs fxFlex.sm="150px" fxFlex.md="150px" fxFlex.gt-md> | ||
69 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
70 | + <mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle> | ||
71 | + <mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | ||
72 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker"> | ||
73 | + </mat-form-field> | ||
74 | + <mat-form-field fxFlex.xs fxFlex.sm="150px" fxFlex.md="150px" fxFlex.gt-md> | ||
75 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
76 | + <mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle> | ||
77 | + <mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker> | ||
78 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker"> | ||
79 | + </mat-form-field> | ||
80 | + </div> | ||
81 | + <div fxFlex fxLayoutAlign="center center" style="margin: auto"> | ||
82 | + <div style="text-align: center" | ||
83 | + [innerHTML]="getSchedulerRangeText(alarmScheduleForm)"> | ||
84 | + </div> | ||
85 | + </div> | ||
79 | </div> | 86 | </div> |
80 | </section> | 87 | </section> |
81 | <section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.CUSTOM"> | 88 | <section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.CUSTOM"> |
82 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div> | 89 | <div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div> |
83 | - <div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="16px" formArrayName="items"> | ||
84 | - <div fxLayout="column" fxFlex fxFlex.gt-sm="50"> | ||
85 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="0" fxLayoutAlign="start center"> | ||
86 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 0)"> | ||
87 | - {{ 'device-profile.schedule-day.monday' | translate }} | ||
88 | - </mat-checkbox> | ||
89 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
90 | - <mat-form-field fxFlex="100px"> | ||
91 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
92 | - <mat-datetimepicker-toggle [for]="startTimePicker1" matPrefix></mat-datetimepicker-toggle> | ||
93 | - <mat-datetimepicker #startTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker> | ||
94 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker1"> | ||
95 | - </mat-form-field> | ||
96 | - <mat-form-field fxFlex="100px"> | ||
97 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
98 | - <mat-datetimepicker-toggle [for]="endTimePicker1" matPrefix></mat-datetimepicker-toggle> | ||
99 | - <mat-datetimepicker #endTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker> | ||
100 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker1"> | ||
101 | - </mat-form-field> | ||
102 | - </div> | 90 | + <div fxLayout="column" formArrayName="items" fxLayoutGap="1em"> |
91 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="0" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
92 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 0)"> | ||
93 | + {{ 'device-profile.schedule-day.monday' | translate }} | ||
94 | + </mat-checkbox> | ||
95 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
96 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
97 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
98 | + <mat-datetimepicker-toggle [for]="startTimePicker1" matPrefix></mat-datetimepicker-toggle> | ||
99 | + <mat-datetimepicker #startTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker> | ||
100 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker1"> | ||
101 | + </mat-form-field> | ||
102 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
103 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
104 | + <mat-datetimepicker-toggle [for]="endTimePicker1" matPrefix></mat-datetimepicker-toggle> | ||
105 | + <mat-datetimepicker #endTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker> | ||
106 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker1"> | ||
107 | + </mat-form-field> | ||
103 | </div> | 108 | </div> |
104 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="1" fxLayoutAlign="start center"> | ||
105 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 1)"> | ||
106 | - {{ 'device-profile.schedule-day.tuesday' | translate }} | ||
107 | - </mat-checkbox> | ||
108 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
109 | - <mat-form-field fxFlex="100px"> | ||
110 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
111 | - <mat-datetimepicker-toggle [for]="startTimePicker2" matPrefix></mat-datetimepicker-toggle> | ||
112 | - <mat-datetimepicker #startTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker> | ||
113 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker2"> | ||
114 | - </mat-form-field> | ||
115 | - <mat-form-field fxFlex="100px"> | ||
116 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
117 | - <mat-datetimepicker-toggle [for]="endTimePicker2" matPrefix></mat-datetimepicker-toggle> | ||
118 | - <mat-datetimepicker #endTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker> | ||
119 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker2"> | ||
120 | - </mat-form-field> | ||
121 | - </div> | 109 | + <div fxFlex fxLayoutAlign="center center" |
110 | + style="text-align: center" | ||
111 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(0))"> | ||
122 | </div> | 112 | </div> |
123 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="2" fxLayoutAlign="start center"> | ||
124 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 2)"> | ||
125 | - {{ 'device-profile.schedule-day.wednesday' | translate }} | ||
126 | - </mat-checkbox> | ||
127 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
128 | - <mat-form-field fxFlex="100px"> | ||
129 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
130 | - <mat-datetimepicker-toggle [for]="startTimePicker3" matPrefix></mat-datetimepicker-toggle> | ||
131 | - <mat-datetimepicker #startTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker> | ||
132 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker3"> | ||
133 | - </mat-form-field> | ||
134 | - <mat-form-field fxFlex="100px"> | ||
135 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
136 | - <mat-datetimepicker-toggle [for]="endTimePicker3" matPrefix></mat-datetimepicker-toggle> | ||
137 | - <mat-datetimepicker #endTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker> | ||
138 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker3"> | ||
139 | - </mat-form-field> | ||
140 | - </div> | 113 | + </div> |
114 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="1" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
115 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 1)"> | ||
116 | + {{ 'device-profile.schedule-day.tuesday' | translate }} | ||
117 | + </mat-checkbox> | ||
118 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
119 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
120 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
121 | + <mat-datetimepicker-toggle [for]="startTimePicker2" matPrefix></mat-datetimepicker-toggle> | ||
122 | + <mat-datetimepicker #startTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker> | ||
123 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker2"> | ||
124 | + </mat-form-field> | ||
125 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
126 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
127 | + <mat-datetimepicker-toggle [for]="endTimePicker2" matPrefix></mat-datetimepicker-toggle> | ||
128 | + <mat-datetimepicker #endTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker> | ||
129 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker2"> | ||
130 | + </mat-form-field> | ||
141 | </div> | 131 | </div> |
142 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="3" fxLayoutAlign="start center"> | ||
143 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 3)"> | ||
144 | - {{ 'device-profile.schedule-day.thursday' | translate }} | ||
145 | - </mat-checkbox> | ||
146 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
147 | - <mat-form-field fxFlex="100px"> | ||
148 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
149 | - <mat-datetimepicker-toggle [for]="startTimePicker4" matPrefix></mat-datetimepicker-toggle> | ||
150 | - <mat-datetimepicker #startTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker> | ||
151 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker4"> | ||
152 | - </mat-form-field> | ||
153 | - <mat-form-field fxFlex="100px"> | ||
154 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
155 | - <mat-datetimepicker-toggle [for]="endTimePicker4" matPrefix></mat-datetimepicker-toggle> | ||
156 | - <mat-datetimepicker #endTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker> | ||
157 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker4"> | ||
158 | - </mat-form-field> | ||
159 | - </div> | 132 | + <div fxFlex fxLayoutAlign="center center" |
133 | + style="text-align: center" | ||
134 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(1))"> | ||
160 | </div> | 135 | </div> |
161 | </div> | 136 | </div> |
162 | - <div fxLayout="column" fxFlex fxFlex.gt-sm="50"> | ||
163 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="4" fxLayoutAlign="start center"> | ||
164 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 4)"> | ||
165 | - {{ 'device-profile.schedule-day.friday' | translate }} | ||
166 | - </mat-checkbox> | ||
167 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
168 | - <mat-form-field fxFlex="100px"> | ||
169 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
170 | - <mat-datetimepicker-toggle [for]="startTimePicker5" matPrefix></mat-datetimepicker-toggle> | ||
171 | - <mat-datetimepicker #startTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker> | ||
172 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker5"> | ||
173 | - </mat-form-field> | ||
174 | - <mat-form-field fxFlex="100px"> | ||
175 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
176 | - <mat-datetimepicker-toggle [for]="endTimePicker5" matPrefix></mat-datetimepicker-toggle> | ||
177 | - <mat-datetimepicker #endTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker> | ||
178 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker5"> | ||
179 | - </mat-form-field> | ||
180 | - </div> | 137 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="2" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> |
138 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 2)"> | ||
139 | + {{ 'device-profile.schedule-day.wednesday' | translate }} | ||
140 | + </mat-checkbox> | ||
141 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
142 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
143 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
144 | + <mat-datetimepicker-toggle [for]="startTimePicker3" matPrefix></mat-datetimepicker-toggle> | ||
145 | + <mat-datetimepicker #startTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker> | ||
146 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker3"> | ||
147 | + </mat-form-field> | ||
148 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
149 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
150 | + <mat-datetimepicker-toggle [for]="endTimePicker3" matPrefix></mat-datetimepicker-toggle> | ||
151 | + <mat-datetimepicker #endTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker> | ||
152 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker3"> | ||
153 | + </mat-form-field> | ||
154 | + </div> | ||
155 | + <div fxFlex fxLayoutAlign="center center" | ||
156 | + style="text-align: center" | ||
157 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(2))"> | ||
158 | + </div> | ||
159 | + </div> | ||
160 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="3" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
161 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 3)"> | ||
162 | + {{ 'device-profile.schedule-day.thursday' | translate }} | ||
163 | + </mat-checkbox> | ||
164 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
165 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
166 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
167 | + <mat-datetimepicker-toggle [for]="startTimePicker4" matPrefix></mat-datetimepicker-toggle> | ||
168 | + <mat-datetimepicker #startTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker> | ||
169 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker4"> | ||
170 | + </mat-form-field> | ||
171 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
172 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
173 | + <mat-datetimepicker-toggle [for]="endTimePicker4" matPrefix></mat-datetimepicker-toggle> | ||
174 | + <mat-datetimepicker #endTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker> | ||
175 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker4"> | ||
176 | + </mat-form-field> | ||
181 | </div> | 177 | </div> |
182 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="5" fxLayoutAlign="start center"> | ||
183 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 5)"> | ||
184 | - {{ 'device-profile.schedule-day.saturday' | translate }} | ||
185 | - </mat-checkbox> | ||
186 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
187 | - <mat-form-field fxFlex="100px"> | ||
188 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
189 | - <mat-datetimepicker-toggle [for]="startTimePicker6" matPrefix></mat-datetimepicker-toggle> | ||
190 | - <mat-datetimepicker #startTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker> | ||
191 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker6"> | ||
192 | - </mat-form-field> | ||
193 | - <mat-form-field fxFlex="100px"> | ||
194 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
195 | - <mat-datetimepicker-toggle [for]="endTimePicker6" matPrefix></mat-datetimepicker-toggle> | ||
196 | - <mat-datetimepicker #endTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker> | ||
197 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker6"> | ||
198 | - </mat-form-field> | ||
199 | - </div> | 178 | + <div fxFlex fxLayoutAlign="center center" |
179 | + style="text-align: center" | ||
180 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(3))"> | ||
181 | + </div> | ||
182 | + </div> | ||
183 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="4" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
184 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 4)"> | ||
185 | + {{ 'device-profile.schedule-day.friday' | translate }} | ||
186 | + </mat-checkbox> | ||
187 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
188 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
189 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
190 | + <mat-datetimepicker-toggle [for]="startTimePicker5" matPrefix></mat-datetimepicker-toggle> | ||
191 | + <mat-datetimepicker #startTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker> | ||
192 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker5"> | ||
193 | + </mat-form-field> | ||
194 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
195 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
196 | + <mat-datetimepicker-toggle [for]="endTimePicker5" matPrefix></mat-datetimepicker-toggle> | ||
197 | + <mat-datetimepicker #endTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker> | ||
198 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker5"> | ||
199 | + </mat-form-field> | ||
200 | + </div> | ||
201 | + <div fxFlex fxLayoutAlign="center center" | ||
202 | + style="text-align: center" | ||
203 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(4))"> | ||
204 | + </div> | ||
205 | + </div> | ||
206 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="5" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
207 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 5)"> | ||
208 | + {{ 'device-profile.schedule-day.saturday' | translate }} | ||
209 | + </mat-checkbox> | ||
210 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
211 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
212 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
213 | + <mat-datetimepicker-toggle [for]="startTimePicker6" matPrefix></mat-datetimepicker-toggle> | ||
214 | + <mat-datetimepicker #startTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker> | ||
215 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker6"> | ||
216 | + </mat-form-field> | ||
217 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
218 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
219 | + <mat-datetimepicker-toggle [for]="endTimePicker6" matPrefix></mat-datetimepicker-toggle> | ||
220 | + <mat-datetimepicker #endTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker> | ||
221 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker6"> | ||
222 | + </mat-form-field> | ||
223 | + </div> | ||
224 | + <div fxFlex fxLayoutAlign="center center" | ||
225 | + style="text-align: center" | ||
226 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(5))"> | ||
227 | + </div> | ||
228 | + </div> | ||
229 | + <div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="6" fxLayoutAlign="start center" fxLayoutAlign.xs="center start"> | ||
230 | + <mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 6)"> | ||
231 | + {{ 'device-profile.schedule-day.sunday' | translate }} | ||
232 | + </mat-checkbox> | ||
233 | + <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
234 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
235 | + <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
236 | + <mat-datetimepicker-toggle [for]="startTimePicker7" matPrefix></mat-datetimepicker-toggle> | ||
237 | + <mat-datetimepicker #startTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker> | ||
238 | + <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker7"> | ||
239 | + </mat-form-field> | ||
240 | + <mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px"> | ||
241 | + <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
242 | + <mat-datetimepicker-toggle [for]="endTimePicker7" matPrefix></mat-datetimepicker-toggle> | ||
243 | + <mat-datetimepicker #endTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker> | ||
244 | + <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker7"> | ||
245 | + </mat-form-field> | ||
200 | </div> | 246 | </div> |
201 | - <div fxLayout="row" fxLayoutGap="8px" formGroupName="6" fxLayoutAlign="start center"> | ||
202 | - <mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 6)"> | ||
203 | - {{ 'device-profile.schedule-day.sunday' | translate }} | ||
204 | - </mat-checkbox> | ||
205 | - <div fxLayout="row" fxLayoutGap="8px" fxFlex> | ||
206 | - <mat-form-field fxFlex="100px"> | ||
207 | - <mat-label translate>device-profile.schedule-time-from</mat-label> | ||
208 | - <mat-datetimepicker-toggle [for]="startTimePicker7" matPrefix></mat-datetimepicker-toggle> | ||
209 | - <mat-datetimepicker #startTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker> | ||
210 | - <input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker7"> | ||
211 | - </mat-form-field> | ||
212 | - <mat-form-field fxFlex="100px"> | ||
213 | - <mat-label translate>device-profile.schedule-time-to</mat-label> | ||
214 | - <mat-datetimepicker-toggle [for]="endTimePicker7" matPrefix></mat-datetimepicker-toggle> | ||
215 | - <mat-datetimepicker #endTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker> | ||
216 | - <input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker7"> | ||
217 | - </mat-form-field> | ||
218 | - </div> | 247 | + <div fxFlex fxLayoutAlign="center center" |
248 | + style="text-align: center" | ||
249 | + [innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(6))"> | ||
219 | </div> | 250 | </div> |
220 | </div> | 251 | </div> |
221 | </div> | 252 | </div> |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +::ng-deep { | ||
17 | + .nowrap { | ||
18 | + white-space: nowrap; | ||
19 | + } | ||
20 | +} |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | 16 | ||
17 | import { Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | import { Component, forwardRef, Input, OnInit } from '@angular/core'; |
18 | import { | 18 | import { |
19 | + AbstractControl, | ||
19 | ControlValueAccessor, | 20 | ControlValueAccessor, |
20 | FormArray, | 21 | FormArray, |
21 | FormBuilder, | 22 | FormBuilder, |
@@ -35,6 +36,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox'; | @@ -35,6 +36,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox'; | ||
35 | @Component({ | 36 | @Component({ |
36 | selector: 'tb-alarm-schedule', | 37 | selector: 'tb-alarm-schedule', |
37 | templateUrl: './alarm-schedule.component.html', | 38 | templateUrl: './alarm-schedule.component.html', |
39 | + styleUrls: ['./alarm-schedule.component.scss'], | ||
38 | providers: [{ | 40 | providers: [{ |
39 | provide: NG_VALUE_ACCESSOR, | 41 | provide: NG_VALUE_ACCESSOR, |
40 | useExisting: forwardRef(() => AlarmScheduleComponent), | 42 | useExisting: forwardRef(() => AlarmScheduleComponent), |
@@ -79,7 +81,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | @@ -79,7 +81,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | ||
79 | items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i))) | 81 | items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i))) |
80 | }); | 82 | }); |
81 | this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { | 83 | this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { |
82 | - this.alarmScheduleForm.reset({type, items: this.defaultItems}, {emitEvent: false}); | 84 | + this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: this.defaultTimezone}, {emitEvent: false}); |
83 | this.updateValidators(type, true); | 85 | this.updateValidators(type, true); |
84 | this.alarmScheduleForm.updateValueAndValidity(); | 86 | this.alarmScheduleForm.updateValueAndValidity(); |
85 | }); | 87 | }); |
@@ -131,13 +133,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | @@ -131,13 +133,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | ||
131 | this.modelValue.items | 133 | this.modelValue.items |
132 | .sort((a, b) => a.dayOfWeek - b.dayOfWeek) | 134 | .sort((a, b) => a.dayOfWeek - b.dayOfWeek) |
133 | .forEach((item, index) => { | 135 | .forEach((item, index) => { |
134 | - if (item.enabled) { | ||
135 | - this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false}); | ||
136 | - this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent: false}); | ||
137 | - } else { | ||
138 | - this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false}); | ||
139 | - this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent: false}); | ||
140 | - } | 136 | + this.disabledSelectedTime(item.enabled, index); |
141 | alarmDays.push({ | 137 | alarmDays.push({ |
142 | enabled: item.enabled, | 138 | enabled: item.enabled, |
143 | startsOn: this.timestampToTime(item.startsOn), | 139 | startsOn: this.timestampToTime(item.startsOn), |
@@ -206,15 +202,15 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | @@ -206,15 +202,15 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | ||
206 | .filter(day => !!day); | 202 | .filter(day => !!day); |
207 | } | 203 | } |
208 | if (isDefined(value.startsOn) && value.startsOn !== 0) { | 204 | if (isDefined(value.startsOn) && value.startsOn !== 0) { |
209 | - value.startsOn = this.timeToTimestamp(value.startsOn); | 205 | + value.startsOn = this.timeToTimestampUTC(value.startsOn); |
210 | } | 206 | } |
211 | if (isDefined(value.endsOn) && value.endsOn !== 0) { | 207 | if (isDefined(value.endsOn) && value.endsOn !== 0) { |
212 | - value.endsOn = this.timeToTimestamp(value.endsOn); | 208 | + value.endsOn = this.timeToTimestampUTC(value.endsOn); |
213 | } | 209 | } |
214 | if (isDefined(value.items)){ | 210 | if (isDefined(value.items)){ |
215 | value.items = this.alarmScheduleForm.getRawValue().items; | 211 | value.items = this.alarmScheduleForm.getRawValue().items; |
216 | value.items = value.items.map((item) => { | 212 | value.items = value.items.map((item) => { |
217 | - return { ...item, startsOn: this.timeToTimestamp(item.startsOn), endsOn: this.timeToTimestamp(item.endsOn)}; | 213 | + return { ...item, startsOn: this.timeToTimestampUTC(item.startsOn), endsOn: this.timeToTimestampUTC(item.endsOn)}; |
218 | }); | 214 | }); |
219 | } | 215 | } |
220 | this.modelValue = value; | 216 | this.modelValue = value; |
@@ -222,7 +218,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | @@ -222,7 +218,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | ||
222 | } | 218 | } |
223 | } | 219 | } |
224 | 220 | ||
225 | - private timeToTimestamp(date: Date | number): number { | 221 | + private timeToTimestampUTC(date: Date | number): number { |
226 | if (typeof date === 'number' || date === null) { | 222 | if (typeof date === 'number' || date === null) { |
227 | return 0; | 223 | return 0; |
228 | } | 224 | } |
@@ -244,16 +240,39 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | @@ -244,16 +240,39 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, | ||
244 | 240 | ||
245 | changeCustomScheduler($event: MatCheckboxChange, index: number) { | 241 | changeCustomScheduler($event: MatCheckboxChange, index: number) { |
246 | const value = $event.checked; | 242 | const value = $event.checked; |
247 | - if (value) { | 243 | + this.disabledSelectedTime(value, index, true); |
244 | + } | ||
245 | + | ||
246 | + private disabledSelectedTime(enable: boolean, index: number, emitEvent = false) { | ||
247 | + if (enable) { | ||
248 | this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false}); | 248 | this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false}); |
249 | - this.itemsSchedulerForm.at(index).get('endsOn').enable(); | 249 | + this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent}); |
250 | } else { | 250 | } else { |
251 | this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false}); | 251 | this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false}); |
252 | - this.itemsSchedulerForm.at(index).get('endsOn').disable(); | 252 | + this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent}); |
253 | + } | ||
254 | + } | ||
255 | + | ||
256 | + private timeToMoment(date: Date | number): _moment.Moment { | ||
257 | + if (typeof date === 'number' || date === null) { | ||
258 | + return _moment([1970, 0, 1, 0, 0, 0, 0]); | ||
259 | + } | ||
260 | + return _moment([1970, 0, 1, date.getHours(), date.getMinutes(), 0, 0]); | ||
261 | + } | ||
262 | + | ||
263 | + getSchedulerRangeText(control: FormGroup | AbstractControl): string { | ||
264 | + const start = this.timeToMoment(control.get('startsOn').value); | ||
265 | + const end = this.timeToMoment(control.get('endsOn').value); | ||
266 | + if (start < end) { | ||
267 | + return `<span><span class="nowrap">${start.format('hh:mm A')}</span> – <span class="nowrap">${end.format('hh:mm A')}</span></span>`; | ||
268 | + } else if (start.valueOf() === 0 && end.valueOf() === 0 || start.isSame(_moment([1970, 0])) && end.isSame(_moment([1970, 0]))) { | ||
269 | + return '<span><span class="nowrap">12:00 AM</span> – <span class="nowrap">12:00 PM</span></span>'; | ||
253 | } | 270 | } |
271 | + return `<span><span class="nowrap">12:00 AM</span> – <span class="nowrap">${end.format('hh:mm A')}</span>` + | ||
272 | + ` and <span class="nowrap">${start.format('hh:mm A')}</span> – <span class="nowrap">12:00 PM</span></span>`; | ||
254 | } | 273 | } |
255 | 274 | ||
256 | - private get itemsSchedulerForm(): FormArray { | 275 | + get itemsSchedulerForm(): FormArray { |
257 | return this.alarmScheduleForm.get('items') as FormArray; | 276 | return this.alarmScheduleForm.get('items') as FormArray; |
258 | } | 277 | } |
259 | } | 278 | } |
@@ -48,7 +48,7 @@ | @@ -48,7 +48,7 @@ | ||
48 | </button> | 48 | </button> |
49 | </div> | 49 | </div> |
50 | <div *ngIf="!createAlarmRulesFormArray().controls.length"> | 50 | <div *ngIf="!createAlarmRulesFormArray().controls.length"> |
51 | - <span translate fxLayoutAlign="center center" | 51 | + <span translate fxLayoutAlign="center center" style="margin: 16px 0" |
52 | class="tb-prompt">device-profile.no-create-alarm-rules</span> | 52 | class="tb-prompt">device-profile.no-create-alarm-rules</span> |
53 | </div> | 53 | </div> |
54 | <div fxLayout="row" *ngIf="!disabled"> | 54 | <div fxLayout="row" *ngIf="!disabled"> |
@@ -57,7 +57,7 @@ | @@ -57,7 +57,7 @@ | ||
57 | (click)="addCreateAlarmRule()" | 57 | (click)="addCreateAlarmRule()" |
58 | matTooltip="{{ 'device-profile.add-create-alarm-rule' | translate }}" | 58 | matTooltip="{{ 'device-profile.add-create-alarm-rule' | translate }}" |
59 | matTooltipPosition="above"> | 59 | matTooltipPosition="above"> |
60 | - <mat-icon>add_circle_outline</mat-icon> | 60 | + <mat-icon class="button-icon">add_circle_outline</mat-icon> |
61 | {{ 'device-profile.add-create-alarm-rule' | translate }} | 61 | {{ 'device-profile.add-create-alarm-rule' | translate }} |
62 | </button> | 62 | </button> |
63 | </div> | 63 | </div> |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | :host { | 17 | :host { |
18 | .create-alarm-rule { | 18 | .create-alarm-rule { |
19 | - border: 1px groove rgba(0, 0, 0, .25); | 19 | + border: 2px groove rgba(0, 0, 0, .45); |
20 | border-radius: 4px; | 20 | border-radius: 4px; |
21 | padding: 8px; | 21 | padding: 8px; |
22 | } | 22 | } |
@@ -28,4 +28,9 @@ | @@ -28,4 +28,9 @@ | ||
28 | width: 160px; | 28 | width: 160px; |
29 | } | 29 | } |
30 | } | 30 | } |
31 | + .button-icon{ | ||
32 | + font-size: 20px; | ||
33 | + width: 20px; | ||
34 | + height: 20px; | ||
35 | + } | ||
31 | } | 36 | } |
@@ -18,24 +18,11 @@ | @@ -18,24 +18,11 @@ | ||
18 | <mat-expansion-panel class="device-profile-alarm" fxFlex [formGroup]="alarmFormGroup" [(expanded)]="expanded"> | 18 | <mat-expansion-panel class="device-profile-alarm" fxFlex [formGroup]="alarmFormGroup" [(expanded)]="expanded"> |
19 | <mat-expansion-panel-header> | 19 | <mat-expansion-panel-header> |
20 | <div fxFlex fxLayout="row" fxLayoutAlign="start center"> | 20 | <div fxFlex fxLayout="row" fxLayoutAlign="start center"> |
21 | - <mat-panel-title [fxShow]="!expanded"> | 21 | + <mat-panel-title> |
22 | <div fxLayout="row" fxFlex fxLayoutAlign="start center"> | 22 | <div fxLayout="row" fxFlex fxLayoutAlign="start center"> |
23 | {{ alarmFormGroup.get('alarmType').value }} | 23 | {{ alarmFormGroup.get('alarmType').value }} |
24 | </div> | 24 | </div> |
25 | </mat-panel-title> | 25 | </mat-panel-title> |
26 | - <mat-form-field floatLabel="always" | ||
27 | - style="width: 600px;" | ||
28 | - [fxShow]="expanded" | ||
29 | - (keydown)="!disabled ? $event.stopPropagation() : null;" | ||
30 | - (click)="!disabled ? $event.stopPropagation() : null;"> | ||
31 | - <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label> | ||
32 | - <input required matInput formControlName="alarmType" placeholder="Enter alarm type"> | ||
33 | - <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')"> | ||
34 | - {{ 'device-profile.alarm-type-required' | translate }} | ||
35 | - </mat-error> | ||
36 | - <mat-hint *ngIf="!disabled" | ||
37 | - innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint> | ||
38 | - </mat-form-field> | ||
39 | <span fxFlex></span> | 26 | <span fxFlex></span> |
40 | <button *ngIf="!disabled" mat-icon-button style="min-width: 40px;" | 27 | <button *ngIf="!disabled" mat-icon-button style="min-width: 40px;" |
41 | type="button" | 28 | type="button" |
@@ -46,6 +33,50 @@ | @@ -46,6 +33,50 @@ | ||
46 | </button> | 33 | </button> |
47 | </div> | 34 | </div> |
48 | </mat-expansion-panel-header> | 35 | </mat-expansion-panel-header> |
36 | + <div fxLayout="column" fxLayoutGap="0.5em"> | ||
37 | + <mat-divider></mat-divider> | ||
38 | + <mat-form-field fxFlex floatLabel="always"> | ||
39 | + <mat-label>{{'device-profile.alarm-type' | translate}}</mat-label> | ||
40 | + <input required matInput formControlName="alarmType" placeholder="Enter alarm type"> | ||
41 | + <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')"> | ||
42 | + {{ 'device-profile.alarm-type-required' | translate }} | ||
43 | + </mat-error> | ||
44 | + <mat-hint *ngIf="!disabled" | ||
45 | + innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint> | ||
46 | + </mat-form-field> | ||
47 | + </div> | ||
48 | + <mat-expansion-panel class="advanced-settings" [expanded]="false"> | ||
49 | + <mat-expansion-panel-header> | ||
50 | + <mat-panel-title> | ||
51 | + <div fxFlex fxLayout="row" fxLayoutAlign="end center"> | ||
52 | + <div class="tb-small" translate>device-profile.advanced-settings</div> | ||
53 | + </div> | ||
54 | + </mat-panel-title> | ||
55 | + </mat-expansion-panel-header> | ||
56 | + <mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;"> | ||
57 | + {{ 'device-profile.propagate-alarm' | translate }} | ||
58 | + </mat-checkbox> | ||
59 | + <section *ngIf="alarmFormGroup.get('propagate').value === true"> | ||
60 | + <mat-form-field floatLabel="always" class="mat-block"> | ||
61 | + <mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label> | ||
62 | + <mat-chip-list #relationTypesChipList [disabled]="disabled"> | ||
63 | + <mat-chip | ||
64 | + *ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;" | ||
65 | + (removed)="removeRelationType(key)"> | ||
66 | + {{key}} | ||
67 | + <mat-icon matChipRemove>close</mat-icon> | ||
68 | + </mat-chip> | ||
69 | + <input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}" | ||
70 | + style="max-width: 200px;" | ||
71 | + [matChipInputFor]="relationTypesChipList" | ||
72 | + [matChipInputSeparatorKeyCodes]="separatorKeysCodes" | ||
73 | + (matChipInputTokenEnd)="addRelationType($event)" | ||
74 | + [matChipInputAddOnBlur]="true"> | ||
75 | + </mat-chip-list> | ||
76 | + <mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint> | ||
77 | + </mat-form-field> | ||
78 | + </section> | ||
79 | + </mat-expansion-panel> | ||
49 | <div fxFlex fxLayout="column"> | 80 | <div fxFlex fxLayout="column"> |
50 | <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div> | 81 | <div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div> |
51 | <tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;"> | 82 | <tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;"> |
@@ -68,7 +99,7 @@ | @@ -68,7 +99,7 @@ | ||
68 | </button> | 99 | </button> |
69 | </div> | 100 | </div> |
70 | <div *ngIf="!alarmFormGroup.get('clearRule').value"> | 101 | <div *ngIf="!alarmFormGroup.get('clearRule').value"> |
71 | - <span translate fxLayoutAlign="center center" | 102 | + <span translate fxLayoutAlign="center center" style="margin: 16px 0" |
72 | class="tb-prompt">device-profile.no-clear-alarm-rule</span> | 103 | class="tb-prompt">device-profile.no-clear-alarm-rule</span> |
73 | </div> | 104 | </div> |
74 | <div fxLayout="row" *ngIf="!disabled" | 105 | <div fxLayout="row" *ngIf="!disabled" |
@@ -78,41 +109,9 @@ | @@ -78,41 +109,9 @@ | ||
78 | (click)="addClearAlarmRule()" | 109 | (click)="addClearAlarmRule()" |
79 | matTooltip="{{ 'device-profile.add-clear-alarm-rule' | translate }}" | 110 | matTooltip="{{ 'device-profile.add-clear-alarm-rule' | translate }}" |
80 | matTooltipPosition="above"> | 111 | matTooltipPosition="above"> |
81 | - <mat-icon>add_circle_outline</mat-icon> | 112 | + <mat-icon class="button-icon">add_circle_outline</mat-icon> |
82 | {{ 'device-profile.add-clear-alarm-rule' | translate }} | 113 | {{ 'device-profile.add-clear-alarm-rule' | translate }} |
83 | </button> | 114 | </button> |
84 | </div> | 115 | </div> |
85 | </div> | 116 | </div> |
86 | - <mat-expansion-panel class="advanced-settings" [expanded]="false"> | ||
87 | - <mat-expansion-panel-header> | ||
88 | - <mat-panel-title> | ||
89 | - <div fxFlex fxLayout="row" fxLayoutAlign="end center"> | ||
90 | - <div class="tb-small" translate>device-profile.advanced-settings</div> | ||
91 | - </div> | ||
92 | - </mat-panel-title> | ||
93 | - </mat-expansion-panel-header> | ||
94 | - <mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;"> | ||
95 | - {{ 'device-profile.propagate-alarm' | translate }} | ||
96 | - </mat-checkbox> | ||
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> | ||
117 | - </mat-expansion-panel> | ||
118 | </mat-expansion-panel> | 117 | </mat-expansion-panel> |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | :host { | 17 | :host { |
18 | display: block; | 18 | display: block; |
19 | .clear-alarm-rule { | 19 | .clear-alarm-rule { |
20 | - border: 1px groove rgba(0, 0, 0, .25); | 20 | + border: 2px groove rgba(0, 0, 0, .45); |
21 | border-radius: 4px; | 21 | border-radius: 4px; |
22 | padding: 8px; | 22 | padding: 8px; |
23 | } | 23 | } |
@@ -28,13 +28,16 @@ | @@ -28,13 +28,16 @@ | ||
28 | .mat-expansion-panel-header { | 28 | .mat-expansion-panel-header { |
29 | padding: 0 24px 0 8px; | 29 | padding: 0 24px 0 8px; |
30 | &.mat-expanded { | 30 | &.mat-expanded { |
31 | - height: 80px; | 31 | + height: 48px; |
32 | } | 32 | } |
33 | } | 33 | } |
34 | } | 34 | } |
35 | &.advanced-settings { | 35 | &.advanced-settings { |
36 | border: none; | 36 | border: none; |
37 | padding: 0; | 37 | padding: 0; |
38 | + .mat-expansion-panel-header { | ||
39 | + padding: 0 8px; | ||
40 | + } | ||
38 | } | 41 | } |
39 | } | 42 | } |
40 | } | 43 | } |
@@ -43,7 +46,7 @@ | @@ -43,7 +46,7 @@ | ||
43 | .mat-expansion-panel { | 46 | .mat-expansion-panel { |
44 | &.device-profile-alarm { | 47 | &.device-profile-alarm { |
45 | .mat-expansion-panel-body { | 48 | .mat-expansion-panel-body { |
46 | - padding: 0 8px; | 49 | + padding: 0 8px 8px; |
47 | } | 50 | } |
48 | } | 51 | } |
49 | &.advanced-settings { | 52 | &.advanced-settings { |
@@ -51,5 +54,10 @@ | @@ -51,5 +54,10 @@ | ||
51 | padding: 0; | 54 | padding: 0; |
52 | } | 55 | } |
53 | } | 56 | } |
57 | + .button-icon{ | ||
58 | + font-size: 20px; | ||
59 | + width: 20px; | ||
60 | + height: 20px; | ||
61 | + } | ||
54 | } | 62 | } |
55 | } | 63 | } |
ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html
deleted
100644 → 0
1 | -<!-- | ||
2 | - | ||
3 | - Copyright © 2016-2020 The Thingsboard Authors | ||
4 | - | ||
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | - you may not use this file except in compliance with the License. | ||
7 | - You may obtain a copy of the License at | ||
8 | - | ||
9 | - http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | - | ||
11 | - Unless required by applicable law or agreed to in writing, software | ||
12 | - distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | - See the License for the specific language governing permissions and | ||
15 | - limitations under the License. | ||
16 | - | ||
17 | ---> | ||
18 | -<div [formGroup]="deviceProfileDataFormGroup" style="padding-bottom: 16px;"> | ||
19 | - <mat-accordion multi="true"> | ||
20 | - <mat-expansion-panel *ngIf="displayProfileConfiguration" [expanded]="true"> | ||
21 | - <mat-expansion-panel-header> | ||
22 | - <mat-panel-title> | ||
23 | - <div translate>device-profile.profile-configuration</div> | ||
24 | - </mat-panel-title> | ||
25 | - </mat-expansion-panel-header> | ||
26 | - <tb-device-profile-configuration | ||
27 | - formControlName="configuration" | ||
28 | - required> | ||
29 | - </tb-device-profile-configuration> | ||
30 | - </mat-expansion-panel> | ||
31 | - <mat-expansion-panel *ngIf="displayTransportConfiguration" [expanded]="true"> | ||
32 | - <mat-expansion-panel-header> | ||
33 | - <mat-panel-title> | ||
34 | - <div translate>device-profile.transport-configuration</div> | ||
35 | - </mat-panel-title> | ||
36 | - </mat-expansion-panel-header> | ||
37 | - <tb-device-profile-transport-configuration | ||
38 | - formControlName="transportConfiguration" | ||
39 | - required> | ||
40 | - </tb-device-profile-transport-configuration> | ||
41 | - </mat-expansion-panel> | ||
42 | - <mat-expansion-panel [expanded]="true"> | ||
43 | - <mat-expansion-panel-header> | ||
44 | - <mat-panel-title> | ||
45 | - <div>{{'device-profile.alarm-rules-with-count' | translate: | ||
46 | - {count: deviceProfileDataFormGroup.get('alarms').value ? | ||
47 | - deviceProfileDataFormGroup.get('alarms').value.length : 0} }}</div> | ||
48 | - </mat-panel-title> | ||
49 | - </mat-expansion-panel-header> | ||
50 | - <tb-device-profile-alarms | ||
51 | - formControlName="alarms"> | ||
52 | - </tb-device-profile-alarms> | ||
53 | - </mat-expansion-panel> | ||
54 | - </mat-accordion> | ||
55 | -</div> |
1 | -/// | ||
2 | -/// Copyright © 2016-2020 The Thingsboard Authors | ||
3 | -/// | ||
4 | -/// Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | -/// you may not use this file except in compliance with the License. | ||
6 | -/// You may obtain a copy of the License at | ||
7 | -/// | ||
8 | -/// http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | -/// | ||
10 | -/// Unless required by applicable law or agreed to in writing, software | ||
11 | -/// distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | -/// See the License for the specific language governing permissions and | ||
14 | -/// limitations under the License. | ||
15 | -/// | ||
16 | - | ||
17 | -import { Component, forwardRef, Input, OnInit } from '@angular/core'; | ||
18 | -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | ||
19 | -import { Store } from '@ngrx/store'; | ||
20 | -import { AppState } from '@app/core/core.state'; | ||
21 | -import { coerceBooleanProperty } from '@angular/cdk/coercion'; | ||
22 | -import { | ||
23 | - DeviceProfileData, | ||
24 | - DeviceProfileType, | ||
25 | - deviceProfileTypeConfigurationInfoMap, | ||
26 | - DeviceTransportType, deviceTransportTypeConfigurationInfoMap | ||
27 | -} from '@shared/models/device.models'; | ||
28 | - | ||
29 | -@Component({ | ||
30 | - selector: 'tb-device-profile-data', | ||
31 | - templateUrl: './device-profile-data.component.html', | ||
32 | - styleUrls: [], | ||
33 | - providers: [{ | ||
34 | - provide: NG_VALUE_ACCESSOR, | ||
35 | - useExisting: forwardRef(() => DeviceProfileDataComponent), | ||
36 | - multi: true | ||
37 | - }] | ||
38 | -}) | ||
39 | -export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit { | ||
40 | - | ||
41 | - deviceProfileDataFormGroup: FormGroup; | ||
42 | - | ||
43 | - private requiredValue: boolean; | ||
44 | - get required(): boolean { | ||
45 | - return this.requiredValue; | ||
46 | - } | ||
47 | - @Input() | ||
48 | - set required(value: boolean) { | ||
49 | - this.requiredValue = coerceBooleanProperty(value); | ||
50 | - } | ||
51 | - | ||
52 | - @Input() | ||
53 | - disabled: boolean; | ||
54 | - | ||
55 | - displayProfileConfiguration: boolean; | ||
56 | - displayTransportConfiguration: boolean; | ||
57 | - | ||
58 | - private propagateChange = (v: any) => { }; | ||
59 | - | ||
60 | - constructor(private store: Store<AppState>, | ||
61 | - private fb: FormBuilder) { | ||
62 | - } | ||
63 | - | ||
64 | - registerOnChange(fn: any): void { | ||
65 | - this.propagateChange = fn; | ||
66 | - } | ||
67 | - | ||
68 | - registerOnTouched(fn: any): void { | ||
69 | - } | ||
70 | - | ||
71 | - ngOnInit() { | ||
72 | - this.deviceProfileDataFormGroup = this.fb.group({ | ||
73 | - configuration: [null, Validators.required], | ||
74 | - transportConfiguration: [null, Validators.required], | ||
75 | - alarms: [null] | ||
76 | - }); | ||
77 | - this.deviceProfileDataFormGroup.valueChanges.subscribe(() => { | ||
78 | - this.updateModel(); | ||
79 | - }); | ||
80 | - } | ||
81 | - | ||
82 | - setDisabledState(isDisabled: boolean): void { | ||
83 | - this.disabled = isDisabled; | ||
84 | - if (this.disabled) { | ||
85 | - this.deviceProfileDataFormGroup.disable({emitEvent: false}); | ||
86 | - } else { | ||
87 | - this.deviceProfileDataFormGroup.enable({emitEvent: false}); | ||
88 | - } | ||
89 | - } | ||
90 | - | ||
91 | - writeValue(value: DeviceProfileData | null): void { | ||
92 | - const deviceProfileType = value?.configuration?.type; | ||
93 | - this.displayProfileConfiguration = deviceProfileType && | ||
94 | - deviceProfileTypeConfigurationInfoMap.get(deviceProfileType).hasProfileConfiguration; | ||
95 | - const deviceTransportType = value?.transportConfiguration?.type; | ||
96 | - this.displayTransportConfiguration = deviceTransportType && | ||
97 | - deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration; | ||
98 | - this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false}); | ||
99 | - this.deviceProfileDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false}); | ||
100 | - this.deviceProfileDataFormGroup.patchValue({alarms: value?.alarms}, {emitEvent: false}); | ||
101 | - } | ||
102 | - | ||
103 | - private updateModel() { | ||
104 | - let deviceProfileData: DeviceProfileData = null; | ||
105 | - if (this.deviceProfileDataFormGroup.valid) { | ||
106 | - deviceProfileData = this.deviceProfileDataFormGroup.getRawValue(); | ||
107 | - } | ||
108 | - this.propagateChange(deviceProfileData); | ||
109 | - } | ||
110 | -} |
@@ -53,7 +53,7 @@ | @@ -53,7 +53,7 @@ | ||
53 | labelText="device-profile.default-rule-chain" | 53 | labelText="device-profile.default-rule-chain" |
54 | formControlName="defaultRuleChainId"> | 54 | formControlName="defaultRuleChainId"> |
55 | </tb-rule-chain-autocomplete> | 55 | </tb-rule-chain-autocomplete> |
56 | - <mat-form-field class="mat-block"> | 56 | + <mat-form-field fxHide class="mat-block"> |
57 | <mat-label translate>device-profile.type</mat-label> | 57 | <mat-label translate>device-profile.type</mat-label> |
58 | <mat-select formControlName="type" required> | 58 | <mat-select formControlName="type" required> |
59 | <mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> | 59 | <mat-option *ngFor="let type of deviceProfileTypes" [value]="type"> |
@@ -65,21 +65,6 @@ | @@ -65,21 +65,6 @@ | ||
65 | </mat-error> | 65 | </mat-error> |
66 | </mat-form-field> | 66 | </mat-form-field> |
67 | <mat-form-field class="mat-block"> | 67 | <mat-form-field class="mat-block"> |
68 | - <mat-label translate>device-profile.transport-type</mat-label> | ||
69 | - <mat-select formControlName="transportType" required> | ||
70 | - <mat-option *ngFor="let type of deviceTransportTypes" [value]="type"> | ||
71 | - {{deviceTransportTypeTranslations.get(type) | translate}} | ||
72 | - </mat-option> | ||
73 | - </mat-select> | ||
74 | - <mat-error *ngIf="entityForm.get('transportType').hasError('required')"> | ||
75 | - {{ 'device-profile.transport-type-required' | translate }} | ||
76 | - </mat-error> | ||
77 | - </mat-form-field> | ||
78 | - <tb-device-profile-data | ||
79 | - formControlName="profileData" | ||
80 | - required> | ||
81 | - </tb-device-profile-data> | ||
82 | - <mat-form-field class="mat-block"> | ||
83 | <mat-label translate>device-profile.description</mat-label> | 68 | <mat-label translate>device-profile.description</mat-label> |
84 | <textarea matInput formControlName="description" rows="2"></textarea> | 69 | <textarea matInput formControlName="description" rows="2"></textarea> |
85 | </mat-form-field> | 70 | </mat-form-field> |
@@ -77,7 +77,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -77,7 +77,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
77 | name: [entity ? entity.name : '', [Validators.required]], | 77 | name: [entity ? entity.name : '', [Validators.required]], |
78 | type: [entity ? entity.type : null, [Validators.required]], | 78 | type: [entity ? entity.type : null, [Validators.required]], |
79 | transportType: [entity ? entity.transportType : null, [Validators.required]], | 79 | transportType: [entity ? entity.transportType : null, [Validators.required]], |
80 | - profileData: [entity && !this.isAdd ? entity.profileData : {}, []], | 80 | + profileData: this.fb.group({ |
81 | + configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required], | ||
82 | + transportConfiguration: [entity && !this.isAdd ? entity.profileData?.transportConfiguration : {}, Validators.required], | ||
83 | + alarms: [entity && !this.isAdd ? entity.profileData?.alarms : []] | ||
84 | + }), | ||
81 | defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], | 85 | defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], |
82 | description: [entity ? entity.description : '', []], | 86 | description: [entity ? entity.description : '', []], |
83 | } | 87 | } |
@@ -138,7 +142,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -138,7 +142,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
138 | if (formValue.defaultRuleChainId) { | 142 | if (formValue.defaultRuleChainId) { |
139 | formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); | 143 | formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId); |
140 | } | 144 | } |
141 | - return formValue; | 145 | + return super.prepareFormValue(formValue); |
142 | } | 146 | } |
143 | 147 | ||
144 | onDeviceProfileIdCopied(event) { | 148 | onDeviceProfileIdCopied(event) { |
@@ -16,9 +16,5 @@ | @@ -16,9 +16,5 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;"> | 18 | <form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;"> |
19 | - <tb-json-object-edit | ||
20 | - [required]="required" | ||
21 | - label="{{ 'device-profile.type-default' | translate }}" | ||
22 | - formControlName="configuration"> | ||
23 | - </tb-json-object-edit> | 19 | + |
24 | </form> | 20 | </form> |
@@ -16,9 +16,5 @@ | @@ -16,9 +16,5 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <form [formGroup]="defaultDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;"> | 18 | <form [formGroup]="defaultDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;"> |
19 | - <tb-json-object-edit | ||
20 | - [required]="required" | ||
21 | - label="{{ 'device-profile.transport-type-default' | translate }}" | ||
22 | - formControlName="configuration"> | ||
23 | - </tb-json-object-edit> | 19 | + |
24 | </form> | 20 | </form> |
@@ -16,6 +16,52 @@ | @@ -16,6 +16,52 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <mat-tab *ngIf="entity" | 18 | <mat-tab *ngIf="entity" |
19 | + label="{{ 'device-profile.transport-configuration' | translate }}" #transportType="matTab"> | ||
20 | + <div class="mat-padding" [formGroup]="detailsForm"> | ||
21 | + <mat-form-field class="mat-block"> | ||
22 | + <mat-label translate>device-profile.transport-type</mat-label> | ||
23 | + <mat-select formControlName="transportType" required> | ||
24 | + <mat-option *ngFor="let type of deviceTransportTypes" [value]="type"> | ||
25 | + {{deviceTransportTypeTranslations.get(type) | translate}} | ||
26 | + </mat-option> | ||
27 | + </mat-select> | ||
28 | + <mat-hint *ngIf="detailsForm.get('transportType').value"> | ||
29 | + {{deviceTransportTypeHints.get(detailsForm.get('transportType').value) | translate}} | ||
30 | + </mat-hint> | ||
31 | + <mat-error *ngIf="detailsForm.get('transportType').hasError('required')"> | ||
32 | + {{ 'device-profile.transport-type-required' | translate }} | ||
33 | + </mat-error> | ||
34 | + </mat-form-field> | ||
35 | + <div formGroupName="profileData"> | ||
36 | + <tb-device-profile-transport-configuration | ||
37 | + formControlName="transportConfiguration" | ||
38 | + required> | ||
39 | + </tb-device-profile-transport-configuration> | ||
40 | + </div> | ||
41 | + </div> | ||
42 | +</mat-tab> | ||
43 | +<mat-tab *ngIf="entity" | ||
44 | + label="{{'device-profile.alarm-rules' | translate: | ||
45 | + {count: entity.profileData.alarms && entity.profileData.alarms.length ? | ||
46 | + entity.profileData.alarms.length : 0} }}" #alarmRules="matTab"> | ||
47 | + <div class="mat-padding" [formGroup]="detailsForm"> | ||
48 | + <div formGroupName="profileData"> | ||
49 | + <tb-device-profile-alarms formControlName="alarms"></tb-device-profile-alarms> | ||
50 | + </div> | ||
51 | + </div> | ||
52 | +</mat-tab> | ||
53 | +<mat-tab *ngIf="false" | ||
54 | + label="{{'device-profile.profile-configuration' | translate }}" #deviceProfile="matTab"> | ||
55 | + <div class="mat-padding" [formGroup]="detailsForm"> | ||
56 | + <div formGroupName="profileData"> | ||
57 | + <tb-device-profile-configuration | ||
58 | + formControlName="configuration" | ||
59 | + required> | ||
60 | + </tb-device-profile-configuration> | ||
61 | + </div> | ||
62 | + </div> | ||
63 | +</mat-tab> | ||
64 | +<mat-tab *ngIf="entity && !isEdit" | ||
19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> | 65 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | 66 | <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" |
21 | [active]="attributesTab.isActive" | 67 | [active]="attributesTab.isActive" |
@@ -23,7 +69,7 @@ | @@ -23,7 +69,7 @@ | ||
23 | [entityName]="entity.name"> | 69 | [entityName]="entity.name"> |
24 | </tb-attribute-table> | 70 | </tb-attribute-table> |
25 | </mat-tab> | 71 | </mat-tab> |
26 | -<mat-tab *ngIf="entity" | 72 | +<mat-tab *ngIf="entity && !isEdit" |
27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> | 73 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | 74 | <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" |
29 | disableAttributeScopeSelection | 75 | disableAttributeScopeSelection |
@@ -32,11 +78,11 @@ | @@ -32,11 +78,11 @@ | ||
32 | [entityName]="entity.name"> | 78 | [entityName]="entity.name"> |
33 | </tb-attribute-table> | 79 | </tb-attribute-table> |
34 | </mat-tab> | 80 | </mat-tab> |
35 | -<mat-tab *ngIf="entity" | 81 | +<mat-tab *ngIf="entity && !isEdit" |
36 | label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab"> | 82 | label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab"> |
37 | <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table> | 83 | <tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table> |
38 | </mat-tab> | 84 | </mat-tab> |
39 | -<mat-tab *ngIf="entity" | 85 | +<mat-tab *ngIf="entity && !isEdit" |
40 | label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> | 86 | label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> |
41 | <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" | 87 | <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" |
42 | [entityId]="entity.id"></tb-event-table> | 88 | [entityId]="entity.id"></tb-event-table> |
@@ -18,7 +18,12 @@ import { Component } from '@angular/core'; | @@ -18,7 +18,12 @@ import { Component } from '@angular/core'; | ||
18 | import { Store } from '@ngrx/store'; | 18 | import { Store } from '@ngrx/store'; |
19 | import { AppState } from '@core/core.state'; | 19 | import { AppState } from '@core/core.state'; |
20 | import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; | 20 | import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; |
21 | -import { DeviceProfile } from '@shared/models/device.models'; | 21 | +import { |
22 | + DeviceProfile, | ||
23 | + DeviceTransportType, | ||
24 | + deviceTransportTypeHintMap, | ||
25 | + deviceTransportTypeTranslationMap | ||
26 | +} from '@shared/models/device.models'; | ||
22 | 27 | ||
23 | @Component({ | 28 | @Component({ |
24 | selector: 'tb-device-profile-tabs', | 29 | selector: 'tb-device-profile-tabs', |
@@ -27,6 +32,12 @@ import { DeviceProfile } from '@shared/models/device.models'; | @@ -27,6 +32,12 @@ import { DeviceProfile } from '@shared/models/device.models'; | ||
27 | }) | 32 | }) |
28 | export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { | 33 | export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { |
29 | 34 | ||
35 | + deviceTransportTypes = Object.keys(DeviceTransportType); | ||
36 | + | ||
37 | + deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; | ||
38 | + | ||
39 | + deviceTransportTypeHints = deviceTransportTypeHintMap; | ||
40 | + | ||
30 | constructor(protected store: Store<AppState>) { | 41 | constructor(protected store: Store<AppState>) { |
31 | super(store); | 42 | super(store); |
32 | } | 43 | } |
@@ -59,6 +59,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon | @@ -59,6 +59,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon | ||
59 | this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE); | 59 | this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE); |
60 | this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE); | 60 | this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE); |
61 | 61 | ||
62 | + this.config.hideDetailsTabsOnEdit = false; | ||
63 | + | ||
62 | this.config.addDialogStyle = {width: '1000px'}; | 64 | this.config.addDialogStyle = {width: '1000px'}; |
63 | 65 | ||
64 | this.config.columns.push( | 66 | this.config.columns.push( |
@@ -33,7 +33,7 @@ export enum DeviceProfileType { | @@ -33,7 +33,7 @@ export enum DeviceProfileType { | ||
33 | export enum DeviceTransportType { | 33 | export enum DeviceTransportType { |
34 | DEFAULT = 'DEFAULT', | 34 | DEFAULT = 'DEFAULT', |
35 | MQTT = 'MQTT', | 35 | MQTT = 'MQTT', |
36 | -// LWM2M = 'LWM2M' | 36 | + // LWM2M = 'LWM2M' |
37 | } | 37 | } |
38 | 38 | ||
39 | export enum MqttTransportPayloadType { | 39 | export enum MqttTransportPayloadType { |
@@ -68,7 +68,7 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | @@ -68,7 +68,7 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | ||
68 | [ | 68 | [ |
69 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'], | 69 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'], |
70 | [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'], | 70 | [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'], |
71 | - // [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m'] | 71 | + // [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m'] |
72 | ] | 72 | ] |
73 | ); | 73 | ); |
74 | 74 | ||
@@ -76,7 +76,7 @@ export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( | @@ -76,7 +76,7 @@ export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( | ||
76 | [ | 76 | [ |
77 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], | 77 | [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], |
78 | [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'], | 78 | [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'], |
79 | - // [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint'] | 79 | + // [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint'] |
80 | ] | 80 | ] |
81 | ); | 81 | ); |
82 | 82 |