This is an automated email from the ASF dual-hosted git repository. dominikriemer pushed a commit to branch improve-dataset-selection-charts in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 741ec9d89d1b48dc5421deaaa61d57ee64fc1a90 Author: Dominik Riemer <[email protected]> AuthorDate: Sat Jun 13 13:21:21 2026 +0200 feat: Add search feature to dataset selection in chart view --- ui/deployment/i18n/de.json | 2 + ui/deployment/i18n/en.json | 2 + ui/deployment/i18n/pl.json | 2 + .../chart-data-settings.component.html | 56 +++++-- .../data-settings/chart-data-settings.component.ts | 164 +++++++++++++++++---- 5 files changed, 189 insertions(+), 37 deletions(-) diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json index 27fccbbc08..2eea78a5da 100644 --- a/ui/deployment/i18n/de.json +++ b/ui/deployment/i18n/de.json @@ -705,6 +705,7 @@ "No charts match your search.": "Keine Diagramme entsprechen deiner Suche.", "No data available.": "Keine Daten verfügbar", "No datasets available": "Keine Datensätze verfügbar", + "No datasets match your search.": "Keine Datensätze entsprechen deiner Suche.", "No entries available.": "Keine Einträge vorhanden.", "No export providers found": "Keine Exportanbieter gefunden", "No fields selected.": "Keine Felder ausgewählt.", @@ -898,6 +899,7 @@ "Schema fields": "Schemafelder", "Script": "Skript", "Search": "Suche", + "Search datasets": "Datensätze suchen", "Search charts": "Diagramme suchen", "Searching for available extensions, please wait...": "Nach verfügbaren Extensions suchen, bitte warten...", "Second": "Sekunde", diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json index 86d7ca4e3f..0c4193aaa4 100644 --- a/ui/deployment/i18n/en.json +++ b/ui/deployment/i18n/en.json @@ -705,6 +705,7 @@ "No charts match your search.": null, "No data available.": null, "No datasets available": null, + "No datasets match your search.": null, "No entries available.": null, "No export providers found": null, "No fields selected.": null, @@ -898,6 +899,7 @@ "Schema fields": null, "Script": null, "Search": null, + "Search datasets": null, "Search charts": null, "Searching for available extensions, please wait...": null, "Second": null, diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json index d17c3bcc58..a53abc89a1 100644 --- a/ui/deployment/i18n/pl.json +++ b/ui/deployment/i18n/pl.json @@ -705,6 +705,7 @@ "No charts match your search.": "Żadne wykresy nie pasują do wyszukiwania.", "No data available.": "Brak dostępnych danych.", "No datasets available": "Brak dostępnych zbiorów danych", + "No datasets match your search.": "Żadne zbiory danych nie pasują do wyszukiwania.", "No entries available.": "Brak dostępnych wpisów.", "No export providers found": "Nie znaleziono exportera", "No fields selected.": "Nie wybrano żadnych pól.", @@ -898,6 +899,7 @@ "Schema fields": "Pola schematu", "Script": "Skrypt", "Search": "Szukaj", + "Search datasets": "Szukaj zbiorów danych", "Search charts": "Szukaj wykresów", "Searching for available extensions, please wait...": "Wyszukiwanie dostępnych rozszerzeń, proszę czekać...", "Second": "Sekunda", 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 b4a7dcd331..dc988a4145 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 @@ -85,24 +85,51 @@ } @if (availableMeasurements.length !== 0) { <mat-form-field - color="accent" fxFlex="100" appearance="outline" subscriptSizing="dynamic" > - <mat-select + <mat-icon matPrefix>search</mat-icon> + <input + matInput + [placeholder]=" + 'Search datasets' | translate + " + [value]="measurementInputValue" + (input)=" + onMeasurementSearchChange( + $any($event.target).value + ) + " + [matAutocomplete]="datasetAutocomplete" data-cy="data-explorer-select-data-set" - [(value)]="sourceConfig.measureName" - (selectionChange)=" - updateMeasure( + /> + @if (hasActiveMeasurementSearch()) { + <button + mat-icon-button + matSuffix + type="button" + [attr.aria-label]=" + 'Clear search' | translate + " + (click)="clearMeasurementSearch()" + > + <mat-icon>close</mat-icon> + </button> + } + <mat-autocomplete + #datasetAutocomplete="matAutocomplete" + autoActiveFirstOption + (optionSelected)=" + onMeasurementSelected( sourceConfig, - $event.value + $event ) " > @for ( - measurement of availableMeasurements; - track measurement + measurement of filteredMeasurements; + track measurement.elementId ) { <mat-option [value]="measurement.measureName" @@ -112,7 +139,18 @@ }}</span> </mat-option> } - </mat-select> + @if ( + filteredMeasurements.length === 0 && + hasActiveMeasurementSearch() + ) { + <mat-option disabled> + {{ + 'No datasets match your search.' + | translate + }} + </mat-option> + } + </mat-autocomplete> </mat-form-field> } </sp-split-section> 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 478d92561b..71477ca0ac 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 @@ -29,6 +29,7 @@ import { DataExplorerDataConfig, DataExplorerWidgetModel, DataLakeMeasure, + DatasetSummaryDto, DatalakeRestService, SourceConfig, } from '@streampipes/platform-services'; @@ -59,8 +60,12 @@ import { SpAlertBannerComponent, SplitSectionComponent, } from '@streampipes/shared-ui'; -import { MatFormField } from '@angular/material/form-field'; -import { MatOption, MatSelect } from '@angular/material/select'; +import { + MatFormField, + MatPrefix, + MatSuffix, +} from '@angular/material/form-field'; +import { MatOption } from '@angular/material/core'; import { MatIcon } from '@angular/material/icon'; import { MatRadioButton, MatRadioGroup } from '@angular/material/radio'; import { FormsModule } from '@angular/forms'; @@ -72,6 +77,11 @@ import { AggregateConfigurationComponent } from './aggregate-configuration/aggre import { FilterSelectionPanelComponent } from './filter-selection-panel/filter-selection-panel.component'; import { OrderSelectionPanelComponent } from './order-selection-panel/order-selection-panel.component'; import { TranslatePipe } from '@ngx-translate/core'; +import { + MatAutocomplete, + MatAutocompleteSelectedEvent, + MatAutocompleteTrigger, +} from '@angular/material/autocomplete'; @Component({ selector: 'sp-chart-data-settings', @@ -91,7 +101,8 @@ import { TranslatePipe } from '@ngx-translate/core'; LayoutGapDirective, MatButton, MatFormField, - MatSelect, + MatPrefix, + MatSuffix, MatOption, MatIcon, MatRadioGroup, @@ -102,6 +113,8 @@ import { TranslatePipe } from '@ngx-translate/core'; FormFieldComponent, MatInput, MatCheckbox, + MatAutocomplete, + MatAutocompleteTrigger, AggregateConfigurationComponent, FieldSelectionPanelComponent, FilterSelectionPanelComponent, @@ -137,7 +150,9 @@ export class ChartDataSettingsComponent implements OnInit { @ViewChild('groupSelectionPanel') groupSelectionPanel: GroupSelectionPanelComponent; - availableMeasurements: DataLakeMeasure[] = []; + availableMeasurements: DatasetSummaryDto[] = []; + filteredMeasurements: DatasetSummaryDto[] = []; + measurementInputValue = ''; step = 0; @@ -149,33 +164,42 @@ export class ChartDataSettingsComponent implements OnInit { } ngOnInit(): void { + this.syncCurrentMeasure(); this.loadPipelinesAndMeasurements(); } loadPipelinesAndMeasurements() { - this.datalakeRestService - .getAllMeasurementSeries() - .subscribe(response => { - this.availableMeasurements = response; - this.availableMeasurements.sort((a, b) => - a.measureName.localeCompare(b.measureName), - ); + this.datalakeRestService.getMeasurementSummary().subscribe(response => { + this.availableMeasurements = response.resources.sort((a, b) => + a.measureName.localeCompare(b.measureName), + ); + this.applyMeasurementSearch(); - if (!this.sourceConfig) { - const defaultConfigs = this.findDefaultConfig(); - this.initializeSourceConfig(defaultConfigs.measureName); - if (defaultConfigs.measureName !== undefined) { - this.updateMeasure( - this.sourceConfig, - defaultConfigs.measureName, - ); - } + if (!this.sourceConfig) { + const defaultConfigs = this.findDefaultConfig(); + this.initializeSourceConfig(defaultConfigs.measureName); + if (defaultConfigs.measureName !== undefined) { + this.loadMeasurement( + defaultConfigs.measureName, + true, + true, + ); } - }); + } else if ( + !this.sourceConfig.measure && + this.sourceConfig.measureName + ) { + this.loadMeasurement( + this.sourceConfig.measureName, + false, + false, + ); + } + }); } findDefaultConfig(): { - measureName: string; + measureName: string | undefined; } { if (this.availableMeasurements.length > 0) { return { @@ -187,7 +211,84 @@ export class ChartDataSettingsComponent implements OnInit { } updateMeasure(sourceConfig: SourceConfig, measureName: string) { - sourceConfig.measure = this.findMeasure(measureName); + sourceConfig.measureName = measureName; + this.measurementInputValue = measureName; + this.loadMeasurement(measureName, true, true); + } + + onMeasurementSearchChange(value: string): void { + this.measurementInputValue = value; + this.applyMeasurementSearch(); + } + + clearMeasurementSearch(): void { + this.measurementInputValue = ''; + this.applyMeasurementSearch(); + } + + hasActiveMeasurementSearch(): boolean { + return this.measurementInputValue.trim().length > 0; + } + + onMeasurementSelected( + sourceConfig: SourceConfig, + event: MatAutocompleteSelectedEvent, + ): void { + this.updateMeasure(sourceConfig, event.option.value); + } + + private applyMeasurementSearch(): void { + const query = this.measurementInputValue.trim().toLowerCase(); + if (!query) { + this.filteredMeasurements = this.availableMeasurements; + return; + } + + this.filteredMeasurements = this.availableMeasurements.filter( + measurement => + measurement.measureName.toLowerCase().includes(query) || + measurement.pipelines.some(pipeline => + pipeline.toLowerCase().includes(query), + ), + ); + } + + private loadMeasurement( + measureName: string, + resetQueryConfig: boolean, + refreshData: boolean, + ): void { + this.datalakeRestService + .getMeasurementByName(measureName) + .subscribe(measure => + this.applySelectedMeasurement( + measure, + resetQueryConfig, + refreshData, + ), + ); + } + + private applySelectedMeasurement( + measure: DataLakeMeasure, + resetQueryConfig: boolean, + refreshData: boolean, + ): void { + const sourceConfig = this.sourceConfig; + if (!sourceConfig) { + return; + } + + this.dataLakeMeasure = measure; + this.dataLakeMeasureChange.emit(measure); + sourceConfig.measureName = measure.measureName; + sourceConfig.measure = measure; + this.measurementInputValue = measure.measureName; + + if (!resetQueryConfig) { + return; + } + sourceConfig.queryConfig.fields = []; if (this.fieldSelectionPanel) { this.fieldSelectionPanel.applyDefaultFields(); @@ -197,13 +298,20 @@ export class ChartDataSettingsComponent implements OnInit { if (this.groupSelectionPanel) { this.groupSelectionPanel.applyDefaultFields(); } - this.triggerDataRefresh(); + + if (refreshData) { + this.triggerDataRefresh(); + } } - findMeasure(measureName: string) { - return this.availableMeasurements.find( - m => m.measureName === measureName, - ); + private syncCurrentMeasure(): void { + if (this.sourceConfig?.measure) { + this.dataLakeMeasure = this.sourceConfig.measure; + this.dataLakeMeasureChange.emit(this.sourceConfig.measure); + this.measurementInputValue = this.sourceConfig.measure.measureName; + } else if (this.sourceConfig?.measureName) { + this.measurementInputValue = this.sourceConfig.measureName; + } } changeDataAggregation() {
