Browse Source

UI: Auto-switch to edit mode for empty dashboard. Navigate to dashboard page right after adding dashboard. Implement aggregated value card advanced settings form.

feature/basic-widget-config
Igor Kulikov 3 years ago
parent
commit
b7b34da831
  1. 8
      application/src/main/data/json/system/widget_bundles/cards.json
  2. 9
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  3. 4
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  4. 7
      ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts
  5. 3
      ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts
  6. 8
      ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts
  7. 65
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html
  8. 111
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts
  9. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
  10. 20
      ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts
  11. 3
      ui-ngx/src/assets/locale/locale.constant-en_US.json

8
application/src/main/data/json/system/widget_bundles/cards.json

File diff suppressed because one or more lines are too long

9
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -31,7 +31,6 @@ import {
} from '@shared/models/dashboard.models';
import { deepClone, isDefined, isDefinedAndNotNull, isString, isUndefined } from '@core/utils';
import {
DataKey,
Datasource,
datasourcesHasOnlyComparisonAggregation,
DatasourceType,
@ -484,6 +483,14 @@ export class DashboardUtilsService {
return widgetsArray;
}
public isEmptyDashboard(dashboard: Dashboard): boolean {
if (dashboard?.configuration?.widgets) {
return Object.keys(dashboard?.configuration?.widgets).length === 0;
} else {
return true;
}
}
public addWidgetToLayout(dashboard: Dashboard,
targetState: string,
targetLayout: DashboardLayoutId,

4
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -71,7 +71,6 @@ import { MediaBreakpoints } from '@shared/models/constants';
import { AuthUser } from '@shared/models/user.model';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import {
DatasourceType,
Widget,
WidgetConfig,
WidgetInfo,
@ -415,6 +414,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.updateLayoutSizes();
});
this.dashboardResize$.observe(this.dashboardContainer.nativeElement);
if (!this.widgetEditMode && !this.readonly && this.dashboardUtils.isEmptyDashboard(this.dashboard)) {
this.setEditMode(true, false);
}
}
private init(data: DashboardPageInitData) {

7
ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts

@ -90,7 +90,7 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo
protected setupDefaults(configData: WidgetConfigComponentData) {
this.setupDefaultDatasource(configData, [
{ name: 'watermeter', label: 'Watermeter', type: DataKeyType.timeseries, units: 'm³', decimals: 0 }
{ name: 'watermeter', label: 'Watermeter', type: DataKeyType.timeseries, color: 'rgba(0, 0, 0, 0.87)', units: 'm³', decimals: 0 }
],
createDefaultAggregatedValueLatestDataKeys('watermeter', 'm³')
);
@ -143,7 +143,7 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo
showChart: [settings.showChart, []],
chartUnits: [dataKey?.units, []],
chartDecimals: [dataKey?.decimals, []],
chartColor: [settings.chartColor, []],
chartColor: [dataKey?.color, []],
values: [this.getValues(configData.config.datasources, keyName), []],
@ -188,10 +188,9 @@ export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigCo
if (dataKey) {
dataKey.units = config.chartUnits;
dataKey.decimals = config.chartDecimals;
dataKey.color = config.chartColor;
}
this.widgetConfig.config.settings.chartColor = config.chartColor;
this.setValues(config.values, this.widgetConfig.config.datasources);
this.widgetConfig.config.settings.background = config.background;

3
ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts

@ -79,7 +79,6 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit
values: {[key: string]: AggregatedValueCardValue} = {};
showChart = true;
chartColor: string;
showDate = true;
dateFormat: DateFormatProcessor;
@ -123,7 +122,6 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit
}
this.showChart = this.settings.showChart;
this.chartColor = this.settings.chartColor;
if (this.showChart) {
if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) {
this.flotDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0];
@ -132,7 +130,6 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit
showLines: true,
lineWidth: 2
} as TbFlotKeySettings;
this.flotDataKey.color = this.chartColor;
}
}

8
ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts

@ -41,7 +41,6 @@ export interface AggregatedValueCardWidgetSettings {
dateFont: Font;
dateColor: string;
showChart: boolean;
chartColor: string;
background: BackgroundSettings;
}
@ -136,7 +135,6 @@ export const aggregatedValueCardDefaultSettings: AggregatedValueCardWidgetSettin
},
dateColor: 'rgba(0, 0, 0, 0.38)',
showChart: true,
chartColor: 'rgba(0, 0, 0, 0.87)',
background: {
type: BackgroundType.color,
color: '#fff',
@ -164,7 +162,7 @@ export const aggregatedValueCardDefaultKeySettings: AggregatedValueCardKeySettin
export const createDefaultAggregatedValueLatestDataKeys = (keyName: string, units): DataKey[] => [
{
name: keyName, label: keyName, type: DataKeyType.timeseries, units, decimals: 0,
name: keyName, label: 'Latest', type: DataKeyType.timeseries, units, decimals: 0,
aggregationType: AggregationType.NONE,
settings: {
position: AggregatedValueCardKeyPosition.center,
@ -181,7 +179,7 @@ export const createDefaultAggregatedValueLatestDataKeys = (keyName: string, unit
} as AggregatedValueCardKeySettings
},
{
name: keyName, label: 'Delta percent ' + keyName, type: DataKeyType.timeseries, units: '%', decimals: 0,
name: keyName, label: 'Delta percent', type: DataKeyType.timeseries, units: '%', decimals: 0,
aggregationType: AggregationType.AVG,
comparisonEnabled: true,
timeForComparison: 'previousInterval',
@ -210,7 +208,7 @@ export const createDefaultAggregatedValueLatestDataKeys = (keyName: string, unit
} as AggregatedValueCardKeySettings
},
{
name: keyName, label: 'Delta absolute ' + keyName, type: DataKeyType.timeseries, units, decimals: 1,
name: keyName, label: 'Delta absolute', type: DataKeyType.timeseries, units, decimals: 1,
aggregationType: AggregationType.AVG,
comparisonEnabled: true,
timeForComparison: 'previousInterval',

65
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html

@ -0,0 +1,65 @@
<!--
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.
-->
<ng-container [formGroup]="aggregatedValueCardWidgetSettingsForm">
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.aggregated-value-card.aggregated-value-card-style</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showSubtitle">
{{ 'widgets.aggregated-value-card.subtitle' | translate }}
</mat-slide-toggle>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="subtitle" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<tb-font-settings formControlName="subtitleFont"
clearButton
[previewText]="aggregatedValueCardWidgetSettingsForm.get('subtitle').value">
</tb-font-settings>
<tb-color-input asBoxInput
colorClearButton
formControlName="subtitleColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row column-xs">
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showDate">
{{ 'widgets.value-card.date' | translate }}
</mat-slide-toggle>
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px">
<tb-date-format-select fxFlex formControlName="dateFormat"></tb-date-format-select>
<tb-font-settings formControlName="dateFont"
[previewText]="datePreviewFn">
</tb-font-settings>
<tb-color-input asBoxInput
colorClearButton
formControlName="dateColor">
</tb-color-input>
</div>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="showChart">
{{ 'widgets.aggregated-value-card.chart' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between">
<div>{{ 'widgets.background.background' | translate }}</div>
<tb-background-settings formControlName="background">
</tb-background-settings>
</div>
</div>
</ng-container>

111
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts

@ -0,0 +1,111 @@
///
/// 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, Injector } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models';
import { aggregatedValueCardDefaultSettings } from '@home/components/widget/lib/cards/aggregated-value-card.models';
@Component({
selector: 'tb-aggregated-value-card-widget-settings',
templateUrl: './aggregated-value-card-widget-settings.component.html',
styleUrls: []
})
export class AggregatedValueCardWidgetSettingsComponent extends WidgetSettingsComponent {
aggregatedValueCardWidgetSettingsForm: UntypedFormGroup;
datePreviewFn = this._datePreviewFn.bind(this);
constructor(protected store: Store<AppState>,
private $injector: Injector,
private fb: UntypedFormBuilder) {
super(store);
}
protected settingsForm(): UntypedFormGroup {
return this.aggregatedValueCardWidgetSettingsForm;
}
protected defaultSettings(): WidgetSettings {
return {...aggregatedValueCardDefaultSettings};
}
protected onSettingsSet(settings: WidgetSettings) {
this.aggregatedValueCardWidgetSettingsForm = this.fb.group({
showSubtitle: [settings.showSubtitle, []],
subtitle: [settings.subtitle, []],
subtitleFont: [settings.subtitleFont, []],
subtitleColor: [settings.subtitleColor, []],
showDate: [settings.showDate, []],
dateFormat: [settings.dateFormat, []],
dateFont: [settings.dateFont, []],
dateColor: [settings.dateColor, []],
showChart: [settings.showChart, []],
background: [settings.background, []]
});
}
protected validatorTriggers(): string[] {
return ['showSubtitle', 'showDate'];
}
protected updateValidators(emitEvent: boolean) {
const showSubtitle: boolean = this.aggregatedValueCardWidgetSettingsForm.get('showSubtitle').value;
const showDate: boolean = this.aggregatedValueCardWidgetSettingsForm.get('showDate').value;
if (showSubtitle) {
this.aggregatedValueCardWidgetSettingsForm.get('subtitle').enable();
this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').enable();
this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').enable();
} else {
this.aggregatedValueCardWidgetSettingsForm.get('subtitle').disable();
this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').disable();
this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').disable();
}
if (showDate) {
this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').enable();
this.aggregatedValueCardWidgetSettingsForm.get('dateFont').enable();
this.aggregatedValueCardWidgetSettingsForm.get('dateColor').enable();
} else {
this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').disable();
this.aggregatedValueCardWidgetSettingsForm.get('dateFont').disable();
this.aggregatedValueCardWidgetSettingsForm.get('dateColor').disable();
}
this.aggregatedValueCardWidgetSettingsForm.get('subtitle').updateValueAndValidity({emitEvent});
this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').updateValueAndValidity({emitEvent});
this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').updateValueAndValidity({emitEvent});
this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').updateValueAndValidity({emitEvent});
this.aggregatedValueCardWidgetSettingsForm.get('dateFont').updateValueAndValidity({emitEvent});
this.aggregatedValueCardWidgetSettingsForm.get('dateColor').updateValueAndValidity({emitEvent});
}
private _datePreviewFn(): string {
const dateFormat: DateFormatSettings = this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').value;
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat);
processor.update(Date.now());
return processor.formatted;
}
}

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts

@ -270,6 +270,9 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings
import {
AggregatedValueCardKeySettingsComponent
} from '@home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component';
import {
AggregatedValueCardWidgetSettingsComponent
} from '@home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component';
@NgModule({
declarations: [
@ -370,7 +373,8 @@ import {
DocLinksWidgetSettingsComponent,
QuickLinksWidgetSettingsComponent,
ValueCardWidgetSettingsComponent,
AggregatedValueCardKeySettingsComponent
AggregatedValueCardKeySettingsComponent,
AggregatedValueCardWidgetSettingsComponent
],
imports: [
CommonModule,
@ -476,7 +480,8 @@ import {
DocLinksWidgetSettingsComponent,
QuickLinksWidgetSettingsComponent,
ValueCardWidgetSettingsComponent,
AggregatedValueCardKeySettingsComponent
AggregatedValueCardKeySettingsComponent,
AggregatedValueCardWidgetSettingsComponent
]
})
export class WidgetSettingsModule {
@ -548,4 +553,5 @@ export const widgetSettingsComponentsMap: {[key: string]: Type<IWidgetSettingsCo
'tb-quick-links-widget-settings': QuickLinksWidgetSettingsComponent,
'tb-value-card-widget-settings': ValueCardWidgetSettingsComponent,
'tb-aggregated-value-card-key-settings': AggregatedValueCardKeySettingsComponent,
'tb-aggregated-value-card-widget-settings': AggregatedValueCardWidgetSettingsComponent
};

20
ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts

@ -103,9 +103,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
this.config.deleteEntitiesContent = () => this.translate.instant('dashboard.delete-dashboards-text');
this.config.loadEntity = id => this.dashboardService.getDashboard(id.id);
this.config.saveEntity = dashboard => {
return this.dashboardService.saveDashboard(dashboard as Dashboard);
};
this.config.saveEntity = dashboard => this.dashboardService.saveDashboard(dashboard as Dashboard);
this.config.onEntityAction = action => this.onDashboardAction(action);
this.config.detailsReadonly = () => (this.config.componentsData.dashboardScope === 'customer_user' ||
this.config.componentsData.dashboardScope === 'edge_customer_user');
@ -118,6 +116,10 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}
return true;
};
this.config.entityAdded = dashboard => {
this.openDashboard(null, dashboard);
};
}
resolve(route: ActivatedRouteSnapshot): Observable<EntityTableConfig<DashboardInfo | Dashboard>> {
@ -178,13 +180,9 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
if (dashboardScope === 'tenant') {
columns.push(
new EntityTableColumn<DashboardInfo>('customersTitle', 'dashboard.assignedToCustomers',
'50%', entity => {
return getDashboardAssignedCustomersText(entity);
}, () => ({}), false),
'50%', entity => getDashboardAssignedCustomersText(entity), () => ({}), false),
new EntityTableColumn<DashboardInfo>('dashboardIsPublic', 'dashboard.public', '60px',
entity => {
return checkBoxCell(isPublicDashboard(entity));
}, () => ({}), false),
entity => checkBoxCell(isPublicDashboard(entity)), () => ({}), false),
);
}
return columns;
@ -269,7 +267,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
{
name: this.translate.instant('edge.unassign-from-edge'),
icon: 'assignment_return',
isEnabled: (entity) => true,
isEnabled: () => true,
onAction: ($event, entity) => this.unassignFromEdge($event, entity)
}
);
@ -383,7 +381,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
}
}
importDashboard($event: Event) {
importDashboard(_$event: Event) {
this.importExport.importDashboard().subscribe(
(dashboard) => {
if (dashboard) {

3
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -5761,7 +5761,8 @@
"add-value": "Add value",
"remove-value": "Remove value",
"no-values": "No values configured",
"aggregation": "Aggregation"
"aggregation": "Aggregation",
"aggregated-value-card-style": "Aggregated value card style"
},
"table": {
"common-table-settings": "Common Table Settings",

Loading…
Cancel
Save