UI: Improve custom dialog error diagnostics. Improve widget error message formatting.
This commit is contained in:
parent
6ccc21632d
commit
dbb5a8cc8b
@ -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<any> {
|
||||
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<string> {
|
||||
return this.dialog.open<ColorPickerDialogComponent, ColorPickerDialogData, string>(ColorPickerDialogComponent,
|
||||
{
|
||||
|
||||
@ -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')}<br/>
|
||||
<br/><i>${this.translate.instant('dialog.error-message-title')}</i><br/><br/>${data.message}`;
|
||||
}
|
||||
if (this.widgetEditMode) {
|
||||
const message: WindowMessage = {
|
||||
type: 'widgetException',
|
||||
|
||||
@ -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<CustomDialog
|
||||
protected router: Router,
|
||||
public viewContainerRef: ViewContainerRef,
|
||||
public dialogRef: MatDialogRef<CustomDialogContainerComponent>,
|
||||
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<CustomDialog
|
||||
useValue: dialogRef
|
||||
}]
|
||||
});
|
||||
try {
|
||||
this.customComponentRef = this.viewContainerRef.createComponent(this.data.customComponentFactory, 0, injector);
|
||||
} catch (e: any) {
|
||||
let message;
|
||||
if (e.message?.startsWith('NG0')) {
|
||||
message = this.translate.instant('widget-action.custom-pretty-template-error');
|
||||
} else {
|
||||
message = this.translate.instant('widget-action.custom-pretty-controller-error');
|
||||
}
|
||||
dialogRef.close();
|
||||
console.error(e);
|
||||
this.dialogService.errorAlert(this.translate.instant('widget-action.custom-pretty-error-title'), message, e);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
</tb-legend>
|
||||
</div>
|
||||
<div class="tb-absolute-fill tb-widget-error" *ngIf="widgetErrorData">
|
||||
<span>Widget Error: {{ widgetErrorData.name + ": " + widgetErrorData.message}}</span>
|
||||
<span [innerHtml]="('Widget Error:<br/><br/>' + widgetErrorData.message) | safe:'html'"></span>
|
||||
</div>
|
||||
<div class="tb-absolute-fill tb-widget-no-data" *ngIf="displayNoData">
|
||||
<span fxLayoutAlign="center center"
|
||||
|
||||
@ -489,9 +489,6 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe
|
||||
if (!this.gotError) {
|
||||
this.gotError = true;
|
||||
let errorInfo = 'Error:';
|
||||
if (details.name) {
|
||||
errorInfo += ' ' + details.name + ':';
|
||||
}
|
||||
if (details.message) {
|
||||
errorInfo += ' ' + details.message;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
-->
|
||||
<h2 mat-dialog-title>{{data.title}}</h2>
|
||||
<div mat-dialog-content [innerHTML]="data.message"></div>
|
||||
<div mat-dialog-content [innerHTML]="data.message | safe:'html'"></div>
|
||||
<div mat-dialog-actions fxLayoutAlign="end center">
|
||||
<button mat-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>{{data.ok}}</button>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<h2 mat-dialog-title>{{title}}</h2>
|
||||
<div mat-dialog-content class="tb-error-alert-dialog-content">
|
||||
<div>{{ message }}</div>
|
||||
<div class="error-message-title" translate>dialog.error-message-title</div>
|
||||
<div class="error-message-content"> {{ errorMessage }} </div>
|
||||
<mat-expansion-panel *ngIf="errorDetails">
|
||||
<mat-expansion-panel-header>{{ 'dialog.error-details-title' | translate }}</mat-expansion-panel-header>
|
||||
<small class="error-details-content" [innerHTML]="errorDetails"></small>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
<div mat-dialog-actions fxLayoutAlign="end center">
|
||||
<button mat-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>{{data.ok}}</button>
|
||||
</div>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<ErrorAlertDialogComponent>,
|
||||
@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', '<br/>');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -51,7 +51,7 @@
|
||||
padding: 0 18px;
|
||||
margin: 8px;
|
||||
.toast-text {
|
||||
padding: 0 6px;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
button {
|
||||
|
||||
@ -123,6 +123,7 @@ import { JsFuncComponent } from '@shared/components/js-func.component';
|
||||
import { JsonFormComponent } from '@shared/components/json-form/json-form.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';
|
||||
import { MaterialIconsDialogComponent } from '@shared/components/dialog/material-icons-dialog.component';
|
||||
import { MaterialIconSelectComponent } from '@shared/components/material-icon-select.component';
|
||||
@ -305,6 +306,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
WidgetsBundleSelectComponent,
|
||||
ConfirmDialogComponent,
|
||||
AlertDialogComponent,
|
||||
ErrorAlertDialogComponent,
|
||||
TodoDialogComponent,
|
||||
ColorPickerDialogComponent,
|
||||
MaterialIconsDialogComponent,
|
||||
@ -528,6 +530,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
|
||||
MarkdownModule,
|
||||
ConfirmDialogComponent,
|
||||
AlertDialogComponent,
|
||||
ErrorAlertDialogComponent,
|
||||
TodoDialogComponent,
|
||||
ColorPickerDialogComponent,
|
||||
MaterialIconsDialogComponent,
|
||||
|
||||
@ -1789,7 +1789,9 @@
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"close": "Close dialog"
|
||||
"close": "Close dialog",
|
||||
"error-message-title": "Error message:",
|
||||
"error-details-title": "Error details"
|
||||
},
|
||||
"direction": {
|
||||
"column": "Column",
|
||||
@ -3991,7 +3993,8 @@
|
||||
"alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities",
|
||||
"search": "Search widget",
|
||||
"filter": "Widget filter type",
|
||||
"loading-widgets": "Loading widgets..."
|
||||
"loading-widgets": "Loading widgets...",
|
||||
"widget-template-error": "Invalid widget HTML template."
|
||||
},
|
||||
"widget-action": {
|
||||
"header-button": "Widget header button",
|
||||
@ -4000,6 +4003,9 @@
|
||||
"open-dashboard": "Navigate to other dashboard",
|
||||
"custom": "Custom action",
|
||||
"custom-pretty": "Custom action (with HTML template)",
|
||||
"custom-pretty-error-title": "Custom dialog error",
|
||||
"custom-pretty-template-error": "Invalid custom dialog template.",
|
||||
"custom-pretty-controller-error": "Error occurred while evaluating custom dialog function.",
|
||||
"mobile-action": "Mobile action",
|
||||
"target-dashboard-state": "Target dashboard state",
|
||||
"target-dashboard-state-required": "Target dashboard state is required",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user