From 3491419dd9c552c9083ddc03720cb834a990c650 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 14 Jul 2025 17:37:31 +0300 Subject: [PATCH 01/29] Timewindow: remove hide parameters from configuration if they are disabled --- .../timewindow-config-dialog.component.ts | 32 +++++++++++ .../src/app/shared/models/time/time.models.ts | 56 ++++++++++--------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index 1e1f831c4f..7d69f99603 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -506,6 +506,38 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On delete this.timewindow.history.disableCustomGroupInterval; } + if (!this.timewindow.hideAggregation) { + delete this.timewindow.hideAggregation; + } + if (!this.timewindow.hideAggInterval) { + delete this.timewindow.hideAggInterval; + } + if (!this.timewindow.hideTimezone) { + delete this.timewindow.hideTimezone; + } + + if (!this.timewindow.realtime.hideInterval) { + delete this.timewindow.realtime.hideInterval; + } + if (!this.timewindow.realtime.hideLastInterval) { + delete this.timewindow.realtime.hideLastInterval; + } + if (!this.timewindow.realtime.hideQuickInterval) { + delete this.timewindow.realtime.hideQuickInterval; + } + if (!this.timewindow.history.hideInterval) { + delete this.timewindow.history.hideInterval; + } + if (!this.timewindow.history.hideLastInterval) { + delete this.timewindow.history.hideLastInterval; + } + if (!this.timewindow.history.hideFixedInterval) { + delete this.timewindow.history.hideFixedInterval; + } + if (!this.timewindow.history.hideQuickInterval) { + delete this.timewindow.history.hideQuickInterval; + } + if (!this.aggregation) { delete this.timewindow.aggregation; } diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index c204cb6867..5442cb95ca 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -281,19 +281,12 @@ export const historyInterval = (timewindowMs: number): Timewindow => ({ export const defaultTimewindow = (timeService: TimeService): Timewindow => { const currentTime = moment().valueOf(); return { - displayValue: '', - hideAggregation: false, - hideAggInterval: false, - hideTimezone: false, selectedTab: TimewindowType.REALTIME, realtime: { realtimeType: RealtimeWindowType.LAST_INTERVAL, interval: SECOND, timewindowMs: MINUTE, quickInterval: QuickTimeInterval.CURRENT_DAY, - hideInterval: false, - hideLastInterval: false, - hideQuickInterval: false }, history: { historyType: HistoryWindowType.LAST_INTERVAL, @@ -304,10 +297,6 @@ export const defaultTimewindow = (timeService: TimeService): Timewindow => { endTimeMs: currentTime }, quickInterval: QuickTimeInterval.CURRENT_DAY, - hideInterval: false, - hideLastInterval: false, - hideFixedInterval: false, - hideQuickInterval: false }, aggregation: { type: AggregationType.AVG, @@ -331,34 +320,41 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO if (value.allowedAggTypes?.length) { model.allowedAggTypes = value.allowedAggTypes; } - model.hideAggregation = value.hideAggregation; - model.hideAggInterval = value.hideAggInterval; - model.hideTimezone = value.hideTimezone; + if (value.hideAggregation) { + model.hideAggregation = value.hideAggregation; + } + if (value.hideAggInterval) { + model.hideAggInterval = value.hideAggInterval; + } + if (value.hideTimezone) { + model.hideTimezone = value.hideTimezone; + } + model.selectedTab = getTimewindowType(value); // for backward compatibility - if (isDefinedAndNotNull((value as any).hideInterval)) { + if ((value as any).hideInterval) { model.realtime.hideInterval = (value as any).hideInterval; model.history.hideInterval = (value as any).hideInterval; delete (value as any).hideInterval; } - if (isDefinedAndNotNull((value as any).hideLastInterval)) { + if ((value as any).hideLastInterval) { model.realtime.hideLastInterval = (value as any).hideLastInterval; delete (value as any).hideLastInterval; } - if (isDefinedAndNotNull((value as any).hideQuickInterval)) { + if ((value as any).hideQuickInterval) { model.realtime.hideQuickInterval = (value as any).hideQuickInterval; delete (value as any).hideQuickInterval; } if (isDefined(value.realtime)) { - if (isDefinedAndNotNull(value.realtime.hideInterval)) { + if (value.realtime.hideInterval) { model.realtime.hideInterval = value.realtime.hideInterval; } - if (isDefinedAndNotNull(value.realtime.hideLastInterval)) { + if (value.realtime.hideLastInterval) { model.realtime.hideLastInterval = value.realtime.hideLastInterval; } - if (isDefinedAndNotNull(value.realtime.hideQuickInterval)) { + if (value.realtime.hideQuickInterval) { model.realtime.hideQuickInterval = value.realtime.hideQuickInterval; } if (value.realtime.disableCustomInterval) { @@ -392,16 +388,16 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO } } if (isDefined(value.history)) { - if (isDefinedAndNotNull(value.history.hideInterval)) { + if (value.history.hideInterval) { model.history.hideInterval = value.history.hideInterval; } - if (isDefinedAndNotNull(value.history.hideLastInterval)) { + if (value.history.hideLastInterval) { model.history.hideLastInterval = value.history.hideLastInterval; } - if (isDefinedAndNotNull(value.history.hideFixedInterval)) { + if (value.history.hideFixedInterval) { model.history.hideFixedInterval = value.history.hideFixedInterval; } - if (isDefinedAndNotNull(value.history.hideQuickInterval)) { + if (value.history.hideQuickInterval) { model.history.hideQuickInterval = value.history.hideQuickInterval; } if (value.history.disableCustomInterval) { @@ -1098,9 +1094,15 @@ export const cloneSelectedTimewindow = (timewindow: Timewindow): Timewindow => { if (timewindow.allowedAggTypes?.length) { cloned.allowedAggTypes = timewindow.allowedAggTypes; } - cloned.hideAggregation = timewindow.hideAggregation || false; - cloned.hideAggInterval = timewindow.hideAggInterval || false; - cloned.hideTimezone = timewindow.hideTimezone || false; + if (timewindow.hideAggregation) { + cloned.hideAggregation = timewindow.hideAggregation; + } + if (timewindow.hideAggInterval) { + cloned.hideAggInterval = timewindow.hideAggInterval; + } + if (timewindow.hideTimezone) { + cloned.hideTimezone = timewindow.hideTimezone; + } if (isDefined(timewindow.selectedTab)) { cloned.selectedTab = timewindow.selectedTab; } From 083078b7f584f20567668fc16ba2a58d27beb72d Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Fri, 18 Jul 2025 18:56:02 +0300 Subject: [PATCH 02/29] Timewindow: leave only selected realtime/history and aggregation parameters for saving and remove others from configuration --- .../core/services/dashboard-utils.service.ts | 3 +- .../timewindow-config-dialog.component.ts | 104 +++++++++--------- .../time/timewindow-panel.component.ts | 100 +++++++++-------- .../components/time/timewindow.component.ts | 3 +- .../src/app/shared/models/time/time.models.ts | 87 +++++++++++++-- 5 files changed, 192 insertions(+), 105 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 1511840c57..ecb3e65c61 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -295,7 +295,8 @@ export class DashboardUtilsService { widgetConfig.datasources = this.validateAndUpdateDatasources(widgetConfig.datasources); if (type === widgetType.latest) { const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); - widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, onlyHistoryTimewindow, this.timeService); + widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, + onlyHistoryTimewindow, this.timeService, false); } else if (type === widgetType.rpc) { if (widgetConfig.targetDeviceAliasIds && widgetConfig.targetDeviceAliasIds.length) { widgetConfig.targetDevice = { diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index 7d69f99603..07e532cac0 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -152,73 +152,73 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm = this.fb.group({ selectedTab: [isDefined(this.timewindow.selectedTab) ? this.timewindow.selectedTab : TimewindowType.REALTIME], realtime: this.fb.group({ - realtimeType: [ isDefined(realtime?.realtimeType) ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL ], - timewindowMs: [ isDefined(realtime?.timewindowMs) ? this.timewindow.realtime.timewindowMs : null ], - interval: [ isDefined(realtime?.interval) ? this.timewindow.realtime.interval : null ], - quickInterval: [ isDefined(realtime?.quickInterval) ? this.timewindow.realtime.quickInterval : null ], - disableCustomInterval: [ isDefinedAndNotNull(this.timewindow.realtime?.disableCustomInterval) - ? this.timewindow.realtime?.disableCustomInterval : false ], - disableCustomGroupInterval: [ isDefinedAndNotNull(this.timewindow.realtime?.disableCustomGroupInterval) - ? this.timewindow.realtime?.disableCustomGroupInterval : false ], - hideInterval: [ isDefinedAndNotNull(this.timewindow.realtime.hideInterval) - ? this.timewindow.realtime.hideInterval : false ], + realtimeType: [ isDefined(realtime?.realtimeType) ? realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL ], + timewindowMs: [ isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : null ], + interval: [ isDefined(realtime?.interval) ? realtime.interval : null ], + quickInterval: [ isDefined(realtime?.quickInterval) ? realtime.quickInterval : null ], + disableCustomInterval: [ isDefinedAndNotNull(realtime?.disableCustomInterval) + ? realtime.disableCustomInterval : false ], + disableCustomGroupInterval: [ isDefinedAndNotNull(realtime?.disableCustomGroupInterval) + ? realtime.disableCustomGroupInterval : false ], + hideInterval: [ isDefinedAndNotNull(realtime?.hideInterval) + ? realtime.hideInterval : false ], hideLastInterval: [{ - value: isDefinedAndNotNull(this.timewindow.realtime.hideLastInterval) - ? this.timewindow.realtime.hideLastInterval : false, - disabled: this.timewindow.realtime.hideInterval + value: isDefinedAndNotNull(realtime?.hideLastInterval) + ? realtime.hideLastInterval : false, + disabled: realtime?.hideInterval }], hideQuickInterval: [{ - value: isDefinedAndNotNull(this.timewindow.realtime.hideQuickInterval) - ? this.timewindow.realtime.hideQuickInterval : false, - disabled: this.timewindow.realtime.hideInterval + value: isDefinedAndNotNull(realtime?.hideQuickInterval) + ? realtime.hideQuickInterval : false, + disabled: realtime?.hideInterval }], advancedParams: this.fb.group({ - allowedLastIntervals: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.allowedLastIntervals) - ? this.timewindow.realtime.advancedParams.allowedLastIntervals : null ], - allowedQuickIntervals: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.allowedQuickIntervals) - ? this.timewindow.realtime.advancedParams.allowedQuickIntervals : null ], - lastAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.lastAggIntervalsConfig) - ? this.timewindow.realtime.advancedParams.lastAggIntervalsConfig : null ], - quickAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.realtime?.advancedParams?.quickAggIntervalsConfig) - ? this.timewindow.realtime.advancedParams.quickAggIntervalsConfig : null ] + allowedLastIntervals: [ isDefinedAndNotNull(realtime?.advancedParams?.allowedLastIntervals) + ? realtime.advancedParams.allowedLastIntervals : null ], + allowedQuickIntervals: [ isDefinedAndNotNull(realtime?.advancedParams?.allowedQuickIntervals) + ? realtime.advancedParams.allowedQuickIntervals : null ], + lastAggIntervalsConfig: [ isDefinedAndNotNull(realtime?.advancedParams?.lastAggIntervalsConfig) + ? realtime.advancedParams.lastAggIntervalsConfig : null ], + quickAggIntervalsConfig: [ isDefinedAndNotNull(realtime?.advancedParams?.quickAggIntervalsConfig) + ? realtime.advancedParams.quickAggIntervalsConfig : null ] }) }), history: this.fb.group({ - historyType: [ isDefined(history?.historyType) ? this.timewindow.history.historyType : HistoryWindowType.LAST_INTERVAL ], - timewindowMs: [ isDefined(history?.timewindowMs) ? this.timewindow.history.timewindowMs : null ], - interval: [ isDefined(history?.interval) ? this.timewindow.history.interval : null ], - fixedTimewindow: [ isDefined(history?.fixedTimewindow) ? this.timewindow.history.fixedTimewindow : null ], - quickInterval: [ isDefined(history?.quickInterval) ? this.timewindow.history.quickInterval : null ], - disableCustomInterval: [ isDefinedAndNotNull(this.timewindow.history?.disableCustomInterval) - ? this.timewindow.history?.disableCustomInterval : false ], - disableCustomGroupInterval: [ isDefinedAndNotNull(this.timewindow.history?.disableCustomGroupInterval) - ? this.timewindow.history?.disableCustomGroupInterval : false ], - hideInterval: [ isDefinedAndNotNull(this.timewindow.history.hideInterval) - ? this.timewindow.history.hideInterval : false ], + historyType: [ isDefined(history?.historyType) ? history.historyType : HistoryWindowType.LAST_INTERVAL ], + timewindowMs: [ isDefined(history?.timewindowMs) ? history.timewindowMs : null ], + interval: [ isDefined(history?.interval) ? history.interval : null ], + fixedTimewindow: [ isDefined(history?.fixedTimewindow) ? history.fixedTimewindow : null ], + quickInterval: [ isDefined(history?.quickInterval) ? history.quickInterval : null ], + disableCustomInterval: [ isDefinedAndNotNull(history?.disableCustomInterval) + ? history.disableCustomInterval : false ], + disableCustomGroupInterval: [ isDefinedAndNotNull(history?.disableCustomGroupInterval) + ? history.disableCustomGroupInterval : false ], + hideInterval: [ isDefinedAndNotNull(history?.hideInterval) + ? history.hideInterval : false ], hideLastInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideLastInterval) - ? this.timewindow.history.hideLastInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideLastInterval) + ? history.hideLastInterval : false, + disabled: history?.hideInterval }], hideQuickInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideQuickInterval) - ? this.timewindow.history.hideQuickInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideQuickInterval) + ? history.hideQuickInterval : false, + disabled: history?.hideInterval }], hideFixedInterval: [{ - value: isDefinedAndNotNull(this.timewindow.history.hideFixedInterval) - ? this.timewindow.history.hideFixedInterval : false, - disabled: this.timewindow.history.hideInterval + value: isDefinedAndNotNull(history?.hideFixedInterval) + ? history.hideFixedInterval : false, + disabled: history?.hideInterval }], advancedParams: this.fb.group({ - allowedLastIntervals: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.allowedLastIntervals) - ? this.timewindow.history.advancedParams.allowedLastIntervals : null ], - allowedQuickIntervals: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.allowedQuickIntervals) - ? this.timewindow.history.advancedParams.allowedQuickIntervals : null ], - lastAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.lastAggIntervalsConfig) - ? this.timewindow.history.advancedParams.lastAggIntervalsConfig : null ], - quickAggIntervalsConfig: [ isDefinedAndNotNull(this.timewindow.history?.advancedParams?.quickAggIntervalsConfig) - ? this.timewindow.history.advancedParams.quickAggIntervalsConfig : null ] + allowedLastIntervals: [ isDefinedAndNotNull(history?.advancedParams?.allowedLastIntervals) + ? history.advancedParams.allowedLastIntervals : null ], + allowedQuickIntervals: [ isDefinedAndNotNull(history?.advancedParams?.allowedQuickIntervals) + ? history.advancedParams.allowedQuickIntervals : null ], + lastAggIntervalsConfig: [ isDefinedAndNotNull(history?.advancedParams?.lastAggIntervalsConfig) + ? history.advancedParams.lastAggIntervalsConfig : null ], + quickAggIntervalsConfig: [ isDefinedAndNotNull(history?.advancedParams?.quickAggIntervalsConfig) + ? history.advancedParams.quickAggIntervalsConfig : null ] }) }), aggregation: this.fb.group({ diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index d7a0d44013..9831ec0599 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -27,12 +27,14 @@ import { } from '@angular/core'; import { AggregationType, + clearTimewindowConfig, currentHistoryTimewindow, currentRealtimeTimewindow, historyAllowedAggIntervals, HistoryWindowType, historyWindowTypeTranslations, Interval, + MINUTE, QuickTimeInterval, realtimeAllowedAggIntervals, RealtimeWindowType, @@ -167,14 +169,14 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O }); } - if ((this.isEdit || !this.timewindow.realtime.hideLastInterval) && !this.quickIntervalOnly) { + if ((this.isEdit || !this.timewindow.realtime?.hideLastInterval) && !this.quickIntervalOnly) { this.realtimeTimewindowOptions.push({ name: this.translate.instant(realtimeWindowTypeTranslations.get(RealtimeWindowType.LAST_INTERVAL)), value: this.realtimeTypes.LAST_INTERVAL }); } - if (this.isEdit || !this.timewindow.realtime.hideQuickInterval || this.quickIntervalOnly) { + if (this.isEdit || !this.timewindow.realtime?.hideQuickInterval || this.quickIntervalOnly) { this.realtimeTimewindowOptions.push({ name: this.translate.instant(realtimeWindowTypeTranslations.get(RealtimeWindowType.INTERVAL)), value: this.realtimeTypes.INTERVAL @@ -188,21 +190,21 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O }); } - if (this.isEdit || !this.timewindow.history.hideLastInterval) { + if (this.isEdit || !this.timewindow.history?.hideLastInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.LAST_INTERVAL)), value: this.historyTypes.LAST_INTERVAL }); } - if (this.isEdit || !this.timewindow.history.hideFixedInterval) { + if (this.isEdit || !this.timewindow.history?.hideFixedInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.FIXED)), value: this.historyTypes.FIXED }); } - if (this.isEdit || !this.timewindow.history.hideQuickInterval) { + if (this.isEdit || !this.timewindow.history?.hideQuickInterval) { this.historyTimewindowOptions.push({ name: this.translate.instant(historyWindowTypeTranslations.get(HistoryWindowType.INTERVAL)), value: this.historyTypes.INTERVAL @@ -211,10 +213,10 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.realtimeTypeSelectionAvailable = this.realtimeTimewindowOptions.length > 1; this.historyTypeSelectionAvailable = this.historyTimewindowOptions.length > 1; - this.realtimeIntervalSelectionAvailable = this.isEdit || !(this.timewindow.realtime.hideInterval || - (this.timewindow.realtime.hideLastInterval && this.timewindow.realtime.hideQuickInterval)); - this.historyIntervalSelectionAvailable = this.isEdit || !(this.timewindow.history.hideInterval || - (this.timewindow.history.hideLastInterval && this.timewindow.history.hideQuickInterval && this.timewindow.history.hideFixedInterval)); + this.realtimeIntervalSelectionAvailable = this.isEdit || !(this.timewindow.realtime?.hideInterval || + (this.timewindow.realtime?.hideLastInterval && this.timewindow.realtime?.hideQuickInterval)); + this.historyIntervalSelectionAvailable = this.isEdit || !(this.timewindow.history?.hideInterval || + (this.timewindow.history?.hideLastInterval && this.timewindow.history?.hideQuickInterval && this.timewindow.history?.hideFixedInterval)); this.aggregationOptionsAvailable = this.aggregation && (this.isEdit || !(this.timewindow.hideAggregation && this.timewindow.hideAggInterval)); @@ -230,28 +232,28 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O const aggregation = this.timewindow.aggregation; if (!this.isEdit) { - if (realtime.hideLastInterval && !realtime.hideQuickInterval) { + if (realtime?.hideLastInterval && !realtime?.hideQuickInterval) { realtime.realtimeType = RealtimeWindowType.INTERVAL; } - if (realtime.hideQuickInterval && !realtime.hideLastInterval) { + if (realtime?.hideQuickInterval && !realtime?.hideLastInterval) { realtime.realtimeType = RealtimeWindowType.LAST_INTERVAL; } - if (history.hideLastInterval) { + if (history?.hideLastInterval) { if (!history.hideFixedInterval) { history.historyType = HistoryWindowType.FIXED; } else if (!history.hideQuickInterval) { history.historyType = HistoryWindowType.INTERVAL; } } - if (history.hideFixedInterval) { + if (history?.hideFixedInterval) { if (!history.hideLastInterval) { history.historyType = HistoryWindowType.LAST_INTERVAL; } else if (!history.hideQuickInterval) { history.historyType = HistoryWindowType.INTERVAL; } } - if (history.hideQuickInterval) { + if (history?.hideQuickInterval) { if (!history.hideLastInterval) { history.historyType = HistoryWindowType.LAST_INTERVAL; } else if (!history.hideFixedInterval) { @@ -265,29 +267,29 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O realtime: this.fb.group({ realtimeType: [{ value: isDefined(realtime?.realtimeType) ? realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, - disabled: realtime.hideInterval + disabled: realtime?.hideInterval }], timewindowMs: [{ - value: isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : null, - disabled: realtime.hideInterval || realtime.hideLastInterval + value: isDefined(realtime?.timewindowMs) ? realtime.timewindowMs : MINUTE, + disabled: realtime?.hideInterval || realtime?.hideLastInterval }], interval: [{ value:isDefined(realtime?.interval) ? realtime.interval : null, disabled: hideAggInterval }], quickInterval: [{ - value: isDefined(realtime?.quickInterval) ? realtime.quickInterval : null, - disabled: realtime.hideInterval || realtime.hideQuickInterval + value: isDefined(realtime?.quickInterval) ? realtime.quickInterval : QuickTimeInterval.CURRENT_DAY, + disabled: realtime?.hideInterval || realtime?.hideQuickInterval }] }), history: this.fb.group({ historyType: [{ value: isDefined(history?.historyType) ? history.historyType : HistoryWindowType.LAST_INTERVAL, - disabled: history.hideInterval + disabled: history?.hideInterval }], timewindowMs: [{ - value: isDefined(history?.timewindowMs) ? history.timewindowMs : null, - disabled: history.hideInterval || history.hideLastInterval + value: isDefined(history?.timewindowMs) ? history.timewindowMs : MINUTE, + disabled: history?.hideInterval || history?.hideLastInterval }], interval: [{ value:isDefined(history?.interval) ? history.interval : null, @@ -296,11 +298,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O fixedTimewindow: [{ value: isDefined(history?.fixedTimewindow) && this.timewindow.selectedTab === TimewindowType.HISTORY && history.historyType === HistoryWindowType.FIXED ? history.fixedTimewindow : null, - disabled: history.hideInterval || history.hideFixedInterval + disabled: history?.hideInterval || history?.hideFixedInterval }], quickInterval: [{ - value: isDefined(history?.quickInterval) ? history.quickInterval : null, - disabled: history.hideInterval || history.hideQuickInterval + value: isDefined(history?.quickInterval) ? history.quickInterval : QuickTimeInterval.CURRENT_DAY, + disabled: history?.hideInterval || history?.hideQuickInterval }] }), aggregation: this.fb.group({ @@ -380,6 +382,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O takeUntil(this.destroy$) ).subscribe(() => { this.prepareTimewindowConfig(); + this.clearTimewindowConfig(); this.changeTimewindow.emit(this.timewindow); }); } @@ -407,6 +410,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O update() { this.prepareTimewindowConfig(); + this.clearTimewindowConfig(); this.result = this.timewindow; this.overlayRef?.dispose(); } @@ -414,21 +418,25 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O private prepareTimewindowConfig() { const timewindowFormValue = this.timewindowForm.getRawValue(); this.timewindow.selectedTab = timewindowFormValue.selectedTab; - this.timewindow.realtime = {...this.timewindow.realtime, ...{ - realtimeType: timewindowFormValue.realtime.realtimeType, - timewindowMs: timewindowFormValue.realtime.timewindowMs, - quickInterval: timewindowFormValue.realtime.quickInterval, - interval: timewindowFormValue.realtime.interval - }}; - this.timewindow.history = {...this.timewindow.history, ...{ - historyType: timewindowFormValue.history.historyType, - timewindowMs: timewindowFormValue.history.timewindowMs, - interval: timewindowFormValue.history.interval, - fixedTimewindow: timewindowFormValue.history.fixedTimewindow, - quickInterval: timewindowFormValue.history.quickInterval, - }}; + if (this.timewindow.selectedTab === TimewindowType.REALTIME) { + this.timewindow.realtime = {...this.timewindow.realtime, ...{ + realtimeType: timewindowFormValue.realtime.realtimeType, + timewindowMs: timewindowFormValue.realtime.timewindowMs, + quickInterval: timewindowFormValue.realtime.quickInterval, + }}; + } else { + this.timewindow.history = {...this.timewindow.history, ...{ + historyType: timewindowFormValue.history.historyType, + timewindowMs: timewindowFormValue.history.timewindowMs, + fixedTimewindow: timewindowFormValue.history.fixedTimewindow, + quickInterval: timewindowFormValue.history.quickInterval, + }}; + } if (this.aggregation) { + this.timewindow.realtime.interval = timewindowFormValue.realtime.interval; + this.timewindow.history.interval = timewindowFormValue.history.interval; + this.timewindow.aggregation = { type: timewindowFormValue.aggregation.type, limit: timewindowFormValue.aggregation.limit @@ -439,6 +447,10 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } } + private clearTimewindowConfig() { + clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); + } + private updateTimewindowForm() { this.timewindowForm.patchValue(this.timewindow, {emitEvent: false}); this.updateValidators(this.timewindowForm.get('aggregation.type').value); @@ -568,12 +580,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } private updateTimewindowAdvancedParams() { - this.realtimeDisableCustomInterval = this.timewindow.realtime.disableCustomInterval; - this.realtimeDisableCustomGroupInterval = this.timewindow.realtime.disableCustomGroupInterval; - this.historyDisableCustomInterval = this.timewindow.history.disableCustomInterval; - this.historyDisableCustomGroupInterval = this.timewindow.history.disableCustomGroupInterval; + this.realtimeDisableCustomInterval = this.timewindow.realtime?.disableCustomInterval; + this.realtimeDisableCustomGroupInterval = this.timewindow.realtime?.disableCustomGroupInterval; + this.historyDisableCustomInterval = this.timewindow.history?.disableCustomInterval; + this.historyDisableCustomGroupInterval = this.timewindow.history?.disableCustomGroupInterval; - if (this.timewindow.realtime.advancedParams) { + if (this.timewindow.realtime?.advancedParams) { this.realtimeAdvancedParams = this.timewindow.realtime.advancedParams; this.realtimeAllowedLastIntervals = this.timewindow.realtime.advancedParams.allowedLastIntervals; this.realtimeAllowedQuickIntervals = this.timewindow.realtime.advancedParams.allowedQuickIntervals; @@ -582,7 +594,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.realtimeAllowedLastIntervals = null; this.realtimeAllowedQuickIntervals = null; } - if (this.timewindow.history.advancedParams) { + if (this.timewindow.history?.advancedParams) { this.historyAdvancedParams = this.timewindow.history.advancedParams; this.historyAllowedLastIntervals = this.timewindow.history.advancedParams.allowedLastIntervals; this.historyAllowedQuickIntervals = this.timewindow.history.advancedParams.allowedQuickIntervals; diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index a52e3eb091..07f90db66e 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -319,7 +319,8 @@ export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChan } writeValue(obj: Timewindow): void { - this.innerValue = initModelFromDefaultTimewindow(obj, this.quickIntervalOnly, this.historyOnly, this.timeService); + this.innerValue = initModelFromDefaultTimewindow(obj, this.quickIntervalOnly, this.historyOnly, this.timeService, + this.aggregation); this.timewindowDisabled = this.isTimewindowDisabled(); if (this.onHistoryOnlyChanged()) { setTimeout(() => { diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index 5442cb95ca..7c53d93edf 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -15,11 +15,12 @@ /// import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined } from '@app/core/utils'; +import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined, isUndefinedOrNull } from '@app/core/utils'; import moment_ from 'moment'; import * as momentTz from 'moment-timezone'; import { IntervalType } from '@shared/models/telemetry/telemetry.models'; import { FormGroup } from '@angular/forms'; +import { isEmpty } from 'lodash'; const moment = moment_; @@ -314,7 +315,7 @@ const getTimewindowType = (timewindow: Timewindow): TimewindowType => { }; export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalOnly: boolean, - historyOnly: boolean, timeService: TimeService): Timewindow => { + historyOnly: boolean, timeService: TimeService, hasAggregation: boolean): Timewindow => { const model = defaultTimewindow(timeService); if (value) { if (value.allowedAggTypes?.length) { @@ -446,7 +447,9 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO } model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2); } - model.timezone = value.timezone; + if (value.timezone) { + model.timezone = value.timezone; + } } if (quickIntervalOnly) { model.realtime.realtimeType = RealtimeWindowType.INTERVAL; @@ -454,6 +457,7 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO if (historyOnly) { model.selectedTab = TimewindowType.HISTORY; } + clearTimewindowConfig(model, quickIntervalOnly, historyOnly, hasAggregation); return model; }; @@ -1106,13 +1110,82 @@ export const cloneSelectedTimewindow = (timewindow: Timewindow): Timewindow => { if (isDefined(timewindow.selectedTab)) { cloned.selectedTab = timewindow.selectedTab; } - cloned.realtime = deepClone(timewindow.realtime); - cloned.history = deepClone(timewindow.history); - cloned.aggregation = deepClone(timewindow.aggregation); - cloned.timezone = timewindow.timezone; + if (isDefined(timewindow.realtime)) { + cloned.realtime = deepClone(timewindow.realtime); + } + if (isDefined(timewindow.history)) { + cloned.history = deepClone(timewindow.history); + } + if (isDefined(timewindow.aggregation)) { + cloned.aggregation = deepClone(timewindow.aggregation); + } + if (timewindow.timezone) { + cloned.timezone = timewindow.timezone; + } return cloned; }; +export const clearTimewindowConfig = (timewindow: Timewindow, quickIntervalOnly: boolean, + historyOnly: boolean, hasAggregation: boolean, hasTimezone = true): Timewindow => { + if (timewindow.selectedTab === TimewindowType.REALTIME) { + if (quickIntervalOnly || timewindow.realtime.realtimeType === RealtimeWindowType.INTERVAL) { + delete timewindow.realtime.timewindowMs; + } else { + delete timewindow.realtime.quickInterval; + } + + delete timewindow.history.historyType; + delete timewindow.history.timewindowMs; + delete timewindow.history.fixedTimewindow; + delete timewindow.history.quickInterval; + + delete timewindow.history.interval; + if (!hasAggregation) { + delete timewindow.realtime.interval; + } + } else { + if (timewindow.history.historyType === HistoryWindowType.LAST_INTERVAL) { + delete timewindow.history.fixedTimewindow; + delete timewindow.history.quickInterval; + } else if (timewindow.history.historyType === HistoryWindowType.FIXED) { + delete timewindow.history.timewindowMs; + delete timewindow.history.quickInterval; + } else if (timewindow.history.historyType === HistoryWindowType.INTERVAL) { + delete timewindow.history.timewindowMs; + delete timewindow.history.fixedTimewindow; + } else { + delete timewindow.history.timewindowMs; + delete timewindow.history.fixedTimewindow; + delete timewindow.history.quickInterval; + } + + delete timewindow.realtime.realtimeType; + delete timewindow.realtime.timewindowMs; + delete timewindow.realtime.quickInterval; + + delete timewindow.realtime.interval; + if (!hasAggregation) { + delete timewindow.history.interval; + } + } + + if (!hasAggregation) { + delete timewindow.aggregation; + } + + if (isEmpty(timewindow.history)) { + delete timewindow.history; + } + if (historyOnly || isEmpty(timewindow.realtime)) { + delete timewindow.realtime; + } + + if (!hasTimezone || isUndefinedOrNull(timewindow.timezone)) { + delete timewindow.timezone; + } + return timewindow; +}; + export interface TimeInterval { name: string; translateParams: {[key: string]: any}; From 6d509ca5d9312416cae4e6ebed154ef799a991e1 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 21 Jul 2025 16:19:06 +0300 Subject: [PATCH 03/29] Timewindow: optimize advanced configuration parameters update, add deepClean --- ui-ngx/src/app/core/utils.ts | 30 ++++++++++++- .../timewindow-config-dialog.component.ts | 45 +++++-------------- .../time/timewindow-panel.component.ts | 10 ++--- .../src/app/shared/models/time/time.models.ts | 15 +++---- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 353727e006..d0bd9ae484 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -27,7 +27,8 @@ import { serverErrorCodesTranslations } from '@shared/models/constants'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { CompiledTbFunction, - compileTbFunction, GenericFunction, + compileTbFunction, + GenericFunction, isNotEmptyTbFunction, TbFunction } from '@shared/models/js-function.models'; @@ -773,6 +774,33 @@ export function deepTrim(obj: T): T { }, (Array.isArray(obj) ? [] : {}) as T); } +export function deepClean | any[]>(obj: T, { + cleanKeys = [] +} = {}): T { + return _.transform(obj, (result, value, key) => { + if (cleanKeys.includes(key)) { + return; + } + if (Array.isArray(value) || isLiteralObject(value)) { + value = deepClean(value, {cleanKeys}); + } + if(isLiteralObject(value) && isEmpty(value)) { + return; + } + if (Array.isArray(value) && !value.length) { + return; + } + if (value === undefined || value === null || value === '' || Number.isNaN(value)) { + return; + } + + if (Array.isArray(result)) { + return result.push(value); + } + result[key] = value; + }); +} + export function generateSecret(length?: number): string { if (isUndefined(length) || length == null) { length = 1; diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index 07e532cac0..33a9d44cf3 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -36,7 +36,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isObject, mergeDeep } from '@core/utils'; +import { deepClean, deepClone, isDefined, isDefinedAndNotNull, isEmpty, mergeDeepIgnoreArray } from '@core/utils'; import { ToggleHeaderOption } from '@shared/components/toggle-header.component'; import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -423,7 +423,7 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On update() { const timewindowFormValue = this.timewindowForm.getRawValue(); - this.timewindow = mergeDeep(this.timewindow, timewindowFormValue); + this.timewindow = mergeDeepIgnoreArray(this.timewindow, timewindowFormValue); const realtimeConfigurableLastIntervalsAvailable = !(timewindowFormValue.hideAggInterval && (timewindowFormValue.realtime.hideInterval || timewindowFormValue.realtime.hideLastInterval)); @@ -434,62 +434,41 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On const historyConfigurableQuickIntervalsAvailable = !(timewindowFormValue.hideAggInterval && (timewindowFormValue.history.hideInterval || timewindowFormValue.history.hideQuickInterval)); - if (realtimeConfigurableLastIntervalsAvailable && timewindowFormValue.realtime.advancedParams.allowedLastIntervals?.length) { - this.timewindow.realtime.advancedParams.allowedLastIntervals = timewindowFormValue.realtime.advancedParams.allowedLastIntervals; - } else { + if (!realtimeConfigurableLastIntervalsAvailable) { delete this.timewindow.realtime.advancedParams.allowedLastIntervals; } - if (realtimeConfigurableQuickIntervalsAvailable && timewindowFormValue.realtime.advancedParams.allowedQuickIntervals?.length) { - this.timewindow.realtime.advancedParams.allowedQuickIntervals = timewindowFormValue.realtime.advancedParams.allowedQuickIntervals; - } else { + if (!realtimeConfigurableQuickIntervalsAvailable) { delete this.timewindow.realtime.advancedParams.allowedQuickIntervals; } - if (realtimeConfigurableLastIntervalsAvailable && isObject(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig) && - Object.keys(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig).length) { + if (realtimeConfigurableLastIntervalsAvailable && !isEmpty(timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig)) { this.timewindow.realtime.advancedParams.lastAggIntervalsConfig = timewindowFormValue.realtime.advancedParams.lastAggIntervalsConfig; } else { delete this.timewindow.realtime.advancedParams.lastAggIntervalsConfig; } - if (realtimeConfigurableQuickIntervalsAvailable && isObject(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig) && - Object.keys(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig).length) { + if (realtimeConfigurableQuickIntervalsAvailable && !isEmpty(timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig)) { this.timewindow.realtime.advancedParams.quickAggIntervalsConfig = timewindowFormValue.realtime.advancedParams.quickAggIntervalsConfig; } else { delete this.timewindow.realtime.advancedParams.quickAggIntervalsConfig; } - if (historyConfigurableLastIntervalsAvailable && timewindowFormValue.history.advancedParams.allowedLastIntervals?.length) { - this.timewindow.history.advancedParams.allowedLastIntervals = timewindowFormValue.history.advancedParams.allowedLastIntervals; - } else { + if (!historyConfigurableLastIntervalsAvailable) { delete this.timewindow.history.advancedParams.allowedLastIntervals; } - if (historyConfigurableQuickIntervalsAvailable && timewindowFormValue.history.advancedParams.allowedQuickIntervals?.length) { - this.timewindow.history.advancedParams.allowedQuickIntervals = timewindowFormValue.history.advancedParams.allowedQuickIntervals; - } else { + if (!historyConfigurableQuickIntervalsAvailable) { delete this.timewindow.history.advancedParams.allowedQuickIntervals; } - if (historyConfigurableLastIntervalsAvailable && isObject(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig) && - Object.keys(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig).length) { + if (historyConfigurableLastIntervalsAvailable && !isEmpty(timewindowFormValue.history.advancedParams.lastAggIntervalsConfig)) { this.timewindow.history.advancedParams.lastAggIntervalsConfig = timewindowFormValue.history.advancedParams.lastAggIntervalsConfig; } else { delete this.timewindow.history.advancedParams.lastAggIntervalsConfig; } - if (historyConfigurableQuickIntervalsAvailable && isObject(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig) && - Object.keys(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig).length) { + if (historyConfigurableQuickIntervalsAvailable && !isEmpty(timewindowFormValue.history.advancedParams.quickAggIntervalsConfig)) { this.timewindow.history.advancedParams.quickAggIntervalsConfig = timewindowFormValue.history.advancedParams.quickAggIntervalsConfig; } else { delete this.timewindow.history.advancedParams.quickAggIntervalsConfig; } - if (!Object.keys(this.timewindow.realtime.advancedParams).length) { - delete this.timewindow.realtime.advancedParams; - } - if (!Object.keys(this.timewindow.history.advancedParams).length) { - delete this.timewindow.history.advancedParams; - } - - if (timewindowFormValue.allowedAggTypes?.length && !timewindowFormValue.hideAggregation) { - this.timewindow.allowedAggTypes = timewindowFormValue.allowedAggTypes; - } else { + if (timewindowFormValue.hideAggregation) { delete this.timewindow.allowedAggTypes; } @@ -541,7 +520,7 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On if (!this.aggregation) { delete this.timewindow.aggregation; } - this.dialogRef.close(this.timewindow); + this.dialogRef.close(deepClean(this.timewindow)); } cancel() { diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 9831ec0599..7c6445c6b2 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -382,8 +382,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O takeUntil(this.destroy$) ).subscribe(() => { this.prepareTimewindowConfig(); - this.clearTimewindowConfig(); - this.changeTimewindow.emit(this.timewindow); + this.changeTimewindow.emit(this.clearTimewindowConfig()); }); } } @@ -410,8 +409,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O update() { this.prepareTimewindowConfig(); - this.clearTimewindowConfig(); - this.result = this.timewindow; + this.result = this.clearTimewindowConfig(); this.overlayRef?.dispose(); } @@ -447,8 +445,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } } - private clearTimewindowConfig() { - clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); + private clearTimewindowConfig(): Timewindow { + return clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); } private updateTimewindowForm() { diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index 7c53d93edf..cace546b19 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -15,12 +15,11 @@ /// import { TimeService } from '@core/services/time.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined, isUndefinedOrNull } from '@app/core/utils'; +import { deepClean, deepClone, isDefined, isDefinedAndNotNull, isNumeric, isUndefined } from '@app/core/utils'; import moment_ from 'moment'; import * as momentTz from 'moment-timezone'; import { IntervalType } from '@shared/models/telemetry/telemetry.models'; import { FormGroup } from '@angular/forms'; -import { isEmpty } from 'lodash'; const moment = moment_; @@ -457,8 +456,7 @@ export const initModelFromDefaultTimewindow = (value: Timewindow, quickIntervalO if (historyOnly) { model.selectedTab = TimewindowType.HISTORY; } - clearTimewindowConfig(model, quickIntervalOnly, historyOnly, hasAggregation); - return model; + return clearTimewindowConfig(model, quickIntervalOnly, historyOnly, hasAggregation); }; export const toHistoryTimewindow = (timewindow: Timewindow, startTimeMs: number, endTimeMs: number, @@ -1173,17 +1171,14 @@ export const clearTimewindowConfig = (timewindow: Timewindow, quickIntervalOnly: delete timewindow.aggregation; } - if (isEmpty(timewindow.history)) { - delete timewindow.history; - } - if (historyOnly || isEmpty(timewindow.realtime)) { + if (historyOnly) { delete timewindow.realtime; } - if (!hasTimezone || isUndefinedOrNull(timewindow.timezone)) { + if (!hasTimezone) { delete timewindow.timezone; } - return timewindow; + return deepClean(timewindow); }; export interface TimeInterval { From d95a00e4af4fb5b369d32f4d6f19cd659dc4577d Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 21 Jul 2025 17:22:34 +0300 Subject: [PATCH 04/29] Timewindow: optimize false properties deletion --- ui-ngx/src/app/core/utils.ts | 17 ++++++ .../timewindow-config-dialog.component.ts | 57 ++++--------------- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index d0bd9ae484..9937689ba8 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -197,6 +197,23 @@ export function deleteNullProperties(obj: any) { }); } +export function deleteFalseProperties(obj: any) { + if (isUndefinedOrNull(obj)) { + return; + } + Object.keys(obj).forEach((propName) => { + if (obj[propName] === false || isUndefinedOrNull(obj[propName])) { + delete obj[propName]; + } else if (isObject(obj[propName])) { + deleteFalseProperties(obj[propName]); + } else if (Array.isArray(obj[propName])) { + (obj[propName] as any[]).forEach((elem) => { + deleteFalseProperties(elem); + }); + } + }); +} + export function objToBase64(obj: any): string { const json = JSON.stringify(obj); return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g, diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index 33a9d44cf3..42e04cfc46 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -36,7 +36,15 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TimeService } from '@core/services/time.service'; -import { deepClean, deepClone, isDefined, isDefinedAndNotNull, isEmpty, mergeDeepIgnoreArray } from '@core/utils'; +import { + deepClean, + deepClone, + deleteFalseProperties, + isDefined, + isDefinedAndNotNull, + isEmpty, + mergeDeepIgnoreArray +} from '@core/utils'; import { ToggleHeaderOption } from '@shared/components/toggle-header.component'; import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -472,54 +480,11 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On delete this.timewindow.allowedAggTypes; } - if (!this.timewindow.realtime.disableCustomInterval) { - delete this.timewindow.realtime.disableCustomInterval; - } - if (!this.timewindow.realtime.disableCustomGroupInterval) { - delete this.timewindow.realtime.disableCustomGroupInterval; - } - if (!this.timewindow.history.disableCustomInterval) { - delete this.timewindow.history.disableCustomInterval; - } - if (!this.timewindow.history.disableCustomGroupInterval) { - delete this.timewindow.history.disableCustomGroupInterval; - } - - if (!this.timewindow.hideAggregation) { - delete this.timewindow.hideAggregation; - } - if (!this.timewindow.hideAggInterval) { - delete this.timewindow.hideAggInterval; - } - if (!this.timewindow.hideTimezone) { - delete this.timewindow.hideTimezone; - } - - if (!this.timewindow.realtime.hideInterval) { - delete this.timewindow.realtime.hideInterval; - } - if (!this.timewindow.realtime.hideLastInterval) { - delete this.timewindow.realtime.hideLastInterval; - } - if (!this.timewindow.realtime.hideQuickInterval) { - delete this.timewindow.realtime.hideQuickInterval; - } - if (!this.timewindow.history.hideInterval) { - delete this.timewindow.history.hideInterval; - } - if (!this.timewindow.history.hideLastInterval) { - delete this.timewindow.history.hideLastInterval; - } - if (!this.timewindow.history.hideFixedInterval) { - delete this.timewindow.history.hideFixedInterval; - } - if (!this.timewindow.history.hideQuickInterval) { - delete this.timewindow.history.hideQuickInterval; - } - if (!this.aggregation) { delete this.timewindow.aggregation; } + + deleteFalseProperties(this.timewindow); this.dialogRef.close(deepClean(this.timewindow)); } From 81a5a3c3eaf8c7ab860ba4060edc9dba049bf8f4 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 21 Jul 2025 16:19:06 +0300 Subject: [PATCH 05/29] Timewindow: fixed updating aggregation interval --- .../app/shared/components/time/timewindow-panel.component.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 7c6445c6b2..6b5d434102 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -421,6 +421,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O realtimeType: timewindowFormValue.realtime.realtimeType, timewindowMs: timewindowFormValue.realtime.timewindowMs, quickInterval: timewindowFormValue.realtime.quickInterval, + interval: timewindowFormValue.realtime.interval, }}; } else { this.timewindow.history = {...this.timewindow.history, ...{ @@ -428,13 +429,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O timewindowMs: timewindowFormValue.history.timewindowMs, fixedTimewindow: timewindowFormValue.history.fixedTimewindow, quickInterval: timewindowFormValue.history.quickInterval, + interval: timewindowFormValue.history.interval, }}; } if (this.aggregation) { - this.timewindow.realtime.interval = timewindowFormValue.realtime.interval; - this.timewindow.history.interval = timewindowFormValue.history.interval; - this.timewindow.aggregation = { type: timewindowFormValue.aggregation.type, limit: timewindowFormValue.aggregation.limit From 35b349bd6e155f9a31baf4a9787b884fe49d9e75 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Mon, 21 Jul 2025 16:19:06 +0300 Subject: [PATCH 06/29] Timewindow: additional conditioning for clear config function; refactoring --- .../time/timewindow-panel.component.ts | 19 +++++++++---------- .../src/app/shared/models/time/time.models.ts | 18 +++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 6b5d434102..2f0a1aa603 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -381,8 +381,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O this.timewindowForm.valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => { - this.prepareTimewindowConfig(); - this.changeTimewindow.emit(this.clearTimewindowConfig()); + this.changeTimewindow.emit(this.prepareTimewindowConfig()); }); } } @@ -408,12 +407,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } update() { - this.prepareTimewindowConfig(); - this.result = this.clearTimewindowConfig(); + this.result = this.prepareTimewindowConfig(); this.overlayRef?.dispose(); } - private prepareTimewindowConfig() { + private prepareTimewindowConfig(clearConfig = true): Timewindow { const timewindowFormValue = this.timewindowForm.getRawValue(); this.timewindow.selectedTab = timewindowFormValue.selectedTab; if (this.timewindow.selectedTab === TimewindowType.REALTIME) { @@ -442,10 +440,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O if (this.timezone) { this.timewindow.timezone = timewindowFormValue.timezone; } - } - private clearTimewindowConfig(): Timewindow { - return clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); + if (clearConfig) { + return clearTimewindowConfig(this.timewindow, this.quickIntervalOnly, this.historyOnly, this.aggregation, this.timezone); + } else { + return deepClone(this.timewindow); + } } private updateTimewindowForm() { @@ -555,7 +555,6 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O } openTimewindowConfig() { - this.prepareTimewindowConfig(); this.dialog.open( TimewindowConfigDialogComponent, { autoFocus: false, @@ -564,7 +563,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O data: { quickIntervalOnly: this.quickIntervalOnly, aggregation: this.aggregation, - timewindow: deepClone(this.timewindow) + timewindow: this.prepareTimewindowConfig(false) } }).afterClosed() .subscribe((res) => { diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index cace546b19..35ff3a0572 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -1132,12 +1132,12 @@ export const clearTimewindowConfig = (timewindow: Timewindow, quickIntervalOnly: delete timewindow.realtime.quickInterval; } - delete timewindow.history.historyType; - delete timewindow.history.timewindowMs; - delete timewindow.history.fixedTimewindow; - delete timewindow.history.quickInterval; + delete timewindow.history?.historyType; + delete timewindow.history?.timewindowMs; + delete timewindow.history?.fixedTimewindow; + delete timewindow.history?.quickInterval; - delete timewindow.history.interval; + delete timewindow.history?.interval; if (!hasAggregation) { delete timewindow.realtime.interval; } @@ -1157,11 +1157,11 @@ export const clearTimewindowConfig = (timewindow: Timewindow, quickIntervalOnly: delete timewindow.history.quickInterval; } - delete timewindow.realtime.realtimeType; - delete timewindow.realtime.timewindowMs; - delete timewindow.realtime.quickInterval; + delete timewindow.realtime?.realtimeType; + delete timewindow.realtime?.timewindowMs; + delete timewindow.realtime?.quickInterval; - delete timewindow.realtime.interval; + delete timewindow.realtime?.interval; if (!hasAggregation) { delete timewindow.history.interval; } From f1c80e43795946cb7b3d5485930faaf3fa790228 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Tue, 22 Jul 2025 20:44:43 +0300 Subject: [PATCH 07/29] Timewindow: remove timewindow config from widget if unused --- .../core/services/dashboard-utils.service.ts | 31 +++++++++++++++++-- .../dashboard-page.component.ts | 3 +- .../config/widget-config.component.models.ts | 17 ++++++++-- .../widget/widget-config.component.ts | 29 ++++++++++------- ui-ngx/src/app/shared/models/widget.models.ts | 8 +++++ 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index ecb3e65c61..b743126fdc 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -38,6 +38,7 @@ import { import { deepClone, isDefined, isDefinedAndNotNull, isNotEmptyStr, isString, isUndefined } from '@core/utils'; import { Datasource, + datasourcesHasAggregation, datasourcesHasOnlyComparisonAggregation, DatasourceType, defaultLegendConfig, @@ -49,7 +50,8 @@ import { WidgetConfigMode, WidgetSize, widgetType, - WidgetTypeDescriptor + WidgetTypeDescriptor, + widgetTypeHasTimewindow } from '@app/shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; import { AliasFilterType, EntityAlias, EntityAliasFilter } from '@app/shared/models/alias.models'; @@ -295,8 +297,11 @@ export class DashboardUtilsService { widgetConfig.datasources = this.validateAndUpdateDatasources(widgetConfig.datasources); if (type === widgetType.latest) { const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); - widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, - onlyHistoryTimewindow, this.timeService, false); + const aggregationEnabledForKeys = datasourcesHasAggregation(widgetConfig.datasources); + if (aggregationEnabledForKeys) { + widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, + onlyHistoryTimewindow, this.timeService, false); + } } else if (type === widgetType.rpc) { if (widgetConfig.targetDeviceAliasIds && widgetConfig.targetDeviceAliasIds.length) { widgetConfig.targetDevice = { @@ -346,9 +351,29 @@ export class DashboardUtilsService { } } } + + this.removeTimewindowConfigIfUnused(widgetConfig, type); return widgetConfig; } + public removeTimewindowConfigIfUnused(widgetConfig: WidgetConfig, type: widgetType) { + const widgetHasTimewindow = widgetTypeHasTimewindow(type) || (type === widgetType.latest && datasourcesHasAggregation(widgetConfig.datasources)); + if (!widgetHasTimewindow || widgetConfig.useDashboardTimewindow) { + delete widgetConfig.displayTimewindow; + delete widgetConfig.timewindow; + delete widgetConfig.timewindowStyle; + + if (!widgetHasTimewindow) { + delete widgetConfig.useDashboardTimewindow; + } + } + } + + public prepareWidgetForSaving(widget: Widget): Widget { + this.removeTimewindowConfigIfUnused(widget.config, widget.type); + return widget; + } + public prepareWidgetForScadaLayout(widget: Widget, isScada: boolean): Widget { const config = widget.config; config.showTitle = false; diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 60d05d4c86..4c528fd55a 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -1322,6 +1322,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } private addWidgetToDashboard(widget: Widget) { + this.dashboardUtils.prepareWidgetForSaving(widget); if (this.addingLayoutCtx) { this.addWidgetToLayout(widget, this.addingLayoutCtx.id); this.addingLayoutCtx = null; @@ -1409,7 +1410,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC saveWidget() { this.editWidgetComponent.widgetFormGroup.markAsPristine(); - const widget = deepClone(this.editingWidget); + const widget = this.dashboardUtils.prepareWidgetForSaving(deepClone(this.editingWidget)); const widgetLayout = deepClone(this.editingWidgetLayout); const id = this.editingWidgetOriginal.id; this.dashboardConfiguration.widgets[id] = widget; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index ec6af2857c..b359a16f51 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -23,11 +23,19 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { DataKey, DatasourceType, Widget, WidgetConfigMode, widgetType } from '@shared/models/widget.models'; +import { + DataKey, + DatasourceType, + Widget, + widgetTypeCanHaveTimewindow, + WidgetConfigMode, + widgetType +} from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; -import { isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; import { IAliasController } from '@core/api/widget-api.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models'; export type WidgetConfigCallbacks = DatasourceCallbacks & WidgetActionCallbacks; @@ -107,6 +115,11 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement if (this.isAdd) { this.setupDefaults(widgetConfig); } + if (widgetTypeCanHaveTimewindow(widgetConfig.widgetType) && isUndefinedOrNull(widgetConfig.config.timewindow)) { + widgetConfig.config.timewindow = initModelFromDefaultTimewindow(null, + widgetConfig.widgetType === widgetType.latest, false, this.widgetConfigComponent.timeService, + widgetConfig.widgetType === widgetType.timeseries); + } this.onConfigSet(widgetConfig); this.updateValidators(false); for (const trigger of this.validatorTriggers()) { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 4c2d3d1f43..3cc9d4c6d1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -38,6 +38,7 @@ import { TargetDevice, targetDeviceValid, Widget, + widgetTypeCanHaveTimewindow, WidgetConfigMode, widgetType } from '@shared/models/widget.models'; @@ -53,7 +54,7 @@ import { Validators } from '@angular/forms'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { deepClone, genNextLabel, isDefined, isObject } from '@app/core/utils'; +import { deepClone, genNextLabel, isDefined, isDefinedAndNotNull, isObject } from '@app/core/utils'; import { alarmFields, AlarmSearchStatus } from '@shared/models/alarm.models'; import { IAliasController } from '@core/api/widget-api.models'; import { EntityAlias } from '@shared/models/alias.models'; @@ -84,6 +85,8 @@ import { DataKeySettingsFunction } from '@home/components/widget/lib/settings/co import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { WidgetService } from '@core/http/widget.service'; +import { TimeService } from '@core/services/time.service'; +import { initModelFromDefaultTimewindow } from '@shared/models/time/time.models'; import Timeout = NodeJS.Timeout; @Component({ @@ -201,6 +204,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe constructor(protected store: Store, private utils: UtilsService, private entityService: EntityService, + public timeService: TimeService, private dialog: MatDialog, public translate: TranslateService, private fb: UntypedFormBuilder, @@ -366,16 +370,16 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.dataSettings = this.fb.group({}); this.targetDeviceSettings = this.fb.group({}); this.advancedSettings = this.fb.group({}); - if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) { + if (widgetTypeCanHaveTimewindow(this.widgetType)) { this.dataSettings.addControl('timewindowConfig', this.fb.control({ - useDashboardTimewindow: true, - displayTimewindow: true, + useDashboardTimewindow: this.widgetType !== widgetType.latest, + displayTimewindow: this.widgetType !== widgetType.latest, timewindow: null, timewindowStyle: null })); - if (this.widgetType === widgetType.alarm) { - this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null)); - } + } + if (this.widgetType === widgetType.alarm) { + this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null)); } if (this.modelValue.isDataEnabled) { if (this.widgetType !== widgetType.rpc && @@ -529,14 +533,17 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe }, {emitEvent: false} ); - if (this.widgetType === widgetType.timeseries || this.widgetType === widgetType.alarm || this.widgetType === widgetType.latest) { + if (widgetTypeCanHaveTimewindow(this.widgetType)) { const useDashboardTimewindow = isDefined(config.useDashboardTimewindow) ? - config.useDashboardTimewindow : true; + config.useDashboardTimewindow : this.widgetType !== widgetType.latest; this.dataSettings.get('timewindowConfig').patchValue({ useDashboardTimewindow, displayTimewindow: isDefined(config.displayTimewindow) ? - config.displayTimewindow : true, - timewindow: config.timewindow, + config.displayTimewindow : this.widgetType !== widgetType.latest, + timewindow: isDefinedAndNotNull(config.timewindow) + ? config.timewindow + : initModelFromDefaultTimewindow(null, this.widgetType === widgetType.latest, this.onlyHistoryTimewindow(), + this.timeService, this.widgetType === widgetType.timeseries), timewindowStyle: config.timewindowStyle }, {emitEvent: false}); } diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 00b62cfb72..45ae5986d9 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -489,6 +489,14 @@ export const targetDeviceValid = (targetDevice?: TargetDevice): boolean => ((targetDevice.type === TargetDeviceType.device && !!targetDevice.deviceId) || (targetDevice.type === TargetDeviceType.entity && !!targetDevice.entityAliasId)); +export const widgetTypeHasTimewindow = (type: widgetType): boolean => { + return type === widgetType.timeseries || type === widgetType.alarm; +} + +export const widgetTypeCanHaveTimewindow = (type: widgetType): boolean => { + return widgetTypeHasTimewindow(type) || type === widgetType.latest; +} + export const datasourcesHasAggregation = (datasources?: Array): boolean => { if (datasources) { const foundDatasource = datasources.find(datasource => { From 442c45524e30a9094f1685e404befa8d865feb09 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Tue, 5 Aug 2025 16:36:29 +0300 Subject: [PATCH 08/29] Refactoring: optimize condition for latest widget timewindow; fix types --- ui-ngx/src/app/core/services/dashboard-utils.service.ts | 7 +++---- ui-ngx/src/app/core/utils.ts | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index b743126fdc..7101aa0a2e 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -296,9 +296,8 @@ export class DashboardUtilsService { } widgetConfig.datasources = this.validateAndUpdateDatasources(widgetConfig.datasources); if (type === widgetType.latest) { - const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); - const aggregationEnabledForKeys = datasourcesHasAggregation(widgetConfig.datasources); - if (aggregationEnabledForKeys) { + if (datasourcesHasAggregation(widgetConfig.datasources)) { + const onlyHistoryTimewindow = datasourcesHasOnlyComparisonAggregation(widgetConfig.datasources); widgetConfig.timewindow = initModelFromDefaultTimewindow(widgetConfig.timewindow, true, onlyHistoryTimewindow, this.timeService, false); } @@ -356,7 +355,7 @@ export class DashboardUtilsService { return widgetConfig; } - public removeTimewindowConfigIfUnused(widgetConfig: WidgetConfig, type: widgetType) { + private removeTimewindowConfigIfUnused(widgetConfig: WidgetConfig, type: widgetType) { const widgetHasTimewindow = widgetTypeHasTimewindow(type) || (type === widgetType.latest && datasourcesHasAggregation(widgetConfig.datasources)); if (!widgetHasTimewindow || widgetConfig.useDashboardTimewindow) { delete widgetConfig.displayTimewindow; diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 9937689ba8..0c1b066887 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -197,7 +197,7 @@ export function deleteNullProperties(obj: any) { }); } -export function deleteFalseProperties(obj: any) { +export function deleteFalseProperties(obj: Record): void { if (isUndefinedOrNull(obj)) { return; } From 88ec0f91d0c1817bdd17171403a8455e4be8bd2d Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Wed, 6 Aug 2025 18:40:00 +0300 Subject: [PATCH 09/29] Timewindow: revert default values for useDashboardTimewindow and displayTimewindow --- .../home/components/widget/widget-config.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 3cc9d4c6d1..e34783f5b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -372,8 +372,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettings = this.fb.group({}); if (widgetTypeCanHaveTimewindow(this.widgetType)) { this.dataSettings.addControl('timewindowConfig', this.fb.control({ - useDashboardTimewindow: this.widgetType !== widgetType.latest, - displayTimewindow: this.widgetType !== widgetType.latest, + useDashboardTimewindow: true, + displayTimewindow: true, timewindow: null, timewindowStyle: null })); @@ -535,11 +535,11 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe ); if (widgetTypeCanHaveTimewindow(this.widgetType)) { const useDashboardTimewindow = isDefined(config.useDashboardTimewindow) ? - config.useDashboardTimewindow : this.widgetType !== widgetType.latest; + config.useDashboardTimewindow : true; this.dataSettings.get('timewindowConfig').patchValue({ useDashboardTimewindow, displayTimewindow: isDefined(config.displayTimewindow) ? - config.displayTimewindow : this.widgetType !== widgetType.latest, + config.displayTimewindow : true, timewindow: isDefinedAndNotNull(config.timewindow) ? config.timewindow : initModelFromDefaultTimewindow(null, this.widgetType === widgetType.latest, this.onlyHistoryTimewindow(), From 8e1cde1a65ec203d33cd55aede8aac847277c47b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Aug 2025 18:27:42 +0300 Subject: [PATCH 10/29] UI: Imp create new for entity autocomplete --- .../home/components/ai-model/ai-model-dialog.component.ts | 5 +++++ .../components/rule-node/external/ai-config.component.html | 2 +- .../components/rule-node/external/ai-config.component.ts | 5 +++-- .../mobile/applications/mobile-app-dialog.component.ts | 4 ++++ .../pages/mobile/bundes/mobile-bundle-dialog.component.html | 4 ++-- .../pages/mobile/bundes/mobile-bundle-dialog.component.ts | 5 +++-- .../components/entity/entity-autocomplete.component.html | 3 +++ .../components/entity/entity-autocomplete.component.ts | 6 +++--- 8 files changed, 24 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts index db5d1d7e23..9be6cfd797 100644 --- a/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/ai-model/ai-model-dialog.component.ts @@ -40,6 +40,7 @@ import { map } from 'rxjs/operators'; export interface AIModelDialogData { AIModel?: AiModel; isAdd?: boolean; + name?: string; } @Component({ @@ -110,6 +111,10 @@ export class AIModelDialogComponent extends DialogComponent { diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html index 80519cea28..5dad0cacf7 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.html @@ -29,7 +29,7 @@ labelText="rule-node-config.ai.model" (entityChanged)="onEntityChange($event)" [entityType]="entityType.AI_MODEL" - (createNew)="createModelAi('modelId')" + (createNew)="createModelAi($event, 'modelId')" formControlName="modelId"> diff --git a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts index 47313da81d..49be7df8b6 100644 --- a/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/rule-node/external/ai-config.component.ts @@ -99,12 +99,13 @@ export class AiConfigComponent extends RuleNodeConfigurationComponent { return this.translate.instant(`rule-node-config.ai.response-format-hint-${this.aiConfigForm.get('responseFormat.type').value}`); } - createModelAi(formControl: string) { + createModelAi(name: string, formControl: string) { this.dialog.open(AIModelDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - isAdd: true + isAdd: true, + name } }).afterClosed() .subscribe((model) => { diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts index f5a9dc4a5a..423979b4bc 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-dialog.component.ts @@ -29,6 +29,7 @@ import { MobileAppService } from '@core/http/mobile-app.service'; export interface MobileAppDialogData { platformType: PlatformType; + name?: string } @Component({ @@ -55,6 +56,9 @@ export class MobileAppDialogComponent extends DialogComponent { this.mobileAppComponent.entityForm.markAsDirty(); + if (this.data.name) { + this.mobileAppComponent.entityForm.get('title').patchValue(this.data.name, {emitEvent: false}); + } this.mobileAppComponent.entityForm.patchValue({platformType: this.data.platformType}); this.mobileAppComponent.entityForm.get('platformType').disable({emitEvent: false}); this.mobileAppComponent.isEdit = true; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html index 6d4bd01d1b..87f2481565 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.html @@ -58,7 +58,7 @@ labelText="mobile.android-application" [entityType]="entityType.MOBILE_APP" [entitySubtype]="platformType.ANDROID" - (createNew)="createApplication('androidAppId', platformType.ANDROID)" + (createNew)="createApplication($event, 'androidAppId', platformType.ANDROID)" formControlName="androidAppId"> diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts index 3e31401703..a866685282 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-dialog.component.ts @@ -148,12 +148,13 @@ export class MobileBundleDialogComponent extends DialogComponent(MobileAppDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - platformType + platformType, + name } }).afterClosed() .subscribe((app) => { diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html index 92a316efe9..37882a4879 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html @@ -71,6 +71,9 @@ {{ noEntitiesMatchingText | translate: {entity: searchText} }} + + entity.create-new-key + diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts index 8cb785c6f1..9137a3b2d7 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.ts @@ -142,7 +142,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit entityChanged = new EventEmitter>(); @Output() - createNew = new EventEmitter(); + createNew = new EventEmitter(); @ViewChild('entityInput', {static: true}) entityInput: ElementRef; @@ -451,9 +451,9 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit return entityType; } - createNewEntity($event: Event) { + createNewEntity($event: Event, searchText?: string) { $event.stopPropagation(); - this.createNew.emit(); + this.createNew.emit(searchText); } get showEntityLink(): boolean { From 09d08216a77824afe36592b0e9840f79c6a6b118 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 11 Aug 2025 09:15:44 +0300 Subject: [PATCH 11/29] UI: Ref create new link --- .../components/entity/entity-autocomplete.component.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html index 37882a4879..db7300530e 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html @@ -71,9 +71,11 @@ {{ noEntitiesMatchingText | translate: {entity: searchText} }} - - entity.create-new-key - + @if (allowCreateNew) { + + entity.create-new-key + + } From 13c0926e67f8435cb412b4c27921482f9edefc2c Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Mon, 11 Aug 2025 12:18:27 +0300 Subject: [PATCH 12/29] Fix some MSA tests not running --- msa/black-box-tests/src/test/resources/connectivity.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/msa/black-box-tests/src/test/resources/connectivity.xml b/msa/black-box-tests/src/test/resources/connectivity.xml index 425fbd67eb..a366522ae0 100644 --- a/msa/black-box-tests/src/test/resources/connectivity.xml +++ b/msa/black-box-tests/src/test/resources/connectivity.xml @@ -1,7 +1,7 @@