Commit 87874c3eb66c0eada6c432edd10381ded4cbaeec
Merge branch 'master' into lwm2m_create_coap_resource
# Conflicts: # ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html
Showing
28 changed files
with
509 additions
and
353 deletions
@@ -16,16 +16,17 @@ | @@ -16,16 +16,17 @@ | ||
16 | 16 | ||
17 | import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; | 17 | import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; |
18 | import { | 18 | import { |
19 | - AggregationType, calculateIntervalComparisonEndTime, | ||
20 | - calculateIntervalEndTime, calculateIntervalStartEndTime, | 19 | + AggregationType, |
20 | + calculateIntervalComparisonEndTime, | ||
21 | + calculateIntervalEndTime, | ||
22 | + calculateIntervalStartEndTime, | ||
21 | getCurrentTime, | 23 | getCurrentTime, |
22 | - getCurrentTimeForComparison, getTime, | 24 | + getTime, |
23 | SubscriptionTimewindow | 25 | SubscriptionTimewindow |
24 | } from '@shared/models/time/time.models'; | 26 | } from '@shared/models/time/time.models'; |
25 | import { UtilsService } from '@core/services/utils.service'; | 27 | import { UtilsService } from '@core/services/utils.service'; |
26 | -import { deepClone } from '@core/utils'; | 28 | +import { deepClone, isNumeric } from '@core/utils'; |
27 | import Timeout = NodeJS.Timeout; | 29 | import Timeout = NodeJS.Timeout; |
28 | -import * as moment_ from 'moment'; | ||
29 | 30 | ||
30 | export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void; | 31 | export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void; |
31 | 32 | ||
@@ -407,24 +408,11 @@ export class DataAggregator { | @@ -407,24 +408,11 @@ export class DataAggregator { | ||
407 | } | 408 | } |
408 | } | 409 | } |
409 | 410 | ||
410 | - private isNumeric(val: any): boolean { | ||
411 | - return (val - parseFloat( val ) + 1) >= 0; | ||
412 | - } | ||
413 | - | ||
414 | private convertValue(val: string): any { | 411 | private convertValue(val: string): any { |
415 | - if (!this.noAggregation || val && this.isNumeric(val)) { | 412 | + if (!this.noAggregation || val && isNumeric(val) && Number(val).toString() === val) { |
416 | return Number(val); | 413 | return Number(val); |
417 | - } else { | ||
418 | - return val; | ||
419 | - } | ||
420 | - } | ||
421 | - | ||
422 | - private getCurrentTime() { | ||
423 | - if (this.subsTw.timeForComparison) { | ||
424 | - return getCurrentTimeForComparison(this.subsTw.timeForComparison as moment_.unitOfTime.DurationConstructor, this.subsTw.timezone); | ||
425 | - } else { | ||
426 | - return getCurrentTime(this.subsTw.timezone); | ||
427 | } | 414 | } |
415 | + return val; | ||
428 | } | 416 | } |
429 | 417 | ||
430 | } | 418 | } |
@@ -38,7 +38,7 @@ import { | @@ -38,7 +38,7 @@ import { | ||
38 | } from '@shared/models/telemetry/telemetry.models'; | 38 | } from '@shared/models/telemetry/telemetry.models'; |
39 | import { UtilsService } from '@core/services/utils.service'; | 39 | import { UtilsService } from '@core/services/utils.service'; |
40 | import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service'; | 40 | import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service'; |
41 | -import { deepClone, isDefined, isDefinedAndNotNull, isObject, objectHashCode } from '@core/utils'; | 41 | +import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isObject, objectHashCode } from '@core/utils'; |
42 | import { PageData } from '@shared/models/page/page-data'; | 42 | import { PageData } from '@shared/models/page/page-data'; |
43 | import { DataAggregator } from '@core/api/data-aggregator'; | 43 | import { DataAggregator } from '@core/api/data-aggregator'; |
44 | import { NULL_UUID } from '@shared/models/id/has-uuid'; | 44 | import { NULL_UUID } from '@shared/models/id/has-uuid'; |
@@ -667,8 +667,7 @@ export class EntityDataSubscription { | @@ -667,8 +667,7 @@ export class EntityDataSubscription { | ||
667 | if (prevDataCb) { | 667 | if (prevDataCb) { |
668 | dataAggregator.updateOnDataCb(prevDataCb); | 668 | dataAggregator.updateOnDataCb(prevDataCb); |
669 | } | 669 | } |
670 | - } | ||
671 | - if (!this.history && !isUpdate) { | 670 | + } else if (!this.history && !isUpdate) { |
672 | this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); | 671 | this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); |
673 | } | 672 | } |
674 | } | 673 | } |
@@ -743,16 +742,11 @@ export class EntityDataSubscription { | @@ -743,16 +742,11 @@ export class EntityDataSubscription { | ||
743 | } | 742 | } |
744 | } | 743 | } |
745 | 744 | ||
746 | - private isNumeric(val: any): boolean { | ||
747 | - return (val - parseFloat( val ) + 1) >= 0; | ||
748 | - } | ||
749 | - | ||
750 | private convertValue(val: string): any { | 745 | private convertValue(val: string): any { |
751 | - if (val && this.isNumeric(val) && Number(val).toString() === val) { | 746 | + if (val && isNumeric(val) && Number(val).toString() === val) { |
752 | return Number(val); | 747 | return Number(val); |
753 | - } else { | ||
754 | - return val; | ||
755 | } | 748 | } |
749 | + return val; | ||
756 | } | 750 | } |
757 | 751 | ||
758 | private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData { | 752 | private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData { |
@@ -231,7 +231,6 @@ export class DashboardUtilsService { | @@ -231,7 +231,6 @@ export class DashboardUtilsService { | ||
231 | private createDefaultGridSettings(): GridSettings { | 231 | private createDefaultGridSettings(): GridSettings { |
232 | return { | 232 | return { |
233 | backgroundColor: '#eeeeee', | 233 | backgroundColor: '#eeeeee', |
234 | - color: 'rgba(0,0,0,0.870588)', | ||
235 | columns: 24, | 234 | columns: 24, |
236 | margin: 10, | 235 | margin: 10, |
237 | backgroundSizeMode: '100%' | 236 | backgroundSizeMode: '100%' |
@@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service'; | @@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service'; | ||
27 | 27 | ||
28 | const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; | 28 | const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; |
29 | const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; | 29 | const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; |
30 | +const dashboardLayoutHandler = 'tbMobileDashboardLayoutHandler'; | ||
30 | const navigationHandler = 'tbMobileNavigationHandler'; | 31 | const navigationHandler = 'tbMobileNavigationHandler'; |
31 | const mobileHandler = 'tbMobileHandler'; | 32 | const mobileHandler = 'tbMobileHandler'; |
32 | 33 | ||
@@ -43,6 +44,7 @@ export class MobileService { | @@ -43,6 +44,7 @@ export class MobileService { | ||
43 | 44 | ||
44 | private reloadUserObservable: Observable<boolean>; | 45 | private reloadUserObservable: Observable<boolean>; |
45 | private lastDashboardId: string; | 46 | private lastDashboardId: string; |
47 | + private toggleLayoutFunction: () => void; | ||
46 | 48 | ||
47 | constructor(@Inject(WINDOW) private window: Window, | 49 | constructor(@Inject(WINDOW) private window: Window, |
48 | private router: Router, | 50 | private router: Router, |
@@ -65,12 +67,26 @@ export class MobileService { | @@ -65,12 +67,26 @@ export class MobileService { | ||
65 | } | 67 | } |
66 | } | 68 | } |
67 | 69 | ||
68 | - public onDashboardLoaded() { | 70 | + public onDashboardLoaded(hasRightLayout: boolean, rightLayoutOpened: boolean) { |
69 | if (this.mobileApp) { | 71 | if (this.mobileApp) { |
70 | - this.mobileChannel.callHandler(dashboardLoadedHandler); | 72 | + this.mobileChannel.callHandler(dashboardLoadedHandler, hasRightLayout, rightLayoutOpened); |
71 | } | 73 | } |
72 | } | 74 | } |
73 | 75 | ||
76 | + public onDashboardRightLayoutChanged(opened: boolean) { | ||
77 | + if (this.mobileApp) { | ||
78 | + this.mobileChannel.callHandler(dashboardLayoutHandler, opened); | ||
79 | + } | ||
80 | + } | ||
81 | + | ||
82 | + public registerToggleLayoutFunction(toggleLayoutFunction: () => void) { | ||
83 | + this.toggleLayoutFunction = toggleLayoutFunction; | ||
84 | + } | ||
85 | + | ||
86 | + public unregisterToggleLayoutFunction() { | ||
87 | + this.toggleLayoutFunction = null; | ||
88 | + } | ||
89 | + | ||
74 | public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): | 90 | public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): |
75 | Observable<WidgetMobileActionResult<T>> { | 91 | Observable<WidgetMobileActionResult<T>> { |
76 | if (this.mobileApp) { | 92 | if (this.mobileApp) { |
@@ -110,6 +126,11 @@ export class MobileService { | @@ -110,6 +126,11 @@ export class MobileService { | ||
110 | const reloadUserMessage: ReloadUserMessage = message.data; | 126 | const reloadUserMessage: ReloadUserMessage = message.data; |
111 | this.reloadUser(reloadUserMessage); | 127 | this.reloadUser(reloadUserMessage); |
112 | break; | 128 | break; |
129 | + case 'toggleDashboardLayout': | ||
130 | + if (this.toggleLayoutFunction) { | ||
131 | + this.toggleLayoutFunction(); | ||
132 | + } | ||
133 | + break; | ||
113 | } | 134 | } |
114 | } | 135 | } |
115 | } | 136 | } |
@@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
149 | @Input() | 149 | @Input() |
150 | currentState: string; | 150 | currentState: string; |
151 | 151 | ||
152 | + private hideToolbarValue = false; | ||
153 | + | ||
152 | @Input() | 154 | @Input() |
153 | - hideToolbar: boolean; | 155 | + set hideToolbar(hideToolbar: boolean) { |
156 | + this.hideToolbarValue = hideToolbar; | ||
157 | + } | ||
158 | + | ||
159 | + get hideToolbar(): boolean { | ||
160 | + return (this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit; | ||
161 | + } | ||
154 | 162 | ||
155 | @Input() | 163 | @Input() |
156 | syncStateWithQueryParam = true; | 164 | syncStateWithQueryParam = true; |
@@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
269 | return !this.widgetEditMode && !this.hideToolbar && | 277 | return !this.widgetEditMode && !this.hideToolbar && |
270 | (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch()); | 278 | (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch()); |
271 | } | 279 | } |
280 | + | ||
272 | set toolbarOpened(toolbarOpened: boolean) { | 281 | set toolbarOpened(toolbarOpened: boolean) { |
273 | } | 282 | } |
274 | 283 | ||
@@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
329 | this.dashboardCtx.aliasController.updateAliases(); | 338 | this.dashboardCtx.aliasController.updateAliases(); |
330 | setTimeout(() => { | 339 | setTimeout(() => { |
331 | this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName()); | 340 | this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName()); |
332 | - this.mobileService.onDashboardLoaded(); | 341 | + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened); |
333 | }); | 342 | }); |
334 | } | 343 | } |
335 | } | 344 | } |
@@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
340 | this.isMobile = !state.matches; | 349 | this.isMobile = !state.matches; |
341 | } | 350 | } |
342 | )); | 351 | )); |
352 | + if (this.isMobileApp) { | ||
353 | + this.mobileService.registerToggleLayoutFunction(() => { | ||
354 | + setTimeout(() => { | ||
355 | + this.toggleLayouts(); | ||
356 | + this.cd.detectChanges(); | ||
357 | + }); | ||
358 | + }); | ||
359 | + } | ||
343 | } | 360 | } |
344 | 361 | ||
345 | private init(data: any) { | 362 | private init(data: any) { |
@@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
430 | } | 447 | } |
431 | 448 | ||
432 | ngOnDestroy(): void { | 449 | ngOnDestroy(): void { |
450 | + if (this.isMobileApp) { | ||
451 | + this.mobileService.unregisterToggleLayoutFunction(); | ||
452 | + } | ||
433 | this.rxSubscriptions.forEach((subscription) => { | 453 | this.rxSubscriptions.forEach((subscription) => { |
434 | subscription.unsubscribe(); | 454 | subscription.unsubscribe(); |
435 | }); | 455 | }); |
@@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
469 | } | 489 | } |
470 | } | 490 | } |
471 | 491 | ||
492 | + private hideToolbarSetting(): boolean { | ||
493 | + if (this.dashboard.configuration.settings && | ||
494 | + isDefined(this.dashboard.configuration.settings.hideToolbar)) { | ||
495 | + return this.dashboard.configuration.settings.hideToolbar; | ||
496 | + } else { | ||
497 | + return false; | ||
498 | + } | ||
499 | + } | ||
500 | + | ||
472 | public displayTitle(): boolean { | 501 | public displayTitle(): boolean { |
473 | if (this.dashboard.configuration.settings && | 502 | if (this.dashboard.configuration.settings && |
474 | isDefined(this.dashboard.configuration.settings.showTitle)) { | 503 | isDefined(this.dashboard.configuration.settings.showTitle)) { |
@@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
546 | } | 575 | } |
547 | 576 | ||
548 | public showRightLayoutSwitch(): boolean { | 577 | public showRightLayoutSwitch(): boolean { |
549 | - return this.isMobile && this.layouts.right.show; | 578 | + return this.isMobile && !this.isMobileApp && this.layouts.right.show; |
550 | } | 579 | } |
551 | 580 | ||
552 | public toggleLayouts() { | 581 | public toggleLayouts() { |
553 | this.isRightLayoutOpened = !this.isRightLayoutOpened; | 582 | this.isRightLayoutOpened = !this.isRightLayoutOpened; |
583 | + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); | ||
554 | } | 584 | } |
555 | 585 | ||
556 | public openRightLayout() { | 586 | public openRightLayout() { |
557 | this.isRightLayoutOpened = true; | 587 | this.isRightLayoutOpened = true; |
588 | + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); | ||
558 | } | 589 | } |
559 | 590 | ||
560 | public mainLayoutWidth(): string { | 591 | public mainLayoutWidth(): string { |
@@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
782 | this.updateLayouts(layoutsData); | 813 | this.updateLayouts(layoutsData); |
783 | } | 814 | } |
784 | setTimeout(() => { | 815 | setTimeout(() => { |
785 | - this.mobileService.onDashboardLoaded(); | 816 | + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened); |
786 | }); | 817 | }); |
787 | } | 818 | } |
788 | 819 |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<form (ngSubmit)="save()"> | 18 | +<form (ngSubmit)="save()" style="width: 750px;"> |
19 | <mat-toolbar color="primary"> | 19 | <mat-toolbar color="primary"> |
20 | <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2> | 20 | <h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2> |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
@@ -28,8 +28,8 @@ | @@ -28,8 +28,8 @@ | ||
28 | <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> | 28 | <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
29 | </mat-progress-bar> | 29 | </mat-progress-bar> |
30 | <div mat-dialog-content> | 30 | <div mat-dialog-content> |
31 | - <fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="16px"> | ||
32 | - <div *ngIf="settings" [formGroup]="settingsFormGroup"> | 31 | + <fieldset [disabled]="isLoading$ | async"> |
32 | + <div *ngIf="settings" [formGroup]="settingsFormGroup" fxLayout="column"> | ||
33 | <mat-form-field class="mat-block"> | 33 | <mat-form-field class="mat-block"> |
34 | <mat-label translate>dashboard.state-controller</mat-label> | 34 | <mat-label translate>dashboard.state-controller</mat-label> |
35 | <mat-select required formControlName="stateControllerId"> | 35 | <mat-select required formControlName="stateControllerId"> |
@@ -38,112 +38,116 @@ | @@ -38,112 +38,116 @@ | ||
38 | </mat-option> | 38 | </mat-option> |
39 | </mat-select> | 39 | </mat-select> |
40 | </mat-form-field> | 40 | </mat-form-field> |
41 | - <div fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px"> | ||
42 | - <mat-checkbox fxFlex formControlName="toolbarAlwaysOpen"> | ||
43 | - {{ 'dashboard.toolbar-always-open' | translate }} | ||
44 | - </mat-checkbox> | ||
45 | - <mat-checkbox fxFlex formControlName="showTitle"> | 41 | + <fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px"> |
42 | + <legend class="group-title" translate>dashboard.title-settings</legend> | ||
43 | + <mat-slide-toggle fxFlex formControlName="showTitle"> | ||
46 | {{ 'dashboard.display-title' | translate }} | 44 | {{ 'dashboard.display-title' | translate }} |
47 | - </mat-checkbox> | 45 | + </mat-slide-toggle> |
48 | <tb-color-input fxFlex | 46 | <tb-color-input fxFlex |
49 | label="{{'dashboard.title-color' | translate}}" | 47 | label="{{'dashboard.title-color' | translate}}" |
50 | icon="format_color_fill" | 48 | icon="format_color_fill" |
51 | openOnInput | 49 | openOnInput |
52 | formControlName="titleColor"> | 50 | formControlName="titleColor"> |
53 | </tb-color-input> | 51 | </tb-color-input> |
54 | - </div> | ||
55 | - <div fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 8px;" | ||
56 | - fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px"> | ||
57 | - <mat-checkbox fxFlex formControlName="showDashboardsSelect"> | 52 | + </fieldset> |
53 | + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> | ||
54 | + <legend class="group-title" translate>dashboard.dashboard-logo-settings</legend> | ||
55 | + <mat-slide-toggle formControlName="showDashboardLogo"> | ||
56 | + {{ 'dashboard.display-dashboard-logo' | translate }} | ||
57 | + </mat-slide-toggle> | ||
58 | + <tb-image-input fxFlex | ||
59 | + label="{{'dashboard.dashboard-logo-image' | translate}}" | ||
60 | + formControlName="dashboardLogoUrl"> | ||
61 | + </tb-image-input> | ||
62 | + </fieldset> | ||
63 | + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> | ||
64 | + <legend class="group-title" translate>dashboard.toolbar-settings</legend> | ||
65 | + <mat-slide-toggle formControlName="hideToolbar"> | ||
66 | + {{ 'dashboard.hide-toolbar' | translate }} | ||
67 | + </mat-slide-toggle> | ||
68 | + <mat-slide-toggle formControlName="toolbarAlwaysOpen"> | ||
69 | + {{ 'dashboard.toolbar-always-open' | translate }} | ||
70 | + </mat-slide-toggle> | ||
71 | + <mat-slide-toggle formControlName="showDashboardsSelect"> | ||
58 | {{ 'dashboard.display-dashboards-selection' | translate }} | 72 | {{ 'dashboard.display-dashboards-selection' | translate }} |
59 | - </mat-checkbox> | ||
60 | - <mat-checkbox fxFlex formControlName="showEntitiesSelect"> | 73 | + </mat-slide-toggle> |
74 | + <mat-slide-toggle formControlName="showEntitiesSelect"> | ||
61 | {{ 'dashboard.display-entities-selection' | translate }} | 75 | {{ 'dashboard.display-entities-selection' | translate }} |
62 | - </mat-checkbox> | ||
63 | - <mat-checkbox fxFlex formControlName="showFilters"> | 76 | + </mat-slide-toggle> |
77 | + <mat-slide-toggle formControlName="showFilters"> | ||
64 | {{ 'dashboard.display-filters' | translate }} | 78 | {{ 'dashboard.display-filters' | translate }} |
65 | - </mat-checkbox> | ||
66 | - <mat-checkbox fxFlex formControlName="showDashboardTimewindow"> | 79 | + </mat-slide-toggle> |
80 | + <mat-slide-toggle formControlName="showDashboardTimewindow"> | ||
67 | {{ 'dashboard.display-dashboard-timewindow' | translate }} | 81 | {{ 'dashboard.display-dashboard-timewindow' | translate }} |
68 | - </mat-checkbox> | ||
69 | - <mat-checkbox fxFlex formControlName="showDashboardExport"> | 82 | + </mat-slide-toggle> |
83 | + <mat-slide-toggle formControlName="showDashboardExport"> | ||
70 | {{ 'dashboard.display-dashboard-export' | translate }} | 84 | {{ 'dashboard.display-dashboard-export' | translate }} |
71 | - </mat-checkbox> | ||
72 | - <mat-checkbox fxFlex formControlName="showUpdateDashboardImage"> | 85 | + </mat-slide-toggle> |
86 | + <mat-slide-toggle formControlName="showUpdateDashboardImage"> | ||
73 | {{ 'dashboard.display-update-dashboard-image' | translate }} | 87 | {{ 'dashboard.display-update-dashboard-image' | translate }} |
74 | - </mat-checkbox> | ||
75 | - </div> | ||
76 | - <mat-checkbox formControlName="showDashboardLogo"> | ||
77 | - {{ 'dashboard.display-dashboard-logo' | translate }} | ||
78 | - </mat-checkbox> | ||
79 | - <tb-image-input fxFlex *ngIf="settingsFormGroup.get('showDashboardLogo').value" | ||
80 | - label="{{'dashboard.dashboard-logo-image' | translate}}" | ||
81 | - formControlName="dashboardLogoUrl"> | ||
82 | - </tb-image-input> | 88 | + </mat-slide-toggle> |
89 | + </fieldset> | ||
83 | </div> | 90 | </div> |
84 | - <div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup"> | ||
85 | - <tb-color-input fxFlex | ||
86 | - label="{{'layout.color' | translate}}" | ||
87 | - icon="format_color_fill" | ||
88 | - openOnInput | ||
89 | - formControlName="color"> | ||
90 | - </tb-color-input> | ||
91 | - <mat-form-field class="mat-block"> | ||
92 | - <mat-label translate>dashboard.columns-count</mat-label> | ||
93 | - <input matInput formControlName="columns" type="number" step="any" min="10" | ||
94 | - max="1000" required> | ||
95 | - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')"> | ||
96 | - {{ 'dashboard.columns-count-required' | translate }} | ||
97 | - </mat-error> | ||
98 | - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')"> | ||
99 | - {{ 'dashboard.min-columns-count-message' | translate }} | ||
100 | - </mat-error> | ||
101 | - <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')"> | ||
102 | - {{ 'dashboard.max-columns-count-message' | translate }} | ||
103 | - </mat-error> | ||
104 | - </mat-form-field> | ||
105 | - <mat-form-field fxFlex class="mat-block"> | ||
106 | - <mat-label translate>dashboard.widgets-margins</mat-label> | ||
107 | - <input matInput formControlName="margin" type="number" step="any" min="0" | ||
108 | - max="50" required> | ||
109 | - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')"> | ||
110 | - {{ 'dashboard.margin-required' | translate }} | ||
111 | - </mat-error> | ||
112 | - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')"> | ||
113 | - {{ 'dashboard.min-margin-message' | translate }} | ||
114 | - </mat-error> | ||
115 | - <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')"> | ||
116 | - {{ 'dashboard.max-margin-message' | translate }} | ||
117 | - </mat-error> | ||
118 | - </mat-form-field> | ||
119 | - <mat-checkbox fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;"> | ||
120 | - {{ 'dashboard.autofill-height' | translate }} | ||
121 | - </mat-checkbox> | ||
122 | - <tb-color-input fxFlex | ||
123 | - label="{{'dashboard.background-color' | translate}}" | ||
124 | - icon="format_color_fill" | ||
125 | - openOnInput | ||
126 | - formControlName="backgroundColor"> | ||
127 | - </tb-color-input> | ||
128 | - <tb-image-input fxFlex | ||
129 | - label="{{'dashboard.background-image' | translate}}" | ||
130 | - formControlName="backgroundImageUrl"> | ||
131 | - </tb-image-input> | ||
132 | - <mat-form-field class="mat-block"> | ||
133 | - <mat-label translate>dashboard.background-size-mode</mat-label> | ||
134 | - <mat-select formControlName="backgroundSizeMode"> | ||
135 | - <mat-option value="100%">Fit width</mat-option> | ||
136 | - <mat-option value="auto 100%">Fit height</mat-option> | ||
137 | - <mat-option value="cover">Cover</mat-option> | ||
138 | - <mat-option value="contain">Contain</mat-option> | ||
139 | - <mat-option value="auto">Original size</mat-option> | ||
140 | - </mat-select> | ||
141 | - </mat-form-field> | ||
142 | - <small translate>dashboard.mobile-layout</small> | ||
143 | - <div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs fxLayoutGap="8px" style="margin-top: 8px"> | ||
144 | - <mat-checkbox fxFlex formControlName="mobileAutoFillHeight"> | 91 | + <div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup" fxLayout="column"> |
92 | + <fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px"> | ||
93 | + <legend class="group-title" translate>dashboard.layout-settings</legend> | ||
94 | + <mat-form-field class="mat-block"> | ||
95 | + <mat-label translate>dashboard.columns-count</mat-label> | ||
96 | + <input matInput formControlName="columns" type="number" step="any" min="10" | ||
97 | + max="1000" required> | ||
98 | + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')"> | ||
99 | + {{ 'dashboard.columns-count-required' | translate }} | ||
100 | + </mat-error> | ||
101 | + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')"> | ||
102 | + {{ 'dashboard.min-columns-count-message' | translate }} | ||
103 | + </mat-error> | ||
104 | + <mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')"> | ||
105 | + {{ 'dashboard.max-columns-count-message' | translate }} | ||
106 | + </mat-error> | ||
107 | + </mat-form-field> | ||
108 | + <mat-form-field fxFlex class="mat-block"> | ||
109 | + <mat-label translate>dashboard.widgets-margins</mat-label> | ||
110 | + <input matInput formControlName="margin" type="number" step="any" min="0" | ||
111 | + max="50" required> | ||
112 | + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')"> | ||
113 | + {{ 'dashboard.margin-required' | translate }} | ||
114 | + </mat-error> | ||
115 | + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')"> | ||
116 | + {{ 'dashboard.min-margin-message' | translate }} | ||
117 | + </mat-error> | ||
118 | + <mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')"> | ||
119 | + {{ 'dashboard.max-margin-message' | translate }} | ||
120 | + </mat-error> | ||
121 | + </mat-form-field> | ||
122 | + <mat-slide-toggle fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;"> | ||
123 | + {{ 'dashboard.autofill-height' | translate }} | ||
124 | + </mat-slide-toggle> | ||
125 | + <tb-color-input fxFlex | ||
126 | + label="{{'dashboard.background-color' | translate}}" | ||
127 | + icon="format_color_fill" | ||
128 | + openOnInput | ||
129 | + formControlName="backgroundColor"> | ||
130 | + </tb-color-input> | ||
131 | + <tb-image-input fxFlex | ||
132 | + label="{{'dashboard.background-image' | translate}}" | ||
133 | + formControlName="backgroundImageUrl"> | ||
134 | + </tb-image-input> | ||
135 | + <mat-form-field class="mat-block"> | ||
136 | + <mat-label translate>dashboard.background-size-mode</mat-label> | ||
137 | + <mat-select formControlName="backgroundSizeMode"> | ||
138 | + <mat-option value="100%">Fit width</mat-option> | ||
139 | + <mat-option value="auto 100%">Fit height</mat-option> | ||
140 | + <mat-option value="cover">Cover</mat-option> | ||
141 | + <mat-option value="contain">Contain</mat-option> | ||
142 | + <mat-option value="auto">Original size</mat-option> | ||
143 | + </mat-select> | ||
144 | + </mat-form-field> | ||
145 | + </fieldset> | ||
146 | + <fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px"> | ||
147 | + <legend class="group-title" translate>dashboard.mobile-layout</legend> | ||
148 | + <mat-slide-toggle fxFlex formControlName="mobileAutoFillHeight"> | ||
145 | {{ 'dashboard.autofill-height' | translate }} | 149 | {{ 'dashboard.autofill-height' | translate }} |
146 | - </mat-checkbox> | 150 | + </mat-slide-toggle> |
147 | <mat-form-field fxFlex class="mat-block"> | 151 | <mat-form-field fxFlex class="mat-block"> |
148 | <mat-label translate>dashboard.mobile-row-height</mat-label> | 152 | <mat-label translate>dashboard.mobile-row-height</mat-label> |
149 | <input matInput formControlName="mobileRowHeight" type="number" step="any" min="5" | 153 | <input matInput formControlName="mobileRowHeight" type="number" step="any" min="5" |
@@ -158,7 +162,7 @@ | @@ -158,7 +162,7 @@ | ||
158 | {{ 'dashboard.max-mobile-row-height-message' | translate }} | 162 | {{ 'dashboard.max-mobile-row-height-message' | translate }} |
159 | </mat-error> | 163 | </mat-error> |
160 | </mat-form-field> | 164 | </mat-form-field> |
161 | - </div> | 165 | + </fieldset> |
162 | </div> | 166 | </div> |
163 | </fieldset> | 167 | </fieldset> |
164 | </div> | 168 | </div> |
@@ -14,13 +14,19 @@ | @@ -14,13 +14,19 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | :host { | 16 | :host { |
17 | - small { | ||
18 | - white-space: normal; | 17 | + .fields-group { |
18 | + padding: 0 8px 8px; | ||
19 | + margin: 10px 0; | ||
20 | + border: 1px groove rgba(0, 0, 0, .25); | ||
21 | + border-radius: 4px; | ||
22 | + legend { | ||
23 | + color: rgba(0, 0, 0, .7); | ||
24 | + } | ||
19 | } | 25 | } |
20 | } | 26 | } |
21 | 27 | ||
22 | :host ::ng-deep { | 28 | :host ::ng-deep { |
23 | - .mat-checkbox-label { | 29 | + .mat-slide-toggle-content { |
24 | white-space: normal; | 30 | white-space: normal; |
25 | } | 31 | } |
26 | } | 32 | } |
@@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | @@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | ||
71 | this.gridSettings = this.data.gridSettings; | 71 | this.gridSettings = this.data.gridSettings; |
72 | 72 | ||
73 | if (this.settings) { | 73 | if (this.settings) { |
74 | + const showTitle = isUndefined(this.settings.showTitle) ? true : this.settings.showTitle; | ||
75 | + const showDashboardLogo = isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo; | ||
76 | + const hideToolbar = isUndefined(this.settings.hideToolbar) ? false : this.settings.hideToolbar; | ||
74 | this.settingsFormGroup = this.fb.group({ | 77 | this.settingsFormGroup = this.fb.group({ |
75 | stateControllerId: [isUndefined(this.settings.stateControllerId) ? 'entity' : this.settings.stateControllerId, []], | 78 | stateControllerId: [isUndefined(this.settings.stateControllerId) ? 'entity' : this.settings.stateControllerId, []], |
76 | - toolbarAlwaysOpen: [isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen, []], | ||
77 | - showTitle: [isUndefined(this.settings.showTitle) ? true : this.settings.showTitle, []], | ||
78 | - titleColor: [isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor, []], | ||
79 | - showDashboardsSelect: [isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect, []], | ||
80 | - showEntitiesSelect: [isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect, []], | ||
81 | - showFilters: [isUndefined(this.settings.showFilters) ? true : this.settings.showFilters, []], | ||
82 | - showDashboardLogo: [isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo, []], | ||
83 | - dashboardLogoUrl: [isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl, []], | ||
84 | - showDashboardTimewindow: [isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow, []], | ||
85 | - showDashboardExport: [isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport, []], | ||
86 | - showUpdateDashboardImage: [isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage, []] | 79 | + showTitle: [showTitle, []], |
80 | + titleColor: [{value: isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor, | ||
81 | + disabled: !showTitle}, []], | ||
82 | + showDashboardLogo: [showDashboardLogo, []], | ||
83 | + dashboardLogoUrl: [{value: isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl, | ||
84 | + disabled: !showDashboardLogo}, []], | ||
85 | + hideToolbar: [hideToolbar, []], | ||
86 | + toolbarAlwaysOpen: [{value: isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen, | ||
87 | + disabled: hideToolbar}, []], | ||
88 | + showDashboardsSelect: [{value: isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect, | ||
89 | + disabled: hideToolbar}, []], | ||
90 | + showEntitiesSelect: [{value: isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect, | ||
91 | + disabled: hideToolbar}, []], | ||
92 | + showFilters: [{value: isUndefined(this.settings.showFilters) ? true : this.settings.showFilters, | ||
93 | + disabled: hideToolbar}, []], | ||
94 | + showDashboardTimewindow: [{value: isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow, | ||
95 | + disabled: hideToolbar}, []], | ||
96 | + showDashboardExport: [{value: isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport, | ||
97 | + disabled: hideToolbar}, []], | ||
98 | + showUpdateDashboardImage: [ | ||
99 | + {value: isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage, | ||
100 | + disabled: hideToolbar}, []] | ||
87 | }); | 101 | }); |
88 | this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe( | 102 | this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe( |
89 | (stateControllerId: StateControllerId) => { | 103 | (stateControllerId: StateControllerId) => { |
@@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | @@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | ||
92 | } | 106 | } |
93 | } | 107 | } |
94 | ); | 108 | ); |
109 | + this.settingsFormGroup.get('showTitle').valueChanges.subscribe( | ||
110 | + (showTitleValue: boolean) => { | ||
111 | + if (showTitleValue) { | ||
112 | + this.settingsFormGroup.get('titleColor').enable(); | ||
113 | + } else { | ||
114 | + this.settingsFormGroup.get('titleColor').disable(); | ||
115 | + } | ||
116 | + } | ||
117 | + ); | ||
118 | + this.settingsFormGroup.get('showDashboardLogo').valueChanges.subscribe( | ||
119 | + (showDashboardLogoValue: boolean) => { | ||
120 | + if (showDashboardLogoValue) { | ||
121 | + this.settingsFormGroup.get('dashboardLogoUrl').enable(); | ||
122 | + } else { | ||
123 | + this.settingsFormGroup.get('dashboardLogoUrl').disable(); | ||
124 | + } | ||
125 | + } | ||
126 | + ); | ||
127 | + this.settingsFormGroup.get('hideToolbar').valueChanges.subscribe( | ||
128 | + (hideToolbarValue: boolean) => { | ||
129 | + if (hideToolbarValue) { | ||
130 | + this.settingsFormGroup.get('toolbarAlwaysOpen').disable(); | ||
131 | + this.settingsFormGroup.get('showDashboardsSelect').disable(); | ||
132 | + this.settingsFormGroup.get('showEntitiesSelect').disable(); | ||
133 | + this.settingsFormGroup.get('showFilters').disable(); | ||
134 | + this.settingsFormGroup.get('showDashboardTimewindow').disable(); | ||
135 | + this.settingsFormGroup.get('showDashboardExport').disable(); | ||
136 | + this.settingsFormGroup.get('showUpdateDashboardImage').disable(); | ||
137 | + } else { | ||
138 | + this.settingsFormGroup.get('toolbarAlwaysOpen').enable(); | ||
139 | + this.settingsFormGroup.get('showDashboardsSelect').enable(); | ||
140 | + this.settingsFormGroup.get('showEntitiesSelect').enable(); | ||
141 | + this.settingsFormGroup.get('showFilters').enable(); | ||
142 | + this.settingsFormGroup.get('showDashboardTimewindow').enable(); | ||
143 | + this.settingsFormGroup.get('showDashboardExport').enable(); | ||
144 | + this.settingsFormGroup.get('showUpdateDashboardImage').enable(); | ||
145 | + } | ||
146 | + } | ||
147 | + ); | ||
95 | } else { | 148 | } else { |
96 | this.settingsFormGroup = this.fb.group({}); | 149 | this.settingsFormGroup = this.fb.group({}); |
97 | } | 150 | } |
98 | 151 | ||
99 | if (this.gridSettings) { | 152 | if (this.gridSettings) { |
153 | + const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight; | ||
100 | this.gridSettingsFormGroup = this.fb.group({ | 154 | this.gridSettingsFormGroup = this.fb.group({ |
101 | - color: [this.gridSettings.color || 'rgba(0,0,0,0.870588)', []], | ||
102 | columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], | 155 | columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], |
103 | margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, | 156 | margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, |
104 | [Validators.required, Validators.min(0), Validators.max(50)]], | 157 | [Validators.required, Validators.min(0), Validators.max(50)]], |
@@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | @@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | ||
106 | backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []], | 159 | backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []], |
107 | backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []], | 160 | backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []], |
108 | backgroundSizeMode: [this.gridSettings.backgroundSizeMode || '100%', []], | 161 | backgroundSizeMode: [this.gridSettings.backgroundSizeMode || '100%', []], |
109 | - mobileAutoFillHeight: [isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight, []], | ||
110 | - mobileRowHeight: [isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight, | ||
111 | - [Validators.required, Validators.min(5), Validators.max(200)]] | 162 | + mobileAutoFillHeight: [mobileAutoFillHeight, []], |
163 | + mobileRowHeight: [{ value: isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight, | ||
164 | + disabled: mobileAutoFillHeight}, [Validators.required, Validators.min(5), Validators.max(200)]] | ||
112 | }); | 165 | }); |
166 | + this.gridSettingsFormGroup.get('mobileAutoFillHeight').valueChanges.subscribe( | ||
167 | + (mobileAutoFillHeightValue: boolean) => { | ||
168 | + if (mobileAutoFillHeightValue) { | ||
169 | + this.gridSettingsFormGroup.get('mobileRowHeight').disable(); | ||
170 | + } else { | ||
171 | + this.gridSettingsFormGroup.get('mobileRowHeight').enable(); | ||
172 | + } | ||
173 | + } | ||
174 | + ); | ||
113 | } else { | 175 | } else { |
114 | this.gridSettingsFormGroup = this.fb.group({}); | 176 | this.gridSettingsFormGroup = this.fb.group({}); |
115 | } | 177 | } |
@@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | @@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS | ||
133 | let settings: DashboardSettings = null; | 195 | let settings: DashboardSettings = null; |
134 | let gridSettings: GridSettings = null; | 196 | let gridSettings: GridSettings = null; |
135 | if (this.settings) { | 197 | if (this.settings) { |
136 | - settings = {...this.settings, ...this.settingsFormGroup.value}; | 198 | + settings = {...this.settings, ...this.settingsFormGroup.getRawValue()}; |
137 | } | 199 | } |
138 | if (this.gridSettings) { | 200 | if (this.gridSettings) { |
139 | - gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.value}; | 201 | + gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.getRawValue()}; |
140 | } | 202 | } |
141 | this.dialogRef.close({settings, gridSettings}); | 203 | this.dialogRef.close({settings, gridSettings}); |
142 | } | 204 | } |
@@ -26,7 +26,6 @@ | @@ -26,7 +26,6 @@ | ||
26 | [style.backgroundImage]="backgroundImage" | 26 | [style.backgroundImage]="backgroundImage" |
27 | [ngStyle]="dashboardStyle"> | 27 | [ngStyle]="dashboardStyle"> |
28 | <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center" | 28 | <section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center" |
29 | - [ngStyle]="{'color': layoutCtx.gridSettings.color}" | ||
30 | style="display: flex; z-index: 1; pointer-events: none;" | 29 | style="display: flex; z-index: 1; pointer-events: none;" |
31 | class="mat-headline tb-absolute-fill"> | 30 | class="mat-headline tb-absolute-fill"> |
32 | <span *ngIf="!isEdit"> | 31 | <span *ngIf="!isEdit"> |
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright © 2016-2021 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 | -:host ::ng-deep { | ||
17 | - textarea.mat-input-element.cdk-textarea-autosize { | ||
18 | - box-sizing: content-box; | ||
19 | - } | ||
20 | -} |
@@ -41,7 +41,7 @@ import { Subject } from 'rxjs'; | @@ -41,7 +41,7 @@ import { Subject } from 'rxjs'; | ||
41 | @Component({ | 41 | @Component({ |
42 | selector: 'tb-security-config-lwm2m-server', | 42 | selector: 'tb-security-config-lwm2m-server', |
43 | templateUrl: './security-config-lwm2m-server.component.html', | 43 | templateUrl: './security-config-lwm2m-server.component.html', |
44 | - styleUrls: ['./security-config-lwm2m-server.component.scss'], | 44 | + styleUrls: [], |
45 | providers: [ | 45 | providers: [ |
46 | { | 46 | { |
47 | provide: NG_VALUE_ACCESSOR, | 47 | provide: NG_VALUE_ACCESSOR, |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; |
18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@app/core/core.state'; | 20 | import { AppState } from '@app/core/core.state'; |
@@ -33,6 +33,8 @@ import { | @@ -33,6 +33,8 @@ import { | ||
33 | transportPayloadTypeTranslationMap, | 33 | transportPayloadTypeTranslationMap, |
34 | } from '@shared/models/device.models'; | 34 | } from '@shared/models/device.models'; |
35 | import { isDefinedAndNotNull } from '@core/utils'; | 35 | import { isDefinedAndNotNull } from '@core/utils'; |
36 | +import { Subject } from 'rxjs'; | ||
37 | +import { takeUntil } from 'rxjs/operators'; | ||
36 | 38 | ||
37 | @Component({ | 39 | @Component({ |
38 | selector: 'tb-coap-device-profile-transport-configuration', | 40 | selector: 'tb-coap-device-profile-transport-configuration', |
@@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils'; | @@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils'; | ||
44 | multi: true | 46 | multi: true |
45 | }] | 47 | }] |
46 | }) | 48 | }) |
47 | -export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { | 49 | +export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { |
48 | 50 | ||
49 | coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); | 51 | coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); |
50 | 52 | ||
@@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control | @@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control | ||
56 | 58 | ||
57 | coapDeviceProfileTransportConfigurationFormGroup: FormGroup; | 59 | coapDeviceProfileTransportConfigurationFormGroup: FormGroup; |
58 | 60 | ||
61 | + private destroy$ = new Subject(); | ||
59 | private requiredValue: boolean; | 62 | private requiredValue: boolean; |
60 | 63 | ||
61 | private transportPayloadTypeConfiguration = this.fb.group({ | 64 | private transportPayloadTypeConfiguration = this.fb.group({ |
@@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control | @@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control | ||
99 | }) | 102 | }) |
100 | } | 103 | } |
101 | ); | 104 | ); |
102 | - this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType') | ||
103 | - .valueChanges.subscribe(coapDeviceType => { | 105 | + this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').valueChanges.pipe( |
106 | + takeUntil(this.destroy$) | ||
107 | + ).subscribe(coapDeviceType => { | ||
104 | this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); | 108 | this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); |
105 | }); | 109 | }); |
106 | - this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | 110 | + this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( |
111 | + takeUntil(this.destroy$) | ||
112 | + ).subscribe(() => { | ||
107 | this.updateModel(); | 113 | this.updateModel(); |
108 | }); | 114 | }); |
109 | } | 115 | } |
110 | 116 | ||
117 | + ngOnDestroy() { | ||
118 | + this.destroy$.next(); | ||
119 | + this.destroy$.complete(); | ||
120 | + } | ||
121 | + | ||
111 | get coapDeviceTypeDefault(): boolean { | 122 | get coapDeviceTypeDefault(): boolean { |
112 | const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; | 123 | const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; |
113 | return coapDeviceType === CoapTransportDeviceType.DEFAULT; | 124 | return coapDeviceType === CoapTransportDeviceType.DEFAULT; |
@@ -14,13 +14,15 @@ | @@ -14,13 +14,15 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; |
18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@app/core/core.state'; | 20 | import { AppState } from '@app/core/core.state'; |
21 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 21 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
22 | import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; | 22 | import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; |
23 | import { deepClone } from '@core/utils'; | 23 | import { deepClone } from '@core/utils'; |
24 | +import { Subject } from 'rxjs'; | ||
25 | +import { takeUntil } from 'rxjs/operators'; | ||
24 | 26 | ||
25 | @Component({ | 27 | @Component({ |
26 | selector: 'tb-device-profile-configuration', | 28 | selector: 'tb-device-profile-configuration', |
@@ -32,12 +34,14 @@ import { deepClone } from '@core/utils'; | @@ -32,12 +34,14 @@ import { deepClone } from '@core/utils'; | ||
32 | multi: true | 34 | multi: true |
33 | }] | 35 | }] |
34 | }) | 36 | }) |
35 | -export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { | 37 | +export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { |
36 | 38 | ||
37 | deviceProfileType = DeviceProfileType; | 39 | deviceProfileType = DeviceProfileType; |
38 | 40 | ||
39 | deviceProfileConfigurationFormGroup: FormGroup; | 41 | deviceProfileConfigurationFormGroup: FormGroup; |
40 | 42 | ||
43 | + private destroy$ = new Subject(); | ||
44 | + | ||
41 | private requiredValue: boolean; | 45 | private requiredValue: boolean; |
42 | get required(): boolean { | 46 | get required(): boolean { |
43 | return this.requiredValue; | 47 | return this.requiredValue; |
@@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor | @@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor | ||
69 | this.deviceProfileConfigurationFormGroup = this.fb.group({ | 73 | this.deviceProfileConfigurationFormGroup = this.fb.group({ |
70 | configuration: [null, Validators.required] | 74 | configuration: [null, Validators.required] |
71 | }); | 75 | }); |
72 | - this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { | 76 | + this.deviceProfileConfigurationFormGroup.valueChanges.pipe( |
77 | + takeUntil(this.destroy$) | ||
78 | + ).subscribe(() => { | ||
73 | this.updateModel(); | 79 | this.updateModel(); |
74 | }); | 80 | }); |
75 | } | 81 | } |
76 | 82 | ||
83 | + ngOnDestroy() { | ||
84 | + this.destroy$.next(); | ||
85 | + this.destroy$.complete(); | ||
86 | + } | ||
87 | + | ||
77 | setDisabledState(isDisabled: boolean): void { | 88 | setDisabledState(isDisabled: boolean): void { |
78 | this.disabled = isDisabled; | 89 | this.disabled = isDisabled; |
79 | if (this.disabled) { | 90 | if (this.disabled) { |
@@ -15,112 +15,107 @@ | @@ -15,112 +15,107 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section [formGroup]="serverFormGroup" style="min-width: 400px;"> | ||
19 | - <div class="mat-padding"> | ||
20 | - <div fxLayout="column"> | ||
21 | - <div fxLayout="row" fxLayoutGap="8px"> | ||
22 | - <mat-form-field class="mat-block"> | ||
23 | - <mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label> | ||
24 | - <mat-select formControlName="securityMode"> | ||
25 | - <mat-option *ngFor="let securityMode of securityConfigLwM2MTypes" | ||
26 | - [value]="securityMode"> | ||
27 | - {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} | ||
28 | - </mat-option> | ||
29 | - </mat-select> | ||
30 | - </mat-form-field> | ||
31 | - <mat-form-field class="mat-block"> | ||
32 | - <mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label> | ||
33 | - <input matInput type="text" formControlName="host" required | ||
34 | - matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}" | ||
35 | - matTooltipPosition="above"> | ||
36 | - <mat-error *ngIf="serverFormGroup.get('host').hasError('required')"> | ||
37 | - {{ 'device-profile.lwm2m.server-host' | translate }} | ||
38 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
39 | - </mat-error> | ||
40 | - </mat-form-field> | ||
41 | - <mat-form-field class="mat-block"> | ||
42 | - <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label> | ||
43 | - <input matInput type="number" formControlName="port" required | ||
44 | - matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}" | ||
45 | - matTooltipPosition="above"> | ||
46 | - <mat-error *ngIf="serverFormGroup.get('port').hasError('required')"> | ||
47 | - {{ 'device-profile.lwm2m.server-port' | translate }} | ||
48 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
49 | - </mat-error> | ||
50 | - </mat-form-field> | ||
51 | - <mat-form-field class="mat-block"> | ||
52 | - <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label> | ||
53 | - <input matInput type="number" formControlName="serverId" required | ||
54 | - matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}" | ||
55 | - matTooltipPosition="above"> | ||
56 | - <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')"> | ||
57 | - {{ 'device-profile.lwm2m.short-id' | translate }} | ||
58 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
59 | - </mat-error> | ||
60 | - </mat-form-field> | ||
61 | - </div> | ||
62 | - </div> | ||
63 | - <div fxLayout="column"> | ||
64 | - <div fxLayout="row" fxLayoutGap="10px"> | ||
65 | - <mat-form-field class="mat-block"> | ||
66 | - <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label> | ||
67 | - <input matInput type="number" formControlName="clientHoldOffTime" required | ||
68 | - matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}" | ||
69 | - matTooltipPosition="above"> | ||
70 | - <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')"> | ||
71 | - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} | ||
72 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
73 | - </mat-error> | ||
74 | - </mat-form-field> | ||
75 | - <mat-form-field class="mat-block"> | ||
76 | - <mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label> | ||
77 | - <input matInput type="number" formControlName="bootstrapServerAccountTimeout" required | ||
78 | - matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}" | ||
79 | - matTooltipPosition="above"> | ||
80 | - <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')"> | ||
81 | - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} | ||
82 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
83 | - </mat-error> | ||
84 | - </mat-form-field> | ||
85 | - <mat-checkbox formControlName="bootstrapServerIs" color="primary"> | ||
86 | - {{ 'device-profile.lwm2m.bootstrap-server' | translate }} | ||
87 | - </mat-checkbox> | ||
88 | - </div> | ||
89 | - <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || | ||
90 | - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509"> | ||
91 | - <mat-form-field class="mat-block"> | ||
92 | - <mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label> | ||
93 | - <textarea matInput | ||
94 | - #serverPublicKey | ||
95 | - maxlength="{{lenMaxServerPublicKey}}" | ||
96 | - cdkTextareaAutosize | ||
97 | - cdkAutosizeMinRows="1" | ||
98 | - cols="1" required | ||
99 | - style="overflow:hidden" | ||
100 | - formControlName="serverPublicKey" | ||
101 | - matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}" | ||
102 | - ></textarea> | ||
103 | - <mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint> | ||
104 | - <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')"> | ||
105 | - {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
106 | - <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
107 | - </mat-error> | ||
108 | - <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') && | ||
109 | - (serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || | ||
110 | - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)"> | ||
111 | - {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
112 | - <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { | ||
113 | - count: 0} }}</strong> | ||
114 | - </mat-error> | ||
115 | - <mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') || | ||
116 | - serverFormGroup.get('serverPublicKey').hasError('minlength')) && | ||
117 | - serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK"> | ||
118 | - {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
119 | - <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { | ||
120 | - count: lenMaxServerPublicKey } }}</strong> | ||
121 | - </mat-error> | ||
122 | - </mat-form-field> | ||
123 | - </div> | ||
124 | - </div> | 18 | +<section [formGroup]="serverFormGroup"> |
19 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px"> | ||
20 | + <mat-form-field fxFlex> | ||
21 | + <mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label> | ||
22 | + <mat-select formControlName="securityMode"> | ||
23 | + <mat-option *ngFor="let securityMode of securityConfigLwM2MTypes" | ||
24 | + [value]="securityMode"> | ||
25 | + {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} | ||
26 | + </mat-option> | ||
27 | + </mat-select> | ||
28 | + </mat-form-field> | ||
29 | + <mat-form-field fxFlex> | ||
30 | + <mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label> | ||
31 | + <input matInput type="text" formControlName="host" required | ||
32 | + matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}" | ||
33 | + matTooltipPosition="above"> | ||
34 | + <mat-error *ngIf="serverFormGroup.get('host').hasError('required')"> | ||
35 | + {{ 'device-profile.lwm2m.server-host' | translate }} | ||
36 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
37 | + </mat-error> | ||
38 | + </mat-form-field> | ||
39 | + <mat-form-field fxFlex> | ||
40 | + <mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label> | ||
41 | + <input matInput type="number" formControlName="port" required | ||
42 | + matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}" | ||
43 | + matTooltipPosition="above"> | ||
44 | + <mat-error *ngIf="serverFormGroup.get('port').hasError('required')"> | ||
45 | + {{ 'device-profile.lwm2m.server-port' | translate }} | ||
46 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
47 | + </mat-error> | ||
48 | + </mat-form-field> | ||
49 | + <mat-form-field fxFlex> | ||
50 | + <mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label> | ||
51 | + <input matInput type="number" formControlName="serverId" required | ||
52 | + matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}" | ||
53 | + matTooltipPosition="above"> | ||
54 | + <mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')"> | ||
55 | + {{ 'device-profile.lwm2m.short-id' | translate }} | ||
56 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
57 | + </mat-error> | ||
58 | + </mat-form-field> | ||
59 | + </div> | ||
60 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px"> | ||
61 | + <mat-form-field fxFlex> | ||
62 | + <mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label> | ||
63 | + <input matInput type="number" formControlName="clientHoldOffTime" required | ||
64 | + matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}" | ||
65 | + matTooltipPosition="above"> | ||
66 | + <mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')"> | ||
67 | + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} | ||
68 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
69 | + </mat-error> | ||
70 | + </mat-form-field> | ||
71 | + <mat-form-field fxFlex> | ||
72 | + <mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label> | ||
73 | + <input matInput type="number" formControlName="bootstrapServerAccountTimeout" required | ||
74 | + matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}" | ||
75 | + matTooltipPosition="above"> | ||
76 | + <mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')"> | ||
77 | + {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} | ||
78 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
79 | + </mat-error> | ||
80 | + </mat-form-field> | ||
81 | + <mat-checkbox fxFlex formControlName="bootstrapServerIs" color="primary"> | ||
82 | + {{ 'device-profile.lwm2m.bootstrap-server' | translate }} | ||
83 | + </mat-checkbox> | ||
84 | + <div fxFlex></div> | ||
85 | + </div> | ||
86 | + <div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || | ||
87 | + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509"> | ||
88 | + <mat-form-field class="mat-block"> | ||
89 | + <mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label> | ||
90 | + <textarea matInput | ||
91 | + #serverPublicKey | ||
92 | + maxlength="{{lenMaxServerPublicKey}}" | ||
93 | + cdkTextareaAutosize | ||
94 | + cdkAutosizeMinRows="1" | ||
95 | + cols="1" required | ||
96 | + style="overflow:hidden" | ||
97 | + formControlName="serverPublicKey" | ||
98 | + matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}" | ||
99 | + ></textarea> | ||
100 | + <mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint> | ||
101 | + <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')"> | ||
102 | + {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
103 | + <strong>{{ 'device-profile.lwm2m.required' | translate }}</strong> | ||
104 | + </mat-error> | ||
105 | + <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') && | ||
106 | + (serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK || | ||
107 | + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)"> | ||
108 | + {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
109 | + <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { | ||
110 | + count: 0} }}</strong> | ||
111 | + </mat-error> | ||
112 | + <mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') || | ||
113 | + serverFormGroup.get('serverPublicKey').hasError('minlength')) && | ||
114 | + serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK"> | ||
115 | + {{ 'device-profile.lwm2m.server-public-key' | translate }} | ||
116 | + <strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { | ||
117 | + count: lenMaxServerPublicKey } }}</strong> | ||
118 | + </mat-error> | ||
119 | + </mat-form-field> | ||
125 | </div> | 120 | </div> |
126 | </section> | 121 | </section> |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; | 17 | import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; |
18 | -import { Component, forwardRef, Input } from '@angular/core'; | 18 | +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; |
19 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | 19 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; |
20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
21 | import { | 21 | import { |
@@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util | @@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util | ||
39 | import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; | 39 | import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; |
40 | import { Direction } from '@shared/models/page/sort-order'; | 40 | import { Direction } from '@shared/models/page/sort-order'; |
41 | import _ from 'lodash'; | 41 | import _ from 'lodash'; |
42 | +import { Subject } from 'rxjs'; | ||
43 | +import { takeUntil } from 'rxjs/operators'; | ||
42 | 44 | ||
43 | @Component({ | 45 | @Component({ |
44 | selector: 'tb-profile-lwm2m-device-transport-configuration', | 46 | selector: 'tb-profile-lwm2m-device-transport-configuration', |
@@ -49,11 +51,12 @@ import _ from 'lodash'; | @@ -49,11 +51,12 @@ import _ from 'lodash'; | ||
49 | multi: true | 51 | multi: true |
50 | }] | 52 | }] |
51 | }) | 53 | }) |
52 | -export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators { | 54 | +export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators, OnDestroy { |
53 | 55 | ||
54 | private configurationValue: Lwm2mProfileConfigModels; | 56 | private configurationValue: Lwm2mProfileConfigModels; |
55 | private requiredValue: boolean; | 57 | private requiredValue: boolean; |
56 | private disabled = false; | 58 | private disabled = false; |
59 | + private destroy$ = new Subject(); | ||
57 | 60 | ||
58 | bindingModeType = BINDING_MODE; | 61 | bindingModeType = BINDING_MODE; |
59 | bindingModeTypes = Object.keys(BINDING_MODE); | 62 | bindingModeTypes = Object.keys(BINDING_MODE); |
@@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
86 | lifetime: [null, Validators.required], | 89 | lifetime: [null, Validators.required], |
87 | defaultMinPeriod: [null, Validators.required], | 90 | defaultMinPeriod: [null, Validators.required], |
88 | notifIfDisabled: [true, []], | 91 | notifIfDisabled: [true, []], |
89 | - binding:[], | 92 | + binding: [], |
90 | bootstrapServer: [null, Validators.required], | 93 | bootstrapServer: [null, Validators.required], |
91 | lwm2mServer: [null, Validators.required], | 94 | lwm2mServer: [null, Validators.required], |
92 | clientStrategy: [1, []], | 95 | clientStrategy: [1, []], |
@@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
96 | this.lwm2mDeviceConfigFormGroup = this.fb.group({ | 99 | this.lwm2mDeviceConfigFormGroup = this.fb.group({ |
97 | configurationJson: [null, Validators.required] | 100 | configurationJson: [null, Validators.required] |
98 | }); | 101 | }); |
99 | - this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { | 102 | + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( |
103 | + takeUntil(this.destroy$) | ||
104 | + ).subscribe((value) => { | ||
100 | this.updateDeviceProfileValue(value); | 105 | this.updateDeviceProfileValue(value); |
101 | }); | 106 | }); |
102 | - this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { | 107 | + this.lwm2mDeviceConfigFormGroup.valueChanges.pipe( |
108 | + takeUntil(this.destroy$) | ||
109 | + ).subscribe(() => { | ||
103 | this.updateModel(); | 110 | this.updateModel(); |
104 | }); | 111 | }); |
105 | this.sortFunction = this.sortObjectKeyPathJson; | 112 | this.sortFunction = this.sortObjectKeyPathJson; |
@@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
112 | registerOnTouched(fn: any): void { | 119 | registerOnTouched(fn: any): void { |
113 | } | 120 | } |
114 | 121 | ||
122 | + ngOnDestroy() { | ||
123 | + this.destroy$.next(); | ||
124 | + this.destroy$.complete(); | ||
125 | + } | ||
126 | + | ||
115 | setDisabledState(isDisabled: boolean): void { | 127 | setDisabledState(isDisabled: boolean): void { |
116 | this.disabled = isDisabled; | 128 | this.disabled = isDisabled; |
117 | if (isDisabled) { | 129 | if (isDisabled) { |
@@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
124 | } | 136 | } |
125 | 137 | ||
126 | writeValue(value: Lwm2mProfileConfigModels | null): void { | 138 | writeValue(value: Lwm2mProfileConfigModels | null): void { |
127 | - this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; | ||
128 | - this.lwm2mDeviceConfigFormGroup.patchValue({ | ||
129 | - configurationJson: this.configurationValue | ||
130 | - }, {emitEvent: false}); | ||
131 | - this.initWriteValue(); | 139 | + if (isDefinedAndNotNull(value)) { |
140 | + if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { | ||
141 | + this.configurationValue = value; | ||
142 | + } else { | ||
143 | + this.configurationValue = getDefaultProfileConfig(); | ||
144 | + } | ||
145 | + this.lwm2mDeviceConfigFormGroup.patchValue({ | ||
146 | + configurationJson: this.configurationValue | ||
147 | + }, {emitEvent: false}); | ||
148 | + this.initWriteValue(); | ||
149 | + } | ||
132 | } | 150 | } |
133 | 151 | ||
134 | private initWriteValue = (): void => { | 152 | private initWriteValue = (): void => { |
@@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | @@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro | ||
257 | instanceUpdate.id = instanceId; | 275 | instanceUpdate.id = instanceId; |
258 | instanceUpdate.resources.forEach(resource => { | 276 | instanceUpdate.resources.forEach(resource => { |
259 | resource.keyName = _.camelCase(resource.name + instanceUpdate.id); | 277 | resource.keyName = _.camelCase(resource.name + instanceUpdate.id); |
260 | - }) | 278 | + }); |
261 | return instanceUpdate; | 279 | return instanceUpdate; |
262 | } | 280 | } |
263 | 281 |
@@ -15,10 +15,19 @@ | @@ -15,10 +15,19 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; | 17 | import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; |
18 | -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | 18 | +import { |
19 | + ControlValueAccessor, | ||
20 | + FormBuilder, | ||
21 | + FormGroup, | ||
22 | + NG_VALIDATORS, | ||
23 | + NG_VALUE_ACCESSOR, | ||
24 | + ValidationErrors, | ||
25 | + Validator, | ||
26 | + Validators | ||
27 | +} from '@angular/forms'; | ||
19 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 28 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
20 | import { Observable } from 'rxjs'; | 29 | import { Observable } from 'rxjs'; |
21 | -import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; | 30 | +import { distinctUntilChanged, filter, mergeMap, share, tap } from 'rxjs/operators'; |
22 | import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; | 31 | import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; |
23 | import { DeviceProfileService } from '@core/http/device-profile.service'; | 32 | import { DeviceProfileService } from '@core/http/device-profile.service'; |
24 | import { Direction } from '@shared/models/page/sort-order'; | 33 | import { Direction } from '@shared/models/page/sort-order'; |
@@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link'; | @@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link'; | ||
33 | provide: NG_VALUE_ACCESSOR, | 42 | provide: NG_VALUE_ACCESSOR, |
34 | useExisting: forwardRef(() => Lwm2mObjectListComponent), | 43 | useExisting: forwardRef(() => Lwm2mObjectListComponent), |
35 | multi: true | 44 | multi: true |
36 | - }] | 45 | + }, |
46 | + { | ||
47 | + provide: NG_VALIDATORS, | ||
48 | + useExisting: forwardRef(() => Lwm2mObjectListComponent), | ||
49 | + multi: true | ||
50 | + } | ||
51 | + ] | ||
37 | }) | 52 | }) |
38 | -export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validators { | 53 | +export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validator { |
39 | 54 | ||
40 | private requiredValue: boolean; | 55 | private requiredValue: boolean; |
41 | private dirty = false; | 56 | private dirty = false; |
42 | - private lw2mModels: Observable<Array<ObjectLwM2M>>; | ||
43 | private modelValue: Array<string> = []; | 57 | private modelValue: Array<string> = []; |
44 | 58 | ||
45 | lwm2mListFormGroup: FormGroup; | 59 | lwm2mListFormGroup: FormGroup; |
@@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
78 | } | 92 | } |
79 | 93 | ||
80 | private updateValidators = (): void => { | 94 | private updateValidators = (): void => { |
81 | - this.lwm2mListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []); | ||
82 | - this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity(); | 95 | + this.lwm2mListFormGroup.get('objectsList').setValidators(this.required ? [Validators.required] : []); |
96 | + this.lwm2mListFormGroup.get('objectsList').updateValueAndValidity(); | ||
83 | } | 97 | } |
84 | 98 | ||
85 | registerOnChange(fn: any): void { | 99 | registerOnChange(fn: any): void { |
@@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
92 | ngOnInit() { | 106 | ngOnInit() { |
93 | this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges | 107 | this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges |
94 | .pipe( | 108 | .pipe( |
109 | + distinctUntilChanged(), | ||
95 | tap((value) => { | 110 | tap((value) => { |
96 | if (value && typeof value !== 'string') { | 111 | if (value && typeof value !== 'string') { |
97 | this.add(value); | 112 | this.add(value); |
@@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
100 | } | 115 | } |
101 | }), | 116 | }), |
102 | filter(searchText => isString(searchText)), | 117 | filter(searchText => isString(searchText)), |
103 | - mergeMap(searchText => this.fetchListObjects(searchText)) | 118 | + mergeMap(searchText => this.fetchListObjects(searchText)), |
119 | + share() | ||
104 | ); | 120 | ); |
105 | } | 121 | } |
106 | 122 | ||
@@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
131 | } | 147 | } |
132 | } | 148 | } |
133 | 149 | ||
150 | + validate(): ValidationErrors | null { | ||
151 | + return this.lwm2mListFormGroup.valid ? null : { | ||
152 | + lwm2mListObj: false | ||
153 | + }; | ||
154 | + } | ||
155 | + | ||
134 | private add(object: ObjectLwM2M): void { | 156 | private add(object: ObjectLwM2M): void { |
135 | if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { | 157 | if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { |
136 | this.modelValue.push(object.keyId); | 158 | this.modelValue.push(object.keyId); |
@@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
157 | return object ? object.name : undefined; | 179 | return object ? object.name : undefined; |
158 | } | 180 | } |
159 | 181 | ||
160 | - private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => { | 182 | + private fetchListObjects = (searchText: string): Observable<Array<ObjectLwM2M>> => { |
161 | this.searchText = searchText; | 183 | this.searchText = searchText; |
162 | - return this.getLwM2mModelsPage().pipe( | ||
163 | - map(objectLwM2Ms => objectLwM2Ms) | ||
164 | - ); | ||
165 | - } | ||
166 | - | ||
167 | - private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> { | ||
168 | const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { | 184 | const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { |
169 | property: 'id', | 185 | property: 'id', |
170 | direction: Direction.ASC | 186 | direction: Direction.ASC |
171 | }); | 187 | }); |
172 | - this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe( | ||
173 | - publishReplay(1), | ||
174 | - refCount() | ||
175 | - ); | ||
176 | - return this.lw2mModels; | 188 | + return this.deviceProfileService.getLwm2mObjectsPage(pageLink); |
177 | } | 189 | } |
178 | 190 | ||
179 | onFocus = (): void => { | 191 | onFocus = (): void => { |
@@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | @@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V | ||
183 | } | 195 | } |
184 | } | 196 | } |
185 | 197 | ||
186 | - private clear = (value: string = ''): void => { | ||
187 | - this.objectInput.nativeElement.value = value; | 198 | + private clear = (): void => { |
188 | this.searchText = ''; | 199 | this.searchText = ''; |
189 | - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value); | 200 | + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null); |
190 | setTimeout(() => { | 201 | setTimeout(() => { |
191 | this.objectInput.nativeElement.blur(); | 202 | this.objectInput.nativeElement.blur(); |
192 | this.objectInput.nativeElement.focus(); | 203 | this.objectInput.nativeElement.focus(); |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<section [formGroup]="resourceFormGroup" class="mat-padding"> | 18 | +<section [formGroup]="resourceFormGroup"> |
19 | <div fxLayout="row" fxFill formArrayName="resources" | 19 | <div fxLayout="row" fxFill formArrayName="resources" |
20 | *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> | 20 | *ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams"> |
21 | <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> | 21 | <div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i"> |
@@ -46,14 +46,14 @@ | @@ -46,14 +46,14 @@ | ||
46 | </div> | 46 | </div> |
47 | <div fxFlex="10" fxLayoutAlign="center center"> | 47 | <div fxFlex="10" fxLayoutAlign="center center"> |
48 | <mat-checkbox formControlName="attribute" color="warn" | 48 | <mat-checkbox formControlName="attribute" color="warn" |
49 | - [checked]="updateObserve(i)" | 49 | + (change)="updateObserve(i)" |
50 | matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" | 50 | matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}" |
51 | matTooltipPosition="above"> | 51 | matTooltipPosition="above"> |
52 | </mat-checkbox> | 52 | </mat-checkbox> |
53 | </div> | 53 | </div> |
54 | <div fxFlex="10" fxLayoutAlign="center center"> | 54 | <div fxFlex="10" fxLayoutAlign="center center"> |
55 | <mat-checkbox formControlName="telemetry" color="primary" | 55 | <mat-checkbox formControlName="telemetry" color="primary" |
56 | - [checked]="updateObserve(i)" | 56 | + (change)="updateObserve(i)" |
57 | matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" | 57 | matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}" |
58 | matTooltipPosition="above"> | 58 | matTooltipPosition="above"> |
59 | </mat-checkbox> | 59 | </mat-checkbox> |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <section [formGroup]="observeAttrTelemetryFormGroup"> | 18 | <section [formGroup]="observeAttrTelemetryFormGroup"> |
19 | - <mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M"> | 19 | + <mat-accordion multi="true" formArrayName="clientLwM2M"> |
20 | <mat-expansion-panel | 20 | <mat-expansion-panel |
21 | *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" | 21 | *ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;" |
22 | [formGroupName]="i"> | 22 | [formGroupName]="i"> |
@@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>( | @@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>( | ||
64 | [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], | 64 | [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], |
65 | [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], | 65 | [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], |
66 | [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'], | 66 | [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'], |
67 | - [BINDING_MODE.T,'T: TCP connection in standard mode'], | 67 | + [BINDING_MODE.T, 'T: TCP connection in standard mode'], |
68 | [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], | 68 | [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], |
69 | [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], | 69 | [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], |
70 | [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'], | 70 | [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'], |
@@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels { | @@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels { | ||
162 | clientLwM2mSettings: ClientLwM2mSettings; | 162 | clientLwM2mSettings: ClientLwM2mSettings; |
163 | observeAttr: ObservableAttributes; | 163 | observeAttr: ObservableAttributes; |
164 | bootstrap: BootstrapSecurityConfig; | 164 | bootstrap: BootstrapSecurityConfig; |
165 | - | ||
166 | } | 165 | } |
167 | 166 | ||
168 | export interface ClientLwM2mSettings { | 167 | export interface ClientLwM2mSettings { |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, forwardRef, Input, OnInit } from '@angular/core'; | 17 | +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; |
18 | import { | 18 | import { |
19 | ControlValueAccessor, | 19 | ControlValueAccessor, |
20 | FormBuilder, | 20 | FormBuilder, |
@@ -39,6 +39,8 @@ import { | @@ -39,6 +39,8 @@ import { | ||
39 | transportPayloadTypeTranslationMap | 39 | transportPayloadTypeTranslationMap |
40 | } from '@shared/models/device.models'; | 40 | } from '@shared/models/device.models'; |
41 | import { isDefinedAndNotNull } from '@core/utils'; | 41 | import { isDefinedAndNotNull } from '@core/utils'; |
42 | +import { Subject } from 'rxjs'; | ||
43 | +import { takeUntil } from 'rxjs/operators'; | ||
42 | 44 | ||
43 | @Component({ | 45 | @Component({ |
44 | selector: 'tb-mqtt-device-profile-transport-configuration', | 46 | selector: 'tb-mqtt-device-profile-transport-configuration', |
@@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils'; | @@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils'; | ||
50 | multi: true | 52 | multi: true |
51 | }] | 53 | }] |
52 | }) | 54 | }) |
53 | -export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { | 55 | +export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { |
54 | 56 | ||
55 | transportPayloadTypes = Object.keys(TransportPayloadType); | 57 | transportPayloadTypes = Object.keys(TransportPayloadType); |
56 | 58 | ||
@@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
58 | 60 | ||
59 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; | 61 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; |
60 | 62 | ||
63 | + private destroy$ = new Subject(); | ||
61 | private requiredValue: boolean; | 64 | private requiredValue: boolean; |
62 | 65 | ||
63 | get required(): boolean { | 66 | get required(): boolean { |
@@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
98 | }) | 101 | }) |
99 | }, {validator: this.uniqueDeviceTopicValidator} | 102 | }, {validator: this.uniqueDeviceTopicValidator} |
100 | ); | 103 | ); |
101 | - this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType') | ||
102 | - .valueChanges.subscribe(payloadType => { | 104 | + this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType').valueChanges.pipe( |
105 | + takeUntil(this.destroy$) | ||
106 | + ).subscribe(payloadType => { | ||
103 | this.updateTransportPayloadBasedControls(payloadType, true); | 107 | this.updateTransportPayloadBasedControls(payloadType, true); |
104 | }); | 108 | }); |
105 | - this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | 109 | + this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( |
110 | + takeUntil(this.destroy$) | ||
111 | + ).subscribe(() => { | ||
106 | this.updateModel(); | 112 | this.updateModel(); |
107 | }); | 113 | }); |
108 | } | 114 | } |
109 | 115 | ||
116 | + ngOnDestroy() { | ||
117 | + this.destroy$.next(); | ||
118 | + this.destroy$.complete(); | ||
119 | + } | ||
120 | + | ||
110 | setDisabledState(isDisabled: boolean): void { | 121 | setDisabledState(isDisabled: boolean): void { |
111 | this.disabled = isDisabled; | 122 | this.disabled = isDisabled; |
112 | if (this.disabled) { | 123 | if (this.disabled) { |
@@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
192 | } | 203 | } |
193 | 204 | ||
194 | private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { | 205 | private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { |
195 | - if (control.value) { | ||
196 | - const formValue = control.value as MqttDeviceProfileTransportConfiguration; | 206 | + if (control.getRawValue()) { |
207 | + const formValue = control.getRawValue() as MqttDeviceProfileTransportConfiguration; | ||
197 | if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { | 208 | if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { |
198 | return {unique: true}; | 209 | return {unique: true}; |
199 | } | 210 | } |
@@ -14,17 +14,19 @@ | @@ -14,17 +14,19 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 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'; | 17 | +import { Component, forwardRef, Input, OnDestroy, 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 { | 22 | import { |
23 | DeviceProfileTransportConfiguration, | 23 | DeviceProfileTransportConfiguration, |
24 | DeviceTransportType, | 24 | DeviceTransportType, |
25 | SnmpDeviceProfileTransportConfiguration | 25 | SnmpDeviceProfileTransportConfiguration |
26 | } from '@shared/models/device.models'; | 26 | } from '@shared/models/device.models'; |
27 | -import {isDefinedAndNotNull} from "@core/utils"; | 27 | +import { isDefinedAndNotNull } from '@core/utils'; |
28 | +import { Subject } from 'rxjs'; | ||
29 | +import { takeUntil } from 'rxjs/operators'; | ||
28 | 30 | ||
29 | export interface OidMappingConfiguration { | 31 | export interface OidMappingConfiguration { |
30 | isAttribute: boolean; | 32 | isAttribute: boolean; |
@@ -44,8 +46,11 @@ export interface OidMappingConfiguration { | @@ -44,8 +46,11 @@ export interface OidMappingConfiguration { | ||
44 | multi: true | 46 | multi: true |
45 | }] | 47 | }] |
46 | }) | 48 | }) |
47 | -export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { | 49 | +export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { |
50 | + | ||
48 | snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; | 51 | snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; |
52 | + | ||
53 | + private destroy$ = new Subject(); | ||
49 | private requiredValue: boolean; | 54 | private requiredValue: boolean; |
50 | private configuration = []; | 55 | private configuration = []; |
51 | 56 | ||
@@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control | @@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control | ||
71 | this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ | 76 | this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ |
72 | configuration: [null, Validators.required] | 77 | configuration: [null, Validators.required] |
73 | }); | 78 | }); |
74 | - this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | 79 | + this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( |
80 | + takeUntil(this.destroy$) | ||
81 | + ).subscribe(() => { | ||
75 | this.updateModel(); | 82 | this.updateModel(); |
76 | }); | 83 | }); |
77 | } | 84 | } |
78 | 85 | ||
86 | + ngOnDestroy() { | ||
87 | + this.destroy$.next(); | ||
88 | + this.destroy$.complete(); | ||
89 | + } | ||
90 | + | ||
79 | registerOnChange(fn: any): void { | 91 | registerOnChange(fn: any): void { |
80 | this.propagateChange = fn; | 92 | this.propagateChange = fn; |
81 | } | 93 | } |
@@ -32,7 +32,7 @@ import { | @@ -32,7 +32,7 @@ import { | ||
32 | }) | 32 | }) |
33 | export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { | 33 | export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> { |
34 | 34 | ||
35 | - deviceTransportTypes = Object.keys(DeviceTransportType); | 35 | + deviceTransportTypes = Object.values(DeviceTransportType); |
36 | 36 | ||
37 | deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; | 37 | deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; |
38 | 38 |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <mat-icon *ngIf="icon">{{icon}}</mat-icon> | 20 | <mat-icon *ngIf="icon">{{icon}}</mat-icon> |
21 | <span *ngIf="label">{{label}}</span> | 21 | <span *ngIf="label">{{label}}</span> |
22 | </mat-label> | 22 | </mat-label> |
23 | - <div matPrefix class="tb-color-preview" (click)="showColorPicker()" style="margin-right: 5px;"> | 23 | + <div matPrefix class="tb-color-preview" (click)="!disabled && showColorPicker()" style="margin-right: 5px;"> |
24 | <div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div> | 24 | <div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div> |
25 | </div> | 25 | </div> |
26 | <input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required"> | 26 | <input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required"> |
@@ -45,7 +45,6 @@ export interface WidgetLayouts { | @@ -45,7 +45,6 @@ export interface WidgetLayouts { | ||
45 | 45 | ||
46 | export interface GridSettings { | 46 | export interface GridSettings { |
47 | backgroundColor?: string; | 47 | backgroundColor?: string; |
48 | - color?: string; | ||
49 | columns?: number; | 48 | columns?: number; |
50 | margin?: number; | 49 | margin?: number; |
51 | backgroundSizeMode?: string; | 50 | backgroundSizeMode?: string; |
@@ -93,6 +92,7 @@ export interface DashboardSettings { | @@ -93,6 +92,7 @@ export interface DashboardSettings { | ||
93 | showDashboardExport?: boolean; | 92 | showDashboardExport?: boolean; |
94 | showUpdateDashboardImage?: boolean; | 93 | showUpdateDashboardImage?: boolean; |
95 | toolbarAlwaysOpen?: boolean; | 94 | toolbarAlwaysOpen?: boolean; |
95 | + hideToolbar?: boolean; | ||
96 | titleColor?: string; | 96 | titleColor?: string; |
97 | } | 97 | } |
98 | 98 |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage'; | 17 | +export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage' | 'toggleDashboardLayout'; |
18 | 18 | ||
19 | export interface WindowMessage { | 19 | export interface WindowMessage { |
20 | type: WindowMessageType; | 20 | type: WindowMessageType; |
@@ -721,6 +721,7 @@ | @@ -721,6 +721,7 @@ | ||
721 | "maximum-upload-file-size": "Maximum upload file size: {{ size }}", | 721 | "maximum-upload-file-size": "Maximum upload file size: {{ size }}", |
722 | "cannot-upload-file": "Cannot upload file", | 722 | "cannot-upload-file": "Cannot upload file", |
723 | "settings": "Settings", | 723 | "settings": "Settings", |
724 | + "layout-settings": "Layout settings", | ||
724 | "columns-count": "Columns count", | 725 | "columns-count": "Columns count", |
725 | "columns-count-required": "Columns count is required.", | 726 | "columns-count-required": "Columns count is required.", |
726 | "min-columns-count-message": "Only 10 minimum column count is allowed.", | 727 | "min-columns-count-message": "Only 10 minimum column count is allowed.", |
@@ -743,15 +744,19 @@ | @@ -743,15 +744,19 @@ | ||
743 | "mobile-row-height-required": "Mobile row height value is required.", | 744 | "mobile-row-height-required": "Mobile row height value is required.", |
744 | "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", | 745 | "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", |
745 | "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", | 746 | "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", |
747 | + "title-settings": "Title settings", | ||
746 | "display-title": "Display dashboard title", | 748 | "display-title": "Display dashboard title", |
747 | - "toolbar-always-open": "Keep toolbar opened", | ||
748 | "title-color": "Title color", | 749 | "title-color": "Title color", |
750 | + "toolbar-settings": "Toolbar settings", | ||
751 | + "hide-toolbar": "Hide toolbar", | ||
752 | + "toolbar-always-open": "Keep toolbar opened", | ||
749 | "display-dashboards-selection": "Display dashboards selection", | 753 | "display-dashboards-selection": "Display dashboards selection", |
750 | "display-entities-selection": "Display entities selection", | 754 | "display-entities-selection": "Display entities selection", |
751 | "display-filters": "Display filters", | 755 | "display-filters": "Display filters", |
752 | "display-dashboard-timewindow": "Display timewindow", | 756 | "display-dashboard-timewindow": "Display timewindow", |
753 | "display-dashboard-export": "Display export", | 757 | "display-dashboard-export": "Display export", |
754 | "display-update-dashboard-image": "Display update dashboard image", | 758 | "display-update-dashboard-image": "Display update dashboard image", |
759 | + "dashboard-logo-settings": "Dashboard logo settings", | ||
755 | "display-dashboard-logo": "Display logo in dashboard fullscreen mode", | 760 | "display-dashboard-logo": "Display logo in dashboard fullscreen mode", |
756 | "dashboard-logo-image": "Dashboard logo image", | 761 | "dashboard-logo-image": "Dashboard logo image", |
757 | "import": "Import dashboard", | 762 | "import": "Import dashboard", |