diff --git a/application/src/main/data/upgrade/3.5.0/schema_update.sql b/application/src/main/data/upgrade/3.5.0/schema_update.sql new file mode 100644 index 0000000000..321112c4eb --- /dev/null +++ b/application/src/main/data/upgrade/3.5.0/schema_update.sql @@ -0,0 +1,23 @@ +-- +-- Copyright © 2016-2023 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- FIX DASHBOARD TEMPLATES AFTER ANGULAR MIGRATION TO VER.15 + +UPDATE dashboard SET configuration = REPLACE(configuration, 'mat-button mat-icon-button', 'mat-icon-button') + WHERE configuration like '%mat-button mat-icon-button%'; + +UPDATE widget_type SET descriptor = REPLACE(descriptor, 'mat-button mat-icon-button', 'mat-icon-button') + WHERE descriptor like '%mat-button mat-icon-button%'; diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 0235e30fdf..51d7435f0e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -256,6 +256,7 @@ public class ThingsboardInstallService { } case "3.5.0": log.info("Upgrading ThingsBoard from version 3.5.0 to 3.5.1 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.5.0"); case "3.5.1": log.info("Upgrading ThingsBoard from version 3.5.1 to 3.5.2 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.5.1"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index d9988b9f5b..ed4bd64c8d 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -714,10 +714,23 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.error("Failed updating schema!!!", e); } break; - case "3.5.1": + case "3.5.0": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); if (isOldSchema(conn, 3005000)) { + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.0", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005001;"); + } + log.info("Schema updated."); + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } + break; + case "3.5.1": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + if (isOldSchema(conn, 3005001)) { schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.1", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 501fbd7189..0d49608a4b 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -32,6 +32,7 @@ import { import { isDefined, isDefinedAndNotNull, isString, isUndefined } from '@core/utils'; import { Datasource, + datasourcesHasOnlyComparisonAggregation, DatasourceType, defaultLegendConfig, Widget, @@ -249,7 +250,8 @@ export class DashboardUtilsService { } }); if (type === widgetType.latest) { - widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, this.timeService); + const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); + widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, onlyHistoryTimewindow, this.timeService); } if (type === widgetType.alarm) { if (!widgetConfig.alarmFilterConfig) { diff --git a/ui-ngx/src/app/core/services/dialog.service.ts b/ui-ngx/src/app/core/services/dialog.service.ts index 6d664085b1..be0c24a958 100644 --- a/ui-ngx/src/app/core/services/dialog.service.ts +++ b/ui-ngx/src/app/core/services/dialog.service.ts @@ -29,6 +29,7 @@ import { } from '@shared/components/dialog/material-icons-dialog.component'; import { ConfirmDialogComponent } from '@shared/components/dialog/confirm-dialog.component'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; +import { ErrorAlertDialogComponent } from '@shared/components/dialog/error-alert-dialog.component'; import { TodoDialogComponent } from '@shared/components/dialog/todo-dialog.component'; @Injectable( @@ -78,6 +79,23 @@ export class DialogService { return dialogRef.afterClosed(); } + errorAlert(title: string, message: string, error: any, ok: string = null, fullscreen: boolean = false): Observable { + const dialogConfig: MatDialogConfig = { + disableClose: true, + data: { + title, + message, + error, + ok: ok || this.translate.instant('action.ok') + } + }; + if (fullscreen) { + dialogConfig.panelClass = ['tb-fullscreen-dialog']; + } + const dialogRef = this.dialog.open(ErrorAlertDialogComponent, dialogConfig); + return dialogRef.afterClosed(); + } + colorPicker(color: string): Observable { return this.dialog.open(ColorPickerDialogComponent, { diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index 813127cbc5..f82c6c3072 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -188,6 +188,10 @@ export class UtilsService { public processWidgetException(exception: any): ExceptionData { const data = this.parseException(exception, -6); + if (data.message?.startsWith('NG0')) { + data.message = `${this.translate.instant('widget.widget-template-error')}
+
${this.translate.instant('dialog.error-message-title')}

${data.message}`; + } if (this.widgetEditMode) { const message: WindowMessage = { type: 'widgetException', diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts index bce66fc3c1..fbdd0a07da 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + Input, + OnDestroy, + OnInit, + ViewChild, + ViewContainerRef +} from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; import { MatDialog } from '@angular/material/dialog'; @@ -32,7 +41,7 @@ import { Subscription } from 'rxjs'; templateUrl: './event-table.component.html', styleUrls: ['./event-table.component.scss'] }) -export class EventTableComponent implements OnInit, AfterViewInit { +export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { @Input() tenantId: string; diff --git a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts index d9ab65813f..dc3e9c7ac2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts @@ -33,6 +33,8 @@ import { CustomDialogComponent, CustomDialogData } from '@home/components/widget/dialog/custom-dialog.component'; +import { DialogService } from '@core/services/dialog.service'; +import { TranslateService } from '@ngx-translate/core'; export interface CustomDialogContainerData { controller: (instance: CustomDialogComponent) => void; @@ -54,6 +56,8 @@ export class CustomDialogContainerComponent extends DialogComponent, + private dialogService: DialogService, + private translate: TranslateService, @Inject(MAT_DIALOG_DATA) public data: CustomDialogContainerData) { super(store, router, dialogRef); let customDialogData: CustomDialogData = { @@ -72,7 +76,19 @@ export class CustomDialogContainerComponent extends DialogComponent
- Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}} +

{{data.title}}

-
+
diff --git a/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.html new file mode 100644 index 0000000000..8554a3d707 --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.html @@ -0,0 +1,30 @@ + +

{{title}}

+
+
{{ message }}
+
dialog.error-message-title
+
{{ errorMessage }}
+ + {{ 'dialog.error-details-title' | translate }} + + +
+
+ +
diff --git a/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.scss b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.scss new file mode 100644 index 0000000000..3ca391dbcd --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.scss @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .mat-mdc-dialog-content { + padding: 0 24px 24px; + } + .tb-error-alert-dialog-content { + .error-message-title { + font-style: italic; + } + .error-message-content { + color: red; + } + .error-details-content { + display: block; + border: solid 1px #d3d3d3; + padding: 8px; + border-radius: 4px; + } + & > *:not(:last-child) { + padding-bottom: 16px; + } + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.ts new file mode 100644 index 0000000000..8ef9032421 --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/error-alert-dialog.component.ts @@ -0,0 +1,49 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +export interface ErrorAlertDialogData { + title: string; + message: string; + error: any; + ok: string; +} + +@Component({ + selector: 'tb-error-alert-dialog', + templateUrl: './error-alert-dialog.component.html', + styleUrls: ['./error-alert-dialog.component.scss'] +}) +export class ErrorAlertDialogComponent { + + title: string; + message: string; + errorMessage: string; + errorDetails?: string; + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ErrorAlertDialogData) { + this.title = this.data.title; + this.message = this.data.message; + this.errorMessage = this.data.error.message ? this.data.error.message : JSON.stringify(this.data.error); + if (this.data.error.stack) { + this.errorDetails = this.data.error.stack.replaceAll('\n', '
'); + } + } + +} diff --git a/ui-ngx/src/app/shared/components/snack-bar-component.scss b/ui-ngx/src/app/shared/components/snack-bar-component.scss index 8832604510..b2db177e06 100644 --- a/ui-ngx/src/app/shared/components/snack-bar-component.scss +++ b/ui-ngx/src/app/shared/components/snack-bar-component.scss @@ -51,7 +51,7 @@ padding: 0 18px; margin: 8px; .toast-text { - padding: 0 6px; + padding: 8px; width: 100%; } button { diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.html b/ui-ngx/src/app/shared/components/time/timewindow.component.html index 91e5dd2f57..e445e99c98 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.html @@ -54,7 +54,7 @@ (click)="toggleTimewindow($event)" matTooltip="{{ 'timewindow.edit' | translate }}" [matTooltipPosition]="tooltipPosition"> - {{innerValue?.displayValue}} | {{innerValue.displayTimezoneAbbr}} + {{innerValue?.displayValue}} | {{innerValue?.displayTimezoneAbbr}}