Browse Source

Merge branch 'rc' into feature/add_show_total_legend_setting_to_latest-chart-widgets

pull/13350/head
Paolo Cristiani 1 year ago
committed by GitHub
parent
commit
51611c03ba
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      application/src/main/data/json/system/widget_types/knob_control.json
  2. 27
      ui-ngx/src/app/core/services/utils.service.ts
  3. 1
      ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html
  4. 23
      ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts
  5. 2
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts
  6. 11
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html
  7. 2
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts
  8. 9
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html
  9. 4
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts
  10. 1
      ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
  11. 3
      ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts
  12. 2
      ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts
  13. 14
      ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts
  14. 5
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts
  15. 7
      ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
  16. 3
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html
  17. 169
      ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts
  18. 11
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html
  19. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts
  20. 9
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html
  21. 3
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts
  22. 3
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html
  23. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts
  24. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html
  25. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts
  26. 1
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html
  27. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts
  28. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html
  29. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts
  30. 122
      ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html
  31. 76
      ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts
  32. 2
      ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts
  33. 41
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts
  34. 4
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts
  35. 1
      ui-ngx/src/app/shared/components/entity/entity-list-select.component.html
  36. 3
      ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts
  37. 8
      ui-ngx/src/app/shared/components/unit-settings-panel.component.ts
  38. 4
      ui-ngx/src/app/shared/models/widget-settings.models.ts
  39. 125
      ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts
  40. 59
      ui-ngx/src/assets/locale/locale.constant-da_DK.json
  41. 37
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  42. 10579
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  43. 37
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  44. 29
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  45. 47
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  46. 31
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  47. 25
      ui-ngx/src/assets/locale/locale.constant-nl_BE.json
  48. 9197
      ui-ngx/src/assets/locale/locale.constant-nl_NL.json
  49. 9197
      ui-ngx/src/assets/locale/locale.constant-no_NO.json

5
application/src/main/data/json/system/widget_types/knob_control.json

@ -12,10 +12,9 @@
"templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "",
"dataKeySettingsSchema": "{}\n",
"dataKeySettingsForm": [],
"settingsDirective": "tb-knob-control-widget-settings",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"units\":null}"
},
"tags": [
"dial",

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

@ -51,7 +51,7 @@ import cssjs from '@core/css/css';
import { isNotEmptyTbFunction } from '@shared/models/js-function.models';
import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models';
const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g');
const i18nRegExp = new RegExp(`{${i18nPrefix}:([^{}]+)}`, 'g');
const predefinedFunctions: { [func: string]: string } = {
Sin: 'return Math.round(1000*Math.sin(time/5000));',
@ -209,21 +209,20 @@ export class UtilsService {
}
public customTranslation(translationValue: string, defaultValue: string = translationValue): string {
if (translationValue && isString(translationValue)) {
if (translationValue.includes(`{${i18nPrefix}`)) {
const matches = translationValue.match(i18nRegExp);
let result = translationValue;
for (const match of matches) {
const translationId = match.substring(6, match.length - 1);
result = result.replace(match, this.doTranslate(translationId, match));
}
return result;
} else {
return this.doTranslate(translationValue, defaultValue, customTranslationsPrefix);
}
} else {
if (!translationValue || !isString(translationValue)) {
return translationValue;
}
if (!translationValue.includes(`{${i18nPrefix}:`)) {
return this.doTranslate(translationValue, defaultValue, customTranslationsPrefix);
}
const matches = translationValue.matchAll(i18nRegExp);
let result = translationValue;
for (const [fullMatch, translationId] of matches) {
if (translationId) {
result = result.replace(fullMatch, this.doTranslate(translationId, fullMatch));
}
}
return result;
}
private doTranslate(translationValue: string, defaultValue: string, prefix?: string): string {

1
ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html

@ -38,6 +38,7 @@
'relation.to-entity' : 'relation.from-entity') | translate}}</small>
<tb-entity-list-select
formControlName="targetEntityIds"
[additionEntityTypes]="additionEntityTypes"
required="true">
</tb-entity-list-select>
<tb-json-object-edit

23
ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.ts

@ -19,7 +19,7 @@ import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import {
CONTAINS_TYPE,
EntityRelation,
@ -33,6 +33,9 @@ import { JsonObjectEditComponent } from '@shared/components/json-object-edit.com
import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { Authority } from '@shared/models/authority.enum';
import { EntityType } from '@shared/models/entity-type.models';
export interface RelationDialogData {
isAdd: boolean;
@ -48,25 +51,29 @@ export interface RelationDialogData {
})
export class RelationDialogComponent extends DialogComponent<RelationDialogComponent, boolean> implements OnInit, ErrorStateMatcher {
relationFormGroup: UntypedFormGroup;
relationFormGroup: FormGroup;
additionEntityTypes: {[key in string]: string} = {};
isAdd: boolean;
direction: EntitySearchDirection;
entitySearchDirection = EntitySearchDirection;
additionalInfo: UntypedFormControl;
additionalInfo: FormControl;
@ViewChild('additionalInfoEdit', {static: true}) additionalInfoEdit: JsonObjectEditComponent;
submitted = false;
private authUser = getCurrentAuthUser(this.store);
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: RelationDialogData,
private entityRelationService: EntityRelationService,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<RelationDialogComponent, boolean>,
public fb: UntypedFormBuilder,
public fb: FormBuilder,
private destroyRef: DestroyRef) {
super(store, router, dialogRef);
this.isAdd = data.isAdd;
@ -74,13 +81,17 @@ export class RelationDialogComponent extends DialogComponent<RelationDialogCompo
}
ngOnInit(): void {
if (this.authUser.authority === Authority.TENANT_ADMIN) {
this.additionEntityTypes = {[EntityType.RULE_CHAIN]: null};
}
this.relationFormGroup = this.fb.group({
type: [this.isAdd ? CONTAINS_TYPE : this.data.relation.type, [Validators.required]],
targetEntityIds: [this.isAdd ? null :
[this.direction === EntitySearchDirection.FROM ? this.data.relation.to : this.data.relation.from],
[Validators.required]]
});
this.additionalInfo = new UntypedFormControl(this.data.relation.additionalInfo ? {...this.data.relation.additionalInfo} : null);
this.additionalInfo = new FormControl(this.data.relation.additionalInfo ? {...this.data.relation.additionalInfo} : null);
if (!this.isAdd) {
this.relationFormGroup.get('type').disable();
this.relationFormGroup.get('targetEntityIds').disable();
@ -94,7 +105,7 @@ export class RelationDialogComponent extends DialogComponent<RelationDialogCompo
);
}
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted);
return originalErrorState || customErrorState;

2
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts

@ -162,6 +162,7 @@ export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDia
if (value === 'headerButton') {
this.widgetActionFormGroup.get('buttonType').enable({emitEvent: false});
this.widgetActionFormGroup.get('buttonColor').enable({emitEvent: false});
this.widgetActionFormGroup.get('customButtonStyle').enable({emitEvent: false});
this.widgetHeaderButtonValidators(true);
} else {
this.widgetActionFormGroup.get('buttonType').disable({emitEvent: false});
@ -169,6 +170,7 @@ export class WidgetActionDialogComponent extends DialogComponent<WidgetActionDia
this.widgetActionFormGroup.get('buttonColor').disable({emitEvent: false});
this.widgetActionFormGroup.get('buttonFillColor').disable({emitEvent: false});
this.widgetActionFormGroup.get('buttonBorderColor').disable({emitEvent: false});
this.widgetActionFormGroup.get('customButtonStyle').disable({emitEvent: false});
}
});
this.widgetActionFormGroup.get('useShowWidgetActionFunction').valueChanges.pipe(

11
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/latest-chart-basic-config.component.html

@ -504,6 +504,17 @@
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.radar-chart.radar-axis</div>
<div class="tb-form-row column">
<div class="tb-form-row no-border no-padding space-between">
<div tb-hint-tooltip-icon="{{ 'widgets.radar-chart.max-axes-scaling-hint' | translate }}">
{{ 'widgets.radar-chart.max-axes-scaling' | translate }}
</div>
<tb-toggle-select formControlName="normalizeAxes">
<tb-toggle-option [value]="false">{{ 'widgets.radar-chart.separate' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="true">{{ 'widgets.radar-chart.common' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="axisShowLabel">
{{ 'widgets.radar-chart.axis-label' | translate }}

2
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/radar-chart-basic-config.component.ts

@ -75,6 +75,7 @@ export class RadarChartBasicConfigComponent extends LatestChartBasicConfigCompon
latestChartWidgetConfigForm.addControl('labelColor', this.fb.control(settings.labelColor, []));
latestChartWidgetConfigForm.addControl('fillAreaSettings', this.fb.control(settings.fillAreaSettings, []));
latestChartWidgetConfigForm.addControl('normalizeAxes', this.fb.control(settings.normalizeAxes, []));
latestChartWidgetConfigForm.addControl('axisShowLabel', this.fb.control(settings.axisShowLabel, []));
latestChartWidgetConfigForm.addControl('axisLabelFont', this.fb.control(settings.axisLabelFont, []));
latestChartWidgetConfigForm.addControl('axisShowTickLabels', this.fb.control(settings.axisShowTickLabels, []));
@ -97,6 +98,7 @@ export class RadarChartBasicConfigComponent extends LatestChartBasicConfigCompon
this.widgetConfig.config.settings.labelColor = config.labelColor;
this.widgetConfig.config.settings.fillAreaSettings = config.fillAreaSettings;
this.widgetConfig.config.settings.normalizeAxes = config.normalizeAxes;
this.widgetConfig.config.settings.axisShowLabel = config.axisShowLabel;
this.widgetConfig.config.settings.axisLabelFont = config.axisLabelFont;
this.widgetConfig.config.settings.axisShowTickLabels = config.axisShowTickLabels;

9
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.html

@ -104,6 +104,7 @@
</tb-time-series-chart-states-panel>
<tb-time-series-chart-y-axes-panel
formControlName="yAxes"
[supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion"
(axisRemoved)="yAxisRemoved($event)">
</tb-time-series-chart-y-axes-panel>
<tb-time-series-chart-thresholds-panel
@ -112,7 +113,8 @@
[dataKeyCallbacks]="callbacks"
[datasource]="datasource"
[widgetConfig]="widgetConfig?.config"
[yAxisIds]="yAxisIds">
[yAxisIds]="yAxisIds"
[supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion">
</tb-time-series-chart-thresholds-panel>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
@ -311,6 +313,11 @@
</div>
</mat-slide-toggle>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="tooltipHideZeroFalse">
{{ 'tooltip.hide-zero-false-tooltip-values' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between">
<div>{{ 'tooltip.background-color' | translate }}</div>
<tb-color-input asBoxInput

4
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts

@ -188,6 +188,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
tooltipDateFont: [settings.tooltipDateFont, []],
tooltipDateColor: [settings.tooltipDateColor, []],
tooltipDateInterval: [settings.tooltipDateInterval, []],
tooltipHideZeroFalse: [settings.tooltipHideZeroFalse ,[]],
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []],
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []],
@ -264,6 +265,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont;
this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor;
this.widgetConfig.config.settings.tooltipDateInterval = config.tooltipDateInterval;
this.widgetConfig.config.settings.tooltipHideZeroFalse = config.tooltipHideZeroFalse;
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor;
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur;
@ -357,6 +359,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').enable();
this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').enable();
this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false});
this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroFalse').enable({emitEvent: false});
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').enable();
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').enable();
if (tooltipShowDate) {
@ -381,6 +384,7 @@ export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigCompon
this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable();
this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable();
this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable();
this.timeSeriesChartWidgetConfigForm.get('tooltipHideZeroFalse').disable();
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').disable();
this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').disable();
}

1
ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts

@ -340,7 +340,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
public onDataUpdated() {
this.alarmsDatasource.updateAlarms();
this.clearCache();
this.ctx.detectChanges();
}
public onEditModeChanged() {

3
ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart-widget.models.ts

@ -50,6 +50,7 @@ export interface RadarChartWidgetSettings extends LatestChartWidgetSettings {
labelFont: Font;
labelColor: string;
fillAreaSettings: ChartFillSettings;
normalizeAxes: boolean;
axisShowLabel: boolean;
axisLabelFont: Font;
axisShowTickLabels: boolean;
@ -88,6 +89,7 @@ export const radarChartWidgetDefaultSettings: RadarChartWidgetSettings = {
end: 20
}
},
normalizeAxes: false,
axisShowLabel: true,
axisLabelFont: {
family: 'Roboto',
@ -123,6 +125,7 @@ export const radarChartWidgetRadarChartSettings = (settings: RadarChartWidgetSet
labelFont: settings.labelFont,
labelColor: settings.labelColor,
fillAreaSettings: settings.fillAreaSettings,
normalizeAxes: settings.normalizeAxes,
axisShowLabel: settings.axisShowLabel,
axisLabelFont: settings.axisLabelFont,
axisShowTickLabels: settings.axisShowTickLabels,

2
ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.models.ts

@ -56,6 +56,7 @@ export interface RadarChartSettings extends LatestChartSettings {
labelFont: Font;
labelColor: string;
fillAreaSettings: ChartFillSettings;
normalizeAxes: boolean;
axisShowLabel: boolean;
axisLabelFont: Font;
axisShowTickLabels: boolean;
@ -100,6 +101,7 @@ export const radarChartDefaultSettings: RadarChartSettings = {
end: 20
}
},
normalizeAxes: false,
axisShowLabel: true,
axisLabelFont: {
family: 'Roboto',

14
ui-ngx/src/app/modules/home/components/widget/lib/chart/radar-chart.ts

@ -31,6 +31,7 @@ import { ComponentStyle } from '@shared/models/widget-settings.models';
import { AreaStyleOption, SeriesLabelOption } from 'echarts/types/src/util/types';
import { RadarIndicatorOption } from 'echarts/types/src/coord/radar/RadarModel';
import { DataKey } from '@shared/models/widget.models';
import { LatestChartDataItem } from '@home/components/widget/lib/chart/latest-chart.models';
export class TbRadarChart extends TbLatestChart<RadarChartSettings> {
@ -141,7 +142,7 @@ export class TbRadarChart extends TbLatestChart<RadarChartSettings> {
if (dataItem.enabled && dataItem.hasValue) {
indicator.push({
name: dataItem.dataKey.label,
color: dataItem.dataKey.color
color: dataItem.dataKey.color,
});
value.push(dataItem.value);
}
@ -149,10 +150,21 @@ export class TbRadarChart extends TbLatestChart<RadarChartSettings> {
if (!indicator.length) {
indicator.push({});
}
if (this.settings.normalizeAxes && indicator.length > 1) {
const maxDataItem = this.findMaxDataItem(this.dataItems);
indicator.map(value => value.max = maxDataItem.value);
}
this.latestChartOption.radar[0].indicator = indicator;
this.latestChartOption.series[0].data[0].value = value;
}
private findMaxDataItem(array: LatestChartDataItem[]): LatestChartDataItem {
if (!array || array.length === 0) return null;
return array.reduce((maxObj, currentObj) => {
return currentObj.value > maxObj.value ? currentObj : maxObj;
}, array[0]);
}
protected forceRedrawOnResize(): boolean {
return true;
}

5
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-tooltip.models.ts

@ -39,6 +39,7 @@ export interface TimeSeriesChartTooltipWidgetSettings {
tooltipValueFormatter?: string | TimeSeriesChartTooltipValueFormatFunction;
tooltipShowDate: boolean;
tooltipDateInterval?: boolean;
tooltipHideZeroFalse?: boolean;
tooltipDateFormat: DateFormatSettings;
tooltipDateFont: Font;
tooltipDateColor: string;
@ -129,7 +130,9 @@ export class TimeSeriesChartTooltip {
this.renderer.appendChild(tooltipItemsElement, this.constructTooltipDateElement(items[0].param, interval));
}
for (const item of items) {
this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item));
if (!this.settings.tooltipHideZeroFalse || item.param.value[1]) {
this.renderer.appendChild(tooltipItemsElement, this.constructTooltipSeriesElement(item));
}
}
}
}

7
ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts

@ -167,7 +167,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private defaultPageSize;
private defaultSortOrder = 'entityName';
private contentsInfo: {[key: string]: CellContentInfo} = {};
private contentsInfo: {[key: string]: WithOptional<CellContentInfo, 'valueFormat'>} = {};
private stylesInfo: {[key: string]: Observable<CellStyleInfo>} = {};
private columnWidth: {[key: string]: string} = {};
private columnDefaultVisibility: {[key: string]: boolean} = {};
@ -275,7 +275,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
public onDataUpdated() {
this.entityDatasource.dataUpdated();
this.clearCache();
this.ctx.detectChanges();
}
public onEditModeChanged() {
@ -760,7 +759,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
return content$;
}
private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any {
private defaultContent(key: EntityColumn, contentInfo: WithOptional<CellContentInfo, 'valueFormat'>, value: any): any {
if (isDefined(value)) {
const entityField = entityFields[key.name];
if (entityField) {
@ -768,7 +767,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss');
}
}
return contentInfo.valueFormat.format(value);
return contentInfo.valueFormat?.format(value) ?? value;
} else {
return '';
}

3
ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.html

@ -27,9 +27,6 @@
<div #knobTopPointer class="top-pointer"></div>
</div>
<div class="top"></div>
<div #knobErrorContainer class="error-container flex flex-row items-center justify-center" [style.background]="error?.length ? 'rgba(255,255,255,0.25)' : 'none'">
<span #knobError class="knob-error" [innerHTML]="error"></span>
</div>
<div #knobTitleContainer class="title-container flex flex-row items-center justify-center">
<span #knobTitle class="knob-title">{{ title }}</span>
</div>

169
ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts

@ -14,37 +14,27 @@
/// limitations under the License.
///
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { ChangeDetectorRef, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { WidgetContext } from '@home/models/widget-component.models';
import { UtilsService } from '@core/services/utils.service';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { isDefined, isNumber } from '@core/utils';
import { deepClone, isDefined } from '@core/utils';
import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge';
import tinycolor from 'tinycolor2';
import { ColorProcessor, gradientColor } from '@shared/models/widget-settings.models';
import { getSourceTbUnitSymbol } from '@shared/models/unit.models';
import { ColorProcessor, gradientColor, ValueFormatProcessor } from '@shared/models/widget-settings.models';
import {
KnobSettings,
knobWidgetDefaultSettings,
prepareKnobSettings
} from '@shared/models/widget/rpc/knob.component.models';
import { ValueType } from '@shared/models/constants';
import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models';
import GenericOptions = CanvasGauges.GenericOptions;
interface KnobSettings {
minValue: number;
maxValue: number;
initialValue: number;
title: string;
getValueMethod: string;
setValueMethod: string;
requestTimeout: number;
requestPersistent: boolean;
persistentPollingInterval: number;
}
@Component({
selector: 'tb-knob',
templateUrl: './knob.component.html',
styleUrls: ['./knob.component.scss']
})
export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
export class KnobComponent extends BasicActionWidgetComponent implements OnInit, OnDestroy {
@ViewChild('knob', {static: true}) knobRef: ElementRef<HTMLElement>;
@ViewChild('knobContainer', {static: true}) knobContainerRef: ElementRef<HTMLElement>;
@ -52,8 +42,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
@ViewChild('knobTopPointer', {static: true}) knobTopPointerRef: ElementRef<HTMLElement>;
@ViewChild('knobValueContainer', {static: true}) knobValueContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobValue', {static: true}) knobValueRef: ElementRef<HTMLElement>;
@ViewChild('knobErrorContainer', {static: true}) knobErrorContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobError', {static: true}) knobErrorRef: ElementRef<HTMLElement>;
@ViewChild('knobTitleContainer', {static: true}) knobTitleContainerRef: ElementRef<HTMLElement>;
@ViewChild('knobTitle', {static: true}) knobTitleRef: ElementRef<HTMLElement>;
@ViewChild('knobMinmaxContainer', {static: true}) knobMinmaxContainerRef: ElementRef<HTMLElement>;
@ -64,7 +52,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
ctx: WidgetContext;
value = '0';
error = '';
title = '';
minValue: number;
maxValue: number;
@ -79,13 +66,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private minDeg = -45;
private maxDeg = 225;
private isSimulated: boolean;
private requestTimeout: number;
private requestPersistent: boolean;
private persistentPollingInterval: number;
private getValueMethod: string;
private setValueMethod: string;
private executingUpdateValue: boolean;
private scheduledValue: number;
private rpcValue: number;
@ -98,8 +78,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private knobValue: JQuery<HTMLElement>;
private knobTitleContainer: JQuery<HTMLElement>;
private knobTitle: JQuery<HTMLElement>;
private knobErrorContainer: JQuery<HTMLElement>;
private knobError: JQuery<HTMLElement>;
private knobMinmaxContainer: JQuery<HTMLElement>;
private minmaxLabel: JQuery<HTMLElement>;
private textMeasure: JQuery<HTMLElement>;
@ -109,9 +87,11 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
private knobResize$: ResizeObserver;
constructor(private utils: UtilsService,
protected store: Store<AppState>) {
super(store);
private valueSetter: ValueSetter<number>;
private valueFormat: ValueFormatProcessor;
constructor(protected cd: ChangeDetectorRef) {
super(cd);
}
ngOnInit(): void {
@ -123,8 +103,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.knobValue = $(this.knobValueRef.nativeElement);
this.knobTitleContainer = $(this.knobTitleContainerRef.nativeElement);
this.knobTitle = $(this.knobTitleRef.nativeElement);
this.knobErrorContainer = $(this.knobErrorContainerRef.nativeElement);
this.knobError = $(this.knobErrorRef.nativeElement);
this.knobMinmaxContainer = $(this.knobMinmaxContainerRef.nativeElement);
this.minmaxLabel = this.knobMinmaxContainer.find<HTMLElement>('.minmax-label');
this.textMeasure = $(this.textMeasureRef.nativeElement);
@ -145,7 +123,31 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
}
private init() {
const settings: KnobSettings = this.ctx.settings;
const settings: KnobSettings = prepareKnobSettings({...deepClone(knobWidgetDefaultSettings), ...this.ctx.settings});
let initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue;
const getInitialStateSettings =
{...settings.initialState, actionLabel: this.ctx.translate.instant('widgets.slider.initial-value')};
this.createValueGetter(getInitialStateSettings, ValueType.DOUBLE, {
next: (value) => {
if (this.canvasBar) {
this.setValue(value);
} else {
initialValue = value;
}
}
});
const valueChangeSettings = {...settings.valueChange,
actionLabel: this.ctx.translate.instant('widgets.slider.on-value-change')};
this.valueSetter = this.createValueSetter(valueChangeSettings);
this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {
units: this.ctx.units,
decimals: this.ctx.decimals,
showZeroDecimals: true
});
this.minValue = isDefined(settings.minValue) ? settings.minValue : 0;
this.maxValue = isDefined(settings.maxValue) ? settings.maxValue : 100;
@ -279,44 +281,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
});
const subscription = this.ctx.defaultSubscription;
const rpcEnabled = subscription.rpcEnabled;
this.isSimulated = this.utils.widgetEditMode;
this.requestTimeout = 500;
if (settings.requestTimeout) {
this.requestTimeout = settings.requestTimeout;
}
this.requestPersistent = false;
if (settings.requestPersistent) {
this.requestPersistent = settings.requestPersistent;
}
this.persistentPollingInterval = 5000;
if (settings.persistentPollingInterval) {
this.persistentPollingInterval = settings.persistentPollingInterval;
}
this.getValueMethod = 'getValue';
if (settings.getValueMethod && settings.getValueMethod.length) {
this.getValueMethod = settings.getValueMethod;
}
this.setValueMethod = 'setValue';
if (settings.setValueMethod && settings.setValueMethod.length) {
this.setValueMethod = settings.setValueMethod;
}
import('@home/components/widget/lib/canvas-digital-gauge').then(
(gauge) => {
this.canvasBar = new gauge.CanvasDigitalGauge(canvasBarData).draw();
const initialValue = isDefined(settings.initialValue) ? settings.initialValue : this.minValue;
this.setValue(initialValue);
if (!rpcEnabled) {
this.onError('Target device is not set!');
} else {
if (!this.isSimulated) {
this.rpcRequestValue();
}
}
}
);
@ -348,7 +316,6 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.canvasBar.update({width: size, height: size} as GenericOptions);
}
this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width());
this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width());
const minmaxHeight = this.knobMinmaxContainer.height();
this.minmaxLabel.css({fontSize: minmaxHeight + 'px', lineHeight: minmaxHeight + 'px'});
this.checkValueSize();
@ -408,27 +375,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
}
private formatValue(value: any): string {
return this.ctx.utils.formatValue(value, this.ctx.decimals, getSourceTbUnitSymbol(this.ctx.units), true);
}
private rpcRequestValue() {
this.error = '';
this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout,
this.requestPersistent, this.persistentPollingInterval).subscribe(
(responseBody) => {
if (isNumber(responseBody)) {
const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals));
this.setValue(numValue);
} else {
const errorText = `Unable to parse response: ${responseBody}`;
this.onError(errorText);
}
},
() => {
const errorText = this.ctx.defaultSubscription.rpcErrorText;
this.onError(errorText);
}
);
return this.valueFormat.format(value);
}
private rpcUpdateValue(value: number) {
@ -440,27 +387,19 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
this.rpcValue = value;
this.executingUpdateValue = true;
}
this.error = '';
this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, value, this.requestTimeout,
this.requestPersistent, this.persistentPollingInterval).subscribe(
() => {
this.executingUpdateValue = false;
if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
this.rpcUpdateValue(this.scheduledValue);
if (!this.ctx.isEdit && !this.ctx.isPreview) {
this.updateValue(this.valueSetter, value, {
next: () => {
this.executingUpdateValue = false;
if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
this.rpcUpdateValue(this.scheduledValue);
}
},
error: () => {
this.executingUpdateValue = false;
}
},
() => {
this.executingUpdateValue = false;
const errorText = this.ctx.defaultSubscription.rpcErrorText;
this.onError(errorText);
}
);
}
private onError(error: string) {
this.error = error;
this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width());
this.ctx.detectChanges();
});
}
}
}

11
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/latest-chart-widget-settings.component.html

@ -437,6 +437,17 @@
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.radar-chart.radar-axis</div>
<div class="tb-form-row column">
<div class="tb-form-row no-border no-padding space-between">
<div tb-hint-tooltip-icon="{{ 'widgets.radar-chart.max-axes-scaling-hint' | translate }}">
{{ 'widgets.radar-chart.max-axes-scaling' | translate }}
</div>
<tb-toggle-select formControlName="normalizeAxes">
<tb-toggle-option [value]="false">{{ 'widgets.radar-chart.separate' | translate }}</tb-toggle-option>
<tb-toggle-option [value]="true">{{ 'widgets.radar-chart.common' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
</div>
<div class="tb-form-row space-between">
<mat-slide-toggle class="mat-slide" formControlName="axisShowLabel">
{{ 'widgets.radar-chart.axis-label' | translate }}

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/radar-chart-widget-settings.component.ts

@ -65,6 +65,7 @@ export class RadarChartWidgetSettingsComponent extends LatestChartWidgetSettings
latestChartWidgetSettingsForm.addControl('labelColor', this.fb.control(settings.labelColor, []));
latestChartWidgetSettingsForm.addControl('fillAreaSettings', this.fb.control(settings.fillAreaSettings, []));
latestChartWidgetSettingsForm.addControl('normalizeAxes', this.fb.control(settings.normalizeAxes, []));
latestChartWidgetSettingsForm.addControl('axisShowLabel', this.fb.control(settings.axisShowLabel, []));
latestChartWidgetSettingsForm.addControl('axisLabelFont', this.fb.control(settings.axisLabelFont, []));
latestChartWidgetSettingsForm.addControl('axisShowTickLabels', this.fb.control(settings.axisShowTickLabels, []));

9
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html

@ -63,6 +63,7 @@
<tb-time-series-chart-y-axes-panel
formControlName="yAxes"
(axisRemoved)="yAxisRemoved($event)"
[supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion"
advanced>
</tb-time-series-chart-y-axes-panel>
<tb-time-series-chart-thresholds-panel
@ -71,7 +72,8 @@
[dataKeyCallbacks]="dataKeyCallbacks"
[datasource]="datasource"
[widgetConfig]="widgetConfig?.config"
[yAxisIds]="yAxisIds">
[yAxisIds]="yAxisIds"
[supportsUnitConversion]="widgetConfig.typeParameters.supportsUnitConversion">
</tb-time-series-chart-thresholds-panel>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.time-series-chart.chart-style</div>
@ -235,6 +237,11 @@
</div>
</mat-slide-toggle>
</div>
<div class="tb-form-row">
<mat-slide-toggle class="mat-slide" formControlName="tooltipHideZeroFalse">
{{ 'tooltip.hide-zero-false-tooltip-values' | translate }}
</mat-slide-toggle>
</div>
<div class="tb-form-row space-between">
<div>{{ 'tooltip.background-color' | translate }}</div>
<tb-color-input asBoxInput

3
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts

@ -163,6 +163,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon
tooltipDateFont: [settings.tooltipDateFont, []],
tooltipDateColor: [settings.tooltipDateColor, []],
tooltipDateInterval: [settings.tooltipDateInterval, []],
tooltipHideZeroFalse: [settings.tooltipHideZeroFalse ,[]],
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []],
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []],
@ -223,6 +224,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon
this.timeSeriesChartWidgetSettingsForm.get('tooltipValueColor').enable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipValueFormatter').enable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').enable({emitEvent: false});
this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroFalse').enable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').enable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable();
if (tooltipShowDate) {
@ -248,6 +250,7 @@ export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsCompon
this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFont').disable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipDateColor').disable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipDateInterval').disable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipHideZeroFalse').disable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').disable();
this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable();
}

3
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html

@ -89,7 +89,8 @@
</tb-color-input>
</div>
<div class="tb-units-field">
<tb-unit-input formControlName="units" supportsUnitConversion>
<tb-unit-input formControlName="units"
[supportsUnitConversion]="supportsUnitConversion">
</tb-unit-input>
</div>
<div class="tb-decimals-field">

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts

@ -106,6 +106,10 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso
@coerceBoolean()
hideYAxis = false;
@Input()
@coerceBoolean()
supportsUnitConversion = false;
@Output()
thresholdRemoved = new EventEmitter();

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html

@ -33,6 +33,7 @@
[formControl]="thresholdControl"
[hideYAxis]="hideYAxis"
[yAxisIds]="yAxisIds"
[supportsUnitConversion]="supportsUnitConversion"
(thresholdRemoved)="removeThreshold($index)">
</tb-time-series-chart-threshold-row>
<mat-divider *ngIf="!$last"></mat-divider>

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts

@ -84,6 +84,10 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce
@coerceBoolean()
hideYAxis = false;
@Input()
@coerceBoolean()
supportsUnitConversion = true;
thresholdsFormGroup: UntypedFormGroup;
private propagateChange = (_val: any) => {};

1
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html

@ -39,6 +39,7 @@
<tb-time-series-chart-y-axis-row class="flex-1"
[formControl]="axisControl"
[advanced]="advanced"
[supportsUnitConversion]="supportsUnitConversion"
(axisRemoved)="removeAxis($index)">
</tb-time-series-chart-y-axis-row>
<div class="tb-form-table-row-cell-buttons">

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts

@ -75,6 +75,10 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
@coerceBoolean()
advanced = false;
@Input()
@coerceBoolean()
supportsUnitConversion = false;
@Output()
axisRemoved = new EventEmitter<TimeSeriesChartYAxisId>();

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html

@ -41,7 +41,7 @@
</mat-form-field>
</div>
<div class="tb-units-field">
<tb-unit-input formControlName="units" supportsUnitConversion>
<tb-unit-input formControlName="units" [supportsUnitConversion]="supportsUnitConversion">
</tb-unit-input>
</div>
<div class="tb-decimals-field">

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts

@ -69,6 +69,10 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O
@coerceBoolean()
advanced = false;
@Input()
@coerceBoolean()
supportsUnitConversion = false;
@Output()
axisRemoved = new EventEmitter();

122
ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.html

@ -15,66 +15,66 @@
limitations under the License.
-->
<section class="tb-widget-settings flex flex-col" [formGroup]="knobControlWidgetSettingsForm">
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.rpc.common-settings</legend>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.knob-title</mat-label>
<input matInput formControlName="title">
</mat-form-field>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.rpc.value-settings</legend>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.initial-value</mat-label>
<input matInput type="number" formControlName="initialValue">
</mat-form-field>
<section class="flex flex-row xs:flex-col gt-xs:gap-2">
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.min-value</mat-label>
<input required matInput type="number" formControlName="minValue">
<ng-container [formGroup]="knobControlWidgetSettingsForm">
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widgets.knob.behavior</div>
<div class="tb-form-row">
<div class="fixed-title-width" tb-hint-tooltip-icon="{{'widgets.knob.initial-value-hint' | translate}}" translate>widgets.knob.initial-value</div>
<tb-get-value-action-settings class="flex-1"
panelTitle="{{ 'widgets.knob.initial-value' | translate }}"
[valueType]="valueType.DOUBLE"
[aliasController]="aliasController"
[targetDevice]="targetDevice"
[widgetType]="widgetType"
formControlName="initialState"></tb-get-value-action-settings>
</div>
<div class="tb-form-row space-between">
<div class="fixed-title-width" tb-hint-tooltip-icon="{{'widgets.knob.on-value-change-hint' | translate}}" translate>widgets.knob.on-value-change</div>
<tb-set-value-action-settings class="flex-1"
panelTitle="{{ 'widgets.knob.on-value-change' | translate }}"
[valueType]="valueType.DOUBLE"
[aliasController]="aliasController"
[targetDevice]="targetDevice"
[widgetType]="widgetType"
formControlName="valueChange"></tb-set-value-action-settings>
</div>
</div>
<div class="tb-form-panel">
<div class="tb-form-panel-title" translate>widget-config.appearance</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width" translate>widgets.rpc.knob-title</div>
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.max-value</mat-label>
<input required matInput type="number" formControlName="maxValue">
</div>
<div class="tb-form-row space-between column-xs">
<div>{{ 'widgets.knob.range' | translate }}</div>
<div class="flex flex-row items-center justify-start gap-2">
<div class="tb-small-label" translate>widgets.knob.min</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="minValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
<div class="tb-small-label" translate>widgets.knob.max</div>
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic">
<input matInput formControlName="maxValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</div>
</div>
<div class="tb-form-row column-xs">
<div class="fixed-title-width">{{ 'widgets.knob.value' | translate }}</div>
<div class="flex flex-1 flex-row items-center justify-start gap-2">
<tb-unit-input class="flex" formControlName="valueUnits" supportsUnitConversion></tb-unit-input>
<mat-form-field appearance="outline" class="number flex" subscriptSizing="dynamic">
<input matInput formControlName="valueDecimals" type="number" min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}">
<div matSuffix class="lt-md:!hidden" translate>widget-config.decimals-suffix</div>
</mat-form-field>
</div>
</div>
<div class="tb-form-row space-between column-xs">
<div class="fixed-title-width" translate>widgets.knob.fallback-initial-value</div>
<mat-form-field class="number" appearance="outline" subscriptSizing="dynamic">
<input matInput formControlName="initialValue" type="number" placeholder="{{ 'widget-config.set' | translate }}">
</mat-form-field>
</section>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.get-value-method</mat-label>
<input required matInput formControlName="getValueMethod">
</mat-form-field>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.set-value-method</mat-label>
<input required matInput formControlName="setValueMethod">
</mat-form-field>
</fieldset>
<fieldset class="fields-group">
<legend class="group-title" translate>widgets.rpc.rpc-settings</legend>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.request-timeout</mat-label>
<input required matInput type="number" min="0" formControlName="requestTimeout">
</mat-form-field>
<fieldset class="fields-group fields-group-slider">
<legend class="group-title" translate>widgets.rpc.persistent-rpc-settings</legend>
<mat-expansion-panel class="tb-settings" [expanded]="knobControlWidgetSettingsForm.get('requestPersistent').value">
<mat-expansion-panel-header class="flex flex-row flex-wrap">
<mat-panel-title>
<mat-slide-toggle formControlName="requestPersistent" (click)="$event.stopPropagation()"
class="flex items-stretch justify-center">
{{ 'widgets.rpc.request-persistent' | translate }}
</mat-slide-toggle>
</mat-panel-title>
<mat-panel-description class="flex items-center justify-end xs:!hidden" translate>
widget-config.advanced-settings
</mat-panel-description>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<mat-form-field class="mat-block flex-1">
<mat-label translate>widgets.rpc.persistent-polling-interval</mat-label>
<input matInput type="number" min="1000" formControlName="persistentPollingInterval">
</mat-form-field>
</ng-template>
</mat-expansion-panel>
</fieldset>
</fieldset>
</section>
</div>
</div>
</ng-container>

76
ui-ngx/src/app/modules/home/components/widget/lib/settings/control/knob-control-widget-settings.component.ts

@ -15,10 +15,13 @@
///
import { Component } from '@angular/core';
import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { knobWidgetDefaultSettings, prepareKnobSettings } from '@shared/models/widget/rpc/knob.component.models';
import { ValueType } from '@shared/models/constants';
import { deepClone } from '@core/utils';
@Component({
selector: 'tb-knob-control-widget-settings',
@ -27,6 +30,16 @@ import { AppState } from '@core/core.state';
})
export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent {
get targetDevice(): TargetDevice {
return this.widgetConfig?.config?.targetDevice;
}
get widgetType(): widgetType {
return this.widgetConfig?.widgetType;
}
valueType = ValueType;
knobControlWidgetSettingsForm: UntypedFormGroup;
constructor(protected store: Store<AppState>,
@ -39,17 +52,25 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent
}
protected defaultSettings(): WidgetSettings {
return {
title: '',
minValue: 0,
maxValue: 100,
initialValue: 50,
getValueMethod: 'getValue',
setValueMethod: 'setValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
};
return knobWidgetDefaultSettings;
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
const knobSettings = prepareKnobSettings(deepClone(settings) as any) as WidgetSettings;
knobSettings.valueDecimals = this.widgetConfig?.config?.decimals ?? 2;
knobSettings.valueUnits = deepClone(this.widgetConfig?.config?.units);
return super.prepareInputSettings(knobSettings);
}
protected prepareOutputSettings(settings: any): WidgetSettings {
const newSettings = deepClone(settings);
if (this.widgetConfig?.config) {
this.widgetConfig.config.units = settings.valueUnits;
this.widgetConfig.config.decimals = settings.valueDecimals;
}
delete newSettings.valueUnits;
delete newSettings.valueDecimals;
return super.prepareOutputSettings(newSettings);
}
protected onSettingsSet(settings: WidgetSettings) {
@ -61,35 +82,16 @@ export class KnobControlWidgetSettingsComponent extends WidgetSettingsComponent
// Value settings
initialValue: [settings.initialValue, []],
initialState: [settings.initialState, []],
valueChange: [settings.valueChange, []],
minValue: [settings.minValue, [Validators.required]],
maxValue: [settings.maxValue, [Validators.required]],
getValueMethod: [settings.getValueMethod, [Validators.required]],
setValueMethod: [settings.setValueMethod, [Validators.required]],
// RPC settings
requestTimeout: [settings.requestTimeout, [Validators.min(0), Validators.required]],
// --> Persistent RPC settings
valueUnits: [settings.valueUnits, []],
valueDecimals: [settings.valueDecimals, []],
initialValue: [settings.initialValue, []],
requestPersistent: [settings.requestPersistent, []],
persistentPollingInterval: [settings.persistentPollingInterval, [Validators.min(1000)]]
});
}
protected validatorTriggers(): string[] {
return ['requestPersistent'];
}
protected updateValidators(emitEvent: boolean): void {
const requestPersistent: boolean = this.knobControlWidgetSettingsForm.get('requestPersistent').value;
if (requestPersistent) {
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').enable({emitEvent});
} else {
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').disable({emitEvent});
}
this.knobControlWidgetSettingsForm.get('persistentPollingInterval').updateValueAndValidity({emitEvent: false});
}
}

2
ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts

@ -107,7 +107,7 @@ export interface CellContentFunctionInfo {
export interface CellContentInfo {
contentFunction: Observable<CellContentFunctionInfo>;
valueFormat?: ValueFormatProcessor
valueFormat: ValueFormatProcessor
}
export type CellStyleFunction = (...args: any[]) => any;

41
ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts

@ -36,8 +36,6 @@ import { PageLink } from '@shared/models/page/page-link';
import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { FileSizePipe } from '@shared/pipe/file-size.pipe';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
@Injectable()
export class OtaUpdateTableConfigResolve {
@ -47,7 +45,6 @@ export class OtaUpdateTableConfigResolve {
constructor(private translate: TranslateService,
private datePipe: DatePipe,
private store: Store<AppState>,
private otaPackageService: OtaPackageService,
private router: Router,
private fileSize: FileSizePipe) {
@ -68,7 +65,9 @@ export class OtaUpdateTableConfigResolve {
}),
new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => {
return entity.url ? (entity.url.length > 20 ? `${entity.url.slice(0, 20)}` : entity.url) : '';
}, () => ({}), true, () => ({}), () => undefined, false,
}, () => ({
'text-wrap': 'nowrap'
}), true, () => ({}), () => undefined, false,
{
name: this.translate.instant('ota-update.copy-direct-url'),
icon: 'content_paste',
@ -78,7 +77,7 @@ export class OtaUpdateTableConfigResolve {
color: 'rgba(0,0,0,.87)'
},
isEnabled: (otaPackage) => !!otaPackage.url,
onAction: ($event, entity) => entity.url,
onAction: (_$event, entity) => entity.url,
type: CellActionDescriptorType.COPY_BUTTON
}),
new EntityTableColumn<OtaPackageInfo>('fileName', 'ota-update.file-name', '20%'),
@ -86,20 +85,22 @@ export class OtaUpdateTableConfigResolve {
return entity.dataSize ? this.fileSize.transform(entity.dataSize) : '';
}),
new EntityTableColumn<OtaPackageInfo>('checksum', 'ota-update.checksum', '220px', entity => {
return entity.checksum ? this.checksumText(entity) : '';
}, () => ({}), true, () => ({}), () => undefined, false,
{
name: this.translate.instant('ota-update.copy-checksum'),
icon: 'content_paste',
style: {
padding: '4px',
'font-size': '16px',
color: 'rgba(0,0,0,.87)'
},
isEnabled: (otaPackage) => !!otaPackage.checksum,
onAction: ($event, entity) => entity.checksum,
type: CellActionDescriptorType.COPY_BUTTON
})
return entity.checksum ? this.checksumText(entity) : '';
}, () => ({
'text-wrap': 'nowrap'
}), true, () => ({}), () => undefined, false,
{
name: this.translate.instant('ota-update.copy-checksum'),
icon: 'content_paste',
style: {
padding: '4px',
'font-size': '16px',
color: 'rgba(0,0,0,.87)'
},
isEnabled: (otaPackage) => !!otaPackage.checksum,
onAction: (_$event, entity) => entity.checksum,
type: CellActionDescriptorType.COPY_BUTTON
})
);
this.config.cellActionDescriptors.push(
@ -149,7 +150,7 @@ export class OtaUpdateTableConfigResolve {
}
}
checksumText(entity): string {
checksumText(entity: OtaPackageInfo): string {
let text = `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`;
if (text.length > 20) {
text = `${text.slice(0, 20)}`;

4
ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts

@ -185,9 +185,11 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
}));
}
prepareFormValue(formValue: any): any {
prepareFormValue(formValue: OtaPackage & {generateChecksum?: boolean}): any {
if (formValue.isURL) {
delete formValue.file;
delete formValue.checksumAlgorithm;
delete formValue.checksum;
} else {
delete formValue.url;
}

1
ui-ngx/src/app/shared/components/entity/entity-list-select.component.html

@ -26,6 +26,7 @@
[useAliasEntityTypes]="useAliasEntityTypes"
[allowedEntityTypes]="allowedEntityTypes"
[filterAllowedEntityTypes]="filterAllowedEntityTypes"
[additionEntityTypes]="additionEntityTypes"
formControlName="entityType">
</tb-entity-type-select>
<tb-entity-list

3
ui-ngx/src/app/shared/components/entity/entity-list-select.component.ts

@ -65,6 +65,9 @@ export class EntityListSelectComponent implements ControlValueAccessor, OnInit {
@Input()
predefinedEntityType: EntityType | AliasEntityType;
@Input()
additionEntityTypes: {[key in string]: string} = {};
displayEntityTypeSelect: boolean;
private defaultEntityType: EntityType | AliasEntityType = null;

8
ui-ngx/src/app/shared/components/unit-settings-panel.component.ts

@ -139,7 +139,13 @@ export class UnitSettingsPanelComponent implements OnInit {
}
clearUnit() {
this.unitSettingsApplied.emit(null);
this.unitSettingForm.reset({
convertUnit: false
});
if (this.required) {
this.unitSettingForm.markAllAsTouched();
}
this.unitSettingForm.markAsDirty();
}
cancel() {

4
ui-ngx/src/app/shared/models/widget-settings.models.ts

@ -907,8 +907,6 @@ export abstract class ValueFormatProcessor {
export class SimpleValueFormatProcessor extends ValueFormatProcessor {
private readonly isDefinedUnit: boolean;
constructor(protected settings: ValueFormatSettings) {
super(settings);
this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null;
@ -917,7 +915,7 @@ export class SimpleValueFormatProcessor extends ValueFormatProcessor {
}
format(value: any): string {
if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDecimals || this.isDefinedUnit || Number(value).toString() === value)) {
if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDecimals || this.unitSymbol || Number(value).toString() === value)) {
return this.formatValue(Number(value));
}
return value ?? '';

125
ui-ngx/src/app/shared/models/widget/rpc/knob.component.models.ts

@ -0,0 +1,125 @@
///
/// Copyright © 2016-2025 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 {
DataToValueType,
GetValueAction,
GetValueSettings,
SetValueAction,
SetValueSettings,
ValueToDataType
} from '@shared/models/action-widget-settings.models';
import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { isDefinedAndNotNull } from '@core/utils';
export interface KnobSettings {
initialState: GetValueSettings<number>;
valueChange: SetValueSettings;
minValue: number;
maxValue: number;
initialValue: number;
title: string;
getValueMethod?: string; //deprecated
setValueMethod?: string; //deprecated
requestTimeout?: number; //deprecated
requestPersistent?: boolean; //deprecated
persistentPollingInterval?: number; //deprecated
}
export const knobWidgetDefaultSettings: KnobSettings = {
initialState: {
action: GetValueAction.EXECUTE_RPC,
defaultValue: 50,
executeRpc: {
method: 'getValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
},
getAttribute: {
key: 'value',
scope: null
},
getTimeSeries: {
key: 'value'
},
getAlarmStatus: {
severityList: null,
typeList: null
},
dataToValue: {
type: DataToValueType.NONE,
compareToValue: true,
dataToValueFunction: '/* Should return double value */\nreturn data;'
}
},
valueChange: {
action: SetValueAction.EXECUTE_RPC,
executeRpc: {
method: 'setValue',
requestTimeout: 500,
requestPersistent: false,
persistentPollingInterval: 5000
},
setAttribute: {
key: 'value',
scope: AttributeScope.SERVER_SCOPE
},
putTimeSeries: {
key: 'value'
},
valueToData: {
type: ValueToDataType.VALUE,
constantValue: 0,
valueToDataFunction: '/* Convert input double value to RPC parameters or attribute/time-series value */\nreturn value;'
}
},
title: '',
minValue: 0,
maxValue: 100,
initialValue: 50
}
export const prepareKnobSettings = (settings: KnobSettings): KnobSettings => {
if (isDefinedAndNotNull(settings.getValueMethod)) {
settings.initialState.executeRpc.method = settings.getValueMethod;
delete settings.getValueMethod;
}
if (isDefinedAndNotNull(settings.setValueMethod)) {
settings.valueChange.executeRpc.method = settings.setValueMethod;
delete settings.setValueMethod;
}
if (isDefinedAndNotNull(settings.requestPersistent)) {
settings.initialState.executeRpc.requestPersistent = settings.requestPersistent;
settings.valueChange.executeRpc.requestPersistent = settings.requestPersistent;
delete settings.requestPersistent;
}
if (isDefinedAndNotNull(settings.persistentPollingInterval)) {
settings.initialState.executeRpc.persistentPollingInterval = settings.persistentPollingInterval;
settings.valueChange.executeRpc.persistentPollingInterval = settings.persistentPollingInterval;
delete settings.persistentPollingInterval;
}
if (isDefinedAndNotNull(settings.requestTimeout)) {
settings.initialState.executeRpc.requestTimeout = settings.requestTimeout;
settings.valueChange.executeRpc.requestTimeout = settings.requestTimeout;
delete settings.requestTimeout;
}
return settings;
}

59
ui-ngx/src/assets/locale/locale.constant-da_DK.json

@ -4808,7 +4808,7 @@
"use-simple-client-http-factory": "Brug simpel HTTP-klientfabrik",
"ignore-request-body": "Uden forespørgselsindhold",
"parse-to-plain-text": "Fortolk som almindelig tekst",
"parse-to-plain-text-hint": "Hvis valgt, transformeres JSON-strengen i beskedens indhold til almindelig tekst.",
"parse-to-plain-text-hint": "Hvis valgt, vil meddelelsens anmodningsindhold blive konverteret fra JSON-streng til almindelig tekst, f.eks. vil msg = \"Hello,\\t\"world\"\" blive analyseret til Hello, \"world\"",
"read-timeout": "Læs timeout i millisekunder",
"read-timeout-hint": "Værdien 0 betyder uendelig timeout",
"max-parallel-requests-count": "Maksimalt antal parallelle forespørgsler",
@ -4816,7 +4816,7 @@
"max-response-size": "Maks. svarstørrelse (i KB)",
"max-response-size-hint": "Maksimal hukommelse tildelt til buffering ved afkodning/enkodning af HTTP-meddelelser",
"headers": "Headers",
"headers-hint": "Brug ${metadataKey} for metadata eller $[messageKey] for beskeddata i header/værdi felter",
"headers-hint": "Brug <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> for værdi fra metadata, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> for værdi fra meddelelsesindhold i header/værdifelter",
"header": "Header",
"header-required": "Header er påkrævet",
"value": "Værdi",
@ -4884,7 +4884,7 @@
"pubsub-topic-name": "Emnenavn",
"pubsub-topic-name-required": "Emnenavn er påkrævet",
"message-attributes": "Beskedattributter",
"message-attributes-hint": "Brug ${metadataKey} eller $[messageKey] i navn/værdi felter",
"message-attributes-hint": "Brug <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000000;\">&#125;</span></code> for værdi fra metadata, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> for værdi fra meddelelsesindhold i navn-/værdifelter",
"connect-timeout": "Forbindelsestimeout (sek)",
"connect-timeout-required": "Forbindelsestimeout er påkrævet.",
"connect-timeout-range": "Timeout skal være mellem 1 og 200.",
@ -5073,18 +5073,18 @@
"failure-if-delta-negative": "Rapportér fejl ved negativ delta",
"failure-if-delta-negative-tooltip": "Fejl ved negativ forskel mellem værdier.",
"use-caching": "Brug caching",
"use-caching-tooltip": "Cacher input-værdi for performance.",
"add-time-difference-between-readings": "Tilføj tidsforskel mellem aflæsninger",
"add-time-difference-between-readings-tooltip": "Tilføjer \"{{periodValueKey}}\" til beskeden.",
"period-value-key": "Tidsforskel nøgle",
"period-value-key-required": "Tidsforskel nøgle er påkrævet.",
"general-pattern-hint": "Brug ${metadataKey} eller $[messageKey]",
"alarm-severity-pattern-hint": "Angiv alarmniveau (CRITICAL, MAJOR, etc.) via metadata eller besked.",
"output-node-name-hint": "Navn på regelnode bruges som relationstype til videresendelse.",
"use-server-ts": "Brug server-tidsstempel",
"use-server-ts-hint": "Sikrer korrekt rækkefølge ved manglende tidsstempel.",
"kv-map-pattern-hint": "Alle felter understøtter templatization.",
"kv-map-single-pattern-hint": "Felt understøtter templatization.",
"use-caching-tooltip": "Regelknuden vil cache værdien af \"{{inputValueKey}}\" fra den indkommende besked for at forbedre ydeevnen. Bemærk, at cachen ikke opdateres, hvis \"{{inputValueKey}}\" ændres et andet sted.",
"add-time-difference-between-readings": "Tilføj tidsforskel mellem aflæsninger af \"{{inputValueKey}}\"",
"add-time-difference-between-readings-tooltip": "Hvis aktiveret, tilføjer regelknuden \"{{periodValueKey}}\" til den udgående besked.",
"period-value-key": "Tidsintervalnøgle",
"period-value-key-required": "Tidsintervalnøgle er påkrævet.",
"general-pattern-hint": "Brug ${metadataKey} for værdi fra metadata, $[messageKey] for værdi fra meddelelsesindhold.",
"alarm-severity-pattern-hint": "Brug <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> for værdi fra metadata, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> for værdi fra meddelelsesindhold. Alarmens alvorlighed skal være et systemniveau (CRITICAL, MAJOR osv.)",
"output-node-name-hint": "<b>Regelknudens navn</b> svarer til <b>relationstypen</b> for den udgående besked og bruges til at videresende beskeder til andre regelknuder i den kaldende regelkæde.",
"use-server-ts": "Brug serverens tidsstempel",
"use-server-ts-hint": "Brug serverens aktuelle tidsstempel for tidsseriedata, der mangler et eksplicit tidsstempel. Dette hjælper med korrekt rækkefølge, når der behandles beskeder fra flere kilder eller ved uordnet ankomst.",
"kv-map-pattern-hint": "Alle inputfelter understøtter skabelonisering. Brug $[messageKey] for værdi fra beskeden og ${metadataKey} for værdi fra metadata.",
"kv-map-single-pattern-hint": "Inputfelt understøtter skabelonisering. Brug $[messageKey] for værdi fra beskeden og ${metadataKey} for værdi fra metadata.",
"shared-scope": "Delt område",
"server-scope": "Serverområde",
"client-scope": "Klientområde",
@ -9191,33 +9191,6 @@
"items-per-page-separator": "af"
},
"language": {
"language": "Language",
"locales": {
"ar_AE": "العربية (الإمارات العربية المتحدة)",
"ca_ES": "català (Espanya)",
"cs_CZ": "čeština (Česko)",
"da_DK": "dansk (Danmark)",
"de_DE": "Deutsch (Deutschland)",
"el_GR": "Ελληνικά (Ελλάδα)",
"en_US": "English (United States)",
"es_ES": "español (España)",
"fa_IR": "فارسی (ایران)",
"fr_FR": "français (France)",
"it_IT": "italiano (Italia)",
"ja_JP": "日本語 (日本)",
"ka_GE": "ქართული (საქართველო)",
"ko_KR": "한국어 (대한민국)",
"lt_LT": "lietuvių (Lietuva)",
"lv_LV": "latviešu (Latvija)",
"nl_BE": "Nederlands (België)",
"pl_PL": "polski (Polska)",
"pt_BR": "português (Brasil)",
"ro_RO": "română (România)",
"sl_SI": "slovenščina (Slovenija)",
"tr_TR": "Türkçe (Türkiye)",
"uk_UA": "українська (Україна)",
"zh_CN": "中文 (中国)",
"zh_TW": "中文 (台灣)"
}
"language": "Language"
}
}

37
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -4814,7 +4814,7 @@
"max-parallel-requests-count": "Max. Anzahl paralleler Anfragen",
"max-parallel-requests-count-hint": "Wert 0 bedeutet unbegrenzte Parallelität",
"max-response-size": "Maximale Antwortgröße (in KB)",
"max-response-size-hint": "Maximaler Speicher für das Puffern von Daten beim Kodieren/Decodieren von HTTP-Nachrichten (z. B. JSON, XML)",
"max-response-size-hint": "Die maximale Menge an Speicher, die zum Puffern von Daten beim Dekodieren oder Kodieren von HTTP-Nachrichten wie JSON- oder XML-Nutzdaten reserviert wird",
"headers": "Header",
"headers-hint": "Verwenden Sie <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> für Werte aus den Metadaten und <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> für Werte aus dem Nachrichtentext in Header-/Wert-Feldern.",
"header": "Header",
@ -4964,7 +4964,7 @@
"proxy-scheme": "Proxy-Schema",
"numbers-to-template": "Telefonnummern zum Template",
"numbers-to-template-required": "Telefonnummern zum Template sind erforderlich",
"numbers-to-template-hint": "Kommagetrennte Telefonnummern. Verwenden Sie <code><span style=\"color: #000;\">${</span>metadataKey<span style=\"color: #000;\">}</span></code> für Werte aus Metadaten und <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> für Werte aus dem Nachrichtentext",
"numbers-to-template-hint": "Kommagetrennte Telefonnummern. Verwenden Sie <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> für Werte aus Metadaten und <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> für Werte aus dem Nachrichtentext",
"sms-message-template": "SMS-Nachrichtenvorlage",
"sms-message-template-required": "SMS-Nachrichtenvorlage ist erforderlich",
"use-system-sms-settings": "Systemweite SMS-Anbietereinstellungen verwenden",
@ -5045,7 +5045,7 @@
"tell-failure-if-absent": "Fehler melden",
"tell-failure-if-absent-hint": "Wenn mindestens ein ausgewählter Schlüssel nicht vorhanden ist, wird die ausgehende Nachricht mit \"Fehler\" gekennzeichnet.",
"get-latest-value-with-ts": "Zeitstempel für letzte Telemetriewerte abrufen",
"get-latest-value-with-ts-hint": "Wenn ausgewählt, enthalten die letzten Telemetriewerte auch einen Zeitstempel, z. B.: \"temp\": \"{\"ts\":1574329385897, \"value\":42}\"",
"get-latest-value-with-ts-hint": "Wenn ausgewählt, enthalten die neuesten Telemetriedaten auch einen Zeitstempel, z.B.: \"temp\": \"&#123;\"ts\":1574329385897, \"value\":42&#125;\"",
"ignore-null-strings": "Leere Zeichenfolgen ignorieren",
"ignore-null-strings-hint": "Wenn ausgewählt, ignoriert der Regelknoten Entitätsfelder mit leerem Wert.",
"add-metadata-key-values-as-kafka-headers": "Metadaten-Schlüssel-Werte-Paare als Kafka-Header hinzufügen",
@ -5079,7 +5079,7 @@
"period-value-key": "Zeitraum-Wert-Schlüssel",
"period-value-key-required": "Zeitraum-Wert-Schlüssel ist erforderlich.",
"general-pattern-hint": "Verwenden Sie ${metadataKey} für Metadatenwert, $[messageKey] für Nachrichtenwert.",
"alarm-severity-pattern-hint": "Verwenden Sie <code><span style=\"color: #000;\">${</span>metadataKey<span style=\"color: #000;\">}</span></code> für Metadatenwert, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)",
"alarm-severity-pattern-hint": "Verwenden Sie <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> für Metadatenwert, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> für Nachrichtenwert. Alarm-Schweregrade sollten systemdefiniert sein (CRITICAL, MAJOR usw.)",
"output-node-name-hint": "Der <b>Name des Regelknotens</b> entspricht dem <b>Beziehungstyp</b> der Ausgabemeldung und wird verwendet, um Nachrichten an andere Knoten in der Regelkette weiterzuleiten.",
"use-server-ts": "Server-Zeitstempel verwenden",
"use-server-ts-hint": "Verwenden Sie den aktuellen Zeitstempel des Servers für Zeitreihen ohne expliziten Zeitstempel. Dies hilft, die Reihenfolge bei mehreren Quellen oder verspäteten Nachrichten zu wahren.",
@ -9191,33 +9191,6 @@
"items-per-page-separator": "von"
},
"language": {
"language": "Sprache",
"locales": {
"ar_AE": "العربية (الإمارات العربية المتحدة)",
"ca_ES": "katalanisch (Spanien)",
"cs_CZ": "tschechisch (Tschechien)",
"da_DK": "dänisch (Dänemark)",
"de_DE": "Deutsch (Deutschland)",
"el_GR": "griechisch (Griechenland)",
"en_US": "Englisch (USA)",
"es_ES": "spanisch (Spanien)",
"fa_IR": "persisch (Iran)",
"fr_FR": "französisch (Frankreich)",
"it_IT": "italienisch (Italien)",
"ja_JP": "japanisch (Japan)",
"ka_GE": "georgisch (Georgien)",
"ko_KR": "koreanisch (Südkorea)",
"lt_LT": "litauisch (Litauen)",
"lv_LV": "lettisch (Lettland)",
"nl_BE": "niederländisch (Belgien)",
"pl_PL": "polnisch (Polen)",
"pt_BR": "portugiesisch (Brasilien)",
"ro_RO": "rumänisch (Rumänien)",
"sl_SI": "slowenisch (Slowenien)",
"tr_TR": "türkisch (Türkei)",
"uk_UA": "ukrainisch (Ukraine)",
"zh_CN": "chinesisch (China)",
"zh_TW": "chinesisch (Taiwan)"
}
"language": "Sprache"
}
}

10579
ui-ngx/src/assets/locale/locale.constant-el_GR.json

File diff suppressed because it is too large

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

@ -5883,6 +5883,7 @@
"date": "Date",
"show-date-time-interval": "Show date time interval",
"show-date-time-interval-hint": "Show date time interval according to the data aggregation.",
"hide-zero-false-tooltip-values": "Hide zero/false values",
"background-color": "Background color",
"background-blur": "Background blur"
},
@ -5942,7 +5943,7 @@
"energy-density": "Energy density",
"force": "Force",
"frequency": "Frequency",
"fuel-efficiency": "fuel efficiency",
"fuel-efficiency": "Fuel efficiency",
"heat-capacity": "Heat capacity",
"illuminance": "Illuminance",
"inductance": "Inductance",
@ -6198,8 +6199,8 @@
"volt": "Volt",
"kilovolt": "Kilovolt",
"megavolt": "Megavolt",
"dbmV": "dBmV",
"dbm": "dBm",
"dbmV": "Decibel volt",
"dbm": "Decibel-milliwatts",
"volt-meter": "Volt-Meter",
"kilovolt-meter": "Kilovolt-Meter",
"megavolt-meter": "Megavolt-Meter",
@ -6234,7 +6235,7 @@
"millimole": "Millimole",
"kilomole": "Kilomole",
"mole-per-cubic-meter": "Mole per Cubic Meter",
"rssi": "RSSI",
"rssi": "Received signal strength indicator",
"ppm": "Parts Per Million",
"ppb": "Parts Per Billion",
"micrograms-per-cubic-meter": "Micrograms per Cubic Meter",
@ -6416,9 +6417,9 @@
"radian-per-second": "Radian per second",
"radian-per-second-squared": "Radian per second squared",
"revolutions-per-minute-per-second": "Angular acceleration",
"deg-per-second": "deg/s",
"deg-per-second": "Degrees per second",
"rotation-per-minute": "Rotation per minute",
"degrees-brix": "Degrees Brix",
"degrees-brix": "Degrees brix",
"katal": "Katal",
"katal-per-cubic-metre": "Katal per Cubic Metre",
"paris-inch": "Paris inch"
@ -7965,6 +7966,18 @@
"fill-area-opacity": "Fill area opacity",
"range-chart-style": "Range chart style"
},
"knob": {
"behavior": "Behavior",
"initial-value": "Initial value",
"initial-value-hint": "Action to get the initial value of the knob.",
"on-value-change": "On value change",
"on-value-change-hint": "Action triggered when the knob value is changed.",
"range": "Range",
"min": "min",
"max": "max",
"value": "Value",
"fallback-initial-value": "Fallback initial value"
},
"rpc": {
"value-settings": "Value settings",
"initial-value": "Initial value",
@ -8021,9 +8034,7 @@
"led-status-value-timeseries": "Device time series containing led status value",
"check-status-method": "RPC check device status method",
"parse-led-status-value-function": "Parse led status value function",
"knob-title": "Knob title",
"min-value": "Minimum value",
"max-value": "Maximum value"
"knob-title": "Knob title"
},
"maps": {
"map-type": {
@ -8878,7 +8889,11 @@
"radar-axis": "Radar axis",
"axis-label": "Axis label",
"ticks-label": "Ticks label",
"radar-chart-style": "Radar chart style"
"radar-chart-style": "Radar chart style",
"max-axes-scaling": "Max axes scaling",
"max-axes-scaling-hint": "Choose whether each radar axis has its own maximum value (Separate) or shares the highest value across all axes based on the widget dataset (Common).",
"separate": "Separate",
"common": "Common"
},
"time-series-chart": {
"chart": "Chart",
@ -9408,6 +9423,8 @@
"lt_LT": "lietuvių (Lietuva)",
"lv_LV": "latviešu (Latvija)",
"nl_BE": "Nederlands (België)",
"nl_NL": "Nederlands (Nederland)",
"no_NO": "norsk (Norge)",
"pl_PL": "polski (Polska)",
"pt_BR": "português (Brasil)",
"ro_RO": "română (România)",

29
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -9191,33 +9191,6 @@
"items-per-page-separator": "de"
},
"language": {
"language": "Language",
"locales": {
"ar_AE": "العربية (الإمارات العربية المتحدة)",
"ca_ES": "català (Espanya)",
"cs_CZ": "čeština (Česko)",
"da_DK": "dansk (Danmark)",
"de_DE": "Deutsch (Deutschland)",
"el_GR": "Ελληνικά (Ελλάδα)",
"en_US": "English (United States)",
"es_ES": "español (España)",
"fa_IR": "فارسی (ایران)",
"fr_FR": "français (France)",
"it_IT": "italiano (Italia)",
"ja_JP": "日本語 (日本)",
"ka_GE": "ქართული (საქართველო)",
"ko_KR": "한국어 (대한민국)",
"lt_LT": "lietuvių (Lietuva)",
"lv_LV": "latviešu (Latvija)",
"nl_BE": "Nederlands (België)",
"pl_PL": "polski (Polska)",
"pt_BR": "português (Brasil)",
"ro_RO": "română (România)",
"sl_SI": "slovenščina (Slovenija)",
"tr_TR": "Türkçe (Türkiye)",
"uk_UA": "українська (Україна)",
"zh_CN": "中文 (中国)",
"zh_TW": "中文 (台灣)"
}
"language": "Idioma"
}
}

47
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -1979,7 +1979,7 @@
"alarm-rule-additional-info": "Informations supplémentaires",
"edit-alarm-rule-additional-info": "Modifier les informations supplémentaires",
"alarm-rule-additional-info-placeholder": "Ajoutez ici vos commentaires ou ajustements à afficher dans les détails de l'alarme",
"alarm-rule-additional-info-hint": "Astuce : utilisez code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> pour insérer les valeurs des clés utilisées dans la condition.",
"alarm-rule-additional-info-hint": "Astuce : utilisez <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> pour insérer les valeurs des clés utilisées dans la condition.",
"alarm-rule-mobile-dashboard": "Tableau de bord mobile",
"alarm-rule-mobile-dashboard-hint": "Utilisé comme tableau de bord des détails de l'alarme dans l'application mobile",
"alarm-rule-no-mobile-dashboard": "Aucun tableau de bord sélectionné",
@ -4783,7 +4783,7 @@
"from-metadata": "Depuis les métadonnées",
"to-template": "À",
"to-template-required": "Le champ À est requis",
"mail-address-list-template-hint": "Liste d’adresses séparées par des virgules, utilisez <code><span style=\"color: #000;\">${metadataKey}</span></code> pour les métadonnées, <code><span style=\"color: #000;\">$[messageKey]</span></code> pour le corps du message.",
"mail-address-list-template-hint": "Liste d’adresses séparées par des virgules, utilisez <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> pour une valeur depuis les métadonnées, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> pour une valeur depuis le corps du message",
"cc-template": "Cc",
"bcc-template": "Cci",
"subject-template": "Objet",
@ -4816,7 +4816,7 @@
"max-response-size": "Taille maximale de réponse (en Ko)",
"max-response-size-hint": "Quantité maximale de mémoire utilisée pour le tampon lors du décodage/encodage des messages HTTP comme les charges JSON ou XML",
"headers": "En-têtes",
"headers-hint": "Utilisez <code><span style=\"color: #000;\">${metadataKey}</span></code> pour une valeur des métadonnées, <code><span style=\"color: #000;\">$[messageKey]</span></code> pour une valeur du corps du message",
"headers-hint": "Utilisez <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> pour une valeur provenant des métadonnées, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> pour une valeur provenant du corps du message dans les champs en-tête/valeur",
"header": "En-tête",
"header-required": "L'en-tête est requis",
"value": "Valeur",
@ -4884,7 +4884,7 @@
"pubsub-topic-name": "Nom du sujet",
"pubsub-topic-name-required": "Le nom du sujet est requis",
"message-attributes": "Attributs du message",
"message-attributes-hint": "Utilisez <code><span style=\"color: #000;\">${metadataKey}</span></code> pour une valeur depuis les métadonnées, <code><span style=\"color: #000;\">$[messageKey]</span></code> depuis le message",
"message-attributes-hint": "Utilisez <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000000;\">&#125;</span></code> pour une valeur provenant des métadonnées, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> pour une valeur provenant du corps du message dans les champs nom/valeur",
"connect-timeout": "Délai de connexion (s)",
"connect-timeout-required": "Le délai de connexion est requis.",
"connect-timeout-range": "Le délai de connexion doit être compris entre 1 et 200.",
@ -4964,7 +4964,7 @@
"proxy-scheme": "Schéma de proxy",
"numbers-to-template": "Numéros de téléphone vers modèle",
"numbers-to-template-required": "Le modèle de numéros de téléphone est requis",
"numbers-to-template-hint": "Liste de numéros séparés par des virgules. Utilisez ${metadataKey} ou $[messageKey].",
"numbers-to-template-hint": "Numéros de téléphone séparés par des virgules, utilisez <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> pour une valeur provenant des métadonnées, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> pour une valeur provenant du corps du message",
"sms-message-template": "Modèle de message SMS",
"sms-message-template-required": "Le modèle de message SMS est requis",
"use-system-sms-settings": "Utiliser les paramètres SMS système",
@ -5079,7 +5079,7 @@
"period-value-key": "Clé de la période",
"period-value-key-required": "La clé de la période est requise.",
"general-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message.",
"alarm-severity-pattern-hint": "Utilisez ${metadataKey} pour les métadonnées, $[messageKey] pour le corps du message. Les niveaux doivent être CRITICAL, MAJOR, etc.",
"alarm-severity-pattern-hint": "Utilisez <code><span style=\"color: #000;\">$&#123;</span>metadataKey<span style=\"color: #000;\">&#125;</span></code> pour une valeur provenant des métadonnées, <code><span style=\"color: #000;\">$[</span>messageKey<span style=\"color: #000;\">]</span></code> pour une valeur provenant du corps du message. La gravité de l'alarme doit être un niveau système (CRITICAL, MAJOR, etc.)",
"output-node-name-hint": "Le <b>nom du nœud</b> correspond au <b>type de relation</b> du message de sortie.",
"use-server-ts": "Utiliser l’horodatage serveur",
"use-server-ts-hint": "Utilise le temps actuel du serveur pour les séries temporelles sans horodatage explicite.",
@ -6434,8 +6434,8 @@
"tidy": "Nettoyer",
"css": "CSS",
"settings-form": "Formulaire de paramètres",
"data-key-settings-form": "Formulaire de paramètres de clé de données",
"latest-data-key-settings-form": "Formulaire de paramètres des dernières données",
"data-key-settings-form": "Formulaire des paramètres de clé de données",
"latest-data-key-settings-form": "Formulaire des paramètres de la dernière clé de données",
"widget-settings": "Paramètres du widget",
"description": "Description",
"tags": "Étiquettes",
@ -7686,7 +7686,7 @@
},
"label-widget": {
"label-pattern": "Modèle",
"label-pattern-hint": "Astuce : par ex. 'Texte <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> unités.' ou <code><span style=\"color: #000;\">$&#123;</span>#&lt;key index&gt;<span style=\"color: #000;\">&#125;</span></code> unités'",
"label-pattern-hint": "Astuce: par ex. 'Texte <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> unités.' ou <code><span style=\"color: #000;\">$&#123;</span>#&lt;key index&gt;<span style=\"color: #000;\">&#125;</span></code> unités",
"label-pattern-required": "Le modèle est requis",
"label-position": "Position (pourcentage par rapport à l’arrière-plan)",
"x-pos": "X",
@ -9191,33 +9191,6 @@
"items-per-page-separator": "sur"
},
"language": {
"language": "Langue",
"locales": {
"ar_AE": "العربية (الإمارات العربية المتحدة)",
"ca_ES": "català (Espanya)",
"cs_CZ": "čeština (Česko)",
"da_DK": "dansk (Danmark)",
"de_DE": "Deutsch (Deutschland)",
"el_GR": "Ελληνικά (Ελλάδα)",
"en_US": "English (United States)",
"es_ES": "español (España)",
"fa_IR": "فارسی (ایران)",
"fr_FR": "français (France)",
"it_IT": "italiano (Italia)",
"ja_JP": "日本語 (日本)",
"ka_GE": "ქართული (საქართველო)",
"ko_KR": "한국어 (대한민국)",
"lt_LT": "lietuvių (Lietuva)",
"lv_LV": "latviešu (Latvija)",
"nl_BE": "Nederlands (België)",
"pl_PL": "polski (Polska)",
"pt_BR": "português (Brasil)",
"ro_RO": "română (România)",
"sl_SI": "slovenščina (Slovenija)",
"tr_TR": "Türkçe (Türkiye)",
"uk_UA": "українська (Україна)",
"zh_CN": "中文 (中国)",
"zh_TW": "中文 (台灣)"
}
"language": "Langue"
}
}

31
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -7686,7 +7686,7 @@
},
"label-widget": {
"label-pattern": "Modello",
"label-pattern-hint": "Suggerimento: ad esempio, 'Testo <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> unità.' oppure <code><span style=\"color: #000;\">$&#123;</span>#&lt;indice chiave&gt;<span style=\"color: #000;\">&#125;</span></code> unità'",
"label-pattern-hint": "Suggerimento: ad esempio, 'Testo <code><span style=\"color: #000;\">$&#123;</span>keyName<span style=\"color: #000;\">&#125;</span></code> unità.' oppure <code><span style=\"color: #000;\">$&#123;</span>#&lt;key index&gt;<span style=\"color: #000;\">&#125;</span></code> unità'",
"label-pattern-required": "Il pattern è obbligatorio",
"label-position": "Posizione (percentuale rispetto allo sfondo)",
"x-pos": "X",
@ -9191,33 +9191,6 @@
"items-per-page-separator": "di"
},
"language": {
"language": "Lingua",
"locales": {
"ar_AE": "العربية (الإمارات العربية المتحدة)",
"ca_ES": "català (Espanya)",
"cs_CZ": "čeština (Česko)",
"da_DK": "dansk (Danmark)",
"de_DE": "Deutsch (Deutschland)",
"el_GR": "Ελληνικά (Ελλάδα)",
"en_US": "English (United States)",
"es_ES": "español (España)",
"fa_IR": "فارسی (ایران)",
"fr_FR": "français (France)",
"it_IT": "italiano (Italia)",
"ja_JP": "日本語 (日本)",
"ka_GE": "ქართული (საქართველო)",
"ko_KR": "한국어 (대한민국)",
"lt_LT": "lietuvių (Lietuva)",
"lv_LV": "latviešu (Latvija)",
"nl_BE": "Nederlands (België)",
"pl_PL": "polski (Polska)",
"pt_BR": "português (Brasil)",
"ro_RO": "română (România)",
"sl_SI": "slovenščina (Slovenija)",
"tr_TR": "Türkçe (Türkiye)",
"uk_UA": "українська (Україна)",
"zh_CN": "中文 (中国)",
"zh_TW": "中文 (台灣)"
}
"language": "Lingua"
}
}

25
ui-ngx/src/assets/locale/locale.constant-nl_BE.json

@ -7196,29 +7196,6 @@
"items-per-page-separator": "van"
},
"language": {
"language": "Taal",
"locales": {
"ca_ES": "Catalaans",
"cs_CZ": "Česky",
"da_DK": "Dansk",
"de_DE": "Deutsch",
"el_GR": "Ελληνικά",
"en_US": "Engels",
"es_ES": "Español",
"fa_IR": "فارسي",
"fr_FR": "Français",
"it_IT": "Italiano",
"ja_JP": "日本語",
"ka_GE": "ქართული",
"ko_KR": "한국어",
"lv_LV": "Kanton Latviešu",
"pt_BR": "Português do Brasil",
"ro_RO": "Română",
"sl_SI": "Slovenščina",
"tr_TR": "Türkçe",
"uk_UA": "Українська",
"zh_CN": "简体中文",
"zh_TW": "繁體中文"
}
"language": "Taal"
}
}

9197
ui-ngx/src/assets/locale/locale.constant-nl_NL.json

File diff suppressed because it is too large

9197
ui-ngx/src/assets/locale/locale.constant-no_NO.json

File diff suppressed because it is too large
Loading…
Cancel
Save