From 38b029aa6ecd4adb5cfad2eb1431af0a991c194a Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Fri, 19 Dec 2025 03:35:25 +0200 Subject: [PATCH 01/18] SED-4450-working on loading spinners --- .../cross-execution-dashboard-state.ts | 22 +++++++++++-- .../cross-execution-heatmap.component.html | 5 +++ .../cross-execution-heatmap.component.scss | 21 ++++++------- .../cross-execution-heatmap.component.ts | 5 ++- .../scheduler-report-view.component.html | 31 +++++++++++++++---- .../scheduler-report-view.component.scss | 20 ++++++------ 6 files changed, 75 insertions(+), 29 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index cb6df9d29..27f5626cf 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -49,6 +49,8 @@ interface EntityWithKeywordsStats { statuses: Record; } +type KeywordsChartState = { chartSettings: TSChartSettings; lastExecutions: Execution[] }; + export type CrossExecutionViewType = 'task' | 'plan'; export abstract class CrossExecutionDashboardState { @@ -62,6 +64,7 @@ export abstract class CrossExecutionDashboardState { readonly task = signal(undefined); readonly plan = signal(undefined); + readonly lastRefreshTrigger = signal<'init' | 'manual' | 'auto'>('init'); // view settings activeTimeRangeSelection = signal(undefined); @@ -74,6 +77,12 @@ export abstract class CrossExecutionDashboardState { abstract readonly executionsTableFilter: Record; abstract getViewType(): CrossExecutionViewType; + executionsChartLoading = signal(false); + summaryWidgetLoading = signal(false); + testCasesCountChartLoading = signal(false); + keywordsCountChartLoading = signal(false); + errorsTableLoading = signal(false); + updateTimeRangeSelection(selection: TimeRangePickerSelection) { this.activeTimeRangeSelection.set(selection); } @@ -113,6 +122,7 @@ export abstract class CrossExecutionDashboardState { readonly executionsDurationTimeSeriesData = this.timeRange$.pipe( switchMap((timeRange) => { + this.summaryWidgetLoading.set(true); const oql = new OQLBuilder() .open('and') .append('attributes.metricType = "executions/duration"') @@ -138,12 +148,14 @@ export abstract class CrossExecutionDashboardState { items[keyAttributes['result'] as string] = bucket.count; total += bucket.count; }); + this.summaryWidgetLoading.set(false); return { items: items, total: total }; }), ); executionsChartSettings$ = this.timeRange$.pipe( switchMap((timeRange) => { + this.executionsChartLoading.set(true); const statusAttribute = 'result'; const oql = new OQLBuilder() .open('and') @@ -226,6 +238,7 @@ export abstract class CrossExecutionDashboardState { }, }, ]; + this.executionsChartLoading.set(false); return { title: '', showLegend: false, @@ -269,8 +282,9 @@ export abstract class CrossExecutionDashboardState { shareReplay(1), ); - keywordsChartSettings$ = this.lastExecutionsSorted$.pipe( + keywordsChartSettings$: Observable = this.lastExecutionsSorted$.pipe( switchMap((executions) => { + this.keywordsCountChartLoading.set(true); return this.timeRange$.pipe( take(1), switchMap((timeRange) => { @@ -339,7 +353,9 @@ export abstract class CrossExecutionDashboardState { return s; }); this.cumulateSeriesData(series); - return this.createKeywordsChart(executions, series); + let chartSettings = this.createKeywordsChart(executions, series); + this.keywordsCountChartLoading.set(false); + return chartSettings; }), ); } @@ -352,6 +368,7 @@ export abstract class CrossExecutionDashboardState { testCasesChartSettings$: Observable<{ chart: TSChartSettings; hasData: boolean; lastExecutions: Execution[] }> = this.lastExecutionsSorted$.pipe( switchMap((executions) => { + this.testCasesCountChartLoading.set(true); return this.timeRange$.pipe( take(1), switchMap((timeRange) => { @@ -422,6 +439,7 @@ export abstract class CrossExecutionDashboardState { return s; }); this.cumulateSeriesData(series); + this.testCasesCountChartLoading.set(false); return { chart: this.createTestCasesChart(executions, series), lastExecutions: executions, diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html index eab9107b0..7c34b7009 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html @@ -1,5 +1,10 @@ + @if (isLoading()) { +
+ +
+ } @if (defaultDateRange(); as dateRange) {
>(); readonly defaultDateRange = input(); + readonly isLoading = signal(false); + protected readonly dashletTitle = computed(() => { let heatmapType = this.heatmapType(); const lastExecutionLabel = ` (last ${this._state.LAST_EXECUTIONS_TO_DISPLAY} executions)`; @@ -122,6 +124,7 @@ export class CrossExecutionHeatmapComponent implements OnInit, OnDestroy { this.heatmapType$, ]).pipe( switchMap(([executions, timeRange, heatmapType]) => { + this.isLoading.set(true); const isKeywordsHeatmap = heatmapType === 'keywords'; const executionIdAttribute = 'eId'; @@ -200,7 +203,7 @@ export class CrossExecutionHeatmapComponent implements OnInit, OnDestroy { }); }); }); - + this.isLoading.set(false); return { data: this.convertToTableData(executions, Object.values(itemsMap)), truncated: false }; }), ); diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index 74af599d8..a60f911a5 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -3,19 +3,25 @@
@if (_state.summaryData$ | async; as summary) { + @if (_state.summaryWidgetLoading()) { + + } - } @else { - - - - - + + + + + + }
@if (_state.executionsChartSettings$ | async; as executionsChartSettings) { + @if (_state.executionsChartLoading()) { + + }
+
@@ -48,7 +55,13 @@ />
@if (reportNodesChartType() === 'keywords') { + @if (_state.keywordsCountChartLoading()) { + + } @if (_state.keywordsChartSettings$ | async; as settings) { + @if (_state.keywordsCountChartLoading()) { + + }
@@ -66,6 +79,9 @@ } } @else if (reportNodesChartType() === 'testcases') { @if (_state.testCasesChartSettings$ | async; as testCasesData) { + @if (_state.testCasesCountChartLoading()) { + + }
@@ -90,6 +106,9 @@
+ @if (_state.errorsTableLoading()) { + + } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.scss b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.scss index 0b6212f9c..a128aecde 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.scss +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.scss @@ -4,6 +4,16 @@ padding-top: 0; } +.spinner-container { + display: flex; + justify-content: center; + position: absolute; + width: 100%; + height: 100%; + align-items: center; + z-index: 100; +} + .header { display: flex; align-items: center; @@ -49,6 +59,7 @@ .piechart-container { width: 44rem; min-width: 44rem; + position: relative; } .row { @@ -121,12 +132,3 @@ .wide-menu { max-width: 400px; } - -.spinner-container { - display: flex; - width: 100%; - height: 100%; - flex-grow: 1; - align-items: center; - justify-content: center; -} From 7850c113d641373bfa3436800437218d69761b4f Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Sat, 20 Dec 2025 01:27:53 +0200 Subject: [PATCH 02/18] SED-4450-working on refresh in dashboard view --- .../cross-execution-dashboard.component.ts | 3 +++ .../heatmap/cross-execution-heatmap.component.html | 2 +- .../report/scheduler-report-view.component.html | 8 +++++--- .../report/scheduler-report-view.component.ts | 1 + .../chart-dashlet/chart-dashlet.component.html | 5 +++++ .../chart-dashlet/chart-dashlet.component.scss | 10 ++++++++++ .../chart-dashlet/chart-dashlet.component.ts | 7 +++++++ .../dashboard-page/dashboard-page.component.html | 1 + .../dashboard-page/dashboard-page.component.ts | 6 +++++- .../components/dashboard/dashboard.component.html | 3 +++ .../components/dashboard/dashboard.component.ts | 1 + .../table-dashlet/table-dashlet.component.html | 5 +++++ .../table-dashlet/table-dashlet.component.scss | 10 ++++++++++ .../table-dashlet/table-dashlet.component.ts | 7 +++++++ 14 files changed, 64 insertions(+), 5 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.ts index 4bfedbf15..697ad331a 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.ts @@ -53,14 +53,17 @@ export class CrossExecutionDashboardComponent implements OnInit { } handleRefreshIntervalChange(interval: number) { + this._state.lastRefreshTrigger.set('auto'); this._state.refreshInterval.set(interval); } handleTimeRangeChange(selection: TimeRangePickerSelection) { + this._state.lastRefreshTrigger.set('manual'); this._state.activeTimeRangeSelection.set(selection); } triggerRefresh() { + this._state.lastRefreshTrigger.set('auto'); this._state.activeTimeRangeSelection.set({ ...this._state.activeTimeRangeSelection()! }); this.fetchLastExecutionTrigger$.next(); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html index 7c34b7009..5e27f9ad2 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/cross-execution-heatmap.component.html @@ -1,6 +1,6 @@ - @if (isLoading()) { + @if (isLoading() && _state.lastRefreshTrigger() !== 'auto') {
diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index a60f911a5..477136ec8 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -129,7 +129,9 @@ -
- -
+ @if (_state.lastRefreshTrigger() !== 'auto') { +
+ +
+ }
diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts index 9948c0a8a..6eadad09c 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts @@ -103,6 +103,7 @@ export class SchedulerReportViewComponent implements OnInit { } handleMainChartZoom(timeRange: TimeRange) { + this._state.lastRefreshTrigger.set('manual'); this._state.executionsChartSettings$.pipe(take(1)).subscribe((chartSettings) => { const base = chartSettings.xAxesSettings.values[0]; const interval = chartSettings.xAxesSettings.values[1] - chartSettings.xAxesSettings.values[0]; diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html index 901945fd8..803ff7570 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html @@ -2,6 +2,11 @@ @if (!_internalSettings) { } @else { + @if (isLoading() && showLoadingSpinnerWhileLoading()) { +
+ +
+ } (false); readonly remove = output(); readonly shiftLeft = output(); readonly shiftRight = output(); readonly zoomReset = output(); + isLoading = signal(false); + groupingSelection: MetricAttributeSelection[] = []; selectedAggregate!: ChartAggregation; selectedAggregatePcl?: number; @@ -460,6 +465,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha axes: axes, truncated: response.truncated, }; + this.isLoading.set(false); }); } @@ -522,6 +528,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha } private fetchDataAndCreateChart(): Observable { + this.isLoading.set(true); const groupDimensions = this.getGroupDimensions(); const oqlFilter = this.composeRequestFilter(); this.requestOql = oqlFilter; diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html index 48133a6d8..90c31b857 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html @@ -42,6 +42,7 @@ @if (timeRange() && dashboard()?.id && !isLoading()) { (false); + lastRefreshTrigger = signal<'auto' | 'manual'>('manual'); timeRange = computed(() => { const pickerSelection = this.activeTimeRangeSelection(); @@ -112,7 +113,7 @@ export class DashboardPageComponent implements OnInit { } handleDashboardUpdate(dashboard: DashboardView) { - // this will make sure there are no conflicts between the dashboard entity shared across this page and actual dashboard component + // the dashboard is editable. this will make sure there are no conflicts between the dashboard entity shared across this page and actual dashboard component const mergedDashboard: DashboardView = { ...dashboard, attributes: this.dashboard!()!.attributes, @@ -127,6 +128,7 @@ export class DashboardPageComponent implements OnInit { } handleFullRangeChanged(range: TimeRange) { + this.lastRefreshTrigger.set('manual'); this.activeTimeRangeSelection.set({ type: 'ABSOLUTE', absoluteSelection: range }); } @@ -140,6 +142,7 @@ export class DashboardPageComponent implements OnInit { } handleTimeRangeChange(pickerSelection: TimeRangePickerSelection) { + this.lastRefreshTrigger.set('manual'); this.activeTimeRangeSelection.set(pickerSelection); } @@ -162,6 +165,7 @@ export class DashboardPageComponent implements OnInit { triggerRefresh() { let rangeSelection = this.activeTimeRangeSelection()!; + this.lastRefreshTrigger.set('auto'); this.activeTimeRangeSelection.set({ ...rangeSelection }); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index 82eec6b74..cf2dd5923 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -101,6 +101,7 @@ #chart [editMode]="editMode" [showExecutionLinks]="showExecutionLinks" + [showLoadingSpinnerWhileLoading]="showLoadingSpinnerOnLoad()" [item]="dashlet" [context]="mainEngine.state.context" [height]="DASHLET_HEIGHT" @@ -115,6 +116,7 @@
Compare
(); + showLoadingSpinnerOnLoad = input(false); timeRangeOptions = TimeSeriesConfig.ANALYTICS_TIME_SELECTION_OPTIONS; diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html index cda269398..a457c3e8d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html @@ -95,6 +95,11 @@
+ @if (isLoading() && showLoadingSpinnerWhileLoading()) { +
+ +
+ } (false); @Output() remove = new EventEmitter(); @Output() shiftLeft = new EventEmitter(); @Output() shiftRight = new EventEmitter(); + isLoading = signal(false); + private _timeSeriesService = inject(TimeSeriesService); private _matDialog = inject(MatDialog); private _timeSeriesEntityService = inject(TimeSeriesEntityService); @@ -132,6 +137,7 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } refresh(blur?: boolean): Observable { + this.isLoading.set(true); return this.fetchBaseData().pipe(tap(() => this.updateTableData())); } @@ -375,6 +381,7 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha this.fetchLegendEntities(tableEntries).subscribe((updatedData) => { this.tableData$.next(updatedData); this.tableIsLoading = false; + this.isLoading.set(false); }); } From 6e2a05294a61e1c2249d9234985be3046a9d47c2 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Tue, 23 Dec 2025 22:27:18 +0200 Subject: [PATCH 03/18] SED-4450-ts dashboard improvements --- .../chart-dashlet/chart-dashlet.component.ts | 3 +- .../dashboard-page.component.html | 1 + .../dashboard-page.component.ts | 19 ++-- .../components/dashboard/dashboard-state.ts | 1 + .../dashboard/dashboard.component.html | 11 ++- .../dashboard/dashboard.component.ts | 96 ++++++++++++------- .../table-dashlet/table-dashlet.component.ts | 1 + .../grouping/ts-grouping.component.ts | 1 + .../time-range-picker.component.ts | 1 + .../types/time-series/time-series-context.ts | 4 +- .../dashboard-filter-bar.component.ts | 25 +++-- .../filter-bar-item.component.ts | 1 + ...rformance-view-time-selection.component.ts | 1 + 13 files changed, 105 insertions(+), 60 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts index fd6750095..adeafae8d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts @@ -79,6 +79,7 @@ const resolutionLabels: Record = { TooltipContentDirective, ChartStandardTooltipComponent, ], + standalone: true, }) export class ChartDashletComponent extends ChartDashlet implements OnInit, OnChanges { private readonly stepped = uPlot.paths.stepped; // this is a function from uplot wich allows to draw 'stepped' or 'stairs like' lines @@ -110,7 +111,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha @Input() height!: number; @Input() editMode = false; @Input() showExecutionLinks = false; - showLoadingSpinnerWhileLoading = input(false); + showLoadingSpinnerWhileLoading = input(true); readonly remove = output(); readonly shiftLeft = output(); diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html index 90c31b857..7bacfbd1e 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html @@ -41,6 +41,7 @@ @if (timeRange() && dashboard()?.id && !isLoading()) { = signal(undefined); @@ -141,11 +143,6 @@ export class DashboardPageComponent implements OnInit { ); } - handleTimeRangeChange(pickerSelection: TimeRangePickerSelection) { - this.lastRefreshTrigger.set('manual'); - this.activeTimeRangeSelection.set(pickerSelection); - } - handleDashboardSettingsChange(context: TimeSeriesContext) { this._urlParamsService.updateUrlParamsFromContext( context, @@ -163,10 +160,18 @@ export class DashboardPageComponent implements OnInit { ); } + handleTimeRangeChange(pickerSelection: TimeRangePickerSelection) { + this.lastRefreshTrigger.set('manual'); + this.activeTimeRangeSelection.set(pickerSelection); + let timeRange = TimeSeriesUtils.convertSelectionToTimeRange(pickerSelection); + this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'manual' }); + } + triggerRefresh() { - let rangeSelection = this.activeTimeRangeSelection()!; + let pickerSelection = this.activeTimeRangeSelection()!; this.lastRefreshTrigger.set('auto'); - this.activeTimeRangeSelection.set({ ...rangeSelection }); + let timeRange = TimeSeriesUtils.convertSelectionToTimeRange(pickerSelection); + this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); } private subscribeToUrlNavigation() { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state.ts index 3300b1667..5dd74f188 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state.ts @@ -9,6 +9,7 @@ export interface DashboardState { getDashlets: () => QueryList; getFilterBar: () => DashboardFilterBarComponent; getRanger: () => PerformanceViewTimeSelectionComponent; + lastChangeType: 'auto' | 'manual'; refreshInProgress: boolean; refreshSubscription?: Subscription; } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index cf2dd5923..d1fb37610 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -41,7 +41,7 @@
} @else {
@if (!compareModeEnabled) { @@ -85,7 +87,7 @@ [editMode]="editMode" [context]="compareEngine!.state.context" [compactView]="compareModeEnabled" - (fullRangeChange)="handleCompareFullRangeChange($event)" + (fullRangeRequestChange)="handleCompareFullRangeChange($event)" />
} @@ -93,6 +95,7 @@
@if (mainEngine.state) { + {{ mainEngine.state.lastChangeType }}
@for (dashlet of dashboard.dashlets; let i = $index; track dashlet.name) { @if (dashlet.type === 'CHART') { @@ -101,7 +104,7 @@ #chart [editMode]="editMode" [showExecutionLinks]="showExecutionLinks" - [showLoadingSpinnerWhileLoading]="showLoadingSpinnerOnLoad()" + [showLoadingSpinnerWhileLoading]="mainEngine.state.lastChangeType === 'manual'" [item]="dashlet" [context]="mainEngine.state.context" [height]="DASHLET_HEIGHT" diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index 01646cb27..67599f08d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -114,10 +114,10 @@ export class DashboardComponent implements OnInit, OnDestroy { fullRangeSelected: boolean = true; timeRangeChangeEffect = effect(() => { - const timeRange = this.timeRange()!; - this.mainEngine?.state.context.updateFullTimeRange(timeRange); + // const timeRange = this.timeRange()!; + // this.mainEngine?.state.context.updateFullTimeRange(timeRange); // this.compareEngine?.state.context.updateFullTimeRange(timeRange); - this.refresh(); + // this.refresh(); }); /** @Output **/ @@ -143,6 +143,25 @@ export class DashboardComponent implements OnInit, OnDestroy { mainEngine!: DashboardStateEngine; compareEngine?: DashboardStateEngine; + public refresh() { + if (!this.compareModeEnabled && this.mainEngine) { + this.mainEngine.triggerRefresh(false); + this.compareEngine?.triggerRefresh(false); + } + } + + public updateFullTimeRange( + timeRange: TimeRange, + opts: { actionType: 'manual' | 'auto'; resetSelection?: boolean }, + ): void { + this.mainEngine.state.lastChangeType = opts.actionType; + this.mainEngine?.state.context.updateFullTimeRange(timeRange, opts.resetSelection); + } + + public getSelectedTimeRange(): TimeRange { + return this.mainEngine.state.context.timeRangeSettings.selectedRange; + } + ngOnInit(): void { const dashboardId = this.id(); const urlParams: DashboardUrlParams = this._urlParamsService.collectUrlParams(); @@ -157,21 +176,21 @@ export class DashboardComponent implements OnInit, OnDestroy { }); } - updateTimeRangeFromSelection() { + /** + * This method is used to notify the parent component that the user wants to change the full time-range, to his current sub-selection + * @protected + */ + protected updateFullTimeRangeFromSelection() { if (!this.fullRangeSelected) { this.fullRangeUpdateRequest.emit(this.mainEngine.state.context.getSelectedTimeRange()); } } - public getSelectedTimeRange(): TimeRange { - return this.mainEngine.state.context.timeRangeSettings.selectedRange; - } - - handleMainFullRangeChange(range: TimeRange) { + protected handleMainFullRangeChangeRequest(range: TimeRange) { this.fullRangeUpdateRequest.emit(range); } - handleCompareFullRangeChange(range: TimeRange) { + protected handleCompareFullRangeChange(range: TimeRange) { this.compareEngine?.state.context.updateFullRangeAndSelection(range); } @@ -181,7 +200,7 @@ export class DashboardComponent implements OnInit, OnDestroy { * 2. Stored state * 3. Dashboard object */ - initState(urlParams: DashboardUrlParams, dashboard: DashboardView): Observable { + private initState(urlParams: DashboardUrlParams, dashboard: DashboardView): Observable { this.dashboard = dashboard; const existingContext = this.storageId ? this._timeSeriesContextFactory.getContext(this.storageId) : undefined; const context$: Observable = existingContext @@ -202,7 +221,17 @@ export class DashboardComponent implements OnInit, OnDestroy { ); } - handleZoomReset() { + protected handleGroupingChange(groupDimensions: string[]) { + this.mainEngine.state.lastChangeType = 'manual'; + this.mainEngine.state.context.updateGrouping(groupDimensions); + } + + protected handleFiltersChange(filters: TsFilteringSettings) { + this.mainEngine.state.lastChangeType = 'manual'; + this.mainEngine.state.context.setFilteringSettings(filters); + } + + protected handleZoomReset() { this.zoomReset.emit(); this.mainEngine.state.context.resetZoom(); } @@ -215,6 +244,7 @@ export class DashboardComponent implements OnInit, OnDestroy { getDashlets: () => this.dashlets, getRanger: () => this.timeRanger!, refreshInProgress: false, + lastChangeType: 'auto', }; this.mainEngine = new DashboardStateEngine(state); this.mainEngine.subscribeForContextChange(); @@ -224,14 +254,7 @@ export class DashboardComponent implements OnInit, OnDestroy { } } - public refresh() { - if (!this.compareModeEnabled && this.mainEngine) { - this.mainEngine.triggerRefresh(false); - this.compareEngine?.triggerRefresh(false); - } - } - - handleResolutionChange(resolution: number) { + protected handleResolutionChange(resolution: number) { if (resolution > 0 && resolution < 1000) { // minimum value should be one second return; @@ -240,17 +263,17 @@ export class DashboardComponent implements OnInit, OnDestroy { this.compareEngine?.state.context.updateChartsResolution(resolution); } - enableEditMode() { + protected enableEditMode() { this.dashboardBackup = JSON.parse(JSON.stringify(this.dashboard)); this.editMode = true; } - cancelEditMode() { + protected cancelEditMode() { this.dashboard = { ...this.dashboardBackup }; this.editMode = false; } - saveEditChanges() { + protected saveEditChanges() { this.editMode = false; this.dashboard.grouping = this.mainEngine.state.context.getGroupDimensions(); // this.dashboard.timeRange = this.mainEngine.state.context.getTimeRangeSettings().pickerSelection; @@ -267,7 +290,7 @@ export class DashboardComponent implements OnInit, OnDestroy { // this.mainEngine.refreshAllCharts(false, true); } - addTableDashlet(metric: MetricType) { + protected addTableDashlet(metric: MetricType) { let tableItem: DashboardItem = { id: 'table-' + new Date().getTime(), type: 'TABLE', @@ -307,7 +330,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ]; } - addChartDashlet(metric: MetricType) { + protected addChartDashlet(metric: MetricType) { const newDashlet: DashboardItem = { id: 'chart-' + new Date().getTime(), name: metric.displayName, @@ -355,7 +378,7 @@ export class DashboardComponent implements OnInit, OnDestroy { } } - createContext( + private createContext( dashboard: DashboardView, urlParams: DashboardUrlParams, existingContext?: TimeSeriesContext, @@ -504,7 +527,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ); } - handleChartDelete(index: number) { + protected handleChartDelete(index: number) { const itemToDelete = this.dashboard.dashlets[index]; this.dashboard.dashlets.splice(index, 1); this.mainEngine.state.context.updateAttributes(this.collectAllAttributes()); @@ -516,7 +539,7 @@ export class DashboardComponent implements OnInit, OnDestroy { this.mainEngine.state.context.updateDashlets(this.dashboard.dashlets); } - handleChartShiftLeft(index: number) { + protected handleChartShiftLeft(index: number) { const listLength = this.dashboard.dashlets.length; let swapIndex = index - 1; if (index === 0) { @@ -529,7 +552,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ]; } - handleChartShiftRight(index: number) { + protected handleChartShiftRight(index: number) { const listLength = this.dashboard.dashlets.length; let swapIndex = index + 1; if (index === listLength - 1) { @@ -542,7 +565,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ]; } - toggleCompareMode() { + protected toggleCompareMode() { this.compareModeEnabled = !this.compareModeEnabled; if (this.compareModeEnabled) { this.enableCompareMode(); @@ -551,7 +574,7 @@ export class DashboardComponent implements OnInit, OnDestroy { } } - disableCompareMode() { + private disableCompareMode() { this.mainEngine.state.context.disableCompareMode(); this.dashlets.forEach((d) => { if (d.getType() === 'TABLE') { @@ -580,7 +603,7 @@ export class DashboardComponent implements OnInit, OnDestroy { return clonedSettings; } - enableCompareMode() { + private enableCompareMode() { const mainState = this.mainEngine.state; const mainTimeSettings = mainState.context.getTimeRangeSettings(); const compareModeContext = this._timeSeriesContextFactory.createContext({ @@ -605,6 +628,7 @@ export class DashboardComponent implements OnInit, OnDestroy { getFilterBar: () => this.compareFilterBar!, getRanger: () => this.compareTimeRanger!, refreshInProgress: false, + lastChangeType: 'auto', }; compareModeContext.settingsChange$.subscribe(() => { this.dashlets.forEach((d) => { @@ -623,11 +647,11 @@ export class DashboardComponent implements OnInit, OnDestroy { }); } - collectAllAttributes(): MetricAttribute[] { + private collectAllAttributes(): MetricAttribute[] { return this.dashboard.dashlets.flatMap((d) => d.attributes); } - removeOneTimeUrlParams() { + private removeOneTimeUrlParams() { const currentParams = { ...this._route.snapshot.queryParams }; currentParams[TimeSeriesConfig.DASHBOARD_URL_PARAMS_PREFIX + 'edit'] = null; @@ -639,7 +663,7 @@ export class DashboardComponent implements OnInit, OnDestroy { }); } - resetDashboard() { + protected resetDashboard() { this._timeSeriesContextFactory.destroyContext(this.storageId); this.dashboard = undefined as any; this.dashboardBackup = undefined as any; @@ -655,7 +679,7 @@ export class DashboardComponent implements OnInit, OnDestroy { }); } - exportRawData(): void { + protected exportRawData(): void { if (this.exportInProgress || !this.mainEngine.state.context) { return; } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts index 3a858d50e..23dea85a2 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts @@ -86,6 +86,7 @@ interface ProcessedBucketResponse { styleUrls: ['./table-dashlet.component.scss'], encapsulation: ViewEncapsulation.None, imports: [COMMON_IMPORTS, TsComparePercentagePipe, TableEntryFormatPipe, MatTooltip], + standalone: true, }) export class TableDashletComponent extends ChartDashlet implements OnInit, OnChanges { readonly COMPARE_COLUMN_ID_SUFFIX = '_comp'; diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/grouping/ts-grouping.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/grouping/ts-grouping.component.ts index 8c6ad0c5b..7bd112450 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/grouping/ts-grouping.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/grouping/ts-grouping.component.ts @@ -9,6 +9,7 @@ const EMPTY_DIMENSIONS_LABEL = 'Empty'; templateUrl: './ts-grouping.component.html', styleUrls: ['./ts-grouping.component.scss'], imports: [COMMON_IMPORTS], + standalone: true, }) export class TsGroupingComponent implements OnInit, OnChanges { readonly NO_GROUPING_OPTION = { label: 'Empty', attributes: [] }; diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.ts index f0096ff8d..66362e6dc 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.ts @@ -28,6 +28,7 @@ import { TIME_UNIT_DICTIONARY, TimeConvertersFactoryService, TimeRange, TimeUnit templateUrl: './time-range-picker.component.html', styleUrls: ['./time-range-picker.component.scss'], imports: [COMMON_IMPORTS], + standalone: true, }) export class TimeRangePickerComponent implements OnInit { timeUnitOptions = [TimeUnit.MINUTE, TimeUnit.HOUR, TimeUnit.DAY, TimeUnit.WEEK, TimeUnit.MONTH, TimeUnit.YEAR]; diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts index 3bde3119d..eae28f77e 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts @@ -110,9 +110,9 @@ export class TimeSeriesContext { * If there is a selection before changing the full range, and it fits inside it, the selection will not be reset. Otherwise it does. * @param range */ - updateFullTimeRange(range: TimeRange) { + updateFullTimeRange(range: TimeRange, resetSelection?: boolean) { range = TimeSeriesUtils.removeFloatingDigits(range); - const isFullRangeSelected = this.isFullRangeSelected(); + const isFullRangeSelected = this.isFullRangeSelected() || resetSelection; const previousSelection = this.timeRangeSettings.selectedRange; if (isFullRangeSelected || !TimeSeriesUtils.intervalIsInside(range, previousSelection)) { // reset it diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts index 90e46d478..e22bccc97 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts @@ -60,6 +60,7 @@ const ATTRIBUTES_REMOVAL_FUNCTION = (field: string) => { styleUrls: ['./dashboard-filter-bar.component.scss'], encapsulation: ViewEncapsulation.None, imports: [COMMON_IMPORTS, TsGroupingComponent, FilterBarItemComponent, MatDivider], + standalone: true, }) export class DashboardFilterBarComponent implements OnInit, OnDestroy { context = input.required(); @@ -69,14 +70,18 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { @Input() compactView = false; @Input() editMode = false; - compareModeEnabled = false; - - readonly fullRangeChange = output(); + /** + * Output used to notify when the full time range of the dashboard should be changed. This is currently triggered only via "use selected execution's time range" filters. + */ + readonly fullRangeRequestChange = output(); @ViewChild(PerformanceViewTimeSelectionComponent) timeSelection?: PerformanceViewTimeSelectionComponent; @ViewChildren(FilterBarItemComponent) filterComponents?: QueryList; @ViewChildren('appliedFilter', { read: ElementRef }) appliedFilters?: QueryList>; + filtersChange = output(); + groupingChange = output(); + private _destroyRef = inject(DestroyRef); filterOptions: MetricAttribute[] = []; // dashboard attributes that are not used in filters yet @@ -123,9 +128,6 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { if (contextInput.getGroupDimensions()) { this.activeGrouping = contextInput.getGroupDimensions(); } - contextInput.compareModeChange$ - .pipe(takeUntilDestroyed(this._destroyRef)) - .subscribe((settings) => (this.compareModeEnabled = settings.enabled)); let contextFiltering = contextInput.getFilteringSettings(); this.oqlValue = contextFiltering.oql || ''; this.activeMode = contextFiltering.mode; @@ -231,7 +233,8 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { this.rawMeasurementsModeActive = response.hasUnknownFields; // for grouping change, we will trigger refresh automatically. otherwise grouping and filters will change together if (!this.rawMeasurementsModeActive) { - this.context().updateGrouping(dimensions); + this.groupingChange.emit(dimensions); + // this.context().updateGrouping(dimensions); } else { // wait for the manual apply } @@ -245,12 +248,14 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { hiddenFilters: this.context().getFilteringSettings().hiddenFilters, oql: this.oqlValue, }; - this.context().setFilteringSettings(settings); + this.filtersChange.emit(settings); + // this.context().setFilteringSettings(settings); } manuallyApplyFilters() { if (this.haveNewGrouping()) { - this.context().updateGrouping(this.activeGrouping); + this.groupingChange.emit(this.activeGrouping); + // this.context().updateGrouping(this.activeGrouping); } if (this.activeMode === TsFilteringMode.STANDARD) { this.emitFiltersChange(); @@ -286,7 +291,7 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { if (item.updateTimeSelectionOnFilterChange && item.searchEntities.length > 0) { // calculate the new time range. if all the entities were deleted, keep the last range. const newRange = this.getExecutionsTimeRange(item); - this.fullRangeChange.emit(newRange); + this.fullRangeRequestChange.emit(newRange); } this.emitFilterChange$.next(); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/filter-bar-item/filter-bar-item.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/filter-bar-item/filter-bar-item.component.ts index 9b9ae860f..b13266b05 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/filter-bar-item/filter-bar-item.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/filter-bar-item/filter-bar-item.component.ts @@ -25,6 +25,7 @@ import { Execution, ExecutiontTaskParameters, Plan } from '@exense/step-core'; templateUrl: './filter-bar-item.component.html', styleUrls: ['./filter-bar-item.component.scss'], imports: [COMMON_IMPORTS, FilterBarPlanItemComponent, FilterBarTaskItemComponent, FilterBarExecutionItemComponent], + standalone: true, }) export class FilterBarItemComponent implements OnInit, OnChanges, AfterViewInit { @Input() item!: FilterBarItem; // should not make edits on it diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts index 158ae505c..4a40057f2 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts @@ -29,6 +29,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; styleUrls: ['./performance-view-time-selection.component.scss'], encapsulation: ViewEncapsulation.None, imports: [COMMON_IMPORTS, TSRangerComponent], + standalone: true, }) export class PerformanceViewTimeSelectionComponent implements OnInit { @Input() context!: TimeSeriesContext; From a0cae89b6cab2f09a79ec659a9adb7b23e3e9eaa Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Thu, 25 Dec 2025 01:19:11 +0200 Subject: [PATCH 04/18] SED-4450-analyics improvements --- .../components/dashboard/dashboard.component.html | 6 +++++- .../performance-view-time-selection.component.html | 5 +++++ .../performance-view-time-selection.component.scss | 11 +++++++++++ .../performance-view-time-selection.component.ts | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index d1fb37610..82b4aedc5 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -29,7 +29,11 @@
- +
@if (compareModeEnabled) {
+ @if (showSpinnerWhileLoading() && isLoading()) { +
+ +
+ } }
diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.scss b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.scss index d5a178b08..6321a550e 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.scss +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.scss @@ -49,4 +49,15 @@ step-execution-time-selection { .time-picker-container { margin-right: 0.2rem; } + + .spinner-container { + display: flex; + justify-content: center; + position: absolute; + width: 100%; + height: 62px; + align-items: center; + z-index: 100; + top: 0; + } } diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts index 4a40057f2..081477895 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/perfomance-view-time-selection/performance-view-time-selection.component.ts @@ -3,9 +3,11 @@ import { DestroyRef, EventEmitter, inject, + input, Input, OnInit, Output, + signal, ViewChild, ViewEncapsulation, } from '@angular/core'; @@ -22,6 +24,7 @@ import { TSRangerSettings } from '../ranger/ts-ranger-settings'; import { TSRangerComponent } from '../ranger/ts-ranger.component'; import { FindBucketsRequestBuilder } from '../../types/find-buckets-request-builder'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { sign } from 'chart.js/helpers'; @Component({ selector: 'step-execution-time-selection', @@ -33,6 +36,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; }) export class PerformanceViewTimeSelectionComponent implements OnInit { @Input() context!: TimeSeriesContext; + showSpinnerWhileLoading = input(false); @Output() rangerLoaded = new EventEmitter(); @@ -44,6 +48,8 @@ export class PerformanceViewTimeSelectionComponent implements OnInit { private _timeSeriesService = inject(TimeSeriesService); private _destroyRef = inject(DestroyRef); + isLoading = signal(false); + ngOnInit(): void { if (!this.context) { throw new Error('Context input is required'); @@ -73,6 +79,7 @@ export class PerformanceViewTimeSelectionComponent implements OnInit { } createRanger(context: TimeSeriesContext): Observable { + this.isLoading.set(true); const customFiltering = JSON.parse(JSON.stringify(this.context.getFilteringSettings())) as TsFilteringSettings; customFiltering.filterItems = []; // ignore visible filters. const request = new FindBucketsRequestBuilder() @@ -105,6 +112,7 @@ export class PerformanceViewTimeSelectionComponent implements OnInit { }, ], }; + this.isLoading.set(false); }), ); } From 59a2e42a1f7b18fc82f6ca603b3ff0b4d5574782 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Mon, 29 Dec 2025 02:11:17 +0200 Subject: [PATCH 05/18] SED-4450-cleanup --- .../report/scheduler-report-view.component.html | 6 ------ .../timeseries/components/dashboard/dashboard.component.ts | 7 ------- .../dashboard-filter-bar/dashboard-filter-bar.component.ts | 1 - 3 files changed, 14 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index a3366d93c..c0ef539ba 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -7,12 +7,6 @@ } - - - - - - }
diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index 67599f08d..64f559e18 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -113,13 +113,6 @@ export class DashboardComponent implements OnInit, OnDestroy { fullRangeSelected: boolean = true; - timeRangeChangeEffect = effect(() => { - // const timeRange = this.timeRange()!; - // this.mainEngine?.state.context.updateFullTimeRange(timeRange); - // this.compareEngine?.state.context.updateFullTimeRange(timeRange); - // this.refresh(); - }); - /** @Output **/ readonly contextSettingsChanged = output(); // used to detect any change, useful for url updates readonly contextSettingsInit = output(); // emit only first time when the context is created diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts index e22bccc97..ee5a7593d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/filter-bar/components/dashboard-filter-bar/dashboard-filter-bar.component.ts @@ -249,7 +249,6 @@ export class DashboardFilterBarComponent implements OnInit, OnDestroy { oql: this.oqlValue, }; this.filtersChange.emit(settings); - // this.context().setFilteringSettings(settings); } manuallyApplyFilters() { From 605a6b3b1e6ea9ece8b5cb5ba5cfc536a5e0bed9 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Wed, 31 Dec 2025 22:23:21 +0200 Subject: [PATCH 06/18] SED-4450-fixes on cross execution analytics --- .../alt-execution-analytics.component.ts | 6 ++- .../cross-execution-dashboard-state.ts | 42 +++++++++++++------ .../cross-execution-dashboard.component.html | 4 +- .../cross-execution-dashboard.component.ts | 11 ----- .../scheduler-performance-view.component.html | 1 + .../scheduler-performance-view.component.ts | 13 +++++- .../dashboard/dashboard.component.html | 2 +- .../dashboard/dashboard.component.ts | 15 +++---- .../execution-dashboard.component.ts | 12 +++++- 9 files changed, 70 insertions(+), 36 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts index 7c0cb630e..76b377728 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, DestroyRef, inject, OnDestroy, OnInit, viewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, DestroyRef, effect, inject, OnDestroy, OnInit, viewChild } from '@angular/core'; import { AltExecutionStateService } from '../../services/alt-execution-state.service'; import { TimeSeriesContext } from '../../../timeseries/modules/_common'; import { DashboardUrlParamsService } from '../../../timeseries/modules/_common/injectables/dashboard-url-params.service'; @@ -24,6 +24,10 @@ export class AltExecutionAnalyticsComponent implements OnInit { activeTimeRangeSelection = toSignal(this._state.timeRangeSelection$); + effect = effect(() => { + console.log(this.activeTimeRangeSelection()); + }); + handleDashboardSettingsChange(context: TimeSeriesContext) { this._urlParamsService.updateUrlParamsFromContext(context, this.activeTimeRangeSelection()!, undefined, false); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index bf54cb5d3..6ba70cbae 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -65,6 +65,8 @@ export abstract class CrossExecutionDashboardState { readonly task = signal(undefined); readonly plan = signal(undefined); readonly lastRefreshTrigger = signal<'init' | 'manual' | 'auto'>('init'); + readonly onRefreshTriggered = new Subject(); + readonly onTimeSelectionChanged = new Subject(); // view settings activeTimeRangeSelection = signal(undefined); @@ -83,8 +85,10 @@ export abstract class CrossExecutionDashboardState { keywordsCountChartLoading = signal(false); errorsTableLoading = signal(false); - updateTimeRangeSelection(selection: TimeRangePickerSelection) { + public updateTimeRangeSelection(selection: TimeRangePickerSelection) { + this.lastRefreshTrigger.set('manual'); this.activeTimeRangeSelection.set(selection); + this.onTimeSelectionChanged.next(this.convertSelectionToTimeRange(selection)); } updateRefreshInterval(interval: number): void { @@ -95,18 +99,20 @@ export abstract class CrossExecutionDashboardState { filter((value): value is TimeRangePickerSelection => value != null), ); + public triggerRefresh() { + this.lastRefreshTrigger.set('auto'); + this.activeTimeRangeSelection.set({ ...this.activeTimeRangeSelection()! }); + this.fetchLastExecutionTrigger$.next(); + let timeRangeSelection = this.activeTimeRangeSelection(); + if (!timeRangeSelection) { + return; + } + const timeRange = this.convertSelectionToTimeRange(timeRangeSelection); + this.onRefreshTriggered.next(timeRange); + } + readonly timeRange$: Observable = this.timeRangeSelection$.pipe( - map((rangeSelection) => { - switch (rangeSelection.type) { - case 'FULL': - throw new Error('Full range is not supported'); - case 'ABSOLUTE': - return rangeSelection.absoluteSelection!; - case 'RELATIVE': - const endTime = new Date().getTime(); - return { from: endTime - rangeSelection.relativeSelection!.timeInMs, to: endTime }; - } - }), + map(this.convertSelectionToTimeRange), filter((range): range is TimeRange => range !== undefined), shareReplay(1), ) as Observable; @@ -118,6 +124,18 @@ export abstract class CrossExecutionDashboardState { shareReplay(1), ); + private convertSelectionToTimeRange(rangeSelection: TimeRangePickerSelection) { + switch (rangeSelection.type) { + case 'FULL': + throw new Error('Full range is not supported'); + case 'ABSOLUTE': + return rangeSelection.absoluteSelection!; + case 'RELATIVE': + const endTime = new Date().getTime(); + return { from: endTime - rangeSelection.relativeSelection!.timeInMs, to: endTime }; + } + } + // charts readonly executionsDurationTimeSeriesData = this.timeRange$.pipe( diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.html index 5c89c0ce9..db0e7d80a 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard.component.html @@ -50,13 +50,13 @@

@if (_state.activeTimeRangeSelection()) {
(undefined); + + private dashboardComponent = viewChild('dashboardComponent', { read: DashboardComponent }); + isLoading = false; dashboardId?: string; @@ -35,6 +39,13 @@ export class SchedulerPerformanceViewComponent implements OnInit { ngOnInit(): void { this.subscribeToUrlNavigation(); + this._state.onRefreshTriggered.subscribe((timeRange) => { + this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); + }); + this._state.onTimeSelectionChanged.subscribe((timeRange) => { + this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'manual' }); + }); + // TODO unsubscribe } handleDashboardSettingsChange(context: TimeSeriesContext) { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index 82b4aedc5..30c151c81 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -45,7 +45,7 @@
} @else {
Date: Thu, 1 Jan 2026 19:09:52 +0200 Subject: [PATCH 07/18] SED-4450-working on execution view --- .../alt-execution-analytics.component.ts | 2 +- .../scheduler-performance-view.component.html | 2 +- .../scheduler-performance-view.component.ts | 7 +++---- .../dashboard-page.component.html | 2 +- .../dashboard/dashboard.component.ts | 6 +++--- .../execution-dashboard.component.html | 3 ++- .../execution-dashboard.component.ts | 19 +++++++++---------- 7 files changed, 20 insertions(+), 21 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts index 76b377728..aa1f5a817 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts @@ -25,7 +25,7 @@ export class AltExecutionAnalyticsComponent implements OnInit { activeTimeRangeSelection = toSignal(this._state.timeRangeSelection$); effect = effect(() => { - console.log(this.activeTimeRangeSelection()); + this.activeTimeRangeSelection(); }); handleDashboardSettingsChange(context: TimeSeriesContext) { diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/performance/scheduler-performance-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/performance/scheduler-performance-view.component.html index 54c9327ec..e5d7c3cbc 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/performance/scheduler-performance-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/performance/scheduler-performance-view.component.html @@ -3,7 +3,7 @@ { + this._state.onRefreshTriggered.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((timeRange) => { this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); }); - this._state.onTimeSelectionChanged.subscribe((timeRange) => { + this._state.onTimeSelectionChanged.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((timeRange) => { this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'manual' }); }); - // TODO unsubscribe } handleDashboardSettingsChange(context: TimeSeriesContext) { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html index 7bacfbd1e..257dec6a0 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard-page/dashboard-page.component.html @@ -44,7 +44,7 @@ #dashboardComponent [id]="dashboard()!.id!" [showLoadingSpinnerOnLoad]="lastRefreshTrigger() === 'manual'" - [timeRange]="timeRange()!" + [initialTimeRange]="timeRange()!" (contextSettingsChanged)="handleDashboardSettingsChange($event)" (contextSettingsInit)="handleDashboardSettingsInit($event)" [showExecutionLinks]="true" diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index 695dd3519..32c84326d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -106,7 +106,7 @@ export class DashboardComponent implements OnInit, OnDestroy { @Input() editable: boolean = true; @Input() hiddenFilters: FilterBarItem[] = []; @Input() showExecutionLinks = true; - timeRange = input.required(); + initialTimeRange = input.required(); showLoadingSpinnerOnLoad = input(false); timeRangeOptions = TimeSeriesConfig.ANALYTICS_TIME_SELECTION_OPTIONS; @@ -366,8 +366,8 @@ export class DashboardComponent implements OnInit, OnDestroy { return existingSettings!; } else { return { - fullRange: this.timeRange()!, - selectedRange: urlParams.selectedTimeRange || this.timeRange()!, + fullRange: this.initialTimeRange()!, + selectedRange: urlParams.selectedTimeRange || this.initialTimeRange()!, }; } } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html index 15082997a..68fea9dd4 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html @@ -1,7 +1,8 @@ @if (isInitialized && timeRange()) { (); // used to detect any change, useful for url updates readonly contextSettingsInit = output(); // emit only first time when the context is created - @ViewChild(DashboardComponent) dashboard!: DashboardComponent; + private dashboardComponent = viewChild('dashboardComponent', { read: DashboardComponent }); private _authService = inject(AuthService); protected _executionViewModeService = inject(ExecutionViewModeService); @@ -64,13 +65,11 @@ export class ExecutionDashboardComponent implements OnInit, OnChanges { private _destroyRef = inject(DestroyRef); - public refresh() {} - public updateFullTimeRange( timeRange: TimeRange, opts: { actionType: 'manual' | 'auto'; resetSelection?: boolean }, ): void { - // this.dashboard.updateFullTimeRange() + this.dashboardComponent()?.updateFullTimeRange(timeRange, opts); } ngOnInit(): void { @@ -105,20 +104,20 @@ export class ExecutionDashboardComponent implements OnInit, OnChanges { } public getSelectedTimeRange() { - return this.dashboard.getSelectedTimeRange(); + return this.dashboardComponent()!.getSelectedTimeRange(); } - getExecutionRange(execution: Execution): Partial { - return { from: execution.startTime, to: execution.endTime }; + getExecutionRange(execution: Execution): TimeRange { + return { from: execution.startTime!, to: execution.endTime || new Date().getTime() }; } ngOnChanges(changes: SimpleChanges): void { + // we do the refresh based on execution change const executionChange = changes['execution']; if (executionChange?.currentValue !== executionChange?.previousValue && !executionChange?.firstChange) { this.executionMode = this._executionViewModeService.getExecutionMode(this.execution()); - this.executionRange = this.getExecutionRange(this.execution()); - // this.dashboard?.refresh(); - // TODO + const timeRange = this.getExecutionRange(this.execution()); + this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); } } From e2965edf6ca25dc9989dd6eb5f31667c6d46060c Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Fri, 2 Jan 2026 00:04:45 +0200 Subject: [PATCH 08/18] SED-4450-loading fixes for compare mode & other features --- .../alt-execution-analytics.component.html | 2 +- .../alt-execution-analytics.component.ts | 34 ++++++++++++++++++- .../legacy-execution-view.component.html | 2 +- .../dashboard-page.component.html | 1 - .../dashboard-page.component.ts | 4 --- .../dashboard/dashboard-state-engine.ts | 1 + .../dashboard/dashboard.component.html | 14 +++++--- .../dashboard/dashboard.component.ts | 21 +++++++----- .../execution-dashboard.component.html | 4 +-- .../execution-dashboard.component.ts | 6 ++-- .../time-range-picker.component.scss | 1 + 11 files changed, 64 insertions(+), 26 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.html b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.html index 0daecfb26..81d4bbcf2 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.html @@ -3,7 +3,7 @@ active.timeRangeSelectionChange$.pipe(withLatestFrom(active.execution$))), + ) + .subscribe(([tr, execution]) => { + if (!tr || !execution) { + return; + } + const timeRange = convertPickerSelectionToTimeRange(tr, execution); + this.dashboardComponent()?.updateFullTimeRange(timeRange!, { actionType: 'manual' }); + }); + + readonly autoRefreshTriggered = this._activeExecutionContext.activeExecution$ + .pipe( + takeUntilDestroyed(), + switchMap((active) => + active.execution$.pipe(map((execution) => [execution, active.getTimeRangeSelection()] as const)), + ), + ) + .subscribe(([execution, timeRangeSelection]) => { + if (!timeRangeSelection || !execution) { + return; + } + const timeRange = convertPickerSelectionToTimeRange(timeRangeSelection, execution); + this.dashboardComponent()?.updateFullTimeRange(timeRange!, { actionType: 'auto' }); + }); + effect = effect(() => { this.activeTimeRangeSelection(); }); diff --git a/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html index f8bf35b55..271e274e8 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html @@ -10,7 +10,7 @@ (false); - lastRefreshTrigger = signal<'auto' | 'manual'>('manual'); timeRange = computed(() => { const pickerSelection = this.activeTimeRangeSelection(); @@ -130,7 +129,6 @@ export class DashboardPageComponent implements OnInit { } handleFullRangeChanged(range: TimeRange) { - this.lastRefreshTrigger.set('manual'); this.activeTimeRangeSelection.set({ type: 'ABSOLUTE', absoluteSelection: range }); } @@ -161,7 +159,6 @@ export class DashboardPageComponent implements OnInit { } handleTimeRangeChange(pickerSelection: TimeRangePickerSelection) { - this.lastRefreshTrigger.set('manual'); this.activeTimeRangeSelection.set(pickerSelection); let timeRange = TimeSeriesUtils.convertSelectionToTimeRange(pickerSelection); this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'manual' }); @@ -169,7 +166,6 @@ export class DashboardPageComponent implements OnInit { triggerRefresh() { let pickerSelection = this.activeTimeRangeSelection()!; - this.lastRefreshTrigger.set('auto'); let timeRange = TimeSeriesUtils.convertSelectionToTimeRange(pickerSelection); this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state-engine.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state-engine.ts index 70552e534..54619218c 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state-engine.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard-state-engine.ts @@ -52,6 +52,7 @@ export class DashboardStateEngine { fullRange: timeRange, selectedRange: timeRange, }; + this.state.lastChangeType = 'manual'; this.state.context.updateTimeRangeSettings(timeRangeSettings); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index 30c151c81..77620e896 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -63,8 +63,8 @@ [compactView]="compareModeEnabled" [showHiddenFilters]="compareModeEnabled" (fullRangeRequestChange)="handleMainFullRangeChangeRequest($event)" - (groupingChange)="handleGroupingChange($event)" - (filtersChange)="handleFiltersChange($event)" + (groupingChange)="handleGroupingChange(mainEngine, $event)" + (filtersChange)="handleFiltersChange(mainEngine, $event)" /> @if (!compareModeEnabled) { @@ -90,8 +90,10 @@ #compareFilterBar [editMode]="editMode" [context]="compareEngine!.state.context" - [compactView]="compareModeEnabled" + [compactView]="true" (fullRangeRequestChange)="handleCompareFullRangeChange($event)" + (groupingChange)="handleGroupingChange(compareEngine!, $event)" + (filtersChange)="handleFiltersChange(compareEngine!, $event)" />
} @@ -123,7 +125,7 @@
Compare
(); - showLoadingSpinnerOnLoad = input(false); + // showLoadingSpinnerOnLoad = input(false); timeRangeOptions = TimeSeriesConfig.ANALYTICS_TIME_SELECTION_OPTIONS; @@ -176,6 +176,7 @@ export class DashboardComponent implements OnInit, OnDestroy { */ protected emitFullRangeUpdateRequest() { if (!this.fullRangeSelected) { + this.mainEngine.state.lastChangeType = 'manual'; this.fullRangeUpdateRequest.emit(this.mainEngine.state.context.getSelectedTimeRange()); } } @@ -215,14 +216,14 @@ export class DashboardComponent implements OnInit, OnDestroy { ); } - protected handleGroupingChange(groupDimensions: string[]) { - this.mainEngine.state.lastChangeType = 'manual'; - this.mainEngine.state.context.updateGrouping(groupDimensions); + protected handleGroupingChange(engine: DashboardStateEngine, groupDimensions: string[]) { + engine.state.lastChangeType = 'manual'; + engine.state.context.updateGrouping(groupDimensions); } - protected handleFiltersChange(filters: TsFilteringSettings) { - this.mainEngine.state.lastChangeType = 'manual'; - this.mainEngine.state.context.setFilteringSettings(filters); + protected handleFiltersChange(engine: DashboardStateEngine, filters: TsFilteringSettings) { + engine.state.lastChangeType = 'manual'; + engine.state.context.setFilteringSettings(filters); } protected handleZoomReset() { @@ -253,8 +254,12 @@ export class DashboardComponent implements OnInit, OnDestroy { // minimum value should be one second return; } + this.mainEngine.state.lastChangeType = 'manual'; this.mainEngine.state.context.updateChartsResolution(resolution); - this.compareEngine?.state.context.updateChartsResolution(resolution); + if (this.compareEngine) { + this.compareEngine.state.lastChangeType = 'manual'; + this.compareEngine.state.context.updateChartsResolution(resolution); + } } protected enableEditMode() { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html index 68fea9dd4..8b556bac9 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/execution-page/execution-dashboard.component.html @@ -1,8 +1,8 @@ -@if (isInitialized && timeRange()) { +@if (isInitialized && initialTimeRange()) { (); - timeRange = input.required(); + initialTimeRange = input.required(); readonly fullRangeUpdateRequest = output(); readonly contextSettingsChanged = output(); // used to detect any change, useful for url updates @@ -116,8 +116,8 @@ export class ExecutionDashboardComponent implements OnInit, OnChanges { const executionChange = changes['execution']; if (executionChange?.currentValue !== executionChange?.previousValue && !executionChange?.firstChange) { this.executionMode = this._executionViewModeService.getExecutionMode(this.execution()); - const timeRange = this.getExecutionRange(this.execution()); - this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); + // const timeRange = this.getExecutionRange(this.execution()); + // this.dashboardComponent()?.updateFullTimeRange(timeRange, { actionType: 'auto' }); } } diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.scss b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.scss index 940edb71b..6a4fae23d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.scss +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/components/time-range-picker/time-range-picker.component.scss @@ -150,6 +150,7 @@ &.compact-btn { height: 40px; + min-width: unset; padding: 0 4px; width: 40px; display: flex; From ac3cb9dff415253ebe28896956008608acd366fe Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Fri, 2 Jan 2026 01:23:55 +0200 Subject: [PATCH 09/18] SED-4450-update the legacy execution page --- .../legacy-execution-view.component.html | 3 +- .../legacy-execution-view.component.ts | 44 ++++++++++++------- .../dashboard/dashboard.component.html | 1 - .../dashboard/dashboard.component.ts | 11 ----- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html index 271e274e8..c55c9bf30 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/dashlet-execution-viz/wrapper/legacy-execution-view.component.html @@ -2,13 +2,14 @@
(); - private _router: Router = inject(Router); private _destroyRef = inject(DestroyRef); private _changeDetectorRef = inject(ChangeDetectorRef); @@ -34,6 +38,22 @@ export class LegacyExecutionViewComponent implements OnInit { private _authService = inject(AuthService); private _urlParamsService = inject(DashboardUrlParamsService); + private dashboardComponent = viewChild('dashboardComponent', { read: ExecutionDashboardComponent }); + + execution = input.required(); + + executionChangeEffect = effect(() => { + // handle autorefresh + let execution = this.execution(); + if (!execution) return; + + untracked(() => { + let timeRangeSelection = this.activeTimeRangeSelection(); + const timeRange = convertPickerSelectionToTimeRange(timeRangeSelection!, execution); + this.dashboardComponent()?.updateFullTimeRange(timeRange!, { actionType: 'auto' }); + }); + }); + readonly timeRangeOptions: TimeRangePickerSelection[] = [ { type: 'FULL' }, ...TimeSeriesConfig.EXECUTION_PAGE_TIME_SELECTION_OPTIONS, @@ -48,14 +68,9 @@ export class LegacyExecutionViewComponent implements OnInit { timeRange: Signal = computed(() => { const pickerSelection = this.activeTimeRangeSelection(); - if (pickerSelection) { - const execution = this.execution(); - const end = execution.endTime || new Date().getTime(); - if (pickerSelection.type === 'FULL') { - return { from: execution.startTime!, to: end }; - } else { - return TimeSeriesUtils.convertSelectionToTimeRange(pickerSelection, end); - } + const execution = this.execution(); + if (pickerSelection && execution) { + return convertPickerSelectionToTimeRange(pickerSelection, this.execution()); } else { return undefined; } @@ -113,13 +128,10 @@ export class LegacyExecutionViewComponent implements OnInit { ); } - handleTimeRangeChange(pickerSelection: TimeRangePickerSelection) { + handleTimeRangeSelectionChange(pickerSelection: TimeRangePickerSelection) { this.activeTimeRangeSelection.set(pickerSelection); - } - - triggerRefresh() { - let rangeSelection = this.activeTimeRangeSelection()!; - this.activeTimeRangeSelection.set({ ...rangeSelection }); + let timeRange = convertPickerSelectionToTimeRange(pickerSelection, this.execution()); + this.dashboardComponent()?.updateFullTimeRange(timeRange!, { actionType: 'manual' }); } private subscribeToUrlNavigation() { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html index 77620e896..3127cd938 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.html @@ -101,7 +101,6 @@
@if (mainEngine.state) { - {{ mainEngine.state.lastChangeType }}
@for (dashlet of dashboard.dashlets; let i = $index; track dashlet.name) { @if (dashlet.type === 'CHART') { diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index f1769df5f..4f5121ac7 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -107,7 +107,6 @@ export class DashboardComponent implements OnInit, OnDestroy { @Input() hiddenFilters: FilterBarItem[] = []; @Input() showExecutionLinks = true; initialTimeRange = input.required(); - // showLoadingSpinnerOnLoad = input(false); timeRangeOptions = TimeSeriesConfig.ANALYTICS_TIME_SELECTION_OPTIONS; @@ -136,13 +135,6 @@ export class DashboardComponent implements OnInit, OnDestroy { mainEngine!: DashboardStateEngine; compareEngine?: DashboardStateEngine; - // public refresh() { - // if (!this.compareModeEnabled && this.mainEngine) { - // // this.mainEngine.triggerRefresh(false); - // // this.compareEngine?.triggerRefresh(false); - // } - // } - public updateFullTimeRange( timeRange: TimeRange, opts: { actionType: 'manual' | 'auto'; resetSelection?: boolean }, @@ -275,7 +267,6 @@ export class DashboardComponent implements OnInit, OnDestroy { protected saveEditChanges() { this.editMode = false; this.dashboard.grouping = this.mainEngine.state.context.getGroupDimensions(); - // this.dashboard.timeRange = this.mainEngine.state.context.getTimeRangeSettings().pickerSelection; this.dashboard.resolution = this.resolution; this.dashboard.filters = this.filterBar?._internalFilters.map((item) => { @@ -285,8 +276,6 @@ export class DashboardComponent implements OnInit, OnDestroy { }) || []; this.dashboardUpdate.emit(this.dashboard); - // this._dashboardService.saveDashboard(this.dashboard).subscribe((response) => {}); - // this.mainEngine.refreshAllCharts(false, true); } protected addTableDashlet(metric: MetricType) { From 44d7d701e7671091fbacdbbc965818aba2700d7e Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Wed, 7 Jan 2026 11:33:10 +0200 Subject: [PATCH 10/18] SED-4450-added table loading --- .../cross-execution-dashboard-state.ts | 3 ++- .../components/table-dashlet/table-dashlet.component.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index 6ba70cbae..9dbca23be 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -64,7 +64,7 @@ export abstract class CrossExecutionDashboardState { readonly task = signal(undefined); readonly plan = signal(undefined); - readonly lastRefreshTrigger = signal<'init' | 'manual' | 'auto'>('init'); + readonly lastRefreshTrigger = signal<'manual' | 'auto'>('manual'); readonly onRefreshTriggered = new Subject(); readonly onTimeSelectionChanged = new Subject(); @@ -308,6 +308,7 @@ export abstract class CrossExecutionDashboardState { const statusAttribute = 'status'; const executionIdAttribute = 'executionId'; if (executions.length === 0) { + this.keywordsCountChartLoading.set(false); return of(this.createKeywordsChart([], [])); } else { const executionsIdsJoined = diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts index 23dea85a2..175bcada9 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts @@ -305,6 +305,7 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } private fetchBaseData(): Observable { + this.isLoading.set(true); return this.fetchData(false).pipe( tap((response) => { this.baseBuckets = response.buckets; From 93af139b6d704f860b557e26336ae2e8ed3b3ebe Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Thu, 8 Jan 2026 01:21:55 +0200 Subject: [PATCH 11/18] SED-4450-fix local data source loading --- .../services/augmented-time-series.service.ts | 4 +- .../components/table/table.component.html | 4 +- .../table/components/table/table.component.ts | 5 +- .../shared/table-fetch-local-data-source.ts | 29 +-- .../table/shared/table-remote-data-source.ts | 1 + .../alt-execution-errors.component.html | 2 +- .../alt-execution-errors.component.ts | 1 + ...rt-performance-overview-chart.component.ts | 2 +- .../cross-execution-dashboard-state.ts | 4 +- .../scheduler-report-view.component.html | 218 +++++++++--------- 10 files changed, 141 insertions(+), 129 deletions(-) diff --git a/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts b/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts index b7005b985..1b61bc547 100644 --- a/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts +++ b/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { FetchBucketsRequest, TimeSeriesService } from '../../generated'; -import { map, Observable, of, OperatorFunction, switchMap } from 'rxjs'; +import { delay, map, Observable, of, OperatorFunction, switchMap } from 'rxjs'; import { TableApiWrapperService } from '../../table'; import { HttpOverrideResponseInterceptor } from '../shared/http-override-response-interceptor'; import { HttpOverrideResponseInterceptorService } from './http-override-response-interceptor.service'; @@ -96,7 +96,7 @@ export class AugmentedTimeSeriesService extends TimeSeriesService implements Htt createErrorsFetchDataSource(): TableFetchLocalDataSource { return new TableFetchLocalDataSource( - (request?: TimeSeriesErrorsRequest) => (!request ? of([]) : this.findErrors(request)), + (request?: TimeSeriesErrorsRequest) => (!request ? of([]) : this.findErrors(request).pipe(delay(10000))), this.createErrorsDataSourceConfig(), ); } diff --git a/projects/step-core/src/lib/modules/table/components/table/table.component.html b/projects/step-core/src/lib/modules/table/components/table/table.component.html index 96a415031..e4dd39acc 100644 --- a/projects/step-core/src/lib/modules/table/components/table/table.component.html +++ b/projects/step-core/src/lib/modules/table/components/table/table.component.html @@ -1,4 +1,6 @@ -@if (useSpinner() && inProgress()) { +{{ inProgressDataSource() }} {{ inProgressExternal() }} + +@if (inProgress()) {
diff --git a/projects/step-core/src/lib/modules/table/components/table/table.component.ts b/projects/step-core/src/lib/modules/table/components/table/table.component.ts index d68a73e0e..b6ff5529d 100644 --- a/projects/step-core/src/lib/modules/table/components/table/table.component.ts +++ b/projects/step-core/src/lib/modules/table/components/table/table.component.ts @@ -192,7 +192,7 @@ export class TableComponent readonly indicatorMode = input(TableIndicatorMode.SKELETON); readonly inProgressExternal = input(false, { alias: 'inProgress' }); - private inProgressDataSource = signal(false); + protected inProgressDataSource = signal(false); protected readonly hasNext = signal(false); protected readonly totalFiltered = signal(null); private isTableReadyToRenderColumns = signal(false); @@ -424,6 +424,7 @@ export class TableComponent this.dataSourceTerminator$?.next(); this.dataSourceTerminator$?.complete(); this.dataSourceTerminator$ = undefined; + console.log('terminate data source'); this.inProgressDataSource.set(false); this.tableSelectionList?.destroy?.(); this.tableSelectionList = undefined; @@ -488,7 +489,9 @@ export class TableComponent this.page!.firstPage(); }); + console.log(tableDataSource); tableDataSource!.inProgress$.pipe(takeUntil(this.dataSourceTerminator$)).subscribe((inProgress) => { + console.log('In progresss observable received', inProgress); this.inProgressDataSource.set(inProgress); }); diff --git a/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts b/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts index a09b353e7..c5f9cddfb 100644 --- a/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts +++ b/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts @@ -23,7 +23,8 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< private currentRequestTerminator$?: Subject; private requestRef$?: Observable; - override readonly inProgress$ = of(false); + //@ts-ignore + override readonly inProgress$; constructor( private retrieveData: (request?: R) => Observable, @@ -42,6 +43,7 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< } override destroy(): void { + console.log('DESTROY datasource'); super.destroy(); this.inProgressInternal$?.complete(); this.reload$?.complete(); @@ -57,21 +59,18 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< const reload$ = new BehaviorSubject | undefined>(config.initialReloadOptions); const inProgressInternal$ = new BehaviorSubject(false); + // Assign synchronously (no queueMicrotask needed) + this.reload$ = reload$; + this.inProgressInternal$ = inProgressInternal$; + (this as FieldAccessor).inProgress$ = inProgressInternal$.asObservable(); + const source$ = this.createDataStream(reload$, inProgressInternal$); super.setupStreams(source$, config); - // Assigning to the class fields is done asynchronously, - // because field definition is read like field initialization, which is invoked after constructor. - // It will override the value in case if it was assigned during the base constructor invocation. - queueMicrotask(() => { - this.reload$ = reload$; - this.inProgressInternal$ = inProgressInternal$; - (this as FieldAccessor).inProgress$ = inProgressInternal$.asObservable(); - if (this.pendingReload) { - this.reload$.next(this.pendingReload); - this.pendingReload = undefined; - } - }); + if (this.pendingReload) { + this.reload$.next(this.pendingReload); + this.pendingReload = undefined; + } } private terminateCurrentRequest(): void { @@ -93,11 +92,14 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< } const isProgressTriggered = !hideProgress && !immediateHideProgress; + console.log('IS PROGRESS TRIGGERED', isProgressTriggered, immediateHideProgress); if (isProgressTriggered) { + console.log('98'); inProgressInternal$.next(true); } if (immediateHideProgress) { + console.log('103'); inProgressInternal$.next(false); } @@ -106,6 +108,7 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< this.requestRef$ = this.retrieveData(request).pipe( tap(() => { if (isProgressTriggered) { + console.log('112'); inProgressInternal$.next(false); } this.requestRef$ = undefined; diff --git a/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts b/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts index 3b29e9088..d624ebac8 100644 --- a/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts +++ b/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts @@ -144,6 +144,7 @@ export class TableRemoteDataSource implements TableDataSource { const isProgressTriggered = !x.hideProgress; if (isProgressTriggered) { + console.log('Remote data source set true'); this.inProgressInternal$.next(true); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.html b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.html index 49d42516e..3e0612a2f 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.html @@ -7,7 +7,7 @@ matSortActive="count" matSortDirection="desc" [visibleColumns]="displayColumns()" - [indicatorMode]="TableIndicatorMode.SKELETON_ON_INITIAL_LOAD" + [indicatorMode]="TableIndicatorMode.SPINNER" > Error Message diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts index eacfadd4b..dcb2dbabd 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts @@ -57,6 +57,7 @@ export class AltExecutionErrorsComponent { if (data instanceof TableLocalDataSource || data instanceof TableRemoteDataSource) { return data; } + console.log('-------------------------'); if (data instanceof Array) { return this._timeSeriesApi.createErrorsLocalDataSource(data); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-report-performance-overview-chart/alt-report-performance-overview-chart.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-report-performance-overview-chart/alt-report-performance-overview-chart.component.ts index 6c22acce9..b070d667a 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-report-performance-overview-chart/alt-report-performance-overview-chart.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-report-performance-overview-chart/alt-report-performance-overview-chart.component.ts @@ -26,7 +26,7 @@ export class AltReportPerformanceOverviewChartComponent { zoomEnabled: true, primaryAxesUnit: 'ms', colorizationType: 'STROKE', - height: 238, + height: 340, }; protected readonly metricKey = 'response-time'; diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index 9dbca23be..f6add3487 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -391,6 +391,7 @@ export abstract class CrossExecutionDashboardState { take(1), switchMap((timeRange) => { if (executions.length === 0) { + this.testCasesCountChartLoading.set(false); return of({ chart: this.createTestCasesChart([], []), hasData: false, lastExecutions: [] }); } else { const executionsIdsJoined = executions.map((e) => `attributes.executionId = ${e.id!}`).join(' or '); @@ -477,7 +478,8 @@ export abstract class CrossExecutionDashboardState { let filterItem = this.getDashboardFilter(); // this is working only with searchEntities for now. extend it if needed const filter = { [filterItem.attributeName]: filterItem.searchEntities[0]?.searchValue }; - this.errorsDataSource.reload({ request: { timeRange: timeRange, ...filter } }); + console.log('reloading error tables'); + this.errorsDataSource.reload({ request: { timeRange: timeRange, ...filter }, hideProgress: false }); }); private getDefaultBands(count: number, skipSeries = 0): Band[] { diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index c0ef539ba..789aace34 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -1,101 +1,101 @@
- -
-
- @if (_state.summaryData$ | async; as summary) { - @if (_state.summaryWidgetLoading()) { - - } - - } -
+ + + + + + + + + + - - - @if (_state.executionsChartSettings$ | async; as executionsChartSettings) { - @if (_state.executionsChartLoading()) { - - } - - - - - - } @else { - - } - - -
-
-
- -
-
-
- - -
- -
- @if (reportNodesChartType() === 'keywords') { - @if (_state.keywordsCountChartLoading()) { - - } - @if (_state.keywordsChartSettings$ | async; as settings) { - @if (_state.keywordsCountChartLoading()) { - - } - - -
- -
-
-
- } - } @else if (reportNodesChartType() === 'testcases') { - @if (_state.testCasesChartSettings$ | async; as testCasesData) { - @if (_state.testCasesCountChartLoading()) { - - } - - -
- -
-
-
- } - } @else { - - } -
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -110,19 +110,19 @@
-
- - - @if (luxonDateRange(); as dateRange) { - - } - - -
+ + + + + + + + + + + + +
From d417be99bf4d86df3d6a73e9eb5b8c4f90a30ade Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Thu, 8 Jan 2026 21:11:28 +0200 Subject: [PATCH 12/18] SED-4450-cross execution header animations --- .../services/augmented-time-series.service.ts | 2 +- .../cross-execution-dashboard-state.ts | 7 +- .../header/report-view-header.component.html | 36 ++- .../header/report-view-header.component.scss | 2 +- .../header/report-view-header.component.ts | 15 +- .../scheduler-report-view.component.html | 218 +++++++++--------- 6 files changed, 155 insertions(+), 125 deletions(-) diff --git a/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts b/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts index 1b61bc547..e5ce31a13 100644 --- a/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts +++ b/projects/step-core/src/lib/client/augmented/services/augmented-time-series.service.ts @@ -96,7 +96,7 @@ export class AugmentedTimeSeriesService extends TimeSeriesService implements Htt createErrorsFetchDataSource(): TableFetchLocalDataSource { return new TableFetchLocalDataSource( - (request?: TimeSeriesErrorsRequest) => (!request ? of([]) : this.findErrors(request).pipe(delay(10000))), + (request?: TimeSeriesErrorsRequest) => (!request ? of([]) : this.findErrors(request)), this.createErrorsDataSourceConfig(), ); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index f6add3487..8833d2606 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -59,7 +59,6 @@ export abstract class CrossExecutionDashboardState { protected _executionService = inject(ExecutionsService); protected _timeSeriesService = inject(AugmentedTimeSeriesService); protected _statusColors = inject(STATUS_COLORS); - private _uPlotUtils = inject(UPlotUtilsService); private readonly fetchLastExecutionTrigger$ = new Subject(); readonly task = signal(undefined); @@ -84,6 +83,9 @@ export abstract class CrossExecutionDashboardState { testCasesCountChartLoading = signal(false); keywordsCountChartLoading = signal(false); errorsTableLoading = signal(false); + successRateValueLoading = signal(false); + averageDurationValueLoading = signal(false); + totalExecutionsValueLoading = signal(false); public updateTimeRangeSelection(selection: TimeRangePickerSelection) { this.lastRefreshTrigger.set('manual'); @@ -141,6 +143,9 @@ export abstract class CrossExecutionDashboardState { readonly executionsDurationTimeSeriesData = this.timeRange$.pipe( switchMap((timeRange) => { this.summaryWidgetLoading.set(true); + this.successRateValueLoading.set(true); + this.totalExecutionsValueLoading.set(true); + this.averageDurationValueLoading.set(true); const oql = new OQLBuilder() .open('and') .append('attributes.metricType = "executions/duration"') diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.html index 4f98cdc15..d16bde17f 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.html @@ -3,11 +3,13 @@
@if (successRateValue$ | async; as data) { - {{ data }} + @if (_state.successRateValueLoading() && displayLoadingOnProgress()) { + + } @else { + {{ data }} + } } @else { -
- -
+ }
@@ -17,11 +19,13 @@
@if (averageExecutionDurationLabel$ | async; as data) { - {{ data }} + @if (_state.averageDurationValueLoading() && displayLoadingOnProgress()) { + + } @else { + {{ data }} + } } @else { -
- -
+ }
@@ -31,13 +35,21 @@
@if ((totalExecutionsCount$ | async) === null) { -
- -
+ } @else { - {{ totalExecutionsCount$ | async | bigNumber }} + @if (_state.totalExecutionsValueLoading() && displayLoadingOnProgress()) { + + } @else { + {{ totalExecutionsCount$ | async | bigNumber }} + } }
+ + +
+ +
+
diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.scss b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.scss index 1b50d16d2..ee3fd7d49 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.scss +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.scss @@ -13,6 +13,6 @@ .card-value { font-weight: bold; font-size: 2rem; - margin-bottom: 2rem; + height: 3.6rem; } } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.ts index b310ba22e..1246c0485 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/header/report-view-header.component.ts @@ -1,6 +1,6 @@ import { Component, computed, inject, OnInit } from '@angular/core'; import { CrossExecutionDashboardState } from '../../cross-execution-dashboard-state'; -import { map, Observable, of, shareReplay, switchMap } from 'rxjs'; +import { finalize, map, Observable, of, pipe, shareReplay, switchMap, tap } from 'rxjs'; import { ReportNodeSummary } from '../../../../../shared/report-node-summary'; import { FilterUtils, OQLBuilder, TimeSeriesConfig } from '../../../../../../timeseries/modules/_common'; import { BucketResponse } from '@exense/step-core'; @@ -14,6 +14,10 @@ import { BucketResponse } from '@exense/step-core'; export class ReportViewHeaderComponent { readonly _state = inject(CrossExecutionDashboardState); + displayLoadingOnProgress = computed(() => { + return this._state.lastRefreshTrigger() === 'manual'; + }); + successRateValue$: Observable = this._state.summaryData$.pipe( map((summaryData: ReportNodeSummary) => { const passed = summaryData.items['PASSED'] || 0; @@ -22,6 +26,9 @@ export class ReportViewHeaderComponent { } return ((passed / summaryData.total) * 100).toFixed(2) + '%'; }), + tap(() => { + this._state.successRateValueLoading.set(false); + }), shareReplay(1), ); @@ -41,6 +48,9 @@ export class ReportViewHeaderComponent { return TimeSeriesConfig.AXES_FORMATTING_FUNCTIONS.time(totalDuration / totalCount); } }), + tap(() => { + this._state.averageDurationValueLoading.set(false); + }), shareReplay(1), ); @@ -53,6 +63,9 @@ export class ReportViewHeaderComponent { }); return totalCount; }), + tap(() => { + this._state.totalExecutionsValueLoading.set(false); + }), shareReplay(1), ); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index 789aace34..c0ef539ba 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -1,101 +1,101 @@
- - - - - - - - - - + +
+
+ @if (_state.summaryData$ | async; as summary) { + @if (_state.summaryWidgetLoading()) { + + } + + } +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @if (_state.executionsChartSettings$ | async; as executionsChartSettings) { + @if (_state.executionsChartLoading()) { + + } + + + + + + } @else { + + } + + +
+
+
+ +
+
+
+ + +
+ +
+ @if (reportNodesChartType() === 'keywords') { + @if (_state.keywordsCountChartLoading()) { + + } + @if (_state.keywordsChartSettings$ | async; as settings) { + @if (_state.keywordsCountChartLoading()) { + + } + + +
+ +
+
+
+ } + } @else if (reportNodesChartType() === 'testcases') { + @if (_state.testCasesChartSettings$ | async; as testCasesData) { + @if (_state.testCasesCountChartLoading()) { + + } + + +
+ +
+
+
+ } + } @else { + + } +
+
+
@@ -110,19 +110,19 @@
- - - - - - - - - - - - - +
+ + + @if (luxonDateRange(); as dateRange) { + + } + + +
From 6ace1980db8aa315bddf81a8f6a605550886eba5 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Thu, 8 Jan 2026 22:23:04 +0200 Subject: [PATCH 13/18] SED-4450-cleanup --- .../components/table/table.component.html | 4 +-- .../table/components/table/table.component.ts | 5 +--- .../shared/table-fetch-local-data-source.ts | 29 +++++++++---------- .../table/shared/table-remote-data-source.ts | 1 - .../alt-execution-errors.component.ts | 1 - .../cross-execution-dashboard-state.ts | 1 - .../services/execution-commands.service.ts | 1 - .../dashboard/dashboard.component.ts | 1 - 8 files changed, 15 insertions(+), 28 deletions(-) diff --git a/projects/step-core/src/lib/modules/table/components/table/table.component.html b/projects/step-core/src/lib/modules/table/components/table/table.component.html index e4dd39acc..96a415031 100644 --- a/projects/step-core/src/lib/modules/table/components/table/table.component.html +++ b/projects/step-core/src/lib/modules/table/components/table/table.component.html @@ -1,6 +1,4 @@ -{{ inProgressDataSource() }} {{ inProgressExternal() }} - -@if (inProgress()) { +@if (useSpinner() && inProgress()) {
diff --git a/projects/step-core/src/lib/modules/table/components/table/table.component.ts b/projects/step-core/src/lib/modules/table/components/table/table.component.ts index b6ff5529d..d68a73e0e 100644 --- a/projects/step-core/src/lib/modules/table/components/table/table.component.ts +++ b/projects/step-core/src/lib/modules/table/components/table/table.component.ts @@ -192,7 +192,7 @@ export class TableComponent readonly indicatorMode = input(TableIndicatorMode.SKELETON); readonly inProgressExternal = input(false, { alias: 'inProgress' }); - protected inProgressDataSource = signal(false); + private inProgressDataSource = signal(false); protected readonly hasNext = signal(false); protected readonly totalFiltered = signal(null); private isTableReadyToRenderColumns = signal(false); @@ -424,7 +424,6 @@ export class TableComponent this.dataSourceTerminator$?.next(); this.dataSourceTerminator$?.complete(); this.dataSourceTerminator$ = undefined; - console.log('terminate data source'); this.inProgressDataSource.set(false); this.tableSelectionList?.destroy?.(); this.tableSelectionList = undefined; @@ -489,9 +488,7 @@ export class TableComponent this.page!.firstPage(); }); - console.log(tableDataSource); tableDataSource!.inProgress$.pipe(takeUntil(this.dataSourceTerminator$)).subscribe((inProgress) => { - console.log('In progresss observable received', inProgress); this.inProgressDataSource.set(inProgress); }); diff --git a/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts b/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts index c5f9cddfb..a09b353e7 100644 --- a/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts +++ b/projects/step-core/src/lib/modules/table/shared/table-fetch-local-data-source.ts @@ -23,8 +23,7 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< private currentRequestTerminator$?: Subject; private requestRef$?: Observable; - //@ts-ignore - override readonly inProgress$; + override readonly inProgress$ = of(false); constructor( private retrieveData: (request?: R) => Observable, @@ -43,7 +42,6 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< } override destroy(): void { - console.log('DESTROY datasource'); super.destroy(); this.inProgressInternal$?.complete(); this.reload$?.complete(); @@ -59,18 +57,21 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< const reload$ = new BehaviorSubject | undefined>(config.initialReloadOptions); const inProgressInternal$ = new BehaviorSubject(false); - // Assign synchronously (no queueMicrotask needed) - this.reload$ = reload$; - this.inProgressInternal$ = inProgressInternal$; - (this as FieldAccessor).inProgress$ = inProgressInternal$.asObservable(); - const source$ = this.createDataStream(reload$, inProgressInternal$); super.setupStreams(source$, config); - if (this.pendingReload) { - this.reload$.next(this.pendingReload); - this.pendingReload = undefined; - } + // Assigning to the class fields is done asynchronously, + // because field definition is read like field initialization, which is invoked after constructor. + // It will override the value in case if it was assigned during the base constructor invocation. + queueMicrotask(() => { + this.reload$ = reload$; + this.inProgressInternal$ = inProgressInternal$; + (this as FieldAccessor).inProgress$ = inProgressInternal$.asObservable(); + if (this.pendingReload) { + this.reload$.next(this.pendingReload); + this.pendingReload = undefined; + } + }); } private terminateCurrentRequest(): void { @@ -92,14 +93,11 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< } const isProgressTriggered = !hideProgress && !immediateHideProgress; - console.log('IS PROGRESS TRIGGERED', isProgressTriggered, immediateHideProgress); if (isProgressTriggered) { - console.log('98'); inProgressInternal$.next(true); } if (immediateHideProgress) { - console.log('103'); inProgressInternal$.next(false); } @@ -108,7 +106,6 @@ export class TableFetchLocalDataSource extends TableLocalDataSource< this.requestRef$ = this.retrieveData(request).pipe( tap(() => { if (isProgressTriggered) { - console.log('112'); inProgressInternal$.next(false); } this.requestRef$ = undefined; diff --git a/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts b/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts index d624ebac8..3b29e9088 100644 --- a/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts +++ b/projects/step-core/src/lib/modules/table/shared/table-remote-data-source.ts @@ -144,7 +144,6 @@ export class TableRemoteDataSource implements TableDataSource { const isProgressTriggered = !x.hideProgress; if (isProgressTriggered) { - console.log('Remote data source set true'); this.inProgressInternal$.next(true); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts index dcb2dbabd..eacfadd4b 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-errors/alt-execution-errors.component.ts @@ -57,7 +57,6 @@ export class AltExecutionErrorsComponent { if (data instanceof TableLocalDataSource || data instanceof TableRemoteDataSource) { return data; } - console.log('-------------------------'); if (data instanceof Array) { return this._timeSeriesApi.createErrorsLocalDataSource(data); } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index 8833d2606..a82902abd 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -483,7 +483,6 @@ export abstract class CrossExecutionDashboardState { let filterItem = this.getDashboardFilter(); // this is working only with searchEntities for now. extend it if needed const filter = { [filterItem.attributeName]: filterItem.searchEntities[0]?.searchValue }; - console.log('reloading error tables'); this.errorsDataSource.reload({ request: { timeRange: timeRange, ...filter }, hideProgress: false }); }); diff --git a/projects/step-frontend/src/lib/modules/execution/services/execution-commands.service.ts b/projects/step-frontend/src/lib/modules/execution/services/execution-commands.service.ts index 7df8268ff..92cc51bac 100644 --- a/projects/step-frontend/src/lib/modules/execution/services/execution-commands.service.ts +++ b/projects/step-frontend/src/lib/modules/execution/services/execution-commands.service.ts @@ -80,7 +80,6 @@ export class ExecutionCommandsService implements OnDestroy { const executionsParameters$ = this.buildExecutionParams(false, false); return executionsParameters$.pipe( map((executionsParameters) => { - console.log('EXECUTION PARAMS', executionsParameters); const name = executionsParameters.description ?? ''; return { attributes: { name }, diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index 4f5121ac7..7779466f6 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -139,7 +139,6 @@ export class DashboardComponent implements OnInit, OnDestroy { timeRange: TimeRange, opts: { actionType: 'manual' | 'auto'; resetSelection?: boolean }, ): void { - console.log('updating full time range'); this.mainEngine.state.lastChangeType = opts.actionType; this.mainEngine?.state.context.updateFullTimeRange(timeRange, opts.resetSelection); } From 8678ed66f0f691a90a5ec8645a863a654bb50bed Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Fri, 9 Jan 2026 00:31:40 +0200 Subject: [PATCH 14/18] SED-4450-fix loading spinners --- .../cross-execution-dashboard-state.ts | 7 +++++-- .../cross-execution-execution-table.component.html | 2 +- .../heatmap/cross-execution-heatmap.component.ts | 1 + .../heatmap/heatmap.component.scss | 1 - .../report/scheduler-report-view.component.html | 5 ++++- .../report/scheduler-report-view.component.ts | 8 +++++++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts index a82902abd..75d887bbd 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/cross-execution-dashboard-state.ts @@ -10,6 +10,7 @@ import { Subject, switchMap, take, + tap, } from 'rxjs'; import { TimeRangePickerSelection } from '../../../../timeseries/modules/_common/types/time-selection/time-range-picker-selection'; import { @@ -296,6 +297,10 @@ export abstract class CrossExecutionDashboardState { ); readonly lastExecutionsSorted$ = this.timeRange$.pipe( + tap(() => { + this.testCasesCountChartLoading.set(true); + this.keywordsCountChartLoading.set(true); + }), switchMap((timeRange) => this.fetchLastExecutions(timeRange)), map((executions) => { executions.sort((a, b) => a.startTime! - b.startTime!); @@ -306,7 +311,6 @@ export abstract class CrossExecutionDashboardState { keywordsChartSettings$: Observable = this.lastExecutionsSorted$.pipe( switchMap((executions) => { - this.keywordsCountChartLoading.set(true); return this.timeRange$.pipe( take(1), switchMap((timeRange) => { @@ -391,7 +395,6 @@ export abstract class CrossExecutionDashboardState { testCasesChartSettings$: Observable<{ chart: TSChartSettings; hasData: boolean; lastExecutions: Execution[] }> = this.lastExecutionsSorted$.pipe( switchMap((executions) => { - this.testCasesCountChartLoading.set(true); return this.timeRange$.pipe( take(1), switchMap((timeRange) => { diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/executions-table/cross-execution-execution-table.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/executions-table/cross-execution-execution-table.component.html index 0818bd263..316fb88c3 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/executions-table/cross-execution-execution-table.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/executions-table/cross-execution-execution-table.component.html @@ -7,7 +7,7 @@ matSortActive="executionTime" matSortDirection="desc" [matSortDisableClear]="true" - [indicatorMode]="TableIndicatorMode.SKELETON_ON_INITIAL_LOAD" + [indicatorMode]="_state.lastRefreshTrigger() === 'manual' ? TableIndicatorMode.SPINNER : TableIndicatorMode.NONE" > = combineLatest([ diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/heatmap.component.scss b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/heatmap.component.scss index 923aa5cd9..bcbdf87db 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/heatmap.component.scss +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/heatmap/heatmap.component.scss @@ -116,6 +116,5 @@ step-heatmap { .heatmap-col-header { font-weight: normal; top: 0; - color: var.$white; } } diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index c0ef539ba..4c28d66a6 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -37,7 +37,7 @@

- +
} } @else if (reportNodesChartType() === 'testcases') { + @if (_state.testCasesCountChartLoading()) { + + } @if (_state.testCasesChartSettings$ | async; as testCasesData) { @if (_state.testCasesCountChartLoading()) { diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts index 6eadad09c..50d8339a0 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.ts @@ -52,10 +52,16 @@ export class SchedulerReportViewComponent implements OnInit { ); switchReportNodesChart(type: ReportNodesChartType) { + this._state.lastRefreshTrigger.set('manual'); + if (type === 'keywords') { + this._state.keywordsCountChartLoading.set(true); + } else { + this._state.testCasesCountChartLoading.set(true); + } this.reportNodesChartType.set(type); } - readonly byExecutionChartTitle = computed(() => { + readonly countChartTitle = computed(() => { const label = this.reportNodesChartType() === 'keywords' ? 'Keyword calls count' : 'Test cases count'; return `${label} (last ${this._state.LAST_EXECUTIONS_TO_DISPLAY} executions)`; }); From 27d4d6be552d45634ee01eeacd6e90aad9df780d Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Tue, 20 Jan 2026 16:48:18 +0200 Subject: [PATCH 15/18] SED-4450-refactor --- .../scheduler-report-view.component.html | 3 -- .../table-dashlet.component.html | 2 +- .../table-dashlet/table-dashlet.component.ts | 35 ++++++++++++------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html index 4c28d66a6..38fafca30 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html +++ b/projects/step-frontend/src/lib/modules/execution/components/schedule-overview/cross-execution-dashboard/report/scheduler-report-view.component.html @@ -52,9 +52,6 @@ } @if (_state.keywordsChartSettings$ | async; as settings) { - @if (_state.keywordsCountChartLoading()) { - - }
diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html index a457c3e8d..a6c0d773f 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html @@ -102,7 +102,7 @@ } ([]); tableDataSource: TableLocalDataSource | undefined; - tableIsLoading = true; columnsDefinition: TableColumn[] = []; visibleColumnsIds: string[] = ['name']; @@ -134,21 +133,32 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } this.prepareState(); this.tableDataSource = new TableLocalDataSource(this.tableData$, this.getDatasourceConfig()); - this.fetchBaseData().subscribe(() => this.updateTableData()); + this.isLoading.set(true); + this.fetchBaseData() + .pipe( + switchMap(() => this.updateTableData()), + finalize(() => this.isLoading.set(false)), + ) + .subscribe(); } - refresh(blur?: boolean): Observable { + public refresh(blur?: boolean): Observable { this.isLoading.set(true); - return this.fetchBaseData().pipe(tap(() => this.updateTableData())); + return this.fetchBaseData().pipe( + switchMap(() => this.updateTableData()), + finalize(() => this.isLoading.set(false)), + ); } - refreshCompareData(): Observable { + public refreshCompareData(): Observable { + this.isLoading.set(true); return this.fetchData(true).pipe( tap((response) => { this.compareBuckets = response.buckets; this.truncated = response.truncated; - this.updateTableData(); }), + switchMap(() => this.updateTableData()), + finalize(() => this.isLoading.set(false)), ); } @@ -305,7 +315,6 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } private fetchBaseData(): Observable { - this.isLoading.set(true); return this.fetchData(false).pipe( tap((response) => { this.baseBuckets = response.buckets; @@ -380,11 +389,11 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha private updateTableData() { const tableEntries = this.mergeBaseAndCompareData(); - this.fetchLegendEntities(tableEntries).subscribe((updatedData) => { - this.tableData$.next(updatedData); - this.tableIsLoading = false; - this.isLoading.set(false); - }); + return this.fetchLegendEntities(tableEntries).pipe( + tap((updatedData) => { + this.tableData$.next(updatedData); + }), + ); } private processResponse(response: TimeSeriesAPIResponse, context: TimeSeriesContext): ProcessedBucketResponse { From 6845068c61f4923a63eaadac55c9c1a100c1b6ed Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Tue, 20 Jan 2026 23:37:35 +0200 Subject: [PATCH 16/18] SED-4450-fix loading + compare mode --- .../chart-dashlet.component.html | 4 +- .../chart-dashlet/chart-dashlet.component.ts | 72 +++++++++++-------- .../dashboard/dashboard.component.ts | 18 ++++- .../table-dashlet.component.html | 9 +-- .../table-dashlet/table-dashlet.component.ts | 21 ++++-- .../types/time-series/time-series-context.ts | 38 +++++++--- .../time-series-chart.component.ts | 10 ++- 7 files changed, 117 insertions(+), 55 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html index 803ff7570..8ba778126 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.html @@ -1,5 +1,5 @@
- @if (!_internalSettings) { + @if (!_internalSettings()) { } @else { @if (isLoading() && showLoadingSpinnerWhileLoading()) { @@ -11,7 +11,7 @@ #chart (zoomReset)="handleZoomReset()" [syncKey]="context.id" - [settings]="_internalSettings!" + [settings]="_internalSettings()!" (lockStateChange)="handleLockStateChange($event)" [height]="height" > diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts index adeafae8d..f2a5e9cd8 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts @@ -31,7 +31,7 @@ import { UPlotUtilsService, } from '../../modules/_common'; import { ChartSkeletonComponent, TimeSeriesChartComponent, TSChartSeries, TSChartSettings } from '../../modules/chart'; -import { defaultIfEmpty, forkJoin, map, Observable, of, Subscription, tap } from 'rxjs'; +import { defaultIfEmpty, forkJoin, map, Observable, of, Subscription, switchMap, tap } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { ChartDashletSettingsComponent } from '../chart-dashlet-settings/chart-dashlet-settings.component'; import { Axis } from 'uplot'; @@ -103,7 +103,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha @ViewChild('settingsMenuTrigger') settingsMenuTrigger?: MatMenuTrigger; @ViewChild('chart') chart!: TimeSeriesChartComponent; - _internalSettings?: TSChartSettings; + _internalSettings = signal(undefined); _attributesByIds: Record = {}; @Input() item!: DashboardItem; @@ -139,7 +139,14 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha throw new Error('Missing input values'); } this.prepareState(this.item); - this.fetchDataAndCreateChart().subscribe(); + this.createChart(); + } + + private createChart(): void { + this.fetchDataAndCreateChartSettings().subscribe((settings) => { + this._internalSettings.set(settings); + console.log(settings); + }); } ngOnChanges(changes: SimpleChanges): void { @@ -205,7 +212,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha if (blur) { this.chart?.setBlur(true); } - return this.fetchDataAndCreateChart(); + return this.fetchDataAndCreateChartSettings().pipe(tap((settings) => this._internalSettings.set(settings))); } handleZoomReset() { @@ -232,9 +239,11 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha } if (this.cachedResponse && this.cachedRequest) { - this.createChart(this.cachedResponse, this.cachedRequest); + this.createChartSettings(this.cachedResponse, this.cachedRequest).subscribe((settings) => + this._internalSettings.set(settings), + ); } else { - this.fetchDataAndCreateChart(); + this.createChart(); } } @@ -284,7 +293,10 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha * When there is no grouping, the key and label will be 'Value'. * If there are grouping, all empty elements will be replaced with an empty label */ - private createChart(response: TimeSeriesAPIResponse, request: FetchBucketsRequest): void { + private createChartSettings( + response: TimeSeriesAPIResponse, + request: FetchBucketsRequest, + ): Observable { let syncGroup: TimeSeriesSyncGroup | undefined; if (this.item.masterChartId) { syncGroup = this.context.getSyncGroup(this.item.masterChartId); @@ -448,26 +460,28 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha ); }; - this.fetchLegendEntities(series).subscribe((v) => { - this._internalSettings = { - title: this.getChartTitle(), - xAxesSettings: { - values: xLabels, - }, - series: series, - tooltipOptions: { - enabled: true, - zAxisLabel: this.getSecondAxesLabel(), - yAxisUnit: yAxesUnit, - useExecutionLinks: this.showExecutionLinks, - fetchExecutionsFn: fetchExecutionsFn, - }, - showLegend: true, - axes: axes, - truncated: response.truncated, - }; - this.isLoading.set(false); - }); + return this.fetchLegendEntities(series).pipe( + map((v) => { + return { + title: this.getChartTitle(), + xAxesSettings: { + values: xLabels, + }, + series: series, + tooltipOptions: { + enabled: true, + zAxisLabel: this.getSecondAxesLabel(), + yAxisUnit: yAxesUnit, + useExecutionLinks: this.showExecutionLinks, + fetchExecutionsFn: fetchExecutionsFn, + }, + showLegend: true, + axes: axes, + truncated: response.truncated, + }; + }), + tap(() => this.isLoading.set(false)), + ); } private removeDataGaps(data: (number | undefined)[]): number[] { @@ -528,7 +542,7 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha return aggregation.params?.[TimeSeriesConfig.RATE_UNIT_PARAM] || 's'; } - private fetchDataAndCreateChart(): Observable { + private fetchDataAndCreateChartSettings(): Observable { this.isLoading.set(true); const groupDimensions = this.getGroupDimensions(); const oqlFilter = this.composeRequestFilter(); @@ -562,8 +576,8 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit, OnCha this.collectionResolutionUsed = response.collectionResolution; this.cachedResponse = response; this.cachedRequest = request; - this.createChart(response, request); }), + switchMap((response) => this.createChartSettings(response, request)), ); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts index 7779466f6..d44a50b33 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/dashboard/dashboard.component.ts @@ -49,7 +49,7 @@ import { import { TableDashletComponent } from '../table-dashlet/table-dashlet.component'; import { ChartDashlet } from '../../modules/_common/types/chart-dashlet'; import { DashboardStateEngine } from './dashboard-state-engine'; -import { forkJoin, map, Observable, of, tap } from 'rxjs'; +import { forkJoin, map, Observable, of, Subscription, tap } from 'rxjs'; //@ts-ignore import uPlot = require('uplot'); @@ -134,6 +134,7 @@ export class DashboardComponent implements OnInit, OnDestroy { mainEngine!: DashboardStateEngine; compareEngine?: DashboardStateEngine; + compareModeChangesSubscription?: Subscription; public updateFullTimeRange( timeRange: TimeRange, @@ -562,6 +563,10 @@ export class DashboardComponent implements OnInit, OnDestroy { } private disableCompareMode() { + if (this.compareModeChangesSubscription) { + this.compareModeChangesSubscription.unsubscribe(); + this.compareModeChangesSubscription = undefined; + } this.mainEngine.state.context.disableCompareMode(); this.dashlets.forEach((d) => { if (d.getType() === 'TABLE') { @@ -591,7 +596,12 @@ export class DashboardComponent implements OnInit, OnDestroy { } private enableCompareMode() { + if (this.compareModeChangesSubscription) { + this.compareModeChangesSubscription.unsubscribe(); + this.compareModeChangesSubscription = undefined; + } const mainState = this.mainEngine.state; + mainState.lastChangeType = 'manual'; const mainTimeSettings = mainState.context.getTimeRangeSettings(); const compareModeContext = this._timeSeriesContextFactory.createContext({ dashlets: JSON.parse(JSON.stringify(this.dashboard.dashlets)), // clone @@ -617,13 +627,16 @@ export class DashboardComponent implements OnInit, OnDestroy { refreshInProgress: false, lastChangeType: 'auto', }; - compareModeContext.settingsChange$.subscribe(() => { + + this.compareModeChangesSubscription = compareModeContext.settingsChange$.subscribe((x) => { + console.log('settings have changed', x); this.dashlets.forEach((d) => { if (d.getType() === 'TABLE') { (d as TableDashletComponent).refreshCompareData().subscribe(); } }); }); + this.compareEngine = new DashboardStateEngine(state); this.compareEngine.subscribeForContextChange(); mainState.context.enableCompareMode(compareModeContext); @@ -680,6 +693,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.mainEngine?.destroy(); this.compareEngine?.destroy(); + this.compareModeChangesSubscription?.unsubscribe(); if (!this.storageId) { this.mainEngine?.state?.context?.destroy?.(); this.compareEngine?.state?.context?.destroy?.(); diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html index a6c0d773f..fa3de3300 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html @@ -95,18 +95,13 @@
+ {{ isLoading() }} @if (isLoading() && showLoadingSpinnerWhileLoading()) {
} - +
diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts index 904731821..4627eadf6 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectorRef, Component, + effect, EventEmitter, inject, input, @@ -101,7 +102,11 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha @Output() shiftLeft = new EventEmitter(); @Output() shiftRight = new EventEmitter(); - isLoading = signal(false); + isLoading = signal(true); + + logEffect = effect(() => { + console.log(this.isLoading()); + }); private _timeSeriesService = inject(TimeSeriesService); private _matDialog = inject(MatDialog); @@ -133,7 +138,6 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } this.prepareState(); this.tableDataSource = new TableLocalDataSource(this.tableData$, this.getDatasourceConfig()); - this.isLoading.set(true); this.fetchBaseData() .pipe( switchMap(() => this.updateTableData()), @@ -143,6 +147,7 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } public refresh(blur?: boolean): Observable { + console.log('refreshing'); this.isLoading.set(true); return this.fetchBaseData().pipe( switchMap(() => this.updateTableData()), @@ -230,12 +235,16 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } enableCompareMode(context: TimeSeriesContext) { + console.log('enabling compare mode in table dashlet'); this.compareModeEnabled = true; this.compareContext = context; this.compareBuckets = this.baseBuckets; this.compareRequestOql = this.baseRequestOql; this.updateVisibleColumns(); - this.updateTableData(); + this.isLoading.set(true); + this.updateTableData() + .pipe(finalize(() => this.isLoading.set(false))) + .subscribe(); } disableCompareMode() { @@ -243,7 +252,10 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha this.compareContext = undefined; this.compareBuckets = []; this.updateVisibleColumns(); - this.updateTableData(); + this.isLoading.set(true); + this.updateTableData() + .pipe(finalize(() => this.isLoading.set(false))) + .subscribe(); } updateVisibleColumns(): void { @@ -270,6 +282,7 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } onColumnPclValueChange(column: TableColumn, value: string) { + console.log('on pc value changes'); const oldValue = column.pclValue; let parsedNumber: number = parseFloat(value); const validPclValue = !isNaN(parsedNumber) && parsedNumber > 0 && parsedNumber < 100; diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts index eae28f77e..be87e2f66 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts @@ -77,16 +77,34 @@ export class TimeSeriesContext { // any specific context change will trigger the main stateChange this.settingsChange$ = merge( - this.compareModeChange$.pipe(skip(1)), - this.inProgress$.pipe(skip(1)), // TODO - this.activeGroupings$.pipe(skip(1)), - this.filterSettings$.pipe(skip(1)), - this.chartsResolution$.pipe(skip(1)), - this.chartsLockedState$.pipe(skip(1)), // TODO - this.fullTimeRangeChange$, - this.selectedTimeRangeChange$, - this.stateChangeInternal$, - ) as Observable; + this.compareModeChange$.pipe( + skip(1), + map(() => 'compareModeChange$'), + ), + this.inProgress$.pipe( + skip(1), + map(() => 'inProgress$'), + ), + this.activeGroupings$.pipe( + skip(1), + map(() => 'activeGroupings$'), + ), + this.filterSettings$.pipe( + skip(1), + map(() => 'filterSettings$'), + ), + this.chartsResolution$.pipe( + skip(1), + map(() => 'chartsResolution$'), + ), + this.chartsLockedState$.pipe( + skip(1), + map(() => 'chartsLockedState$'), + ), + this.fullTimeRangeChange$.pipe(map(() => 'fullTimeRangeChange$')), + this.selectedTimeRangeChange$.pipe(map(() => 'selectedTimeRangeChange$')), + this.stateChangeInternal$.pipe(map(() => 'stateChangeInternal$')), + ) as unknown as Observable; } getTimeRangeSettings() { diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/chart/components/time-series-chart/time-series-chart.component.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/chart/components/time-series-chart/time-series-chart.component.ts index 09e6afe99..268b58d0d 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/chart/components/time-series-chart/time-series-chart.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/chart/components/time-series-chart/time-series-chart.component.ts @@ -75,8 +75,16 @@ export class TimeSeriesChartComponent implements OnInit, OnChanges, OnDestroy, T @Output() lockStateChange = new EventEmitter(); lockState = signal(false); // the state does not change when unlocking from a synced chart + private lockEffectFirstRun = true; + lockEffect = effect(() => { - let locked = this.lockState(); + const locked = this.lockState(); + + if (this.lockEffectFirstRun) { + this.lockEffectFirstRun = false; + return; // skip init + } + this.lockStateChange.emit(locked); }); From defef3753e3d2d65677a3c8f3735e8bc39534443 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Wed, 21 Jan 2026 11:19:30 +0200 Subject: [PATCH 17/18] SED-4450-cleanup --- .../alt-execution-analytics.component.ts | 4 -- .../table-dashlet.component.html | 1 - .../table-dashlet/table-dashlet.component.ts | 5 --- .../types/time-series/time-series-context.ts | 39 +++++-------------- 4 files changed, 10 insertions(+), 39 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts index 46ae33561..6944b0bc8 100644 --- a/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts +++ b/projects/step-frontend/src/lib/modules/execution/components/alt-execution-analytics/alt-execution-analytics.component.ts @@ -56,10 +56,6 @@ export class AltExecutionAnalyticsComponent implements OnInit { this.dashboardComponent()?.updateFullTimeRange(timeRange!, { actionType: 'auto' }); }); - effect = effect(() => { - this.activeTimeRangeSelection(); - }); - handleDashboardSettingsChange(context: TimeSeriesContext) { this._urlParamsService.updateUrlParamsFromContext(context, this.activeTimeRangeSelection()!, undefined, false); } diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html index fa3de3300..3be7a6634 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.html @@ -95,7 +95,6 @@
- {{ isLoading() }} @if (isLoading() && showLoadingSpinnerWhileLoading()) {
diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts index 4627eadf6..e8d6d7470 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/table-dashlet/table-dashlet.component.ts @@ -104,10 +104,6 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha isLoading = signal(true); - logEffect = effect(() => { - console.log(this.isLoading()); - }); - private _timeSeriesService = inject(TimeSeriesService); private _matDialog = inject(MatDialog); private _timeSeriesEntityService = inject(TimeSeriesEntityService); @@ -282,7 +278,6 @@ export class TableDashletComponent extends ChartDashlet implements OnInit, OnCha } onColumnPclValueChange(column: TableColumn, value: string) { - console.log('on pc value changes'); const oldValue = column.pclValue; let parsedNumber: number = parseFloat(value); const validPclValue = !isNaN(parsedNumber) && parsedNumber > 0 && parsedNumber < 100; diff --git a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts index be87e2f66..15b91917a 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/modules/_common/types/time-series/time-series-context.ts @@ -75,36 +75,17 @@ export class TimeSeriesContext { this.chartsResolution$ = new BehaviorSubject(params.resolution || 0); params.metrics?.forEach((m) => (this.indexedMetrics[m.name] = m)); - // any specific context change will trigger the main stateChange this.settingsChange$ = merge( - this.compareModeChange$.pipe( - skip(1), - map(() => 'compareModeChange$'), - ), - this.inProgress$.pipe( - skip(1), - map(() => 'inProgress$'), - ), - this.activeGroupings$.pipe( - skip(1), - map(() => 'activeGroupings$'), - ), - this.filterSettings$.pipe( - skip(1), - map(() => 'filterSettings$'), - ), - this.chartsResolution$.pipe( - skip(1), - map(() => 'chartsResolution$'), - ), - this.chartsLockedState$.pipe( - skip(1), - map(() => 'chartsLockedState$'), - ), - this.fullTimeRangeChange$.pipe(map(() => 'fullTimeRangeChange$')), - this.selectedTimeRangeChange$.pipe(map(() => 'selectedTimeRangeChange$')), - this.stateChangeInternal$.pipe(map(() => 'stateChangeInternal$')), - ) as unknown as Observable; + this.compareModeChange$.pipe(skip(1)), + this.inProgress$.pipe(skip(1)), // TODO + this.activeGroupings$.pipe(skip(1)), + this.filterSettings$.pipe(skip(1)), + this.chartsResolution$.pipe(skip(1)), + this.chartsLockedState$.pipe(skip(1)), // TODO + this.fullTimeRangeChange$, + this.selectedTimeRangeChange$, + this.stateChangeInternal$, + ) as Observable; } getTimeRangeSettings() { From 3d6d90db9ec5f1135f595a819a8c9f15ddea6757 Mon Sep 17 00:00:00 2001 From: lucian-baciu Date: Wed, 4 Feb 2026 23:31:23 +0200 Subject: [PATCH 18/18] SED-4450-fix merge --- .../chart-dashlet/chart-dashlet.component.ts | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts index 822ee97a2..1fa128f3f 100644 --- a/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts +++ b/projects/step-frontend/src/lib/modules/timeseries/components/chart-dashlet/chart-dashlet.component.ts @@ -114,13 +114,13 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit { readonly editMode = input(false); readonly showExecutionLinks = input(false); readonly showLoadingSpinnerWhileLoading = input(true); - + readonly remove = output(); readonly shiftLeft = output(); readonly shiftRight = output(); readonly zoomReset = output(); - isLoading = signal(false); + readonly isLoading = signal(false); groupingSelection: MetricAttributeSelection[] = []; selectedAggregate!: ChartAggregation; @@ -136,21 +136,6 @@ export class ChartDashletComponent extends ChartDashlet implements OnInit { showHigherResolutionWarning = false; collectionResolutionUsed: number = 0; - ngOnInit(): void { - if (!this.item || !this.context || !this.height) { - throw new Error('Missing input values'); - } - this.prepareState(this.item); - this.createChart(); - } - - private createChart(): void { - this.fetchDataAndCreateChartSettings().subscribe((settings) => { - this._internalSettings.set(settings); - console.log(settings); - }); - } - firstEffectTriggered = false;