This is an automated email from the ASF dual-hosted git repository.

dominikriemer pushed a commit to branch add-visualization-options
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit 709feb459c8415344ecf99913a8ad1b6105f941c
Author: Dominik Riemer <[email protected]>
AuthorDate: Sun Jun 14 19:09:23 2026 +0200

    Add central display name configuration
---
 .../model/datalake/data-lake-query-config.model.ts |  1 +
 .../time-series-item-config.component.html         |  5 +-
 .../time-series-item-config.component.ts           | 26 +++++-
 .../config/gauge-widget-config.component.html      |  7 +-
 .../gauge/config/gauge-widget-config.component.ts  | 50 ++++++++++++
 .../charts/gauge/gauge-renderer.service.ts         | 17 ++--
 .../charts/table/table-widget.component.ts         | 58 +++++++++++++-
 .../sp-timeseries-renderer.service.ts              | 13 ++-
 .../chart-shared/services/result-label.service.ts  | 70 ++++++++++++++++
 .../chart-data-settings.component.html             |  5 ++
 .../data-settings/chart-data-settings.component.ts |  3 +
 .../result-label-configuration.component.html      | 49 ++++++++++++
 .../result-label-configuration.component.scss      | 17 ++++
 .../result-label-configuration.component.ts        | 93 ++++++++++++++++++++++
 14 files changed, 394 insertions(+), 20 deletions(-)

diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/datalake/data-lake-query-config.model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/datalake/data-lake-query-config.model.ts
index 60a3957286..fa3f72f93e 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/datalake/data-lake-query-config.model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/datalake/data-lake-query-config.model.ts
@@ -69,6 +69,7 @@ export interface QueryConfig {
     selectedFilters: SelectedFilter[];
     filterExpression?: FilterExpressionGroup;
     fields?: FieldConfig[];
+    resultLabelOverrides?: Record<string, string>;
     groupBy?: FieldConfig[];
     limit?: number;
     page?: number;
diff --git 
a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html
 
b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html
index 1a64d5092d..ba578876af 100644
--- 
a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html
+++ 
b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.html
@@ -91,10 +91,7 @@
                         class="w-100"
                     >
                         <input
-                            [(ngModel)]="
-                                currentlyConfiguredWidget.visualizationConfig
-                                    .displayName[getFieldKey(field)]
-                            "
+                            [ngModel]="getDisplayName(field)"
                             matInput
                             (input)="
                                 onDisplayNameChange($event.target.value, field)
diff --git 
a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts
 
b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts
index fc23ef2c40..30ea20f873 100644
--- 
a/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts
+++ 
b/ui/src/app/chart-shared/components/chart-config/select-color-properties-config/time-series-item-config/time-series-item-config.component.ts
@@ -41,6 +41,7 @@ import { MatOption, MatSelect } from 
'@angular/material/select';
 import { TranslatePipe } from '@ngx-translate/core';
 import { ColorMappingOptionsConfigComponent } from 
'../../color-mapping-options-config/color-mapping-options-config.component';
 import { LayoutDirective } from '@ngbracket/ngx-layout';
+import { ResultLabelService } from '../../../../services/result-label.service';
 
 @Component({
     selector: 'sp-time-series-item-config',
@@ -68,6 +69,8 @@ import { LayoutDirective } from '@ngbracket/ngx-layout';
     ],
 })
 export class SpTimeseriesItemConfigComponent {
+    constructor(private resultLabelService: ResultLabelService) {}
+
     @Input()
     field: DataExplorerField;
 
@@ -124,9 +127,14 @@ export class SpTimeseriesItemConfigComponent {
     }
 
     onDisplayNameChange(searchValue: string, field: DataExplorerField): void {
-        this.currentlyConfiguredWidget.visualizationConfig.displayName[
-            field.fullDbName + field.sourceIndex.toString()
-        ] = searchValue;
+        this.resultLabelService.setOverride(
+            this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
+                field.sourceIndex
+            ].queryConfig,
+            field,
+            searchValue,
+            field.fullDbName,
+        );
         this.viewRefreshEmitter.emit();
     }
 
@@ -134,6 +142,18 @@ export class SpTimeseriesItemConfigComponent {
         return field.fullDbName + field.sourceIndex.toString();
     }
 
+    getDisplayName(field: DataExplorerField): string {
+        return this.resultLabelService.resolveLabel(
+            this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
+                field.sourceIndex
+            ].queryConfig,
+            field,
+            this.currentlyConfiguredWidget.visualizationConfig.displayName[
+                this.getFieldKey(field)
+            ],
+        );
+    }
+
     hasGrouping(field: DataExplorerField): boolean {
         return (
             this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
diff --git 
a/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.html
 
b/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.html
index 389465b05c..70b38520ec 100644
--- 
a/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.html
+++ 
b/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.html
@@ -83,11 +83,8 @@
                     <input
                         matInput
                         type="text"
-                        [(ngModel)]="
-                            currentlyConfiguredWidget.visualizationConfig
-                                .displayName
-                        "
-                        (ngModelChange)="triggerViewRefresh()"
+                        [ngModel]="getDisplayName()"
+                        (ngModelChange)="onDisplayNameChange($event)"
                     />
                 </mat-form-field>
             </sp-form-field>
diff --git 
a/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.ts
 
b/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.ts
index 5fc0916abc..db4057351a 100644
--- 
a/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.ts
+++ 
b/ui/src/app/chart-shared/components/charts/gauge/config/gauge-widget-config.component.ts
@@ -32,6 +32,7 @@ import { MatInput } from '@angular/material/input';
 import { FormsModule } from '@angular/forms';
 import { TranslatePipe } from '@ngx-translate/core';
 import { MatCheckbox } from '@angular/material/checkbox';
+import { ResultLabelService } from '../../../../services/result-label.service';
 
 @Component({
     selector: 'sp-data-explorer-gauge-widget-config',
@@ -53,9 +54,58 @@ export class GaugeWidgetConfigComponent extends 
BaseWidgetConfig<
     GaugeWidgetModel,
     GaugeVisConfig
 > {
+    constructor(private resultLabelService: ResultLabelService) {
+        super();
+    }
+
     setSelectedProperty(field: DataExplorerField) {
         this.currentlyConfiguredWidget.visualizationConfig.selectedProperty =
             field;
+        this.currentlyConfiguredWidget.visualizationConfig.displayName =
+            this.resultLabelService.getOverride(
+                this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
+                    field.sourceIndex
+                ].queryConfig,
+                field,
+            ) ??
+            field.runtimeName ??
+            field.fullDbName;
+        this.triggerViewRefresh();
+    }
+
+    getDisplayName(): string {
+        const selectedProperty =
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedProperty;
+
+        if (!selectedProperty) {
+            return '';
+        }
+
+        return this.resultLabelService.resolveLabel(
+            this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
+                selectedProperty.sourceIndex
+            ].queryConfig,
+            selectedProperty,
+            this.currentlyConfiguredWidget.visualizationConfig.displayName,
+        );
+    }
+
+    onDisplayNameChange(label: string): void {
+        const selectedProperty =
+            
this.currentlyConfiguredWidget.visualizationConfig.selectedProperty;
+
+        if (!selectedProperty) {
+            return;
+        }
+
+        this.resultLabelService.setOverride(
+            this.currentlyConfiguredWidget.dataConfig.sourceConfigs[
+                selectedProperty.sourceIndex
+            ].queryConfig,
+            selectedProperty,
+            label,
+            selectedProperty.fullDbName,
+        );
         this.triggerViewRefresh();
     }
 
diff --git 
a/ui/src/app/chart-shared/components/charts/gauge/gauge-renderer.service.ts 
b/ui/src/app/chart-shared/components/charts/gauge/gauge-renderer.service.ts
index b833ceba59..619c9c3b0d 100644
--- a/ui/src/app/chart-shared/components/charts/gauge/gauge-renderer.service.ts
+++ b/ui/src/app/chart-shared/components/charts/gauge/gauge-renderer.service.ts
@@ -31,6 +31,7 @@ import {
 import { WidgetSize } from '../../../models/dataset.model';
 import { EchartsBasicOptionsGeneratorService } from 
'../../../echarts-renderer/echarts-basic-options-generator.service';
 import { SpFieldUpdateService } from '../../../services/field-update.service';
+import { ResultLabelService } from '../../../services/result-label.service';
 
 @Injectable({ providedIn: 'root' })
 export class SpGaugeRendererService implements 
SpEchartsRenderer<GaugeWidgetModel> {
@@ -38,10 +39,11 @@ export class SpGaugeRendererService implements 
SpEchartsRenderer<GaugeWidgetMode
     protected echartsBaseOptionsGenerator = inject(
         EchartsBasicOptionsGeneratorService,
     );
+    protected resultLabelService = inject(ResultLabelService);
 
     makeSeriesItem(
         seriesName: string,
-        fieldName: string,
+        selectedField: DataExplorerField,
         value: number,
         decimals: number | undefined,
         widgetConfig: GaugeWidgetModel,
@@ -51,9 +53,14 @@ export class SpGaugeRendererService implements 
SpEchartsRenderer<GaugeWidgetMode
         const visConfig = widgetConfig.visualizationConfig;
         const clamp = this.getSizeClamp(widgetSize);
         const useThresholdColors = !!visConfig.enableThresholdColors;
-        const displayName = this.makeDisplayName(
-            visConfig.displayName,
-            fieldName,
+        const displayName = this.resultLabelService.resolveLabel(
+            widgetConfig.dataConfig.sourceConfigs[selectedField.sourceIndex]
+                .queryConfig,
+            selectedField,
+            this.makeDisplayName(
+                visConfig.displayName,
+                selectedField.fullDbName,
+            ),
         );
 
         const series: GaugeSeriesOption = {
@@ -172,7 +179,7 @@ export class SpGaugeRendererService implements 
SpEchartsRenderer<GaugeWidgetMode
             },
             series: this.makeSeriesItem(
                 '',
-                selectedField.fullDbName,
+                selectedField,
                 data,
                 decimals,
                 widgetConfig,
diff --git 
a/ui/src/app/chart-shared/components/charts/table/table-widget.component.ts 
b/ui/src/app/chart-shared/components/charts/table/table-widget.component.ts
index 5a176f73f9..a6061a67d6 100644
--- a/ui/src/app/chart-shared/components/charts/table/table-widget.component.ts
+++ b/ui/src/app/chart-shared/components/charts/table/table-widget.component.ts
@@ -46,6 +46,7 @@ import { NoDataInDateRangeComponent } from 
'../base/no-data/no-data-in-date-rang
 import { TooMuchDataComponent } from 
'../base/too-much-data/too-much-data.component';
 import { TranslatePipe } from '@ngx-translate/core';
 import { WidgetNumberAppearanceConfig } from 
'../../../models/dataview-dashboard.model';
+import { ResultLabelService } from '../../../services/result-label.service';
 
 type SortDirection = 'asc' | 'desc' | '';
 
@@ -120,6 +121,7 @@ const TIMESTAMP_MASK = 'yyyy-mm-dd HH:mm:ss.SSS';
 })
 export class TableWidgetComponent extends 
BaseDataExplorerWidgetDirective<TableWidgetModel> {
     private elRef = inject(ElementRef);
+    private resultLabelService = inject(ResultLabelService);
 
     private static readonly DEFAULT_PAGE_SIZE = 20;
 
@@ -865,8 +867,22 @@ export class TableWidgetComponent extends 
BaseDataExplorerWidgetDirective<TableW
         return Math.max(0, normalizedIndex - firstReorderableIndex);
     }
 
-    headerLabel = (column: string): string =>
-        column === 'time' ? 'Time' : column;
+    headerLabel = (column: string): string => {
+        if (column === 'time') {
+            return 'Time';
+        }
+
+        const field = this.getFieldByColumnName(column);
+        if (!field) {
+            return column;
+        }
+
+        return this.resultLabelService.resolveLabel(
+            this.dataExplorerWidget.dataConfig.sourceConfigs[field.sourceIndex]
+                .queryConfig,
+            field,
+        );
+    };
 
     formatCellValue(column: string, value: unknown): unknown {
         if (column === 'time') {
@@ -1203,6 +1219,44 @@ export class TableWidgetComponent extends 
BaseDataExplorerWidgetDirective<TableW
             : (n - stats.min) / (stats.max - stats.min);
     }
 
+    private getFieldByColumnName(
+        column: string,
+    ): DataExplorerField | undefined {
+        const selectedField = (
+            this.dataExplorerWidget.visualizationConfig.selectedColumns ?? []
+        ).find(f => f.fullDbName === column);
+
+        if (selectedField) {
+            return selectedField;
+        }
+
+        for (const [
+            sourceIndex,
+            sourceConfig,
+        ] of this.dataExplorerWidget.dataConfig.sourceConfigs.entries()) {
+            const groupedField = sourceConfig.queryConfig.groupBy
+                ?.filter(groupBy => groupBy.selected)
+                .find(groupBy => groupBy.runtimeName === column);
+
+            if (groupedField) {
+                return {
+                    runtimeName: groupedField.runtimeName,
+                    measure: sourceConfig.measureName,
+                    fullDbName: groupedField.runtimeName,
+                    sourceIndex,
+                    fieldCharacteristics: {
+                        dimension: true,
+                        numeric: false,
+                        binary: false,
+                        semanticTypes: [],
+                    },
+                };
+            }
+        }
+
+        return undefined;
+    }
+
     private toNumber(value: unknown): number | undefined {
         if (typeof value === 'number' && Number.isFinite(value)) return value;
         if (typeof value === 'string' && value.trim() !== '') {
diff --git 
a/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts
 
b/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts
index ebd417bfa5..b094faedd8 100644
--- 
a/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts
+++ 
b/ui/src/app/chart-shared/components/charts/time-series-chart/sp-timeseries-renderer.service.ts
@@ -27,6 +27,7 @@ import { Injectable } from '@angular/core';
 import { TimeSeriesChartWidgetModel } from 
'./model/time-series-chart-widget.model';
 import { DataExplorerField } from '@streampipes/platform-services';
 import { SpBaseEchartsRenderer } from 
'../../../echarts-renderer/base-echarts-renderer';
+import { ResultLabelService } from '../../../services/result-label.service';
 import {
     GeneratedDataset,
     TagValue,
@@ -44,6 +45,10 @@ import type { FieldUpdateInfo } from 
'../../../models/field-update.model';
 
 @Injectable({ providedIn: 'root' })
 export class SpTimeseriesRendererService extends 
SpBaseEchartsRenderer<TimeSeriesChartWidgetModel> {
+    constructor(private resultLabelService: ResultLabelService) {
+        super();
+    }
+
     applyOptions(
         generatedDataset: GeneratedDataset,
         options: EChartsOption,
@@ -71,10 +76,16 @@ export class SpTimeseriesRendererService extends 
SpBaseEchartsRenderer<TimeSerie
                     const rawDatasetDimensions = dataset.rawDataset.dimensions;
                     const groupIndex = i - dataset.meta.preparedDataStartIndex;
                     const tag = dataset.tagValues[groupIndex];
-                    const displayName =
+                    const legacyDisplayName =
                         widgetConfig.visualizationConfig.displayName[
                             field.fullDbName + field.sourceIndex
                         ];
+                    const displayName = this.resultLabelService.resolveLabel(
+                        
widgetConfig.dataConfig.sourceConfigs[field.sourceIndex]
+                            .queryConfig,
+                        field,
+                        legacyDisplayName,
+                    );
                     const mappedGroupLabel = 
this.colorizationService.findLabel(
                         widgetConfig.visualizationConfig,
                         field,
diff --git a/ui/src/app/chart-shared/services/result-label.service.ts 
b/ui/src/app/chart-shared/services/result-label.service.ts
new file mode 100644
index 0000000000..875ebe2b94
--- /dev/null
+++ b/ui/src/app/chart-shared/services/result-label.service.ts
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { Injectable } from '@angular/core';
+import { DataExplorerField, QueryConfig } from 
'@streampipes/platform-services';
+
+@Injectable({ providedIn: 'root' })
+export class ResultLabelService {
+    makeFieldKey(field: Pick<DataExplorerField, 'fullDbName' | 'sourceIndex'>) 
{
+        return `${field.fullDbName}:${field.sourceIndex}`;
+    }
+
+    getOverrides(queryConfig: QueryConfig): Record<string, string> {
+        queryConfig.resultLabelOverrides ??= {};
+        return queryConfig.resultLabelOverrides;
+    }
+
+    getOverride(
+        queryConfig: QueryConfig,
+        field: Pick<DataExplorerField, 'fullDbName' | 'sourceIndex'>,
+    ): string | undefined {
+        return this.getOverrides(queryConfig)[this.makeFieldKey(field)];
+    }
+
+    resolveLabel(
+        queryConfig: QueryConfig,
+        field: Pick<DataExplorerField, 'fullDbName' | 'sourceIndex'>,
+        legacyLabel?: string,
+    ): string {
+        return (
+            this.getOverride(queryConfig, field) ??
+            legacyLabel ??
+            field.fullDbName
+        );
+    }
+
+    setOverride(
+        queryConfig: QueryConfig,
+        field: Pick<DataExplorerField, 'fullDbName' | 'sourceIndex'>,
+        label: string,
+        fallbackLabel?: string,
+    ): void {
+        const normalizedLabel = label.trim();
+        const fallback = fallbackLabel ?? field.fullDbName;
+        const overrides = this.getOverrides(queryConfig);
+        const key = this.makeFieldKey(field);
+
+        if (!normalizedLabel || normalizedLabel === fallback) {
+            delete overrides[key];
+            return;
+        }
+
+        overrides[key] = normalizedLabel;
+    }
+}
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
index af89a9f414..bb64ea35d8 100644
--- 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
@@ -410,6 +410,11 @@
                                 #groupSelectionPanel
                                 [sourceConfig]="sourceConfig"
                             ></sp-group-selection-panel>
+                            <sp-result-label-configuration
+                                [sourceConfig]="sourceConfig"
+                                [sourceIndex]="0"
+                            >
+                            </sp-result-label-configuration>
                             <sp-order-selection-panel
                                 [sourceConfig]="sourceConfig"
                             >
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
index 5502cff7be..2875af0354 100644
--- 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
@@ -77,6 +77,7 @@ import { AggregateConfigurationComponent } from 
'./aggregate-configuration/aggre
 import { FillConfigurationComponent } from 
'./fill-configuration/fill-configuration.component';
 import { FilterSelectionPanelComponent } from 
'./filter-selection-panel/filter-selection-panel.component';
 import { OrderSelectionPanelComponent } from 
'./order-selection-panel/order-selection-panel.component';
+import { ResultLabelConfigurationComponent } from 
'./result-label-configuration/result-label-configuration.component';
 import { TranslatePipe } from '@ngx-translate/core';
 import {
     MatAutocomplete,
@@ -122,6 +123,7 @@ import {
         FilterSelectionPanelComponent,
         GroupSelectionPanelComponent,
         OrderSelectionPanelComponent,
+        ResultLabelConfigurationComponent,
         TranslatePipe,
     ],
 })
@@ -330,6 +332,7 @@ export class ChartDataSettingsComponent implements OnInit {
             measureName,
             queryConfig: {
                 selectedFilters: [],
+                resultLabelOverrides: {},
                 limit: 100,
                 page: 1,
                 aggregationTimeUnit: 'd',
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.html
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.html
new file mode 100644
index 0000000000..f4dbb538c0
--- /dev/null
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.html
@@ -0,0 +1,49 @@
+<!--
+~ Licensed to the Apache Software Foundation (ASF) under one or more
+~ contributor license agreements.  See the NOTICE file distributed with
+~ this work for additional information regarding copyright ownership.
+~ The ASF licenses this file to You 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.
+~
+-->
+
+@if (selectedResultFields.length > 0) {
+    <sp-split-section [level]="3" [title]="'Result Labels' | translate">
+        <div fxLayout="column" fxLayoutGap="10px">
+            @for (field of selectedResultFields; track field.fullDbName) {
+                <div
+                    fxLayout="row"
+                    fxLayoutAlign="start center"
+                    fxLayoutGap="10px"
+                >
+                    <div fxFlex="35" class="text-xs">
+                        {{ field.fullDbName }}
+                    </div>
+                    <div fxFlex="65">
+                        <mat-form-field
+                            appearance="outline"
+                            color="accent"
+                            class="w-100 form-field-small"
+                        >
+                            <input
+                                matInput
+                                [ngModel]="getLabel(field)"
+                                (ngModelChange)="updateLabel(field, $event)"
+                                [placeholder]="field.fullDbName"
+                            />
+                        </mat-form-field>
+                    </div>
+                </div>
+            }
+        </div>
+    </sp-split-section>
+}
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.scss
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.scss
new file mode 100644
index 0000000000..13cbc4aacb
--- /dev/null
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.scss
@@ -0,0 +1,17 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.ts
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.ts
new file mode 100644
index 0000000000..9bb57c0ce2
--- /dev/null
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/result-label-configuration/result-label-configuration.component.ts
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Component, Input, inject } from '@angular/core';
+import {
+    DataExplorerField,
+    SourceConfig,
+} from '@streampipes/platform-services';
+import { ChartFieldProviderService } from 
'../../../../../../chart-shared/services/chart-field-provider.service';
+import { ResultLabelService } from 
'../../../../../../chart-shared/services/result-label.service';
+import { ChartConfigurationService } from 
'../../../../../../chart-shared/services/chart-configuration.service';
+import { SplitSectionComponent } from '@streampipes/shared-ui';
+import {
+    FlexDirective,
+    LayoutAlignDirective,
+    LayoutDirective,
+    LayoutGapDirective,
+} from '@ngbracket/ngx-layout/flex';
+import { MatFormField } from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { FormsModule } from '@angular/forms';
+import { TranslatePipe } from '@ngx-translate/core';
+
+@Component({
+    selector: 'sp-result-label-configuration',
+    templateUrl: './result-label-configuration.component.html',
+    styleUrls: ['./result-label-configuration.component.scss'],
+    imports: [
+        SplitSectionComponent,
+        LayoutDirective,
+        LayoutAlignDirective,
+        LayoutGapDirective,
+        FlexDirective,
+        MatFormField,
+        MatInput,
+        FormsModule,
+        TranslatePipe,
+    ],
+})
+export class ResultLabelConfigurationComponent {
+    private fieldProvider = inject(ChartFieldProviderService);
+    private resultLabelService = inject(ResultLabelService);
+    private widgetConfigService = inject(ChartConfigurationService);
+
+    @Input() sourceConfig: SourceConfig;
+    @Input() sourceIndex = 0;
+
+    get selectedResultFields(): DataExplorerField[] {
+        if (!this.sourceConfig?.measure) {
+            return [];
+        }
+
+        return this.fieldProvider
+            .generateFieldLists([this.sourceConfig])
+            .allFields.sort((a, b) => 
a.fullDbName.localeCompare(b.fullDbName));
+    }
+
+    getLabel(field: DataExplorerField): string {
+        return this.resultLabelService.resolveLabel(
+            this.sourceConfig.queryConfig,
+            { ...field, sourceIndex: this.sourceIndex },
+        );
+    }
+
+    updateLabel(field: DataExplorerField, label: string): void {
+        this.resultLabelService.setOverride(
+            this.sourceConfig.queryConfig,
+            { ...field, sourceIndex: this.sourceIndex },
+            label,
+            field.fullDbName,
+        );
+
+        this.widgetConfigService.notify({
+            refreshData: false,
+            refreshView: true,
+        });
+    }
+}

Reply via email to