Browse Source

Merge pull request #14159 from mtsymbarov-del/fix/timeseries-entity-alias-sorting

Added option to sort tab entities in alphabetical order
pull/14268/head
Vladyslav Prykhodko 7 months ago
committed by GitHub
parent
commit
ce6f35872b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      application/src/main/data/json/system/widget_types/timeseries_table.json
  2. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
  3. 10
      ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
  4. 2
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html
  5. 44
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
  6. 3
      ui-ngx/src/assets/dashboard/api_usage.json
  7. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
application/src/main/data/json/system/widget_types/timeseries_table.json

@ -17,7 +17,7 @@
"latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings",
"hasBasicMode": true,
"basicModeDirective": "tb-timeseries-table-basic-config",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true,\"configMode\":\"basic\"}"
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"showCellActionsMenu\":true,\"reserveSpaceForHiddenAction\":\"true\",\"showTimestamp\":true,\"dateFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\"},\"displayPagination\":true,\"useEntityLabel\":false,\"defaultPageSize\":10,\"pageStepCount\":3,\"pageStepIncrement\":10,\"hideEmptyLines\":false,\"disableStickyHeader\":false,\"useRowStyleFunction\":false,\"rowStyleFunction\":\"\",\"tabSortKey\":\"timestamp\"},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true,\"configMode\":\"basic\"}"
},
"resources": [
{
@ -32,4 +32,4 @@
"public": true
}
]
}
}

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html

@ -115,6 +115,16 @@
<mat-slide-toggle class="mat-slide" formControlName="useEntityLabel">
{{ 'widgets.table.use-entity-label-tab-name' | translate }}
</mat-slide-toggle>
<div class="tb-form-row justify-between">
<div class="fixed-title-width" translate>widgets.table.sort-by</div>
<mat-form-field class="medium-width" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="tabSortKey" placeholder="{{ 'widget-config.set' | translate }}">
<mat-option [value]="TabSortKey.TIMESTAMP">{{ 'widgets.table.sort-timestamp-option' | translate }}</mat-option>
<mat-option [value]="TabSortKey.NAME_ASC">{{ 'widgets.table.sort-asc' | translate }}</mat-option>
<mat-option [value]="TabSortKey.NAME_DESC">{{ 'widgets.table.sort-desc' | translate }}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="tb-form-panel tb-slide-toggle">
<div class="tb-form-panel-title" style="padding-bottom: 16px;" translate>widgets.table.rows</div>

10
ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts

@ -20,6 +20,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models';
import { TabSortKey } from '@app/modules/home/components/widget/lib/timeseries-table-widget.component'
@Component({
selector: 'tb-timeseries-table-widget-settings',
@ -28,6 +29,8 @@ import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widge
})
export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent {
TabSortKey = TabSortKey;
timeseriesTableWidgetSettingsForm: UntypedFormGroup;
pageStepSizeValues = [];
@ -58,12 +61,14 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
hideEmptyLines: false,
disableStickyHeader: false,
useRowStyleFunction: false,
rowStyleFunction: ''
rowStyleFunction: '',
tabSortKey: TabSortKey.TIMESTAMP
};
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
settings.pageStepIncrement = settings.pageStepIncrement ?? settings.defaultPageSize;
settings.tabSortKey = settings.tabSortKey ?? TabSortKey.TIMESTAMP;
this.pageStepSizeValues = buildPageStepSizeValues(settings.pageStepCount, settings.pageStepIncrement);
return settings;
}
@ -93,7 +98,8 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
hideEmptyLines: [settings.hideEmptyLines, []],
disableStickyHeader: [settings.disableStickyHeader, []],
useRowStyleFunction: [settings.useRowStyleFunction, []],
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]]
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]],
tabSortKey: [settings.tabSortKey, []],
});
}

2
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html

@ -41,7 +41,7 @@
class="flex-1"
mat-stretch-tabs="false"
[(selectedIndex)]="sourceIndex" (selectedIndexChange)="onSourceIndexChanged()">
<mat-tab *ngFor="let source of sources; trackBy: trackBySourcesIndex; let index = index;" [label]="getTabLabel(source)">
<mat-tab *ngFor="let source of sources; trackBy: trackBySourcesIndex; let index = index;" [label]="source.displayName">
<ng-template [ngIf]="isActiveTab(index)">
<div class="table-container flex-1">
<table mat-table [dataSource]="source.timeseriesDatasource" [trackBy]="trackByRowTimestamp"

44
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts

@ -107,11 +107,18 @@ import { FormBuilder } from '@angular/forms';
import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models';
import { DateFormatSettings, ValueFormatProcessor } from '@shared/models/widget-settings.models';
export enum TabSortKey {
NAME_ASC = 'NAME_ASC',
NAME_DESC = 'NAME_DESC',
TIMESTAMP = 'timestamp'
}
export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
showTimestamp: boolean;
showMilliseconds: boolean;
hideEmptyLines: boolean;
dateFormat: DateFormatSettings;
tabSortKey: TabSortKey;
}
interface TimeseriesWidgetLatestDataKeySettings extends TableWidgetDataKeySettings {
@ -152,6 +159,7 @@ interface TimeseriesTableSource {
timeseriesDatasource: TimeseriesDatasource;
header: TimeseriesHeader[];
rowDataTemplate: {[key: string]: any};
displayName: string;
}
@Component({
@ -392,12 +400,33 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.updateDatasources();
}
public getTabLabel(source: TimeseriesTableSource){
if (this.useEntityLabel) {
return source.datasource.entityLabel || source.datasource.entityName;
} else {
return source.datasource.entityName;
private getTabLabel(source: Datasource, entityLabelCache:Map<string,string>):string {
if (entityLabelCache.has(source.entityId)) {
return entityLabelCache.get(source.entityId);
}
const value = this.useEntityLabel
? (source.entityLabel || source.entityName)
: source.entityName;
const translated = this.utils.customTranslation(value);
entityLabelCache.set(source.entityId, translated);
return translated;
}
private sortDatasources(source: Datasource[], entityLabelCache:Map<string,string>): Datasource[] {
source.forEach(ds => this.getTabLabel(ds, entityLabelCache));
if (this.settings.tabSortKey === TabSortKey.TIMESTAMP) {
return source;
}
return source.sort((a, b) => {
const valueA = entityLabelCache.get(a.entityId);
const valueB = entityLabelCache.get(b.entityId);
return this.settings.tabSortKey === TabSortKey.NAME_ASC
? valueA.localeCompare(valueB)
: valueB.localeCompare(valueA);
});
}
private updateDatasources() {
@ -405,9 +434,11 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.sourceIndex = 0;
let keyOffset = 0;
let latestKeyOffset = 0;
const entityLabelCache = new Map<string,string>();
const pageSize = this.displayPagination ? this.defaultPageSize : Number.POSITIVE_INFINITY;
if (this.datasources) {
for (const datasource of this.datasources) {
const sortedDatasources = this.sortDatasources(this.datasources, entityLabelCache);
for (const datasource of sortedDatasources) {
const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder);
const source = {} as TimeseriesTableSource;
source.header = this.prepareHeader(datasource);
@ -424,6 +455,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
source.pageLink = new PageLink(pageSize, 0, null, sortOrder);
source.rowDataTemplate = {};
source.rowDataTemplate.Timestamp = null;
source.displayName = entityLabelCache.get(datasource.entityId);
if (this.showTimestamp) {
source.displayedColumns.push('0');
}

3
ui-ngx/src/assets/dashboard/api_usage.json

@ -98,7 +98,8 @@
"settings": {
"showTimestamp": true,
"displayPagination": true,
"defaultPageSize": 10
"defaultPageSize": 10,
"tabSortKey": "NAME_ASC"
},
"title": "{i18n:api-usage.exceptions}",
"dropShadow": true,

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

@ -9207,7 +9207,11 @@
"alarm-column-error": "At least one alarm column should be specified",
"table-tabs": "Table tabs",
"show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode",
"disable-sorting": "Disable sorting"
"disable-sorting": "Disable sorting",
"sort-by": "Sort tabs by",
"sort-asc": "Name Ascending",
"sort-desc": "Name Descending",
"sort-timestamp-option": "Created time"
},
"latest-chart": {
"total": "Total",

Loading…
Cancel
Save