This is an automated email from the ASF dual-hosted git repository. dominikriemer pushed a commit to branch improve-chart-loading-summary-2 in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 31eec11a8bbe74b01471168f52b5d1c42829dbb4 Author: Dominik Riemer <[email protected]> AuthorDate: Wed Jun 17 21:27:14 2026 +0200 fix: Improve performance of charts --- .../lib/components/sp-table/sp-table.component.ts | 45 ++++++-- .../registry/chart-registry.service.ts | 21 ++++ .../chart-overview-table.component.html | 28 +++-- .../chart-overview-table.component.ts | 57 ++++++---- .../chart-overview/chart-overview.component.html | 2 + .../chart-overview/chart-overview.component.scss | 8 -- .../chart-selection-panel.component.ts | 8 +- .../chart-preview/chart-preview.component.html | 19 ++-- .../chart-preview/chart-preview.component.ts | 29 ++--- .../chart-selection/chart-selection.component.html | 15 ++- .../chart-selection/chart-selection.component.ts | 119 ++++++++++++++------- .../chart-selection/chart-selection.model.ts} | 37 ++----- 12 files changed, 229 insertions(+), 159 deletions(-) diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.ts b/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.ts index d7f0b5c3ce..2b01a2b6cd 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.ts +++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.ts @@ -75,7 +75,12 @@ import { MatSuffix, } from '@angular/material/form-field'; import { MatInput } from '@angular/material/input'; -import { Subscription } from 'rxjs'; +import { + debounceTime, + distinctUntilChanged, + Subject, + Subscription, +} from 'rxjs'; import { MatOption, MatSelect } from '@angular/material/select'; import { SpAssetBrowserService } from '../asset-browser/asset-browser.service'; import { SpLabelComponent } from '../sp-label/sp-label.component'; @@ -207,6 +212,7 @@ export class SpTableComponent<T> viewMode: SpTableGroupViewMode = 'list'; groupBy: SpTableGroupingMode = 'asset'; groupedSections: SpTableGroupedSection<T>[] = []; + renderedGroupedRows: SpTableRenderedRow<T>[] = []; readonly selection = new SelectionModel<T>(true, []); @@ -216,6 +222,8 @@ export class SpTableComponent<T> private assetContextService = inject(SpTableAssetContextService); private renderedDataSubscription?: Subscription; private assetDataSubscription?: Subscription; + private nameSearchSubscription?: Subscription; + private nameSearchInput$ = new Subject<string>(); private viewInitialized = false; private defaultFilterPredicates = new WeakMap< MatTableDataSource<T>, @@ -241,6 +249,9 @@ export class SpTableComponent<T> this.applyAssetContextSortingAccessor(); this.refreshRenderedRows(); }); + this.nameSearchSubscription = this.nameSearchInput$ + .pipe(debounceTime(150), distinctUntilChanged()) + .subscribe(value => this.applyNameSearchFilter(value)); this.updateCompactLayout(); } @@ -300,6 +311,7 @@ export class SpTableComponent<T> ngOnDestroy() { this.renderedDataSubscription?.unsubscribe(); this.assetDataSubscription?.unsubscribe(); + this.nameSearchSubscription?.unsubscribe(); } @HostListener('window:resize') @@ -357,16 +369,7 @@ export class SpTableComponent<T> get renderedDataSource(): MatTableDataSource<T> | SpTableRenderedRow<T>[] { return this.viewMode === 'grouped' - ? this.groupedSections.flatMap(section => [ - { - __spGroupHeader: true as const, - id: section.id, - title: section.title, - color: section.color, - count: section.count, - }, - ...section.rows, - ]) + ? this.renderedGroupedRows : this.dataSource; } @@ -510,10 +513,19 @@ export class SpTableComponent<T> onNameSearchInput(value: string) { this.nameSearchTerm = value; + this.nameSearchInput$.next(value); + } + + private applyNameSearchFilter(value: string) { if (!this.dataSource) { return; } + if (!this.shouldShowNameSearch) { + this.dataSource.filter = ''; + return; + } + const normalizedFilter = value.trim().toLocaleLowerCase(); if (this.dataSource.filter === normalizedFilter) { return; @@ -730,6 +742,7 @@ export class SpTableComponent<T> private rebuildGroupedSections(rows: T[]) { if (!this.assetContextConfig || this.viewMode !== 'grouped') { this.groupedSections = []; + this.renderedGroupedRows = []; return; } @@ -756,6 +769,16 @@ export class SpTableComponent<T> ...group, rows: [...group.rows], })); + this.renderedGroupedRows = this.groupedSections.flatMap(section => [ + { + __spGroupHeader: true as const, + id: section.id, + title: section.title, + color: section.color, + count: section.count, + }, + ...section.rows, + ]); } private resolveGroups( diff --git a/ui/src/app/chart-shared/registry/chart-registry.service.ts b/ui/src/app/chart-shared/registry/chart-registry.service.ts index d1d32dd65d..e7108a6257 100644 --- a/ui/src/app/chart-shared/registry/chart-registry.service.ts +++ b/ui/src/app/chart-shared/registry/chart-registry.service.ts @@ -82,6 +82,8 @@ export class ChartRegistry { private translateService = inject(TranslateService); chartTypes: IWidget<any>[] = []; + registeredChartSummary: Record<string, { icon: string; label: string }> = + {}; constructor() { this.chartTypes = [ @@ -303,10 +305,22 @@ export class ChartRegistry { ), }, ]; + this.makeChartSummary(); + } + + private makeChartSummary() { + this.registeredChartSummary = {}; + this.chartTypes.forEach(c => { + this.registeredChartSummary[c.id] = { + label: c.label, + icon: c.icon, + }; + }); } registerChart(chart: IWidget<any>): void { this.chartTypes.push(chart); + this.makeChartSummary(); } getAvailableChartTemplates(): IWidget<any>[] { @@ -325,6 +339,13 @@ export class ChartRegistry { return this.getChartTemplate(chartId).id; } + getRegisteredChartSummary(chartId: string): { + icon: string; + label: string; + } { + return this.registeredChartSummary[chartId]; + } + private findBackwardsCompatibleChart(chartId: string): IWidget<any> { return this.chartTypes.find( chart => chart.alias !== undefined && chart.alias === chartId, diff --git a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html index 9b55684743..48adec8070 100644 --- a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html +++ b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html @@ -20,28 +20,38 @@ <sp-basic-header-title-component [title]="'Charts' | translate" ></sp-basic-header-title-component> - @if (isLoading) { + @if (isLoading && !hasLoadedCharts) { <div + fxLayout="column" fxFlex="100" - fxLayout="row" fxLayoutAlign="center center" - class="chart-overview-loading" + fxLayoutGap="12px" > + <mat-progress-spinner + mode="indeterminate" + diameter="40" + ></mat-progress-spinner> + <span class="chart-overview-loading-text">{{ + 'Loading' | translate + }}</span> + </div> + } @else { + @if (isLoading) { <div - fxLayout="column" - fxLayoutAlign="center center" - fxLayoutGap="12px" + fxLayout="row" + fxLayoutAlign="end center" + fxLayoutGap="8px" + class="p-2" > <mat-progress-spinner mode="indeterminate" - diameter="40" + diameter="18" ></mat-progress-spinner> <span class="chart-overview-loading-text">{{ 'Loading' | translate }}</span> </div> - </div> - } @else { + } <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"> <sp-table fxFlex="100" diff --git a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts index 4b4ff52f91..28d2659fda 100644 --- a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts +++ b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts @@ -17,6 +17,8 @@ */ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, inject, Input, @@ -53,7 +55,7 @@ import { import { MatDialog } from '@angular/material/dialog'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { ChartRoutingService } from '../../../../chart-shared/services/chart-routing.service'; -import { Subscription } from 'rxjs'; +import { finalize, Subscription } from 'rxjs'; import { MatSort, MatSortHeader } from '@angular/material/sort'; import { FlexDirective, @@ -110,6 +112,7 @@ type ChartOverviewRow = ChartSummaryDto & { MatProgressSpinner, TranslatePipe, ], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ChartOverviewTableComponent implements OnInit, OnDestroy { @Input() @@ -117,7 +120,6 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { @ViewChild(MatSort) set sort(sort: MatSort | undefined) { - this._sort = sort; if (sort) { this.dataSource.sort = sort; } @@ -141,6 +143,7 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { placeholder: 'Search charts', }; isLoading = false; + hasLoadedCharts = false; charts: ChartOverviewRow[] = []; filteredCharts: ChartOverviewRow[] = []; @@ -152,10 +155,10 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { private routingService = inject(ChartRoutingService); private assetFilterService = inject(SpAssetBrowserService); private chartRegistryService = inject(ChartRegistry); + private cdr = inject(ChangeDetectorRef); assetFilter$: Subscription; - currentFilterIds = new Set<string>(); - private _sort?: MatSort; + currentFilterIds?: Set<string>; private chartTypeMetadata = new Map< string, { icon: string; label: string } @@ -167,6 +170,7 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { this.assetFilterService.currentAssetFilter$.subscribe(filter => { this.currentFilterIds = filter?.activeElementIds; this.applyChartFilters(this.currentFilterIds); + this.cdr.markForCheck(); }); this.dataSource.sortingDataAccessor = (chart, column) => { @@ -186,20 +190,29 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { getCharts(): void { this.isLoading = true; - this.dataViewService.getChartSummary().subscribe({ - next: chartSummary => { - this.charts = chartSummary.resources - .map(chart => this.toChartOverviewRow(chart)) - .sort((a, b) => a.name.localeCompare(b.name)); - this.applyChartFilters(this.currentFilterIds); - }, - complete: () => { - this.isLoading = false; - }, - error: () => { - this.isLoading = false; - }, - }); + this.cdr.markForCheck(); + this.dataViewService + .getChartSummary() + .pipe( + finalize(() => { + this.isLoading = false; + this.cdr.markForCheck(); + }), + ) + .subscribe({ + next: chartSummary => { + this.charts = chartSummary.resources + .map(chart => this.toChartOverviewRow(chart)) + .sort((a, b) => a.name.localeCompare(b.name)); + this.hasLoadedCharts = true; + this.applyChartFilters(this.currentFilterIds); + this.cdr.markForCheck(); + }, + error: () => { + this.hasLoadedCharts = true; + this.cdr.markForCheck(); + }, + }); } ngOnDestroy(): void { @@ -306,7 +319,7 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { }); } - applyChartFilters(elementIds: Set<string>): void { + applyChartFilters(elementIds?: Set<string>): void { if (elementIds === undefined) { this.filteredCharts = []; } else if (elementIds.size === 0) { @@ -316,9 +329,6 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { elementIds.has(a.elementId), ); } - if (this._sort) { - this.dataSource.sort = this._sort; - } this.dataSource.data = this.filteredCharts; } @@ -342,7 +352,8 @@ export class ChartOverviewTableComponent implements OnInit, OnDestroy { return cached; } - const template = this.chartRegistryService.getChartTemplate(widgetType); + const template = + this.chartRegistryService.getRegisteredChartSummary(widgetType); const metadata = { icon: template?.icon ?? 'insert_chart', label: template?.label ?? widgetType, diff --git a/ui/src/app/chart/components/chart-overview/chart-overview.component.html b/ui/src/app/chart/components/chart-overview/chart-overview.component.html index e4ee9d1afb..299e3ce368 100644 --- a/ui/src/app/chart/components/chart-overview/chart-overview.component.html +++ b/ui/src/app/chart/components/chart-overview/chart-overview.component.html @@ -40,6 +40,8 @@ </div> <div fxFlex="100" fxLayout="column"> <sp-data-explorer-overview-table + fxLayout="column" + fxFlex="100" [hasDataExplorerWritePrivileges]="hasDataExplorerWritePrivileges" ></sp-data-explorer-overview-table> </div> diff --git a/ui/src/app/chart/components/chart-overview/chart-overview.component.scss b/ui/src/app/chart/components/chart-overview/chart-overview.component.scss index 06c5911910..c10f9025ea 100644 --- a/ui/src/app/chart/components/chart-overview/chart-overview.component.scss +++ b/ui/src/app/chart/components/chart-overview/chart-overview.component.scss @@ -36,14 +36,6 @@ margin-right: 10px; } -.chart-overview-loading { - display: flex; - flex: 1 1 auto; - align-items: center; - justify-content: center; - text-align: center; -} - .chart-overview-loading-text { font-size: var(--font-size-sm); color: var(--color-paragraph); diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.ts b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.ts index a6eef3e077..1f1204530e 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.ts +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.ts @@ -16,7 +16,12 @@ * */ -import { Component, EventEmitter, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Output, +} from '@angular/core'; import { MatTab, MatTabGroup } from '@angular/material/tabs'; import { FlexDirective, @@ -42,6 +47,7 @@ import { TranslatePipe } from '@ngx-translate/core'; ChartSelectionComponent, TranslatePipe, ], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ChartSelectionPanelComponent { @Output() diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html index 29eddbab1d..d046ac5236 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.html @@ -18,27 +18,30 @@ <div class="data-view-preview-outer" - [attr.data-cy]="dataCyId" - [attr.title]="chart.name" + [attr.data-cy]="chartItem.dataCyId" + [attr.title]="chartItem.chart.name" (click)="addChart()" > <div class="chart-preview-header"> - <div class="chart-preview-icon-shell" [attr.title]="widgetTypeLabel"> - <mat-icon>{{ widgetTypeIcon }}</mat-icon> + <div + class="chart-preview-icon-shell" + [attr.title]="chartItem.widgetTypeLabel" + > + <mat-icon>{{ chartItem.widgetTypeIcon }}</mat-icon> </div> <div class="chart-preview-copy"> <div class="chart-preview-title-row"> - <h5 class="chart-preview-title">{{ chart.name }}</h5> + <h5 class="chart-preview-title">{{ chartItem.chart.name }}</h5> </div> <div class="chart-preview-meta"> - @if (chart.datasetName) { + @if (chartItem.chart.datasetName) { <span class="chart-preview-meta-item chart-preview-dataset" - [attr.title]="chart.datasetName" + [attr.title]="chartItem.chart.datasetName" > - {{ chart.datasetName }} + {{ chartItem.chart.datasetName }} </span> } </div> diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts index 4ca3306b26..327a252307 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-preview/chart-preview.component.ts @@ -21,16 +21,14 @@ import { Component, EventEmitter, Input, - OnInit, Output, inject, } from '@angular/core'; -import { ChartSummaryDto } from '@streampipes/platform-services'; -import { ChartRegistry } from '../../../../../../chart-shared/registry/chart-registry.service'; import { MatIcon } from '@angular/material/icon'; import { MatIconButton } from '@angular/material/button'; import { FeatureCardService } from '@streampipes/shared-ui'; import { TranslatePipe } from '@ngx-translate/core'; +import { ChartSelectionItem } from '../chart-selection.model'; @Component({ selector: 'sp-chart-preview', @@ -39,35 +37,24 @@ import { TranslatePipe } from '@ngx-translate/core'; changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIcon, MatIconButton, TranslatePipe], }) -export class ChartPreviewComponent implements OnInit { - private widgetRegistryService = inject(ChartRegistry); +export class ChartPreviewComponent { private featureCardService = inject(FeatureCardService); @Input() - chart: ChartSummaryDto; - - widgetTypeLabel = ''; - widgetTypeIcon = 'insert_chart'; - dataCyId = ''; + chartItem!: ChartSelectionItem; @Output() addChartEmitter: EventEmitter<string> = new EventEmitter<string>(); - ngOnInit(): void { - const template = this.widgetRegistryService.getChartTemplate( - this.chart.widgetType, - ); - this.widgetTypeLabel = template?.label ?? this.chart.widgetType; - this.widgetTypeIcon = template?.icon ?? 'insert_chart'; - this.dataCyId = `add-data-view-btn-${this.chart.name.replaceAll(' ', '')}`; - } - addChart(): void { - this.addChartEmitter.emit(this.chart.elementId); + this.addChartEmitter.emit(this.chartItem.chart.elementId); } openPreview(event: MouseEvent): void { event.stopPropagation(); - this.featureCardService.openFeatureCard('chart', this.chart.elementId); + this.featureCardService.openFeatureCard( + 'chart', + this.chartItem.chart.elementId, + ); } } diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html index 535eed1f08..0554e8ffdd 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html @@ -30,8 +30,8 @@ > <div fxLayout="column" class="chart-selection-heading"> <span class="chart-selection-caption"> - {{ filteredCharts.length }} - @if (filteredCharts.length === 1) { + {{ filteredChartItems.length }} + @if (filteredChartItems.length === 1) { {{ 'chart available' | translate }} } @else { {{ 'charts available' | translate }} @@ -81,11 +81,10 @@ <input [placeholder]="'Search charts' | translate" matInput - [value]="searchTerm" - (input)="onSearchTermChanged($any($event.target).value)" + [formControl]="searchControl" data-cy="chart-selection-search" /> - @if (hasActiveSearch()) { + @if (hasActiveSearch) { <button mat-icon-button matSuffix @@ -115,7 +114,7 @@ 'Loading charts...' | translate }}</span> </div> - } @else if (charts.length > 0 && filteredCharts.length > 0) { + } @else if (charts.length > 0 && filteredChartItems.length > 0) { <cdk-virtual-scroll-viewport fxFlexFill class="chart-selection-results chart-selection-viewport" @@ -125,13 +124,13 @@ > <div *cdkVirtualFor=" - let chart of filteredCharts; + let chartItem of filteredChartItems; trackBy: trackByChartId " class="chart-selection-item" > <sp-chart-preview - [chart]="chart" + [chartItem]="chartItem" (addChartEmitter)="addChartEmitter.emit($event)" > </sp-chart-preview> diff --git a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts index ebba633d40..dc3bd91733 100644 --- a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts @@ -20,12 +20,15 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + DestroyRef, EventEmitter, inject, OnInit, Output, } from '@angular/core'; import { ChartService, ChartSummaryDto } from '@streampipes/platform-services'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AuthService } from '../../../../../services/auth.service'; import { UserPrivilege } from '../../../../../core/auth/user-privilege.enum'; import { ChartRegistry } from '../../../../../chart-shared/registry/chart-registry.service'; @@ -54,6 +57,8 @@ import { CdkVirtualForOf, CdkVirtualScrollViewport, } from '@angular/cdk/scrolling'; +import { debounceTime, distinctUntilChanged, finalize } from 'rxjs'; +import { ChartSelectionItem } from './chart-selection.model'; @Component({ selector: 'sp-chart-selection', @@ -80,6 +85,7 @@ import { CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf, + ReactiveFormsModule, ], }) export class ChartSelectionComponent implements OnInit { @@ -88,14 +94,18 @@ export class ChartSelectionComponent implements OnInit { private chartRegistryService = inject(ChartRegistry); private chartRoutingService = inject(ChartRoutingService); private cdr = inject(ChangeDetectorRef); + private destroyRef = inject(DestroyRef); @Output() addChartEmitter: EventEmitter<string> = new EventEmitter(); charts: ChartSummaryDto[] = []; - filteredCharts: ChartSummaryDto[] = []; + chartItems: ChartSelectionItem[] = []; + filteredChartItems: ChartSelectionItem[] = []; searchTerm = ''; + searchControl = new FormControl('', { nonNullable: true }); isRefreshing = false; + hasActiveSearch = false; readonly chartItemSize = 132; hasChartWritePrivileges: boolean = false; @@ -106,6 +116,16 @@ export class ChartSelectionComponent implements OnInit { ); this.refreshCharts(); + + this.searchControl.valueChanges + .pipe( + debounceTime(150), + distinctUntilChanged(), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(value => { + this.setSearchTerm(value); + }); } navigateToDataViewCreation(): void { @@ -115,62 +135,81 @@ export class ChartSelectionComponent implements OnInit { refreshCharts(): void { this.isRefreshing = true; this.cdr.markForCheck(); - this.dataViewService.getChartSummary().subscribe({ - next: chartSummary => { - this.charts = chartSummary.resources.sort((a, b) => - a.name.localeCompare(b.name), - ); - this.applySearch(); - this.cdr.markForCheck(); - }, - complete: () => { - this.isRefreshing = false; - this.cdr.markForCheck(); - }, - error: () => { - this.isRefreshing = false; - this.cdr.markForCheck(); - }, - }); + this.dataViewService + .getChartSummary() + .pipe( + finalize(() => { + this.isRefreshing = false; + this.cdr.markForCheck(); + }), + ) + .subscribe({ + next: chartSummary => { + this.charts = [...chartSummary.resources].sort((a, b) => + a.name.localeCompare(b.name), + ); + this.chartItems = this.charts.map(chart => + this.toChartSelectionItem(chart), + ); + this.applySearch(); + this.cdr.markForCheck(); + }, + error: () => { + this.charts = []; + this.chartItems = []; + this.filteredChartItems = []; + }, + }); } - onSearchTermChanged(value: string): void { - this.searchTerm = value; - this.applySearch(); - this.cdr.markForCheck(); + clearSearch(): void { + this.searchControl.setValue('', { emitEvent: false }); + this.setSearchTerm(''); } - clearSearch(): void { - this.searchTerm = ''; + trackByChartId(index: number, item: ChartSelectionItem): string { + return item.chart.elementId; + } + + private setSearchTerm(value: string): void { + this.searchTerm = value; + this.hasActiveSearch = this.searchTerm.trim().length > 0; this.applySearch(); this.cdr.markForCheck(); } - hasActiveSearch(): boolean { - return this.searchTerm.trim().length > 0; - } + private toChartSelectionItem(chart: ChartSummaryDto): ChartSelectionItem { + const template = this.chartRegistryService.getRegisteredChartSummary( + chart.widgetType, + ); + const widgetTypeLabel = template?.label ?? chart.widgetType; - trackByChartId(index: number, chart: ChartSummaryDto): string { - return chart.elementId; + return { + chart, + widgetTypeLabel, + widgetTypeIcon: template?.icon ?? 'insert_chart', + dataCyId: `add-data-view-btn-${chart.name.replaceAll(' ', '')}`, + searchText: [ + chart.name, + chart.datasetName, + chart.widgetType, + widgetTypeLabel, + ] + .filter((value): value is string => !!value) + .join(' ') + .toLowerCase(), + }; } private applySearch(): void { const query = this.searchTerm.trim().toLowerCase(); if (!query) { - this.filteredCharts = this.charts; + this.filteredChartItems = this.chartItems; return; } - this.filteredCharts = this.charts.filter(chart => - [ - chart.name, - chart.datasetName, - chart.widgetType, - this.chartRegistryService.getChartTemplate(chart.widgetType) - ?.label, - ] - .filter((value): value is string => !!value) - .some(value => value.toLowerCase().includes(query)), + this.filteredChartItems = this.chartItems.filter(item => + item.searchText.includes(query), ); } } diff --git a/ui/src/app/chart/components/chart-overview/chart-overview.component.scss b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.model.ts similarity index 65% copy from ui/src/app/chart/components/chart-overview/chart-overview.component.scss copy to ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.model.ts index 06c5911910..78e4f80aa5 100644 --- a/ui/src/app/chart/components/chart-overview/chart-overview.component.scss +++ b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.model.ts @@ -16,35 +16,12 @@ * */ -.mat-mdc-header-cell { - font-weight: bold; -} - -.w-100 { - width: 100%; -} - -.p-2 { - padding: 2px; -} - -.m-20 { - margin: 20px; -} - -.mr-10 { - margin-right: 10px; -} - -.chart-overview-loading { - display: flex; - flex: 1 1 auto; - align-items: center; - justify-content: center; - text-align: center; -} +import { ChartSummaryDto } from '@streampipes/platform-services'; -.chart-overview-loading-text { - font-size: var(--font-size-sm); - color: var(--color-paragraph); +export interface ChartSelectionItem { + chart: ChartSummaryDto; + widgetTypeLabel: string; + widgetTypeIcon: string; + dataCyId: string; + searchText: string; }
